diff -Nru zope3-3.4.0/COPYING zope3-3.5~bzr18/COPYING --- zope3-3.4.0/COPYING 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/COPYING 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +See: + + - the copyright notice in: COPYRIGHT.txt + + - The Zope Public License in ZopePublicLicense.txt + + - Other licenses in LICENSES.txt. diff -Nru zope3-3.4.0/COPYRIGHT.txt zope3-3.5~bzr18/COPYRIGHT.txt --- zope3-3.4.0/COPYRIGHT.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/COPYRIGHT.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,9 @@ +Copyright (c) 2004 Zope Corporation 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. diff -Nru zope3-3.4.0/debian/changelog zope3-3.5~bzr18/debian/changelog --- zope3-3.4.0/debian/changelog 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/changelog 2011-09-15 20:24:09.000000000 +0000 @@ -1,337 +1,11 @@ -zope3 (3.4.0-0ubuntu3) jaunty; urgency=low +zope3 (3.5~bzr18-1landscape1) lucid; urgency=low - * fix undeclared file conflicts that cause python-central to - error in postinst (LP: #347939) + * Rebuild without transaction. - -- Michael Vogt Wed, 25 Mar 2009 17:59:15 +0100 + -- Thomas Hervé Thu, 15 Sep 2011 16:23:35 -0400 -zope3 (3.4.0-0ubuntu2) jaunty; urgency=low +zope3 (3.5~bzr18-0landscape1) lucid; urgency=low - * Regenerate the python-zopeinterface pkgconfig file. LP: #332516. + * Initial release. - -- Matthias Klose Sat, 21 Feb 2009 17:55:30 +0100 - -zope3 (3.4.0-0ubuntu1) jaunty; urgency=low - - * New upstream version, final release. - * Merge changes from 3.3.1-6 and 3.3.1-7. - * Build using python2.5. - - -- Matthias Klose Sat, 21 Feb 2009 12:59:05 +0100 - -zope3 (3.4.0~c1-1) experimental; urgency=low - - * New upstream release (candidate 1). - * Removed patches applied upstream: debian/patches/twisted-bug.dpatch, - debian/patches/zope.app.twisted-bbb.dpatch. - - -- Fabio Tranchitella Wed, 19 Mar 2008 10:55:52 +0100 - -zope3 (3.3.1-7) unstable; urgency=low - - * debian/rules: hardcoded version number, we can't really relay on the - name of the source directory. (Closes: #483333) - - -- Fabio Tranchitella Wed, 28 May 2008 13:39:43 +0200 - -zope3 (3.3.1-6) unstable; urgency=low - - * Merge from Ubuntu: - - Set the egg version for zope.interface to 3.3.1. LP: #185418. - - Drop dependency on python-xml. Closes: #468621. - - -- Matthias Klose Tue, 22 Apr 2008 02:01:15 +0200 - -zope3 (3.3.1-5) unstable; urgency=low - - * debian/control: added dependency on ${shlibs:Depends} for - python-zopeinterface; updated Standards-Version to 3.7.3, no changes - required. - - -- Fabio Tranchitella Fri, 18 Jan 2008 08:02:22 +0100 - -zope3 (3.3.1-4) unstable; urgency=low - - * debian/zopeZVER.logrotate.in: use the original mode, user and group while - rotating the logs. (Closes: #453397) - - -- Fabio Tranchitella Thu, 29 Nov 2007 12:50:06 +0100 - -zope3 (3.3.1-3) unstable; urgency=low - - * Merge from Ubuntu: - - Build a zope3-dbg package. - - -- Matthias Klose Fri, 8 Jun 2007 00:58:35 +0200 - -zope3 (3.3.1-2) unstable; urgency=low - - * Build a python-zopeinterface-dbg package. - * Bump debhelper compatibility to v5. - - -- Matthias Klose Sun, 20 May 2007 19:36:20 +0200 - -zope3 (3.3.1-1) unstable; urgency=low - - * New upstream release. - * debian/patches/zope.publisher.dpatch: removed, applyed upstream. - - -- Fabio Tranchitella Mon, 19 Feb 2007 08:48:58 +0100 - -zope3 (3.3.0-6) unstable; urgency=medium - - * debian/control: build-depends on zope-debhelper >= 0.3.6, which fixes a - few important bugs in the maintainer scripts for the zope3-sandbox - package. - - -- Fabio Tranchitella Thu, 18 Jan 2007 10:48:14 +0100 - -zope3 (3.3.0-5) unstable; urgency=medium - - * Fix Section for python-zopeinterface according to ftpmasters' choice. - * Depend on zope-debhelper (>= 0.3.3) to fix a bashism in the generated - config script for zope3-sandbox. - * Add XS-Vcs-Svn field in control file. - - -- Jérémy Bobbio Sat, 18 Nov 2006 11:44:16 +0100 - -zope3 (3.3.0-4) unstable; urgency=low - - * Apply patch as debian/patches/zope.publisher.dpatch from - http://mail.zope.org/pipermail/zope3-checkins/2006-October/028609.html. - (Closes: #395151) - - -- Brian Sutherland Wed, 25 Oct 2006 10:17:07 +0200 - -zope3 (3.3.0-3) unstable; urgency=low - - * Rebuild with updated debhelper. Closes: #391538. - - -- Matthias Klose Sat, 7 Oct 2006 15:51:21 +0200 - -zope3 (3.3.0-2) unstable; urgency=medium - - * debian/patches/zope.app.twisted-bbb: added a backward compatible - package to allow old zope3 instances to be started with zope3 3.3.0. - (Closes: #390321) - * debian/zopeZVER.NEWS.Debian: added a brief description of this - problem. - * Single patch change, urgency to medium to easy the transition - to testing. - - -- Fabio Tranchitella Fri, 6 Oct 2006 09:59:07 +0200 - -zope3 (3.3.0-1) unstable; urgency=low - - * New upstream release. - - -- Fabio Tranchitella Fri, 29 Sep 2006 12:07:34 +0000 - -zope3 (3.2.1-5) unstable; urgency=medium - - * debian/patches/twisted-bug.dpatch: fixes a buggy override for a twisted - method. Without this patch, zope3 doesn't work at all: set urgency - to medium. (Closes: #378030) - * debian/patches/malformed-realm.dpatch: fixes malformed realms. - (Closes: #357461) - - -- Fabio Tranchitella Thu, 17 Aug 2006 10:55:03 +0200 - -zope3 (3.2.1-4) unstable; urgency=medium - - * zope3: Depend on non-versioned python modules. - - -- Matthias Klose Sun, 2 Jul 2006 14:18:38 +0000 - -zope3 (3.2.1-3) unstable; urgency=low - - * Build zope-interface in one package. - - -- Matthias Klose Wed, 14 Jun 2006 18:45:17 +0200 - -zope3 (3.2.1-2) unstable; urgency=low - - * debian/control: added depends on python2.4-xml. (Closes: #362214) - * debian/control: tighten dependency on python2.4-mechanize. - (Closes: #362221) - - -- Fabio Tranchitella Thu, 13 Apr 2006 11:22:08 +0200 - -zope3 (3.2.1-1) unstable; urgency=low - - * New upstream release. - * debian/rules: Removed temporary patch applied with 3.2.0-4. - * debian/rules: build python2.[34]-zopeinterface binary packages. - - -- Fabio Tranchitella Thu, 30 Mar 2006 10:35:14 +0000 - -zope3 (3.2.0-4) unstable; urgency=low - - * debian/rules: Apply temporary patch to fix munging of sys.modules - in zope.testbrowser. (Closes: #351980) - * debian/control: Add myself to uploaders. - * debian/control: Tighten dependency on python2.4-twisted-web2 (M. Klose). - - -- Brian Sutherland Sun, 19 Feb 2006 19:28:54 +0100 - -zope3 (3.2.0-3) unstable; urgency=low - - * Remove twisted modules from zope3, depend on python2.4-twisted-conch, - python2.4-twisted-web2 (based on feedback from #zope3-dev). - - -- Matthias Klose Mon, 13 Feb 2006 18:49:40 +0100 - -zope3 (3.2.0-2) unstable; urgency=low - - * debian/control: zope3 should depend on python2.4-tz (>= 2005r). - - -- Fabio Tranchitella Fri, 3 Feb 2006 10:41:34 +0000 - -zope3 (3.2.0-1) unstable; urgency=low - - * New upstream release. (Closes: #343445) - * debian/zopeZVER.init.in: fixed a typo which prevents zeo instances - to be started by the init script. (Closes: #341529) - * README.debian for zope3: added a note about pyskel.py and test.py - scripts. (Closes: #337354) - - -- Fabio Tranchitella Sun, 15 Jan 2006 17:48:05 +0000 - -zope3 (3.1.0-3) unstable; urgency=low - - * debian/control: depends on lsb-base. (Closes: #334620) - - -- Fabio Tranchitella Thu, 20 Oct 2005 14:02:55 +0000 - -zope3 (3.1.0-2) unstable; urgency=low - - * Fix installation of README.txt, in case we build our own - python2.x-zopeinterface packages (Brian Sutherland). Closes: #332845. - - -- Matthias Klose Sun, 9 Oct 2005 16:12:37 +0200 - -zope3 (3.1.0-1) unstable; urgency=low - - * New upstream version, final release. - - -- Matthias Klose Tue, 4 Oct 2005 13:55:25 +0000 - -zope3 (3.0.93-1) unstable; urgency=low - - * New upstream candidate release (Zope 3.1.0c2) - * debian/rules: different behaviour between Debian and Ubuntu about - python*-zopeinterface binary packages; building for Debian won't - create them, while Ubuntu builders will do. - * debian/control: upgraded dependency on python-tz (>= 2005k) - (Closes: #317742) - - -- Fabio Tranchitella Thu, 15 Sep 2005 14:47:44 +0000 - -zope3 (3.0.92-4) experimental; urgency=low - - * debian/control: build-depends on lsb-release, and use it in debian/rules - to guess the distribution (Debian or Ubuntu). - - -- Fabio Tranchitella Tue, 23 Aug 2005 14:19:53 +0000 - -zope3 (3.0.92-3) experimental; urgency=low - - * Rebuild with zope-debhelper 0.3.2.7. - * debian/patches/deb-zopeconf.dpatch: added --service-port argument to - mkzopeinstance. - - -- Fabio Tranchitella Thu, 18 Aug 2005 13:10:29 +0000 - -zope3 (3.0.92-2) experimental; urgency=low - - * debian/patches/deb-zopeconf.dpatch: symlink /Products to - /lib/python. This is the simplest way to have dzhandle - working for Zope3. - - -- Fabio Tranchitella Wed, 17 Aug 2005 12:19:27 +0000 - -zope3 (3.0.92-1) experimental; urgency=low - - * Zope-3.1 candidate1 release (Zope3-3.1.0c1.tgz) - * debian/rules: added {lintian,linda}-overrides - * Applied patch from Brian Sutherland to use python2.4-tz instead of - shipping it with zope3-lib. (Closes: #317742) - * debian/control: set Standards-Version to 3.6.2 (no changes) - * debian/control: added myself as uploader - * debian/control: renamed zope3-lib into zope3 (Matthias, I know what - you are thinking while reading this entry, but I think it was necessary) - - -- Fabio Tranchitella Sat, 13 Aug 2005 16:41:56 +0000 - -zope3 (3.0.91-1) unstable; urgency=low - - * Zope-3.1 beta1 release. - * Set maintainer to "Debian Zope Team". - * Provide a zope3-lib package only, packages like schooltool and - schoolbell only need this one. - * Explicitely build using python 2.4. - * Disable building the zopeinterface packages. Conflict with - python2.4-zopeinterface, provide it. - * Run the testsuite in the build, ignore the exit code. - * Upstream ships with corrected file permissions, remove the handling - from the rules file. - * Disable building the zope3-sandbox package until dzhandle is - updated and available in unstable. - - -- Matthias Klose Thu, 23 Jun 2005 12:33:36 +0200 - -zopex3 (3.0.0-6) hoary; urgency=low - - * Add __init__.py to zope package in zopeX.Y-interface packages. - - -- Matthias Klose Sun, 13 Feb 2005 21:27:09 +0100 - -zopex3 (3.0.0-4) experimental; urgency=low - - * Upload to experimental. - - -- Matthias Klose Wed, 2 Feb 2005 20:14:50 +0100 - -zopex3 (3.0.0-3) hoary; urgency=low - - * Rename python*-zope-interface packages to python*-zopeinterface, so that - the upstream name can be found in the package name. - * Rename zopex3-lib to zope3-lib. - * Added fixes/patches from Wichert Akkerman : - - Move README.txt to /usr/share/doc for zope-interface packages - - Fix typo in dependencies: it is zopex3-lib, not zope3-lib - - Do not remove tests: they are useful when creating tests for other - zope packages. Also prevents accidenta.ly delete of zope.app.tests - which is quite essential - * zopex3-lib: Depend on python-zopeinterface. - * Remove docutils from zope3-lib package, depend on python-docutils. - - -- Matthias Klose Tue, 11 Jan 2005 08:30:24 +0100 - -zopex3 (3.0.0-2) hoary; urgency=low - - * Fix zopex3-sandbox dependency on the -lib package. - - -- Matthias Klose Sat, 4 Dec 2004 13:52:25 +0100 - -zopex3 (3.0.0-1) hoary; urgency=low - - * ZopeX3-3.0.0 release. - * Renamed package to zopex3. - * Build zope-interface packages python-zope-interface and pythonX.Y-zope-interface. - - -- Matthias Klose Tue, 9 Nov 2004 18:07:25 +0100 - -zope3 (2.90c3-1) hoary; urgency=low - - * New upstream candidate (ZopeX3-3.0.0c3). - - -- Matthias Klose Thu, 28 Oct 2004 00:06:48 +0200 - -zope3 (2.90b3-1) unstable; urgency=low - - * Initial Release (ZopeX3-3.0.0b3). - * EXPERIMENTAL PACKAGES. NO UPGRADE PATH FROM EARLIER VERSIONS OR - TO UPCOMING VERSIONS PROVIDED UNTIL THE FINAL 3.0 RELEASE. - - -- Matthias Klose Mon, 23 Aug 2004 18:01:33 +0200 + -- Free Ekanayaka Fri, 23 Jul 2010 14:25:08 +0200 diff -Nru zope3-3.4.0/debian/compat zope3-3.5~bzr18/debian/compat --- zope3-3.4.0/debian/compat 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/compat 2010-07-23 12:23:50.000000000 +0000 @@ -1 +1 @@ -5 +7 diff -Nru zope3-3.4.0/debian/control zope3-3.5~bzr18/debian/control --- zope3-3.4.0/debian/control 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/control 2010-07-28 08:41:41.000000000 +0000 @@ -1,95 +1,18 @@ Source: zope3 -Section: web -Priority: optional -Maintainer: Debian/Ubuntu Zope Team -Uploaders: Matthias Klose , Fabio Tranchitella , Brian Sutherland -Build-Depends: python2.5-dev (>= 2.5.4-1), python, python-all-dev (>= 2.3.5-11), python-all-dbg, debhelper (>= 5.0.39), python-central (>= 0.6.11), zope-debhelper (>= 0.3.6), lsb-release, dpatch -Standards-Version: 3.8.0 -XS-Python-Version: all -XS-Vcs-Svn: svn://svn.debian.org/pkg-zope/zope3/trunk - -Package: zope3 Section: python -Architecture: any -Pre-Depends: zope-common -Depends: python2.5 (>= 2.5.2), python-zopeinterface (= ${binary:Version}), ${shlibs:Depends}, python-docutils (>= 0.3.7), python-tz (>= 2007f-1), python-mechanize (>= 0.1.2b-1), python-clientform (>= 0.2.2-2), python-twisted-conch (>= 1:0.8), python-twisted-web2 (>= 0.2.0+svn20070403), lsb-base -Conflicts: zopex3-lib, zope3-lib -Replaces: zopex3-lib, zope3-lib -Provides: zope3-lib -Suggests: zope3-dbg -XB-Python-Version: 2.5 -Description: Open Source Web Application Server (Libraries) - Zope is an open source web application server primarily written in - the Python programming language. It features a transactional object - database which can store not only content and custom data, but also - dynamic HTML templates, scripts, a search engine, and relational - database (RDBMS) connections and code. - . - It features a strong through-the-web development model, allowing you - to update your web site from anywhere in the world. To allow for - this, Zope also features a tightly integrated security model. Built - around the concept of "safe delegation of control", Zope's security - architecture also allows you to turn control over parts of a web site - to other organizations or individuals. - . - This package contains the framework and libraries needed to run your - own Zope3 instance. - -Package: zope3-dbg Priority: extra -Section: python -Architecture: any -Pre-Depends: zope-common -Depends: zope3 (= ${binary:Version}), python-dbg, python-zopeinterface-dbg, python-crypto-dbg, python-twisted-bin-dbg, ${shlibs:Depends} -Description: Open Source Web Application Server (debug extensions) - Zope is an open source web application server primarily written in - the Python programming language. It features a transactional object - database which can store not only content and custom data, but also - dynamic HTML templates, scripts, a search engine, and relational - database (RDBMS) connections and code. - . - This package contains the Python extensions built for the Python debug - interpreter. - -Package: zope3-doc -Section: doc -Architecture: all -Suggests: zope3-sandbox -Description: Documentation for Zope3 - Documentation for the Zope3 Web Application Server. +Build-Depends: cdbs (>= 0.4.43), + debhelper (>= 7), + python-all-dev, + python-support, + python-setuptools +Maintainer: Free Ekanayaka +Standards-Version: 3.8.4 +XS-Python-Version: current -Package: zope3-sandbox -Section: web -Architecture: all -Depends: ${zope:Depends}, ${misc:Depends} -Recommends: zope3-doc (>= ${source:Version}) -Description: sandbox instance for the zope3 web application server - Package which creates a "sandbox" instance, getting all Zope - products and packages available which are installed as Debian packages. - . - The sandbox is usable for development and testing. For production - please setup your own Zope 3 instance using dzhandle utility. - -Package: python-zopeinterface -Section: python +Package: python-zope3 Architecture: any -Depends: ${python:Depends}, ${shlibs:Depends} -Conflicts: python-zope-interface, python2.3-zopeinterface, python2.4-zopeinterface -Replaces: python-zope-interface, python2.3-zopeinterface, python2.4-zopeinterface -Provides: python-zope-interface, ${python:Provides} -Suggests: python-zopeinterface-dbg XB-Python-Version: ${python:Versions} -Description: The implementation of interface definitions for Zope 3 - Zope interfaces are objects that specify (document) the external behavior - of objects that "provide" them. - -Package: python-zopeinterface-dbg -Priority: extra -Section: python -Architecture: any -Depends: python-zopeinterface (= ${binary:Version}), python-dbg, ${shlibs:Depends} -Description: The implementation of interface definitions for Zope 3 (debug extension) - Zope interfaces are objects that specify (document) the external behavior - of objects that "provide" them. - . - This package contains the extension built for the Python debug interpreter. +Depends: ${misc:Depends}, ${python:Depends} +Description: Zope 3 web framework. + This package contains all Zope 3 modules needed by the Landscape server. diff -Nru zope3-3.4.0/debian/copyright zope3-3.5~bzr18/debian/copyright --- zope3-3.4.0/debian/copyright 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/copyright 2010-07-27 11:01:36.000000000 +0000 @@ -1,141 +1,8 @@ -This package was debianized by Matthias Klose based -on feedback by Chris McDonough in August 2004. +Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat +Upstream-Name: zope3 -It was downloaded from http://dev.zope.org/Zope3/ - -Upstream Author: Zope Corporation - -Copyright: - -Copyright (c) 2001, 2002, 2003, 2004 Zope Corporation 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 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. - ----------------------------------------------------------------------- - -The XML files in locales directory in the zope.i18n package are -made available under the ICU License: - - ICU License - ICU 1.8.1 and later - - COPYRIGHT AND PERMISSION NOTICE - - Copyright (c) 1995-2002 International Business Machines Corporation - and others - All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, and/or sell copies of the Software, and to permit persons - to whom the Software is furnished to do so, provided that the above - copyright notice(s) and this permission notice appear in all copies of - the Software and that both the above copyright notice(s) and this - permission notice appear in supporting documentation. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - Except as contained in this notice, the name of a copyright holder - shall not be used in advertising or otherwise to promote the sale, use - or other dealings in this Software without prior written authorization - of the copyright holder. - - ----------------------------------------------------------------------------- - - All trademarks and registered trademarks mentioned herein are the - property of their respective owners. - ----------------------------------------------------------------------- - -The docutils package was implemented and placed in the public domain by -David Goodger and others. - -We are rereleasing it under the terms of the Zope Public License 2.0. - -Exceptions to this are the following files within the docutils package: - - roman.py, by Mark Pilgrim: - - This program is free software; you can redistribute it and/or modify - it under the terms of the Python 2.1.1 license, available at - http://www.python.org/2.1.1/license.html - ----------------------------------------------------------------------- - -The zope.testing.doctest module is a copy of the doctest module from -the Python 2.4 standard library. It if the property of the Python -Software Foundation (PSF) and is covered by the PSF license agreement -for Python 2.4. We will no-longer distribute this module with Zope at -some point on the future. +Files: * +Copyright: (C) 2009 Canonical Ltd. +License: LGPL v3 + The full text of the LGPL is distributed in + /usr/share/common-licenses/LGPL-3 on Debian systems. diff -Nru zope3-3.4.0/debian/docs zope3-3.5~bzr18/debian/docs --- zope3-3.4.0/debian/docs 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/debian/docs 2010-07-27 11:01:46.000000000 +0000 @@ -0,0 +1 @@ +COPYRIGHT.txt diff -Nru zope3-3.4.0/debian/patches/00list zope3-3.5~bzr18/debian/patches/00list --- zope3-3.4.0/debian/patches/00list 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/patches/00list 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -deb-zopeconf diff -Nru zope3-3.4.0/debian/patches/deb-zopeconf.dpatch zope3-3.5~bzr18/debian/patches/deb-zopeconf.dpatch --- zope3-3.4.0/debian/patches/deb-zopeconf.dpatch 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/patches/deb-zopeconf.dpatch 1970-01-01 00:00:00.000000000 +0000 @@ -1,215 +0,0 @@ -#! /bin/sh -e -## -## DP: Debian specific configuration; -## DP: Author: Fabio Tranchitella - -. $(dirname $0)/DPATCH - -@DPATCH@ -diff -urN zope3-3.4.01/z/Dependencies/zope.app.server-Zope-3.4.0/zope.app.server/mkzopeinstance.py zope3-3.4.0.debian/z/Dependencies/zope.app.server-Zope-3.4.0/zope.app.server/mkzopeinstance.py ---- zope3-3.4.0/z/Dependencies/zope.app.server-Zope-3.4.0/zope.app.server/mkzopeinstance.py 2008-01-31 23:38:32.000000000 +0100 -+++ zope3-3.4.0.debian/z/Dependencies/zope.app.server-Zope-3.4.0/zope.app.server/mkzopeinstance.py 2008-03-19 10:05:54.000000000 +0100 -@@ -117,8 +117,15 @@ - - options.password = password_manager.encodePassword(options.password) - -+ import pwd, grp -+ uid = pwd.getpwnam(options.srvuser.split(":")[0]) -+ if uid: uid = uid[2] -+ gid = grp.getgrnam(options.srvuser.split(":")[1]) -+ if gid: gid = gid[2] -+ os.chown(options.destination, uid, gid) -+ - # now create the instance! -- self.copy_skeleton() -+ self.copy_skeleton(uid, gid) - if options.add_package_includes: - # need to copy ZCML differently since it's not in the skeleton: - import __main__ -@@ -126,6 +133,18 @@ - os.path.dirname(os.path.realpath(__main__.__file__))) - shutil.copy2(os.path.join(swhome, "securitypolicy.zcml"), - os.path.join(options.destination, "etc")) -+ -+ os.symlink(os.path.join(options.destination, 'lib/python'), \ -+ os.path.join(options.destination, 'Products')) -+ -+ if options.layout == 'fhs': -+ self.movedir(os.path.join(options.destination, 'etc'), -+ os.path.join('/etc/zope3', os.path.basename(options.destination)), -+ uid, gid) -+ self.movedir(os.path.join(options.destination, 'log'), -+ os.path.join('/var/log/zope3', os.path.basename(options.destination)), -+ uid, gid) -+ - return 0 - - def get_skeltarget(self): -@@ -202,7 +221,7 @@ - self.need_blank_line = False - print message - -- def copy_skeleton(self): -+ def copy_skeleton(self, uid, gid): - options = self.options - # TODO we should be able to compute the script - script = os.path.abspath(sys.argv[0]) -@@ -219,17 +238,19 @@ - ("<>", options.destination), - ("<>", zope_home), - ("<>", software_home), -+ ("<>", options.srvuser.split(":")[0]), -+ ("<>", options.srvport), - ] -- self.copytree(self.options.skeleton, self.options.destination) -+ self.copytree(self.options.skeleton, self.options.destination, uid, gid) - if options.zserver: - self.copytree( - os.path.join(os.path.dirname(zope.app.server.__file__), - 'zopeskel'), -- self.options.destination, -+ self.options.destination, uid, gid, - ) - - -- def copytree(self, src, dst): -+ def copytree(self, src, dst, uid, gid): - # Similar to shutil.copytree(), but doesn't care about - # symlinks, doesn't collect errors, and uses self.copyfile() - # instead of shutil.copy2(). -@@ -243,12 +264,13 @@ - if os.path.isdir(srcname): - if not os.path.exists(dstname): - os.mkdir(dstname) -- self.copytree(srcname, dstname) -+ os.chown(dstname, uid, gid) -+ self.copytree(srcname, dstname, uid, gid) - else: -- self.copyfile(srcname, dstname) -+ self.copyfile(srcname, dstname, uid, gid) - # There shouldn't be any need to deal with devices, sockets etc. - -- def copyfile(self, src, dst): -+ def copyfile(self, src, dst, uid, gid): - if dst.endswith(".in"): - dst = dst[:-3] - text = open(src, "rU").read() -@@ -276,6 +298,40 @@ - shutil.copystat(src, dst) - else: - shutil.copy2(src, dst) -+ os.chown(dst, uid, gid) -+ -+ def movedir(self, sourcedir, targetdir, uid, gid): -+ try: -+ os.makedirs(os.path.dirname(targetdir)) -+ if uid is not None: -+ os.chown(os.path.dirname(targetdir), uid, gid) -+ except: -+ pass -+ if not os.path.isdir(targetdir): -+ shutil.move(sourcedir, targetdir) -+ elif os.path.islink(sourcedir): -+ return -+ else: -+ import glob -+ for src in glob.glob(sourcedir + '/*') + glob.glob(targetdir + '/.*'): -+ base = os.path.basename(src) -+ target = os.path.join(targetdir, base) -+ if os.path.exists(target): -+ backup = target + '.old' -+ if os.path.exists(backup): -+ if os.path.isdir(backup): -+ shutil.rmtree(backup, ignore_errors=True) -+ else: -+ os.unlink(backup) -+ os.rename(target, backup) -+ shutil.move(src, target) -+ shutil.rmtree(sourcedir, ignore_errors=True) -+ if uid is not None: -+ os.chown(targetdir, uid, gid) -+ for root, dirs, files in os.walk(targetdir): -+ for name in files + dirs: -+ os.chown(os.path.join(root, name), uid, gid) -+ os.symlink(targetdir, sourcedir) - - SKELTARGET_MESSAGE = """\ - Please choose a directory in which you'd like to install Zope -@@ -314,7 +370,13 @@ - help=("set the name of the password manager" - " to be used for encode the password")) - p.add_option("-u", "--user", dest="username", metavar="USER:PASSWORD", -- help="set the user name and password of the initial user") -+ help="set the user name and password of the initial user") -+ p.add_option("-l", "--layout", dest="layout", metavar="LAYOUT", default='zope', -+ help="layout to use while copying the skeleton files (`fhs' or `zope')") -+ p.add_option("", "--service-user", dest="srvuser", metavar="USER:GROUP", default='zope:zope', -+ help="system user to be used to run the instance (default is zope:zope)") -+ p.add_option("", "--service-port", dest="srvport", metavar="PORT", default='9673', -+ help="HTTP port used to run this instance") - p.add_option("--non-interactive", dest="interactive", action="store_false", - default=True, help="do no interactive prompting") - p.add_option("--zserver", dest="zserver", action="store_true", -@@ -343,4 +405,6 @@ - options.password = None - if options.username and ":" in options.username: - options.username, options.password = options.username.split(":", 1) -+ if options.srvuser.count(":") != 1: -+ p.error("service user must be specified as user:group") - return options -diff -urN zope3-3.4.0~c1/z/Zope/zopeskel/etc/zdaemon.conf.in zope3-3.4.0~c1.debian/z/Zope/zopeskel/etc/zdaemon.conf.in ---- zope3-3.4.0~c1/z/Zope/zopeskel/etc/zdaemon.conf.in 2006-01-11 17:42:09.000000000 +0100 -+++ zope3-3.4.0~c1.debian/z/Zope/zopeskel/etc/zdaemon.conf.in 2008-03-19 10:53:18.000000000 +0100 -@@ -1,4 +1,5 @@ - %define INSTANCE <> -+define ZOPE_USER <> - %define LOGDIR $INSTANCE/log - %define DATADIR $INSTANCE/var - -@@ -34,6 +35,9 @@ - # - # user zope1 - -+ # The user to be used to run this instance -+ user $ZOPE_USER -+ - - - -@@ -41,9 +45,13 @@ - # output (STDOUT). The "path" setting can be a relative or absolute - # filesystem path or the tokens STDOUT or STDERR. - -- -- path $LOGDIR/z3.log -- -+ # This doesn't work: the log will be opened by zdrun.py before the -+ # process spawn, so the owner will be root (0755). The forked -+ # process will SUID to a different user and so won't be able to -+ # write on z3.log and will hang. Let's comment out these bad rows! -+ # -+ # path $LOGDIR/z3.log -+ # - - - path STDOUT -diff -urN zope3-3.4.0~c1/z/Zope/zopeskel/etc/zope.conf.in zope3-3.4.0~c1.debian/z/Zope/zopeskel/etc/zope.conf.in ---- zope3-3.4.0~c1/z/Zope/zopeskel/etc/zope.conf.in 2006-12-20 20:57:24.000000000 +0100 -+++ zope3-3.4.0~c1.debian/z/Zope/zopeskel/etc/zope.conf.in 2008-03-19 10:54:55.000000000 +0100 -@@ -1,6 +1,7 @@ - # This is the configuration file for the Zope Application Server. - - %define INSTANCE <> -+%define HTTPPORT <> - - %define CONFDIR $INSTANCE/etc - %define DATADIR $INSTANCE/var -@@ -24,7 +25,7 @@ - # - - type HTTP -- address 8080 -+ address $HTTPPORT - - - # Ready to go HTTPS server. You just need to make sure OpenSSL is installed. diff -Nru zope3-3.4.0/debian/patches/DPATCH zope3-3.5~bzr18/debian/patches/DPATCH --- zope3-3.4.0/debian/patches/DPATCH 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/patches/DPATCH 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts -patch_opts="${patch_opts:--f --no-backup-if-mismatch}" - -if [ $# -ne 1 ]; then - echo >&2 "`basename $0`: script expects -patch|-unpatch as argument" - exit 1 -fi -case "$1" in - -patch) patch $patch_opts -p1 < $0;; - -unpatch) patch $patch_opts -p1 -R < $0;; - *) - echo >&2 "`basename $0`: script expects -patch|-unpatch as argument" - exit 1;; -esac - -exit 0 diff -Nru zope3-3.4.0/debian/rules zope3-3.5~bzr18/debian/rules --- zope3-3.4.0/debian/rules 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/rules 2011-09-15 20:24:07.000000000 +0000 @@ -1,313 +1,20 @@ -#! /usr/bin/make -f -# -*- makefile -*- +#!/usr/bin/make -f -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 +DEST=opt/canonical/zope3 -# This has to be exported to make some magic below work. -export DH_OPTIONS +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/rules/langpack.mk -# brace expansion used in this Makefile -export SHELL=/bin/bash - -DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) - -version = 3.4.0 -zbase = Zope-3.4.0 - -include /usr/share/pycentral-data/pycentral.mk - -PV := 2.5 -ZVER := 3 -PYVER := python$(PV) -PYVERS := $(shell pyversions -vs) -PYTHON = /usr/bin/$(PYVER) -d = debian/tmp -libdir = usr/lib/zope$(ZVER) -pylibdir = usr/lib/$(PYVER)/$(call sitedir, $(PV)) - -distribution := $(shell lsb_release -is) -with_zif := yes - -p_lib = zope$(ZVER) -p_libdbg = zope$(ZVER)-dbg -p_doc = zope$(ZVER)-doc - -#p_bin = zope3-scripts -#p_add = zope3-addons -#p_dutil = zope3-docutils - -p_zif = python-zopeinterface -p_zifdbg= python-zopeinterface-dbg - -d_lib = debian/$(p_lib) -d_libdbg= debian/$(p_libdbg) -d_bin = debian/$(p_bin) -d_add = debian/$(p_add) -d_dutil = debian/$(p_dutil) -d_doc = debian/$(p_doc) - -d_zif = debian/$(p_zif) -d_zifdbg= debian/$(p_zifdbg) - -unpack: unpack-stamp -unpack-stamp: - tar xfz $(zbase).tgz - mv $(zbase) z - touch unpack-stamp - -configure: config-stamp -config-stamp: unpack-stamp patch-stamp - dh_testdir - cp -a z zdbg - cd z && ./configure \ - --prefix=/usr \ - --with-python=$(PYTHON) \ - --force - cd zdbg && ./configure \ - --prefix=/usr \ - --with-python=$(PYTHON)-dbg \ - --force - touch config-stamp - -build: build-arch build-indep check - -build-arch: build-arch-stamp build-dbg-stamp -build-arch-stamp: config-stamp - cd z && $(PYTHON) install.py build -ifeq ($(with_zif),yes) - set -e; \ - for v in $(PYVERS); do \ - pushd z/Dependencies/zope.interface-$(zbase); \ - /usr/bin/python$$v setup.py build; \ - /usr/bin/python$$v-dbg setup.py build; \ - popd; \ - done -endif - touch build-arch-stamp - -build-dbg: build-dbg-stamp -build-dbg-stamp: config-stamp - cd zdbg && $(PYTHON)-dbg install.py build - touch build-dbg-stamp - -build-indep: build-indep-stamp -build-indep-stamp: - touch build-indep-stamp - -check: build check-stamp -check-stamp: build-arch-stamp - # -cd z && PYTHONPATH=$(shell echo `pwd`/z/build/lib*$(PV)) $(PYTHON) test.py -v - touch check-stamp - -clean: unpatch - dh_testdir - rm -f *-stamp - rm -rf build z zdbg $(zbase) - for f in debian/*.in; do \ - generated=`echo $$f | sed 's,.in$$,,;s,ZVER,$(ZVER),'`; \ - rm -f $$generated $$generated.tmp; \ - done - rm -rf debian/patched - dh_clean - -install: install-indep install-arch -install-indep: build-indep - -install-arch: build-arch build-dbg install-prereq $(PYVERS:%=install-zif-python%) install-zope-dbg - # Remove not needed stuff - rm -fr $(d_lib)/usr/lib/python2.[4-6]/*-packages/twisted/trial/test/scripttest.py \ - $(d_lib)/usr/lib/python2.[4-6]/*-packages/mechanize \ - $(d_lib)/usr/lib/python2.[4-6]/*-packages/ClientCookie \ - $(d_lib)/usr/lib/python2.[4-6]/*-packages/zope/formlib/LICENSE.txt - - # remove docutils and pytz - rm -fr $(d_lib)/usr/lib/python2.[4-6]/*-packages/docutils \ - $(d_lib)/usr/lib/python2.[4-6]/*-packages/pytz \ - $(d_lib)/usr/lib/python2.[4-6]/*-packages/ClientForm.py \ - $(d_lib)/usr/lib/python2.[4-6]/*-packages/pullparser.py - - # remove files from zope3 also in default zopeinterface package - ( \ - cd debian/python-zopeinterface/$(pylibdir) \ - && find . ! -type d \ - ) | ( \ - cd $(d_lib)/$(pylibdir) && xargs rm -f \ - ) - -install-prereq: +install/python-zope3:: + #Install everything under /usr/local to avoid conflicts with other python-zope.X packages dh_testdir dh_testroot - dh_clean -k - for f in debian/*.in; do \ - generated=`echo $$f | sed 's,.in$$,,;s,ZVER,$(ZVER),'`; \ - sed 's,@ZVER@,$(ZVER),g' $$f > $$generated.tmp; \ - if cmp --quiet "$$generated" "$$generated.tmp"; then \ - rm -f $$generated.tmp; \ - else \ - mv -f $$generated.tmp $$generated; \ - fi; \ - done - - dh_installdirs - dh_installzopeinstance -pzope$(ZVER)-sandbox sandbox - cd z && $(PYTHON) install.py install --install-layout=deb \ - --skip-build --no-compile --root $(CURDIR)/$(d) - -find $(d) -name '*.py[co]' | xargs -n 100 rm -f - rm -rf $(d)/$(pylibdir)/twisted - - # $(p_lib) - mkdir -p $(d_lib)/$(libdir) - mv $(d)/usr/lib/* $(d_lib)/usr/lib/ - rmdir $(d)/usr/lib - mv $(d)/usr/{bin,zopeskel} $(d_lib)/$(libdir) - mkdir -p $(d_lib)/$(libdir)/lib/python - dh_link -p$(p_lib) /$(pylibdir)/zope /$(libdir)/lib/python/zope - mkdir -p $(d_lib)/etc/zope$(ZVER) \ - $(d_lib)/var/log/zope$(ZVER) \ - $(d_lib)/var/lib/zope$(ZVER)/instance \ - $(d_lib)/var/lib/zope$(ZVER)/zeo - - # Replace all '#!' calls to python with $(PYTHON) - # and make them executable - for i in `find $(d_lib)/$(libdir) -type f`; do \ - sed '1s,#!.*python[^ ]*\(.*\),#! $(PYTHON)\1,' \ - $$i > $$i.temp; \ - if cmp --quiet $$i $$i.temp; then \ - rm -f $$i.temp; \ - else \ - mv -f $$i.temp $$i; \ - chmod 755 $$i; \ - echo "fixed interpreter: $$i"; \ - fi; \ - done - - -find $(d_lib)/$(pylibdir) -depth -type d -empty -exec rmdir {} \; - - # Lintian and linda overrides - mkdir -p -m 0755 $(d_lib)/usr/share/lintian/overrides - install -m 0644 debian/$(p_lib).lintian-overrides \ - $(d_lib)/usr/share/lintian/overrides/$(p_lib) - mkdir -p -m 0755 $(d_lib)/usr/share/linda/overrides - install -m 0644 debian/$(p_lib).linda-overrides \ - $(d_lib)/usr/share/linda/overrides/$(p_lib) - -install-zope-dbg: - cd zdbg && $(PYTHON)-dbg install.py install --install-layout=deb \ - --skip-build --no-compile --root $(CURDIR)/$(d_libdbg) - find $(d_libdbg) ! -type d ! -name '*.so' | xargs rm -f - find $(d_libdbg) -depth -empty -exec rmdir {} \; - rm -rf debian/zope3-dbg/usr/lib/python*/*-packages/zope/interface - -install-zif-python%: - # install zopeinterface packages - cd z/Dependencies/zope.interface-$(zbase) \ - && /usr/bin/python$* setup.py install --root=$(CURDIR)/$(d_zif) --install-layout=deb - -find debian/python-zopeinterface -name '*.py[co]' | xargs rm -f - - # fix the egg version - for f in $(d_zif)/usr/lib/python$*/*-packages/zope.interface-0.0.0.egg-info; do \ - sed -e '/^Version:/s/:.*/: $(version)/' \ - $(d_zif)/usr/lib/python$*/*-packages/zope.interface-0.0.0.egg-info \ - > $$(dirname $$f)/zope.interface-$(version).egg-info; \ - done - rm -f $(d_zif)/usr/lib/python$*/*-packages/zope.interface-0.0.0.egg-info - - # Copy README.txt to the right place - # (Zope needs the README to be there, see #332845) - mkdir -p $(d_zif)/usr/share/doc/$(p_zif) - -cp -p $$(find $(d_zif) -name README.txt) $(d_zif)/usr/share/doc/$(p_zif) - - # Remove interface module which has been split out - cp -p $(d_lib)/$(pylibdir)/zope/__init__.py \ - $(d_zif)/usr/lib/python$*/*-packages/zope/ - rm -rf $(d_lib)/$(pylibdir)/zope/interface - - # debug extension - cd z/Dependencies/zope.interface-$(zbase) \ - && /usr/bin/python$*-dbg setup.py install --root=$(CURDIR)/$(d_zifdbg) --install-layout=deb - find $(d_zifdbg) ! -type d ! -name '*_d.so' | xargs rm -f - find $(d_zifdbg) -depth -empty -exec rmdir {} \; - - -disabled-script-package: - # $(p_bin) - mkdir -p $(d_bin)/$(libdir) - dh_link -p$(p_bin) \ - /$(libdir)/bin/mkzeoinst.py /usr/bin/zope3-mkzeoinst \ - /$(libdir)/bin/mkzopeinstance /usr/bin/zope3-mkzopeinstance \ - /$(libdir)/bin/runzeo.py /usr/bin/zope3-runzeo \ - /$(libdir)/bin/zconfig /usr/bin/zope3-zconfig \ - /$(libdir)/bin/zconfig_schema2html \ - /usr/bin/zope3-zconfig_schema2html \ - /$(libdir)/bin/zdctl.py /usr/bin/zope3-zdctl \ - /$(libdir)/bin/zdrun.py /usr/bin/zope3-zdrun \ - /$(libdir)/bin/zeoctl.py /usr/bin/zope3-zeoctl \ - /$(libdir)/bin/zeopasswd.py /usr/bin/zope3-zeopasswd \ - /$(libdir)/bin/zopetest /usr/bin/zope3-zopetest - - -# Build architecture independant packages using the common target. -binary-indep: build-indep install-indep - dh_testdir - dh_testroot - dh_installchangelogs -i z/Zope/doc/CHANGES.txt - dh_installdocs -i z/README.txt - dh_installdocs -p$(p_doc) \ - -XCHANGES.txt -XINSTALL.txt -XREADME.txt -XMakefile \ - z/Zope/doc/* - dh_installlogrotate -i - dh_installdebconf -i - dh_installinit -i - dh_installman -i - dh_compress -i -X.rst -X.css - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i - -# Build architecture dependant packages using the common target. -binary-arch: build-arch install-arch - dh_testdir - dh_testroot - dh_installchangelogs -a z/Zope/doc/CHANGES.txt - dh_installdocs -a z/README.txt - dh_installdocs -i - install -m 644 debian/zope3.NEWS.Debian $(CURDIR)/debian/zope3/usr/share/doc/zope3/NEWS.Debian -ifeq ($(distribution),Ubuntu) - mv debian/zope3-sandbox/usr/share/doc/zope3-sandbox/README.Debian \ - debian/zope3-sandbox/usr/share/doc/zope3-sandbox/README.Ubuntu - mv debian/zope3/usr/share/doc/zope3/README.Debian \ - debian/zope3/usr/share/doc/zope3/README.Ubuntu - mv debian/zope3/usr/share/doc/zope3/NEWS.Debian \ - debian/zope3/usr/share/doc/zope3/NEWS.Ubuntu -endif - rm -rf $(d_zifdbg)/usr/share/doc/$(p_zifdbg) - ln -s $(p_zif) $(d_zifdbg)/usr/share/doc/$(p_zifdbg) - rm -rf $(d_libdbg)/usr/share/doc/$(p_libdbg) - ln -s $(p_lib) $(d_libdbg)/usr/share/doc/$(p_libdbg) - dh_installlogrotate -a - dh_installdebconf -a - dh_installinit -a - dh_installman -a - dh_link -a - dh_strip -p$(p_lib) --dbg-package=$(p_libdbg) - dh_strip -p$(p_zif) --dbg-package=$(p_zifdbg) - dh_compress -a -X.rst -X.css - dh_fixperms -a - - DH_PYCENTRAL=nomove dh_pycentral -p$(p_lib) - dh_pycentral -p$(p_zif) - - dh_installdeb -a - dh_shlibdeps -a - dh_gencontrol -a - dh_md5sums -a - dh_builddeb -a - -binary: binary-arch binary-indep -.PHONY: build clean binary-indep binary-arch binary install install-indep install-arch - -include /usr/share/dpatch/dpatch.make + mkdir -p debian/python-zope3/$(DEST)/zope/i18n/locales/data + mkdir -p debian/python-zope3/$(DEST)/zope/app/appsetup/schema + rm -rf src/transaction + python ./setup.py install --root=debian/python-zope3 --install-lib=$(DEST) --install-headers=$(DEST)/include --no-compile -O0 + cd src && for i in $$(find -name "*.pt" | grep -v tests); do cp $$i ../debian/python-zope3/$(DEST)/$$i; done + cd src && for i in $$(find -name "*.xml" | grep -v tests); do cp $$i ../debian/python-zope3/$(DEST)/$$i; done + cd src && for i in $$(find -name "*.zcml" | grep -v test); do cp $$i ../debian/python-zope3/$(DEST)/$$i; done + touch debian/python-zope3/$(DEST)/zope/__init__.py + touch debian/python-zope3/$(DEST)/zope/app/__init__.py diff -Nru zope3-3.4.0/debian/watch zope3-3.5~bzr18/debian/watch --- zope3-3.4.0/debian/watch 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/watch 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -version=3 -http://www.zope.org/Products/Zope3/(3\.[\d\.]+)/Zope-(3\.[\d\.]+)\.t.*gz diff -Nru zope3-3.4.0/debian/zopeZVER.default.in zope3-3.5~bzr18/debian/zopeZVER.default.in --- zope3-3.4.0/debian/zopeZVER.default.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.default.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -# Zope default file -# ----------------- -# -# This file controls the start and the stop of ZEO servers and ZOPE instances -# with the init script provided by zope@ZVER@ package. -# -# The following variables should contain a space sperated list of ZEO server -# and ZOPE instance names which you want to start with the init script. -# Note that ZEO servers will be started _before_ any instance, and that's -# your responsability to check dependencies between ZEO servers and ZOPE -# instances. The order of the variables doesn't matter. -# -# ZEO servers are searched in /var/lib/zope@ZVER@/zeo, while ZOPE instances -# are searched in /var/lib/zope@ZVER@/instance. -# -# ALL means that you would like to start all servers/instances, NONE (or an -# empty value) means, well, none. - -# ZEO servers -ZEOSERVERS="ALL" - -# ZOPE instances -INSTANCES="ALL" diff -Nru zope3-3.4.0/debian/zopeZVER.init.in zope3-3.5~bzr18/debian/zopeZVER.init.in --- zope3-3.4.0/debian/zopeZVER.init.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.init.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -#!/bin/sh - -### BEGIN INIT INFO -# Provides: zope3 -# Required-Start: $syslog $local_fs -# Required-Stop: $syslog $syslog -# Should-Start: $local_fs -# Should-Stop: $local_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start zope3 instances -# Description: Start the instances defined in /etc/default/zope3 -### END INIT INFO - -ZVER=@ZVER@ -[ -d /var/lib/zope$ZVER/instance -a -d /var/lib/zope$ZVER/zeo -a -d /usr/lib/zope$ZVER ] || exit 0 - -. /lib/lsb/init-functions -. /etc/default/zope$ZVER - -if [ "$ZEOSERVERS" = "NONE" -o "$ZEOSERVERS" = "" ]; then - ZEOSERVERS='' - log_warning_msg "Zope$ZVER: ZEO servers have been disabled, edit /etc/default/zope$ZVER to enable them." -elif [ "$ZEOSERVERS" = "ALL" ]; then - ZEOSERVERS='*' -fi - -if [ "$INSTANCES" = "NONE" -o "$INSTANCES" = "" ]; then - INSTANCES='' - log_warning_msg "Zope$ZVER: instances have been disabled, edit /etc/default/zope$ZVER to enable them." -elif [ "$INSTANCES" = "ALL" ]; then - INSTANCES='*' -fi - -case "$1" in - start|stop|restart) - p=''; [ "$1" = "stop" ] && p='p' - - if [ -n "$ZEOSERVERS" ]; then - cd /var/lib/zope$ZVER/zeo - for i in $ZEOSERVERS ; do - if [ "$i" = "*" ]; then - # log_success_msg "Zope$ZVER: no ZEO servers found." - break - elif [ ! -d "$i" ]; then - continue - fi - - if [ -x $i/bin/zeoctl ] ; then - log_begin_msg "Zope$ZVER: ${1}${p}ing $i ZEO server" - $i/bin/zeoctl "$1" >/dev/null 2>&1 - log_end_msg $? - else - log_warning_msg "Zope$ZVER: skipping $i (old/purged)" - fi - done - fi - - if [ -n "$INSTANCES" ]; then - cd /var/lib/zope$ZVER/instance - for i in $INSTANCES ; do - if [ "$i" = "*" ]; then - log_success_msg "Zope$ZVER: no instances found." - break - elif [ ! -d "$i" ]; then - continue - fi - - if [ -x $i/bin/zopectl ] ; then - log_begin_msg "Zope$ZVER: ${1}${p}ing $i instance" - dzhandle -z $ZVER zopectl "$i" $1 >/dev/null 2>&1 - log_end_msg $? - else - log_warning_msg "Zope$ZVER: skipping $i (old/purged)" - fi - done - fi - ;; - - force-reload) - echo "Zope$ZVER doesn't support force-reload, use restart instead." - ;; - - *) - echo "Usage: /etc/init.d/zope$ZVER {start|stop|restart|force-reload}" - exit 1 - ;; -esac - -exit 0 diff -Nru zope3-3.4.0/debian/zopeZVER.linda-overrides.in zope3-3.5~bzr18/debian/zopeZVER.linda-overrides.in --- zope3-3.4.0/debian/zopeZVER.linda-overrides.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.linda-overrides.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -Tag: image-in-usr-lib -Data: /usr/lib/python2.5/site-packages/zope/ -Tag: script-not-executable -Data: /usr/lib/python2.5/site-packages/zope/ -Tag: interpreter-not-absolute -Data: /usr/lib/zope3/zopeskel/bin/ -Tag: unusual-interpreter -Data: /usr/lib/zope3/zopeskel/bin/ -Tag: executable-not-elf-or-script -Data: /usr/lib/zope3/zopeskel/bin/ diff -Nru zope3-3.4.0/debian/zopeZVER.lintian-overrides.in zope3-3.5~bzr18/debian/zopeZVER.lintian-overrides.in --- zope3-3.4.0/debian/zopeZVER.lintian-overrides.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.lintian-overrides.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -zope@ZVER@: image-file-in-usr-lib -zope@ZVER@: interpreter-not-absolute -zope@ZVER@: unusual-interpreter -zope@ZVER@: script-not-executable -zope@ZVER@: executable-not-elf-or-script diff -Nru zope3-3.4.0/debian/zopeZVER.logrotate.in zope3-3.5~bzr18/debian/zopeZVER.logrotate.in --- zope3-3.4.0/debian/zopeZVER.logrotate.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.logrotate.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -# Logrotate configuration file fo Zope Debian package - -"/var/log/zope@ZVER@/*/z3.log" "/var/log/zope@ZVER@/*/access.log" "/var/log/zope@ZVER@/*/transcript.log" { - weekly - missingok - rotate 52 - copytruncate - compress - delaycompress - notifempty - create - sharedscripts - postrotate - sh -c 'for i in /var/lib/zope@ZVER@/instance/* ; do [ -d "$i/log" -a -x "$i/bin/zopectl" -a -e "$i/var/zopectlsock" ] && "$i"/bin/zopectl logreopen; done' - endscript -} diff -Nru zope3-3.4.0/debian/zopeZVER.NEWS.Debian.in zope3-3.5~bzr18/debian/zopeZVER.NEWS.Debian.in --- zope3-3.4.0/debian/zopeZVER.NEWS.Debian.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.NEWS.Debian.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -zope3 (3.3.0-2) unstable; urgency=low - - With the 3.3.0 zope3 upstream release, upstream introduced incompatible - changes which prevent old zope3 instances to be started. For this reason, - this debian package provides a backward-compatible patch to allow: - - >>> import zope.app.twisted.controller - - which upstream modified in: - - >>> import zope.app.appsetup.controller - - This statement is used in the zope3 instances' zopectl script, so either - patch it for your old instances or create new instances and move the Data.fs - in order to complete the upgrade. - - -- Fabio Tranchitella Fri, 6 Oct 2006 09:40:02 +0200 diff -Nru zope3-3.4.0/debian/zopeZVER.postinst.in zope3-3.5~bzr18/debian/zopeZVER.postinst.in --- zope3-3.4.0/debian/zopeZVER.postinst.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.postinst.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -#!/bin/sh -e - -. /usr/share/debconf/confmodule - -#DEBHELPER# - -db_stop -exit 0 diff -Nru zope3-3.4.0/debian/zopeZVER.postrm.in zope3-3.5~bzr18/debian/zopeZVER.postrm.in --- zope3-3.4.0/debian/zopeZVER.postrm.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.postrm.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -#!/bin/sh -e - -zope=zope@ZVER@ - -delete_pyo_pyc () { - t=`tempfile -p zopedel ` - [ -r /var/lib/$zope/_list_of_pyc_pyo_to_be_deleted_ ] && - cat /var/lib/$zope/_list_of_pyc_pyo_to_be_deleted_ | \ - xargs rm -f 2>&1 - rm -f /usr/lib/$zope/debian/*.py[co] - rm -f /var/lib/$zope/_list_of_pyc_pyo_to_be_deleted_ -} - -case "$1" in - failed-upgrade|abort-install|abort-upgrade|disappear) - ;; - upgrade) - delete_pyo_pyc - ;; - remove) - delete_pyo_pyc - ;; - purge) - if [ -d /var/lib/$zope/instance ] ; then - find /var/lib/$zope/instance -maxdepth 3 -type f \ - -path '*/var/Data.fs.*' -or -path '*/bin/*zope*' \ - -or -path '*/log/*.log*' -or -name README.txt \ - | xargs -r rm -f - fi - ;; - - *) - echo "postrm called with unknown argument \`$1'" >&2 - exit 1 - -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff -Nru zope3-3.4.0/debian/zopeZVER.preinst.in zope3-3.5~bzr18/debian/zopeZVER.preinst.in --- zope3-3.4.0/debian/zopeZVER.preinst.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.preinst.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -#! /bin/sh -e - -# summary of how this script can be called: -# * `install' -# * `install' -# * `upgrade' -# * `abort-upgrade' - -case "$1" in - upgrade) - ;; - - install) - ;; - - abort-upgrade) - ;; - - *) - echo "preinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 diff -Nru zope3-3.4.0/debian/zopeZVER.prerm.in zope3-3.5~bzr18/debian/zopeZVER.prerm.in --- zope3-3.4.0/debian/zopeZVER.prerm.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.prerm.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -#!/bin/sh -e - -zope=zope@ZVER@ - -dpkg -L zope@ZVER@ | - awk '$0~/\.py$/ {print $0"c\n" $0"o"}' \ - > /var/lib/$zope/_list_of_pyc_pyo_to_be_deleted_ - -case "$1" in - remove|failed-upgrade|upgrade|deconfigure) - ;; - *) - echo "prerm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 diff -Nru zope3-3.4.0/debian/zopeZVER.README.Debian.in zope3-3.5~bzr18/debian/zopeZVER.README.Debian.in --- zope3-3.4.0/debian/zopeZVER.README.Debian.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER.README.Debian.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -Notes about Debian Zope3 package --------------------------------- - -Some documents refer to scripts pyskel.py and test.py. Those scripts -are specific to particular instances, and can be found in -/var/lib/zope3/instance/myinstance/bin/. The scripts do *not* have -.py extensions. - - -- Fabio Tranchitella Thu, 15 Jan 2006 18:45:00 +0200 diff -Nru zope3-3.4.0/debian/zopeZVER-sandbox.copyright.in zope3-3.5~bzr18/debian/zopeZVER-sandbox.copyright.in --- zope3-3.4.0/debian/zopeZVER-sandbox.copyright.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER-sandbox.copyright.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -Copyright (C) 2005-2008 Fabio Tranchitella - -This package was created and released by the Debian Zope team, under -the terms of the Gnu General Public License, version 2 or later. -See /usr/share/common-licenses/GPL for the full text of that license. diff -Nru zope3-3.4.0/debian/zopeZVER-sandbox.dzinstance.in zope3-3.5~bzr18/debian/zopeZVER-sandbox.dzinstance.in --- zope3-3.4.0/debian/zopeZVER-sandbox.dzinstance.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER-sandbox.dzinstance.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -Package: zope@ZVER@-sandbox -ZopeVersion: @ZVER@ -Name: sandbox -Addon-Mode: all -Addon-Technique: tree-linked -Restart-Policy: end -Port: 8031 diff -Nru zope3-3.4.0/debian/zopeZVER-sandbox.postinst.in zope3-3.5~bzr18/debian/zopeZVER-sandbox.postinst.in --- zope3-3.4.0/debian/zopeZVER-sandbox.postinst.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER-sandbox.postinst.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -#!/bin/sh -e - -. /usr/share/debconf/confmodule - -#DEBHELPER# - -db_stop diff -Nru zope3-3.4.0/debian/zopeZVER-sandbox.README.Debian.in zope3-3.5~bzr18/debian/zopeZVER-sandbox.README.Debian.in --- zope3-3.4.0/debian/zopeZVER-sandbox.README.Debian.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER-sandbox.README.Debian.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -Notes about pre-packaged instances ----------------------------------- - -This is a pre-packaged instance of Zope@ZVER@, and it has been installed in -/var/lib/zope@ZVER@/instance/sandbox. You can modify its configuration -editing the file /etc/zope@ZVER@/sandbox/zope.conf. - -During configuration, debconf is used to ask the system administrator the -user and password for initial user of the instance. If debconf had been -configured to not display these questions, a random password has been -generated. In this case, you have to use zpasswd.py utility to specify a -new password for the initial user in order to have access to the Zope -Management Interface of this instance. - -If a zope@ZVER@ instance called `sandbox' already exists without data, -debconf is used to ask to the system administrator if he wants to abort -the installation or instead to remove the instance before creating the -new one. If you chose `abort` and you really want to install the -zope3-sandbox package, please manually remove the old sandbox before -trying to install again the package: - - # rm -fr /var/lib/zope@ZVER@/instance/sandbox - - -- Fabio Tranchitella Thu, 30 Mar 2006 12:38:00 +0200 diff -Nru zope3-3.4.0/debian/zopeZVER-sandbox.templates.in zope3-3.5~bzr18/debian/zopeZVER-sandbox.templates.in --- zope3-3.4.0/debian/zopeZVER-sandbox.templates.in 2011-09-15 20:37:34.000000000 +0000 +++ zope3-3.5~bzr18/debian/zopeZVER-sandbox.templates.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -Template: zope@ZVER@-sandbox/internal -Type: note -Description: Internal use. diff -Nru zope3-3.4.0/LICENSES.txt zope3-3.5~bzr18/LICENSES.txt --- zope3-3.4.0/LICENSES.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/LICENSES.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,106 @@ + +Copyright (c) 2001, 2002, 2003, 2004 Zope Corporation 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. + +---------------------------------------------------------------------- + +The `mechanize`, `pullparser`, `ClientForm` and `ClientCookie` modules, +included in `zope.testbrowser`, are provided via the BSD License: + +Copyright 2003-2004 John J. Lee +Copyright 1998-2001 Gisle Aas (original libwww-perl code) +All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the University of California, Berkeley nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS 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 REGENTS AND CONTRIBUTORS 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. + +---------------------------------------------------------------------- + +The XML files in locales directory in the zope.i18n package are +made available under the ICU License: + + ICU License - ICU 1.8.1 and later + + COPYRIGHT AND PERMISSION NOTICE + + Copyright (c) 1995-2002 International Business Machines Corporation and others + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, and/or sell copies of the Software, and to permit persons + to whom the Software is furnished to do so, provided that the above + copyright notice(s) and this permission notice appear in all copies of + the Software and that both the above copyright notice(s) and this + permission notice appear in supporting documentation. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + Except as contained in this notice, the name of a copyright holder + shall not be used in advertising or otherwise to promote the sale, use + or other dealings in this Software without prior written authorization + of the copyright holder. + + ----------------------------------------------------------------------------- + + All trademarks and registered trademarks mentioned herein are the property of + their respective owners. + +---------------------------------------------------------------------- + +The docutils package was implemented and placed in the public domain by +David Goodger and others. + +We are rereleasing it under the terms of the Zope Public License 2.0. + +Exceptions to this are the following files within the docutils package: + + roman.py, by Mark Pilgrim: + + This program is free software; you can redistribute it and/or modify + it under the terms of the Python 2.1.1 license, available at + http://www.python.org/2.1.1/license.html + +---------------------------------------------------------------------- + +The zope.testing.doctest module is a copy of the doctest module from +the Python 2.4 standard library. It is the property of the Python +Software Foundation (PSF) and is covered by the PSF license agreement +for Python 2.4. We will no-longer distribute this module with Zope at +some point on the future. diff -Nru zope3-3.4.0/log.ini zope3-3.5~bzr18/log.ini --- zope3-3.4.0/log.ini 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/log.ini 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,38 @@ +# This file configures the logging module: critical errors are logged +# to z3.log; everything else is ignored. + +# To use this configuration, use logging.config.fileConfig("log.ini"). + +# Documentation for the file format is at +# http://www.red-dove.com/python_logging.html#config + +[logger_root] +level=CRITICAL +handlers=normal + +[logger_event] +level=DEBUG +handlers=normal +qualname=event + +[handler_normal] +class=FileHandler +level=NOTSET +formatter=common +args=('z3.log', 'a') +filename=z3.log +mode=a + +[formatter_common] +format=------ + %(asctime)s %(levelname)s %(name)s %(message)s +datefmt=%Y-%m-%dT%H:%M:%S + +[loggers] +keys=root,event + +[handlers] +keys=normal + +[formatters] +keys=common diff -Nru zope3-3.4.0/Makefile zope3-3.5~bzr18/Makefile --- zope3-3.4.0/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/Makefile 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +PYTHON=python +TESTFLAGS=-vpc1 +TESTOPTS= +SETUPFLAGS= +COMPILEFLAGS= + +# XXX What should the default be? +all: inplace + +# Build in-place +inplace: + $(PYTHON) setup.py $(SETUPFLAGS) \ + build_ext -i $(COMPILEFLAGS) + +test_inplace: inplace + $(PYTHON) test.py $(TESTFLAGS) $(TESTOPTS) + +ftest_inplace: inplace + $(PYTHON) test.py -f $(TESTFLAGS) $(TESTOPTS) + +# XXX What should the default be? +test: test_inplace + +ftest: ftest_inplace + +clean: + find . \( -name '*.o' -o -name '*.so' -o -name '*.py[co]' -o -name '*.dll' \) -exec rm -f {} \; + rm -rf build + +realclean: clean + rm -f TAGS tags + $(PYTHON) setup.py clean -a diff -Nru zope3-3.4.0/README.txt zope3-3.5~bzr18/README.txt --- zope3-3.4.0/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,125 @@ +============================ +Welcome to the Zope 3 source +============================ + +This file provides some basic hints for people developing Zope 3 software. +There is more developer info in the Zope 3 Wiki:: + + http://wiki.zope.org/Zope3/Zope3DeveloperInfo + +For information about the current release, see ``doc/CHANGES.txt``. + +Zope 3 is a stable platform on which production systems can and are built. + + +Building and running tests +========================== + +See ``INSTALL.txt`` which Python version is required for Zope 3. + +In the top-level ``Zope3`` directory, you should find a script called +``setup.py``. Run it to build the extension modules needed by Zope. Example:: + + $ cd Zope3 + $ python setup.py -q build_ext -i install_data --install-dir . + +On a Unix variant, you can just type:: + + $ make + +On Windows, if you downloaded the binary distribution, this has already been +done for you (since the compiler we use on Windows isn't free). + +Zope 3 includes unit tests based on the Python ``unittest`` module. If you +check in changes, you *must* verify that all the tests succeed before you +commit changes. + +To run all the tests, use the script test.py:: + + $ python test.py -v + +Use ``test.py -h`` for usage. The test script can run selected tests, stop +after the first error, run the tests in a loop, etc. + + +Starting Zope 3 +=============== + +Before running Zope, you need to create one or more bootstrap users. Copy the +file ``sample_principals.zcml`` to ``principals.zcml``, and edit the result to +your needs. Make sure you change the passwords. + +To run Zope just run the ``z3.py`` script:: + + $ python z3.py + +Or, if you use make on Unix, you can run:: + + $ bin/zopectl fg + +This will run Zope on port 8080. Visit the url:: + + http://localhost:8080/manage + +This goes to the Zope 3 default management interface. Note that this release +of Zope 3 requires recent versions of Mozilla or IE. Note that other modern +browsers, such as Konqueror and Safari, also mostly work well. + +If you insist on using an older browser (or a text-based browser) you can use +the basic Zope 3 skin by putting ``++skin++Basic`` after the server part of +the URL:: + + http://localhost:8080/++skin++Basic/manage + +See ``doc/INSTALL.txt`` for more information. + + +Finding out how to develop applications +======================================= + +There are several documentation sources out there. As of this writing two +books have been published and much online documentation is available: + +- Zope 3 comes with an extensive API documentation tool, which also compiles + many of the package-specific README files. Once you start up Zope, simply go + to:: + + http://localhost:8080/++apidoc++ + +- `Zope 3 Developer's Handbook`: + + * On Paper: http://www.samspublishing.com/title/0672326175 + + * Online: http://dev.zope.org/Zope3/Zope3Book + +- `Web Component Development with Zope 3`: + + * On Paper: http://www.springeronline.com/sgw/cda/frontpage/0,11855,1-102-22-35029949-0,00.html + + * Online: http://www.worldcookery.com + +- The developers tutorial at: + + http://dev.zope.org/Zope3/ProgrammerTutorial + +- Ask questions on the mailing lists and chat channels: + + * Zope 3 Development: ``http://lists.zope.org/mailman/listinfo/zope3-dev`` + + * Zope 3 Users: ``http://lists.zope.org/mailman/listinfo/zope3-users`` + + * IRC channel `#zope3-dev` on irc.freenode.net + +- To keep up with the latest changes, the commits mailing list + ``http://lists.zope.org/mailman/listinfo/zope3-checkins`` + + +Acknowledgements +================ + +Zope 3 is a Zope Community effort. There are many Zope 3 contributors without +whom there wouldn't be a Zope 3. + +See the ``doc/CREDITS.txt`` file for details. + +Much thanks folks! diff -Nru zope3-3.4.0/setup.py zope3-3.5~bzr18/setup.py --- zope3-3.4.0/setup.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/setup.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,140 @@ +############################################################################## +# +# Copyright (c) 2010 Zope Corporation 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. +# +"""Setup for Zope 3 +""" +import os +from setuptools import setup, find_packages, Extension + +# Include directories for C extensions +include = ['src'] + +# Set up dependencies for the BTrees package +base_btrees_depends = [ + "src/BTrees/BTreeItemsTemplate.c", + "src/BTrees/BTreeModuleTemplate.c", + "src/BTrees/BTreeTemplate.c", + "src/BTrees/BucketTemplate.c", + "src/BTrees/MergeTemplate.c", + "src/BTrees/SetOpTemplate.c", + "src/BTrees/SetTemplate.c", + "src/BTrees/TreeSetTemplate.c", + "src/BTrees/sorters.c", + "src/persistent/cPersistence.h", + ] + +_flavors = {"O": "object", "I": "int", "F": "float", 'L': 'int'} + +KEY_H = "src/BTrees/%skeymacros.h" +VALUE_H = "src/BTrees/%svaluemacros.h" + +def BTreeExtension(flavor): + key = flavor[0] + value = flavor[1] + name = "BTrees._%sBTree" % flavor + sources = ["src/BTrees/_%sBTree.c" % flavor] + kwargs = {"include_dirs": include} + if flavor != "fs": + kwargs["depends"] = (base_btrees_depends + [KEY_H % _flavors[key], + VALUE_H % _flavors[value]]) + else: + kwargs["depends"] = base_btrees_depends + if key != "O": + kwargs["define_macros"] = [('EXCLUDE_INTSET_SUPPORT', None)] + return Extension(name, sources, **kwargs) + +exts = [BTreeExtension(flavor) + for flavor in ("OO", "IO", "OI", "II", "IF", + "fs", "LO", "OL", "LL", "LF", + )] + +cPersistence = Extension(name = 'persistent.cPersistence', + include_dirs = include, + sources= ['src/persistent/cPersistence.c', + 'src/persistent/ring.c'], + depends = ['src/persistent/cPersistence.h', + 'src/persistent/ring.h', + 'src/persistent/ring.c'] + ) + +cPickleCache = Extension(name = 'persistent.cPickleCache', + include_dirs = include, + sources= ['src/persistent/cPickleCache.c', + 'src/persistent/ring.c'], + depends = ['src/persistent/cPersistence.h', + 'src/persistent/ring.h', + 'src/persistent/ring.c'] + ) + +TimeStamp = Extension(name = 'persistent.TimeStamp', + include_dirs = include, + sources= ['src/persistent/TimeStamp.c'] + ) + + +exts += [cPersistence, + cPickleCache, + TimeStamp, + ] + +def _modname(path, base, name=''): + if path == base: + return name + dirname, basename = os.path.split(path) + return _modname(dirname, base, basename + '.' + name) + +setup(name='Zope3', + version='3.5dev', + author='Zope Corporation and Contributors', + author_email='zope-dev@zope.org', + description='Zope3', + packages=find_packages('src'), + package_dir={'': 'src'}, + namespace_packages=['zope', 'zope.app', 'zc'], + ext_modules=exts + [ + Extension("zope.interface._zope_interface_coptimizations", + [os.path.join('src', 'zope', + 'interface', + '_zope_interface_coptimizations.c') + ]), + Extension("zope.security._proxy", + [os.path.join('src', 'zope', 'security', + "_proxy.c") + ], include_dirs=include, depends=['src/zope/proxy/proxy.h']), + Extension("zope.security._zope_security_checker", + [os.path.join('src', 'zope', 'security', + "_zope_security_checker.c") + ]), + Extension("zope.hookable._zope_hookable", + [os.path.join('src', 'zope', 'hookable', + "_zope_hookable.c") + ]), + Extension("zope.proxy._zope_proxy_proxy", + [os.path.join('src', 'zope', 'proxy', + "_zope_proxy_proxy.c") + ]), + Extension("zope.i18nmessageid._zope_i18nmessageid_message", + [os.path.join('src', 'zope', 'i18nmessageid', + "_zope_i18nmessageid_message.c") + ]), + Extension("zope.container._zope_container_contained", + [os.path.join("src", "zope", "container", + "_zope_container_contained.c") + ], include_dirs=include), + ], + headers = ['src/persistent/cPersistence.h', + 'src/persistent/py24compat.h', + 'src/persistent/ring.h', + 'src/zope/proxy/proxy.h'], + include_package_data = True, + zip_safe = False, + ) diff -Nru zope3-3.4.0/src/BTrees/BTreeItemsTemplate.c zope3-3.5~bzr18/src/BTrees/BTreeItemsTemplate.c --- zope3-3.4.0/src/BTrees/BTreeItemsTemplate.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/BTreeItemsTemplate.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,698 @@ +/***************************************************************************** + + Copyright (c) 2001, 2002 Zope Corporation 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 + + ****************************************************************************/ + +#define BTREEITEMSTEMPLATE_C "$Id: BTreeItemsTemplate.c 81757 2007-11-11 14:38:41Z ctheune $\n" + +/* A BTreeItems struct is returned from calling .items(), .keys() or + * .values() on a BTree-based data structure, and is also the result of + * taking slices of those. It represents a contiguous slice of a BTree. + * + * The start of the slice is in firstbucket, at offset first. The end of + * the slice is in lastbucket, at offset last. Both endpoints are inclusive. + * It must possible to get from firstbucket to lastbucket via following + * bucket 'next' pointers zero or more times. firstbucket, first, lastbucket, + * and last are readonly after initialization. An empty slice is represented + * by firstbucket == lastbucket == currentbucket == NULL. + * + * 'kind' determines whether this slice represents 'k'eys alone, 'v'alues + * alone, or 'i'items (key+value pairs). 'kind' is also readonly after + * initialization. + * + * The combination of currentbucket, currentoffset and pseudoindex acts as + * a search finger. Offset currentoffset in bucket currentbucket is at index + * pseudoindex, where pseudoindex==0 corresponds to offset first in bucket + * firstbucket, and pseudoindex==-1 corresponds to offset last in bucket + * lastbucket. The function BTreeItems_seek() can be used to set this combo + * correctly for any in-bounds index, and uses this combo on input to avoid + * needing to search from the start (or end) on each call. Calling + * BTreeItems_seek() with consecutive larger positions is very efficent. + * Calling it with consecutive smaller positions is more efficient than if + * a search finger weren't being used at all, but is still quadratic time + * in the number of buckets in the slice. + */ +typedef struct { + PyObject_HEAD + Bucket *firstbucket; /* First bucket */ + Bucket *currentbucket; /* Current bucket (search finger) */ + Bucket *lastbucket; /* Last bucket */ + int currentoffset; /* Offset in currentbucket */ + int pseudoindex; /* search finger index */ + int first; /* Start offset in firstbucket */ + int last; /* End offset in lastbucket */ + char kind; /* 'k', 'v', 'i' */ +} BTreeItems; + +#define ITEMS(O)((BTreeItems*)(O)) + +static PyObject * +newBTreeItems(char kind, + Bucket *lowbucket, int lowoffset, + Bucket *highbucket, int highoffset); + +static void +BTreeItems_dealloc(BTreeItems *self) +{ + Py_XDECREF(self->firstbucket); + Py_XDECREF(self->lastbucket); + Py_XDECREF(self->currentbucket); + PyObject_DEL(self); +} + +static Py_ssize_t +BTreeItems_length_or_nonzero(BTreeItems *self, int nonzero) +{ + Py_ssize_t r; + Bucket *b, *next; + + b = self->firstbucket; + if (b == NULL) + return 0; + + r = self->last + 1 - self->first; + + if (nonzero && r > 0) + /* Short-circuit if all we care about is nonempty */ + return 1; + + if (b == self->lastbucket) + return r; + + Py_INCREF(b); + PER_USE_OR_RETURN(b, -1); + while ((next = b->next)) { + r += b->len; + if (nonzero && r > 0) + /* Short-circuit if all we care about is nonempty */ + break; + + if (next == self->lastbucket) + break; /* we already counted the last bucket */ + + Py_INCREF(next); + PER_UNUSE(b); + Py_DECREF(b); + b = next; + PER_USE_OR_RETURN(b, -1); + } + PER_UNUSE(b); + Py_DECREF(b); + + return r >= 0 ? r : 0; +} + +static Py_ssize_t +BTreeItems_length(BTreeItems *self) +{ + return BTreeItems_length_or_nonzero(self, 0); +} + +/* +** BTreeItems_seek +** +** Find the ith position in the BTreeItems. +** +** Arguments: self The BTree +** i the index to seek to, in 0 .. len(self)-1, or in +** -len(self) .. -1, as for indexing a Python sequence. +** +** +** Returns 0 if successful, -1 on failure to seek (like out-of-bounds). +** Upon successful return, index i is at offset self->currentoffset in bucket +** self->currentbucket. +*/ +static int +BTreeItems_seek(BTreeItems *self, Py_ssize_t i) +{ + int delta, pseudoindex, currentoffset; + Bucket *b, *currentbucket; + int error; + + pseudoindex = self->pseudoindex; + currentoffset = self->currentoffset; + currentbucket = self->currentbucket; + if (currentbucket == NULL) goto no_match; + + delta = i - pseudoindex; + while (delta > 0) { /* move right */ + int max; + /* Want to move right delta positions; the most we can move right in + * this bucket is currentbucket->len - currentoffset - 1 positions. + */ + PER_USE_OR_RETURN(currentbucket, -1); + max = currentbucket->len - currentoffset - 1; + b = currentbucket->next; + PER_UNUSE(currentbucket); + if (delta <= max) { + currentoffset += delta; + pseudoindex += delta; + if (currentbucket == self->lastbucket + && currentoffset > self->last) goto no_match; + break; + } + /* Move to start of next bucket. */ + if (currentbucket == self->lastbucket || b == NULL) goto no_match; + currentbucket = b; + pseudoindex += max + 1; + delta -= max + 1; + currentoffset = 0; + } + while (delta < 0) { /* move left */ + int status; + /* Want to move left -delta positions; the most we can move left in + * this bucket is currentoffset positions. + */ + if ((-delta) <= currentoffset) { + currentoffset += delta; + pseudoindex += delta; + if (currentbucket == self->firstbucket + && currentoffset < self->first) goto no_match; + break; + } + /* Move to end of previous bucket. */ + if (currentbucket == self->firstbucket) goto no_match; + status = PreviousBucket(¤tbucket, self->firstbucket); + if (status == 0) + goto no_match; + else if (status < 0) + return -1; + pseudoindex -= currentoffset + 1; + delta += currentoffset + 1; + PER_USE_OR_RETURN(currentbucket, -1); + currentoffset = currentbucket->len - 1; + PER_UNUSE(currentbucket); + } + + assert(pseudoindex == i); + + /* Alas, the user may have mutated the bucket since the last time we + * were called, and if they deleted stuff, we may be pointing into + * trash memory now. + */ + PER_USE_OR_RETURN(currentbucket, -1); + error = currentoffset < 0 || currentoffset >= currentbucket->len; + PER_UNUSE(currentbucket); + if (error) { + PyErr_SetString(PyExc_RuntimeError, + "the bucket being iterated changed size"); + return -1; + } + + Py_INCREF(currentbucket); + Py_DECREF(self->currentbucket); + self->currentbucket = currentbucket; + self->currentoffset = currentoffset; + self->pseudoindex = pseudoindex; + return 0; + +no_match: + IndexError(i); + return -1; +} + + +/* Return the right kind ('k','v','i') of entry from bucket b at offset i. + * b must be activated. Returns NULL on error. + */ +static PyObject * +getBucketEntry(Bucket *b, int i, char kind) +{ + PyObject *result = NULL; + + assert(b); + assert(0 <= i && i < b->len); + + switch (kind) { + + case 'k': + COPY_KEY_TO_OBJECT(result, b->keys[i]); + break; + + case 'v': + COPY_VALUE_TO_OBJECT(result, b->values[i]); + break; + + case 'i': { + PyObject *key; + PyObject *value;; + + COPY_KEY_TO_OBJECT(key, b->keys[i]); + if (!key) break; + + COPY_VALUE_TO_OBJECT(value, b->values[i]); + if (!value) { + Py_DECREF(key); + break; + } + + result = PyTuple_New(2); + if (result) { + PyTuple_SET_ITEM(result, 0, key); + PyTuple_SET_ITEM(result, 1, value); + } + else { + Py_DECREF(key); + Py_DECREF(value); + } + break; + } + + default: + PyErr_SetString(PyExc_AssertionError, + "getBucketEntry: unknown kind"); + break; + } + return result; +} + +/* +** BTreeItems_item +** +** Arguments: self a BTreeItems structure +** i Which item to inspect +** +** Returns: the BTreeItems_item_BTree of self->kind, i +** (ie pulls the ith item out) +*/ +static PyObject * +BTreeItems_item(BTreeItems *self, Py_ssize_t i) +{ + PyObject *result; + + if (BTreeItems_seek(self, i) < 0) return NULL; + + PER_USE_OR_RETURN(self->currentbucket, NULL); + result = getBucketEntry(self->currentbucket, self->currentoffset, + self->kind); + PER_UNUSE(self->currentbucket); + return result; +} + +/* +** BTreeItems_slice +** +** Creates a new BTreeItems structure representing the slice +** between the low and high range +** +** Arguments: self The old BTreeItems structure +** ilow The start index +** ihigh The end index +** +** Returns: BTreeItems item +*/ +static PyObject * +BTreeItems_slice(BTreeItems *self, Py_ssize_t ilow, Py_ssize_t ihigh) +{ + Bucket *lowbucket; + Bucket *highbucket; + int lowoffset; + int highoffset; + Py_ssize_t length = -1; /* len(self), but computed only if needed */ + + /* Complications: + * A Python slice never raises IndexError, but BTreeItems_seek does. + * Python did only part of index normalization before calling this: + * ilow may be < 0 now, and ihigh may be arbitrarily large. It's + * our responsibility to clip them. + * A Python slice is exclusive of the high index, but a BTreeItems + * struct is inclusive on both ends. + */ + + /* First adjust ilow and ihigh to be legit endpoints in the Python + * sense (ilow inclusive, ihigh exclusive). This block duplicates the + * logic from Python's list_slice function (slicing for builtin lists). + */ + if (ilow < 0) + ilow = 0; + else { + if (length < 0) + length = BTreeItems_length(self); + if (ilow > length) + ilow = length; + } + + if (ihigh < ilow) + ihigh = ilow; + else { + if (length < 0) + length = BTreeItems_length(self); + if (ihigh > length) + ihigh = length; + } + assert(0 <= ilow && ilow <= ihigh); + assert(length < 0 || ihigh <= length); + + /* Now adjust for that our struct is inclusive on both ends. This is + * easy *except* when the slice is empty: there's no good way to spell + * that in an inclusive-on-both-ends scheme. For example, if the + * slice is btree.items([:0]), ilow == ihigh == 0 at this point, and if + * we were to subtract 1 from ihigh that would get interpreted by + * BTreeItems_seek as meaning the *entire* set of items. Setting ilow==1 + * and ihigh==0 doesn't work either, as BTreeItems_seek raises IndexError + * if we attempt to seek to ilow==1 when the underlying sequence is empty. + * It seems simplest to deal with empty slices as a special case here. + */ + if (ilow == ihigh) { + /* empty slice */ + lowbucket = highbucket = NULL; + lowoffset = 1; + highoffset = 0; + } + else { + assert(ilow < ihigh); + --ihigh; /* exclusive -> inclusive */ + + if (BTreeItems_seek(self, ilow) < 0) return NULL; + lowbucket = self->currentbucket; + lowoffset = self->currentoffset; + + if (BTreeItems_seek(self, ihigh) < 0) return NULL; + + highbucket = self->currentbucket; + highoffset = self->currentoffset; + } + return newBTreeItems(self->kind, + lowbucket, lowoffset, highbucket, highoffset); +} + +static PySequenceMethods BTreeItems_as_sequence = { + (lenfunc) BTreeItems_length, + (binaryfunc)0, + (ssizeargfunc)0, + (ssizeargfunc) BTreeItems_item, + (ssizessizeargfunc) BTreeItems_slice, +}; + +/* Number Method items (just for nb_nonzero!) */ + +static int +BTreeItems_nonzero(BTreeItems *self) +{ + return BTreeItems_length_or_nonzero(self, 1); +} + +static PyNumberMethods BTreeItems_as_number_for_nonzero = { + 0,0,0,0,0,0,0,0,0,0, + (inquiry)BTreeItems_nonzero}; + +static PyTypeObject BTreeItemsType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + MOD_NAME_PREFIX "BTreeItems", /*tp_name*/ + sizeof(BTreeItems), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor) BTreeItems_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)0, /*obsolete tp_getattr*/ + (setattrfunc)0, /*obsolete tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + &BTreeItems_as_number_for_nonzero, /*tp_as_number*/ + &BTreeItems_as_sequence, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + + /* Space for future expansion */ + 0L,0L, + "Sequence type used to iterate over BTree items." /* Documentation string */ +}; + +/* Returns a new BTreeItems object representing the contiguous slice from + * offset lowoffset in bucket lowbucket through offset highoffset in bucket + * highbucket, inclusive. Pass lowbucket == NULL for an empty slice. + * The currentbucket is set to lowbucket, currentoffset ot lowoffset, and + * pseudoindex to 0. kind is 'k', 'v' or 'i' (see BTreeItems struct docs). + */ +static PyObject * +newBTreeItems(char kind, + Bucket *lowbucket, int lowoffset, + Bucket *highbucket, int highoffset) +{ + BTreeItems *self; + + UNLESS (self = PyObject_NEW(BTreeItems, &BTreeItemsType)) return NULL; + self->kind=kind; + + self->first=lowoffset; + self->last=highoffset; + + if (! lowbucket || ! highbucket + || (lowbucket == highbucket && lowoffset > highoffset)) + { + self->firstbucket = 0; + self->lastbucket = 0; + self->currentbucket = 0; + } + else + { + Py_INCREF(lowbucket); + self->firstbucket = lowbucket; + Py_INCREF(highbucket); + self->lastbucket = highbucket; + Py_INCREF(lowbucket); + self->currentbucket = lowbucket; + } + + self->currentoffset = lowoffset; + self->pseudoindex = 0; + + return OBJECT(self); +} + +static int +nextBTreeItems(SetIteration *i) +{ + if (i->position >= 0) + { + if (i->position) + { + DECREF_KEY(i->key); + DECREF_VALUE(i->value); + } + + if (BTreeItems_seek(ITEMS(i->set), i->position) >= 0) + { + Bucket *currentbucket; + + currentbucket = BUCKET(ITEMS(i->set)->currentbucket); + UNLESS(PER_USE(currentbucket)) + { + /* Mark iteration terminated, so that finiSetIteration doesn't + * try to redundantly decref the key and value + */ + i->position = -1; + return -1; + } + + COPY_KEY(i->key, currentbucket->keys[ITEMS(i->set)->currentoffset]); + INCREF_KEY(i->key); + + COPY_VALUE(i->value, + currentbucket->values[ITEMS(i->set)->currentoffset]); + INCREF_VALUE(i->value); + + i->position ++; + + PER_UNUSE(currentbucket); + } + else + { + i->position = -1; + PyErr_Clear(); + } + } + return 0; +} + +static int +nextTreeSetItems(SetIteration *i) +{ + if (i->position >= 0) + { + if (i->position) + { + DECREF_KEY(i->key); + } + + if (BTreeItems_seek(ITEMS(i->set), i->position) >= 0) + { + Bucket *currentbucket; + + currentbucket = BUCKET(ITEMS(i->set)->currentbucket); + UNLESS(PER_USE(currentbucket)) + { + /* Mark iteration terminated, so that finiSetIteration doesn't + * try to redundantly decref the key and value + */ + i->position = -1; + return -1; + } + + COPY_KEY(i->key, currentbucket->keys[ITEMS(i->set)->currentoffset]); + INCREF_KEY(i->key); + + i->position ++; + + PER_UNUSE(currentbucket); + } + else + { + i->position = -1; + PyErr_Clear(); + } + } + return 0; +} + +/* Support for the iteration protocol new in Python 2.2. */ + +static PyTypeObject BTreeIter_Type; + +/* The type of iterator objects, returned by e.g. iter(IIBTree()). */ +typedef struct { + PyObject_HEAD + /* We use a BTreeItems object because it's convenient and flexible. + * We abuse it two ways: + * 1. We set currentbucket to NULL when the iteration is finished. + * 2. We don't bother keeping pseudoindex in synch. + */ + BTreeItems *pitems; +} BTreeIter; + +/* Return a new iterator object, to traverse the keys and/or values + * represented by pitems. pitems must not be NULL. Returns NULL if error. + */ +static BTreeIter * +BTreeIter_new(BTreeItems *pitems) +{ + BTreeIter *result; + + assert(pitems != NULL); + result = PyObject_New(BTreeIter, &BTreeIter_Type); + if (result) { + Py_INCREF(pitems); + result->pitems = pitems; + } + return result; +} + +/* The iterator's tp_dealloc slot. */ +static void +BTreeIter_dealloc(BTreeIter *bi) +{ + Py_DECREF(bi->pitems); + PyObject_Del(bi); +} + +/* The implementation of the iterator's tp_iternext slot. Returns "the next" + * item; returns NULL if error; returns NULL without setting an error if the + * iteration is exhausted (that's the way to terminate the iteration protocol). + */ +static PyObject * +BTreeIter_next(BTreeIter *bi, PyObject *args) +{ + PyObject *result = NULL; /* until proven innocent */ + BTreeItems *items = bi->pitems; + int i = items->currentoffset; + Bucket *bucket = items->currentbucket; + + if (bucket == NULL) /* iteration termination is sticky */ + return NULL; + + PER_USE_OR_RETURN(bucket, NULL); + if (i >= bucket->len) { + /* We never leave this routine normally with i >= len: somebody + * else mutated the current bucket. + */ + PyErr_SetString(PyExc_RuntimeError, + "the bucket being iterated changed size"); + /* Arrange for that this error is sticky too. */ + items->currentoffset = INT_MAX; + goto Done; + } + + /* Build the result object, from bucket at offset i. */ + result = getBucketEntry(bucket, i, items->kind); + + /* Advance position for next call. */ + if (bucket == items->lastbucket && i >= items->last) { + /* Next call should terminate the iteration. */ + Py_DECREF(items->currentbucket); + items->currentbucket = NULL; + } + else { + ++i; + if (i >= bucket->len) { + Py_XINCREF(bucket->next); + items->currentbucket = bucket->next; + Py_DECREF(bucket); + i = 0; + } + items->currentoffset = i; + } + +Done: + PER_UNUSE(bucket); + return result; +} + +static PyObject * +BTreeIter_getiter(PyObject *it) +{ + Py_INCREF(it); + return it; +} + +static PyTypeObject BTreeIter_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + MOD_NAME_PREFIX "-iterator", /* tp_name */ + sizeof(BTreeIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)BTreeIter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /*PyObject_GenericGetAttr,*/ /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)BTreeIter_getiter, /* tp_iter */ + (iternextfunc)BTreeIter_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; diff -Nru zope3-3.4.0/src/BTrees/BTreeModuleTemplate.c zope3-3.5~bzr18/src/BTrees/BTreeModuleTemplate.c --- zope3-3.4.0/src/BTrees/BTreeModuleTemplate.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/BTreeModuleTemplate.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,573 @@ +/***************************************************************************** + + Copyright (c) 2001, 2002 Zope Corporation 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 + + ****************************************************************************/ + +#include "Python.h" +/* include structmember.h for offsetof */ +#include "structmember.h" + +#ifdef PERSISTENT +#include "persistent/cPersistence.h" +#else +#define PER_USE_OR_RETURN(self, NULL) +#define PER_ALLOW_DEACTIVATION(self) +#define PER_PREVENT_DEACTIVATION(self) +#define PER_DEL(self) +#define PER_USE(O) 1 +#define PER_ACCESSED(O) 1 +#endif + +#include "py24compat.h" + +/* So sue me. This pair gets used all over the place, so much so that it + * interferes with understanding non-persistence parts of algorithms. + * PER_UNUSE can be used after a successul PER_USE or PER_USE_OR_RETURN. + * It allows the object to become ghostified, and tells the persistence + * machinery that the object's fields were used recently. + */ +#define PER_UNUSE(OBJ) do { \ + PER_ALLOW_DEACTIVATION(OBJ); \ + PER_ACCESSED(OBJ); \ +} while (0) + +/* The tp_name slots of the various BTree types contain the fully + * qualified names of the types, e.g. zodb.btrees.OOBTree.OOBTree. + * The full name is usd to support pickling and because it is not + * possible to modify the __module__ slot of a type dynamically. (This + * may be a bug in Python 2.2). + * + * The MODULE_NAME here used to be "BTrees._". We actually want the module + * name to point to the Python module rather than the C, so the underline + * is now removed. + */ +#define MODULE_NAME "BTrees." MOD_NAME_PREFIX "BTree." + +static PyObject *sort_str, *reverse_str, *__setstate___str, + *_bucket_type_str; +static PyObject *ConflictError = NULL; + +static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;} +#define ASSIGN(V,E) PyVar_Assign(&(V),(E)) +#define UNLESS(E) if (!(E)) +#define OBJECT(O) ((PyObject*)(O)) + +#define MIN_BUCKET_ALLOC 16 +#define MAX_BTREE_SIZE(B) DEFAULT_MAX_BTREE_SIZE +#define MAX_BUCKET_SIZE(B) DEFAULT_MAX_BUCKET_SIZE + +#define SameType_Check(O1, O2) ((O1)->ob_type==(O2)->ob_type) + +#define ASSERT(C, S, R) if (! (C)) { \ + PyErr_SetString(PyExc_AssertionError, (S)); return (R); } + + +#ifdef NEED_LONG_LONG_SUPPORT +/* Helper code used to support long long instead of int. */ + +#ifndef PY_LONG_LONG +#error "PY_LONG_LONG required but not defined" +#endif + +static int +longlong_check(PyObject *ob) +{ + if (PyInt_Check(ob)) + return 1; + + if (PyLong_Check(ob)) { + /* check magnitude */ + PY_LONG_LONG val = PyLong_AsLongLong(ob); + + if (val == -1 && PyErr_Occurred()) + return 0; + return 1; + } + return 0; +} + +static PyObject * +longlong_as_object(PY_LONG_LONG val) +{ + static PY_LONG_LONG maxint = 0; + + if (maxint == 0) + maxint = PyInt_GetMax(); + if ((val > maxint) || (val < (-maxint-1))) + return PyLong_FromLongLong(val); + return PyInt_FromLong((long)val); +} +#endif + + +/* Various kinds of BTree and Bucket structs are instances of + * "sized containers", and have a common initial layout: + * The stuff needed for all Python objects, or all Persistent objects. + * int size: The maximum number of things that could be contained + * without growing the container. + * int len: The number of things currently contained. + * + * Invariant: 0 <= len <= size. + * + * A sized container typically goes on to declare one or more pointers + * to contiguous arrays with 'size' elements each, the initial 'len' of + * which are currently in use. + */ +#ifdef PERSISTENT +#define sizedcontainer_HEAD \ + cPersistent_HEAD \ + int size; \ + int len; +#else +#define sizedcontainer_HEAD \ + PyObject_HEAD \ + int size; \ + int len; +#endif + +/* Nothing is actually of type Sized, but (pointers to) BTree nodes and + * Buckets can be cast to Sized* in contexts that only need to examine + * the members common to all sized containers. + */ +typedef struct Sized_s { + sizedcontainer_HEAD +} Sized; + +#define SIZED(O) ((Sized*)(O)) + +/* A Bucket wraps contiguous vectors of keys and values. Keys are unique, + * and stored in sorted order. The 'values' pointer may be NULL if the + * Bucket is used to implement a set. Buckets serving as leafs of BTrees + * are chained together via 'next', so that the entire BTree contents + * can be traversed in sorted order quickly and easily. + */ +typedef struct Bucket_s { + sizedcontainer_HEAD + struct Bucket_s *next; /* the bucket with the next-larger keys */ + KEY_TYPE *keys; /* 'len' keys, in increasing order */ + VALUE_TYPE *values; /* 'len' corresponding values; NULL if a set */ +} Bucket; + +#define BUCKET(O) ((Bucket*)(O)) + +/* A BTree is complicated. See Maintainer.txt. + */ + +typedef struct BTreeItem_s { + KEY_TYPE key; + Sized *child; /* points to another BTree, or to a Bucket of some sort */ +} BTreeItem; + +typedef struct BTree_s { + sizedcontainer_HEAD + + /* firstbucket points to the bucket containing the smallest key in + * the BTree. This is found by traversing leftmost child pointers + * (data[0].child) until reaching a Bucket. + */ + Bucket *firstbucket; + + /* The BTree points to 'len' children, via the "child" fields of the data + * array. There are len-1 keys in the 'key' fields, stored in increasing + * order. data[0].key is unused. For i in 0 .. len-1, all keys reachable + * from data[i].child are >= data[i].key and < data[i+1].key, at the + * endpoints pretending that data[0].key is minus infinity and + * data[len].key is positive infinity. + */ + BTreeItem *data; +} BTree; + +static PyTypeObject BTreeType; +static PyTypeObject BucketType; + +#define BTREE(O) ((BTree*)(O)) + +/* Use BTREE_SEARCH to find which child pointer to follow. + * RESULT An int lvalue to hold the index i such that SELF->data[i].child + * is the correct node to search next. + * SELF A pointer to a BTree node. + * KEY The key you're looking for, of type KEY_TYPE. + * ONERROR What to do if key comparison raises an exception; for example, + * perhaps 'return NULL'. + * + * See Maintainer.txt for discussion: this is optimized in subtle ways. + * It's recommended that you call this at the start of a routine, waiting + * to check for self->len == 0 after. + */ +#define BTREE_SEARCH(RESULT, SELF, KEY, ONERROR) { \ + int _lo = 0; \ + int _hi = (SELF)->len; \ + int _i, _cmp; \ + for (_i = _hi >> 1; _i > _lo; _i = (_lo + _hi) >> 1) { \ + TEST_KEY_SET_OR(_cmp, (SELF)->data[_i].key, (KEY)) \ + ONERROR; \ + if (_cmp < 0) _lo = _i; \ + else if (_cmp > 0) _hi = _i; \ + else /* equal */ break; \ + } \ + (RESULT) = _i; \ +} + +/* SetIteration structs are used in the internal set iteration protocol. + * When you want to iterate over a set or bucket or BTree (even an + * individual key!), + * 1. Declare a new iterator: + * SetIteration si = {0,0,0}; + * Using "{0,0,0}" or "{0,0}" appear most common. Only one {0} is + * necssary. At least one must be given so that finiSetIteration() works + * correctly even if you don't get around to calling initSetIteration(). + * 2. Initialize it via + * initSetIteration(&si, PyObject *s, useValues) + * It's an error if that returns an int < 0. In case of error on the + * init call, calling finiSetIteration(&si) is optional. But if the + * init call succeeds, you must eventually call finiSetIteration(), + * and whether or not subsequent calls to si.next() fail. + * 3. Get the first element: + * if (si.next(&si) < 0) { there was an error } + * If the set isn't empty, this sets si.position to an int >= 0, + * si.key to the element's key (of type KEY_TYPE), and maybe si.value to + * the element's value (of type VALUE_TYPE). si.value is defined + * iff si.usesValue is true. + * 4. Process all the elements: + * while (si.position >= 0) { + * do something with si.key and/or si.value; + * if (si.next(&si) < 0) { there was an error; } + * } + * 5. Finalize the SetIterator: + * finiSetIteration(&si); + * This is mandatory! si may contain references to iterator objects, + * keys and values, and they must be cleaned up else they'll leak. If + * this were C++ we'd hide that in the destructor, but in C you have to + * do it by hand. + */ +typedef struct SetIteration_s +{ + PyObject *set; /* the set, bucket, BTree, ..., being iterated */ + int position; /* initialized to 0; set to -1 by next() when done */ + int usesValue; /* true iff 'set' has values & we iterate them */ + KEY_TYPE key; /* next() sets to next key */ + VALUE_TYPE value; /* next() may set to next value */ + int (*next)(struct SetIteration_s*); /* function to get next key+value */ +} SetIteration; + +/* Finish the set iteration protocol. This MUST be called by everyone + * who starts a set iteration, unless the initial call to initSetIteration + * failed; in that case, and only that case, calling finiSetIteration is + * optional. + */ +static void +finiSetIteration(SetIteration *i) +{ + assert(i != NULL); + if (i->set == NULL) + return; + Py_DECREF(i->set); + i->set = NULL; /* so it doesn't hurt to call this again */ + + if (i->position > 0) { + /* next() was called at least once, but didn't finish iterating + * (else position would be negative). So the cached key and + * value need to be cleaned up. + */ + DECREF_KEY(i->key); + if (i->usesValue) { + DECREF_VALUE(i->value); + } + } + i->position = -1; /* stop any stray next calls from doing harm */ +} + +static PyObject * +IndexError(int i) +{ + PyObject *v; + + v = PyInt_FromLong(i); + if (!v) { + v = Py_None; + Py_INCREF(v); + } + PyErr_SetObject(PyExc_IndexError, v); + Py_DECREF(v); + return NULL; +} + +/* Search for the bucket immediately preceding *current, in the bucket chain + * starting at first. current, *current and first must not be NULL. + * + * Return: + * 1 *current holds the correct bucket; this is a borrowed reference + * 0 no such bucket exists; *current unaltered + * -1 error; *current unaltered + */ +static int +PreviousBucket(Bucket **current, Bucket *first) +{ + Bucket *trailing = NULL; /* first travels; trailing follows it */ + int result = 0; + + assert(current && *current && first); + if (first == *current) + return 0; + + do { + trailing = first; + PER_USE_OR_RETURN(first, -1); + first = first->next; + + + + + + + + ((trailing)->state==cPersistent_STICKY_STATE + && + ((trailing)->state=cPersistent_UPTODATE_STATE)); + + PER_ACCESSED(trailing); + + + + + + if (first == *current) { + *current = trailing; + result = 1; + break; + } + } while (first); + + return result; +} + +static void * +BTree_Malloc(size_t sz) +{ + void *r; + + ASSERT(sz > 0, "non-positive size malloc", NULL); + + r = malloc(sz); + if (r) + return r; + + PyErr_NoMemory(); + return NULL; +} + +static void * +BTree_Realloc(void *p, size_t sz) +{ + void *r; + + ASSERT(sz > 0, "non-positive size realloc", NULL); + + if (p) + r = realloc(p, sz); + else + r = malloc(sz); + + UNLESS (r) + PyErr_NoMemory(); + + return r; +} + +/* Shared keyword-argument list for BTree/Bucket + * (iter)?(keys|values|items) + */ +static char *search_keywords[] = {"min", "max", + "excludemin", "excludemax", + 0}; + +#include "BTreeItemsTemplate.c" +#include "BucketTemplate.c" +#include "SetTemplate.c" +#include "BTreeTemplate.c" +#include "TreeSetTemplate.c" +#include "SetOpTemplate.c" +#include "MergeTemplate.c" + +static struct PyMethodDef module_methods[] = { + {"difference", (PyCFunction) difference_m, METH_VARARGS, + "difference(o1, o2) -- " + "compute the difference between o1 and o2" + }, + {"union", (PyCFunction) union_m, METH_VARARGS, + "union(o1, o2) -- compute the union of o1 and o2\n" + }, + {"intersection", (PyCFunction) intersection_m, METH_VARARGS, + "intersection(o1, o2) -- " + "compute the intersection of o1 and o2" + }, +#ifdef MERGE + {"weightedUnion", (PyCFunction) wunion_m, METH_VARARGS, + "weightedUnion(o1, o2 [, w1, w2]) -- compute the union of o1 and o2\n" + "\nw1 and w2 are weights." + }, + {"weightedIntersection", (PyCFunction) wintersection_m, METH_VARARGS, + "weightedIntersection(o1, o2 [, w1, w2]) -- " + "compute the intersection of o1 and o2\n" + "\nw1 and w2 are weights." + }, +#endif +#ifdef MULTI_INT_UNION + {"multiunion", (PyCFunction) multiunion_m, METH_VARARGS, + "multiunion(seq) -- compute union of a sequence of integer sets.\n" + "\n" + "Each element of seq must be an integer set, or convertible to one\n" + "via the set iteration protocol. The union returned is an IISet." + }, +#endif + {NULL, NULL} /* sentinel */ +}; + +static char BTree_module_documentation[] = +"\n" +MASTER_ID +BTREEITEMSTEMPLATE_C +"$Id: BTreeModuleTemplate.c 91565 2008-09-27 19:29:35Z jim $\n" +BTREETEMPLATE_C +BUCKETTEMPLATE_C +KEYMACROS_H +MERGETEMPLATE_C +SETOPTEMPLATE_C +SETTEMPLATE_C +TREESETTEMPLATE_C +VALUEMACROS_H +BTREEITEMSTEMPLATE_C +; + +int +init_persist_type(PyTypeObject *type) +{ + type->ob_type = &PyType_Type; + type->tp_base = cPersistenceCAPI->pertype; + + if (PyType_Ready(type) < 0) + return 0; + + return 1; +} + +void +INITMODULE (void) +{ + PyObject *m, *d, *c; + + sort_str = PyString_InternFromString("sort"); + if (!sort_str) + return; + reverse_str = PyString_InternFromString("reverse"); + if (!reverse_str) + return; + __setstate___str = PyString_InternFromString("__setstate__"); + if (!__setstate___str) + return; + _bucket_type_str = PyString_InternFromString("_bucket_type"); + if (!_bucket_type_str) + return; + + /* Grab the ConflictError class */ + m = PyImport_ImportModule("ZODB.POSException"); + if (m != NULL) { + c = PyObject_GetAttrString(m, "BTreesConflictError"); + if (c != NULL) + ConflictError = c; + Py_DECREF(m); + } + + if (ConflictError == NULL) { + Py_INCREF(PyExc_ValueError); + ConflictError=PyExc_ValueError; + } + + /* Initialize the PyPersist_C_API and the type objects. */ + cPersistenceCAPI = PyCObject_Import("persistent.cPersistence", "CAPI"); + if (cPersistenceCAPI == NULL) + return; + + BTreeItemsType.ob_type = &PyType_Type; + BTreeIter_Type.ob_type = &PyType_Type; + BTreeIter_Type.tp_getattro = PyObject_GenericGetAttr; + BucketType.tp_new = PyType_GenericNew; + SetType.tp_new = PyType_GenericNew; + BTreeType.tp_new = PyType_GenericNew; + TreeSetType.tp_new = PyType_GenericNew; + if (!init_persist_type(&BucketType)) + return; + if (!init_persist_type(&BTreeType)) + return; + if (!init_persist_type(&SetType)) + return; + if (!init_persist_type(&TreeSetType)) + return; + + if (PyDict_SetItem(BTreeType.tp_dict, _bucket_type_str, + (PyObject *)&BucketType) < 0) { + fprintf(stderr, "btree failed\n"); + return; + } + if (PyDict_SetItem(TreeSetType.tp_dict, _bucket_type_str, + (PyObject *)&SetType) < 0) { + fprintf(stderr, "bucket failed\n"); + return; + } + + /* Create the module and add the functions */ + m = Py_InitModule4("_" MOD_NAME_PREFIX "BTree", + module_methods, BTree_module_documentation, + (PyObject *)NULL, PYTHON_API_VERSION); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + if (PyDict_SetItemString(d, MOD_NAME_PREFIX "Bucket", + (PyObject *)&BucketType) < 0) + return; + if (PyDict_SetItemString(d, MOD_NAME_PREFIX "BTree", + (PyObject *)&BTreeType) < 0) + return; + if (PyDict_SetItemString(d, MOD_NAME_PREFIX "Set", + (PyObject *)&SetType) < 0) + return; + if (PyDict_SetItemString(d, MOD_NAME_PREFIX "TreeSet", + (PyObject *)&TreeSetType) < 0) + return; + if (PyDict_SetItemString(d, MOD_NAME_PREFIX "TreeIterator", + (PyObject *)&BTreeIter_Type) < 0) + return; + /* We also want to be able to access these constants without the prefix + * so that code can more easily exchange modules (particularly the integer + * and long modules, but also others). The TreeIterator is only internal, + * so we don't bother to expose that. + */ + if (PyDict_SetItemString(d, "Bucket", + (PyObject *)&BucketType) < 0) + return; + if (PyDict_SetItemString(d, "BTree", + (PyObject *)&BTreeType) < 0) + return; + if (PyDict_SetItemString(d, "Set", + (PyObject *)&SetType) < 0) + return; + if (PyDict_SetItemString(d, "TreeSet", + (PyObject *)&TreeSetType) < 0) + return; +#if defined(ZODB_64BIT_INTS) && defined(NEED_LONG_LONG_SUPPORT) + if (PyDict_SetItemString(d, "using64bits", Py_True) < 0) + return; +#else + if (PyDict_SetItemString(d, "using64bits", Py_False) < 0) + return; +#endif +} diff -Nru zope3-3.4.0/src/BTrees/BTreeTemplate.c zope3-3.5~bzr18/src/BTrees/BTreeTemplate.c --- zope3-3.4.0/src/BTrees/BTreeTemplate.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/BTreeTemplate.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2203 @@ +/***************************************************************************** + + Copyright (c) 2001, 2002 Zope Corporation 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 + +****************************************************************************/ + +#define BTREETEMPLATE_C "$Id: BTreeTemplate.c 105216 2009-10-22 14:30:54Z jim $\n" + +/* Sanity-check a BTree. This is a private helper for BTree_check. Return: + * -1 Error. If it's an internal inconsistency in the BTree, + * AssertionError is set. + * 0 No problem found. + * + * nextbucket is the bucket "one beyond the end" of the BTree; the last bucket + * directly reachable from following right child pointers *should* be linked + * to nextbucket (and this is checked). + */ +static int +BTree_check_inner(BTree *self, Bucket *nextbucket) +{ + int i; + Bucket *bucketafter; + Sized *child; + char *errormsg = "internal error"; /* someone should have overriden */ + Sized *activated_child = NULL; + int result = -1; /* until proved innocent */ + +#define CHECK(CONDITION, ERRORMSG) \ + if (!(CONDITION)) { \ + errormsg = (ERRORMSG); \ + goto Error; \ + } + + PER_USE_OR_RETURN(self, -1); + CHECK(self->len >= 0, "BTree len < 0"); + CHECK(self->len <= self->size, "BTree len > size"); + if (self->len == 0) { + /* Empty BTree. */ + CHECK(self->firstbucket == NULL, + "Empty BTree has non-NULL firstbucket"); + result = 0; + goto Done; + } + /* Non-empty BTree. */ + CHECK(self->firstbucket != NULL, "Non-empty BTree has NULL firstbucket"); + + /* Obscure: The first bucket is pointed to at least by self->firstbucket + * and data[0].child of whichever BTree node it's a child of. However, + * if persistence is enabled then the latter BTree node may be a ghost + * at this point, and so its pointers "don't count": we can only rely + * on self's pointers being intact. + */ +#ifdef PERSISTENT + CHECK(self->firstbucket->ob_refcnt >= 1, + "Non-empty BTree firstbucket has refcount < 1"); +#else + CHECK(self->firstbucket->ob_refcnt >= 2, + "Non-empty BTree firstbucket has refcount < 2"); +#endif + + for (i = 0; i < self->len; ++i) { + CHECK(self->data[i].child != NULL, "BTree has NULL child"); + } + + if (SameType_Check(self, self->data[0].child)) { + /* Our children are also BTrees. */ + child = self->data[0].child; + UNLESS (PER_USE(child)) goto Done; + activated_child = child; + CHECK(self->firstbucket == BTREE(child)->firstbucket, + "BTree has firstbucket different than " + "its first child's firstbucket"); + PER_ALLOW_DEACTIVATION(child); + activated_child = NULL; + for (i = 0; i < self->len; ++i) { + child = self->data[i].child; + CHECK(SameType_Check(self, child), + "BTree children have different types"); + if (i == self->len - 1) + bucketafter = nextbucket; + else { + BTree *child2 = BTREE(self->data[i+1].child); + UNLESS (PER_USE(child2)) goto Done; + bucketafter = child2->firstbucket; + PER_ALLOW_DEACTIVATION(child2); + } + if (BTree_check_inner(BTREE(child), bucketafter) < 0) goto Done; + } + } + else { + /* Our children are buckets. */ + CHECK(self->firstbucket == BUCKET(self->data[0].child), + "Bottom-level BTree node has inconsistent firstbucket belief"); + for (i = 0; i < self->len; ++i) { + child = self->data[i].child; + UNLESS (PER_USE(child)) goto Done; + activated_child = child; + CHECK(!SameType_Check(self, child), + "BTree children have different types"); + CHECK(child->len >= 1, "Bucket length < 1"); /* no empty buckets! */ + CHECK(child->len <= child->size, "Bucket len > size"); +#ifdef PERSISTENT + CHECK(child->ob_refcnt >= 1, "Bucket has refcount < 1"); +#else + CHECK(child->ob_refcnt >= 2, "Bucket has refcount < 2"); +#endif + if (i == self->len - 1) + bucketafter = nextbucket; + else + bucketafter = BUCKET(self->data[i+1].child); + CHECK(BUCKET(child)->next == bucketafter, + "Bucket next pointer is damaged"); + PER_ALLOW_DEACTIVATION(child); + activated_child = NULL; + } + } + result = 0; + goto Done; + + Error: + PyErr_SetString(PyExc_AssertionError, errormsg); + result = -1; + Done: + /* No point updating access time -- this isn't a "real" use. */ + PER_ALLOW_DEACTIVATION(self); + if (activated_child) { + PER_ALLOW_DEACTIVATION(activated_child); + } + return result; + +#undef CHECK +} + +/* Sanity-check a BTree. This is the ._check() method. Return: + * NULL Error. If it's an internal inconsistency in the BTree, + * AssertionError is set. + * Py_None No problem found. + */ +static PyObject* +BTree_check(BTree *self) +{ + PyObject *result = NULL; + int i = BTree_check_inner(self, NULL); + + if (i >= 0) { + result = Py_None; + Py_INCREF(result); + } + return result; +} + +/* +** _BTree_get +** +** Search a BTree. +** +** Arguments +** self a pointer to a BTree +** keyarg the key to search for, as a Python object +** has_key true/false; when false, try to return the associated +** value; when true, return a boolean +** Return +** When has_key false: +** If key exists, its associated value. +** If key doesn't exist, NULL and KeyError is set. +** When has_key true: +** A Python int is returned in any case. +** If key exists, the depth of the bucket in which it was found. +** If key doesn't exist, 0. +*/ +static PyObject * +_BTree_get(BTree *self, PyObject *keyarg, int has_key) +{ + KEY_TYPE key; + PyObject *result = NULL; /* guilty until proved innocent */ + int copied = 1; + + COPY_KEY_FROM_ARG(key, keyarg, copied); + UNLESS (copied) return NULL; + + PER_USE_OR_RETURN(self, NULL); + if (self->len == 0) { + /* empty BTree */ + if (has_key) + result = PyInt_FromLong(0); + else + PyErr_SetObject(PyExc_KeyError, keyarg); + } + else { + for (;;) { + int i; + Sized *child; + + BTREE_SEARCH(i, self, key, goto Done); + child = self->data[i].child; + has_key += has_key != 0; /* bump depth counter, maybe */ + if (SameType_Check(self, child)) { + PER_UNUSE(self); + self = BTREE(child); + PER_USE_OR_RETURN(self, NULL); + } + else { + result = _bucket_get(BUCKET(child), keyarg, has_key); + break; + } + } + } + + Done: + PER_UNUSE(self); + return result; +} + +static PyObject * +BTree_get(BTree *self, PyObject *key) +{ + return _BTree_get(self, key, 0); +} + +/* Create a new bucket for the BTree or TreeSet using the class attribute + _bucket_type, which is normally initialized to BucketType or SetType + as appropriate. +*/ +static Sized * +BTree_newBucket(BTree *self) +{ + PyObject *factory; + Sized *result; + + /* _bucket_type_str defined in BTreeModuleTemplate.c */ + factory = PyObject_GetAttr((PyObject *)self->ob_type, _bucket_type_str); + if (factory == NULL) + return NULL; + /* TODO: Should we check that the factory actually returns something + of the appropriate type? How? The C code here is going to + depend on any custom bucket type having the same layout at the + C level. + */ + result = SIZED(PyObject_CallObject(factory, NULL)); + Py_DECREF(factory); + return result; +} + +/* + * Move data from the current BTree, from index onward, to the newly created + * BTree 'next'. self and next must both be activated. If index is OOB (< 0 + * or >= self->len), use self->len / 2 as the index (i.e., split at the + * midpoint). self must have at least 2 children on entry, and index must + * be such that self and next each have at least one child at exit. self's + * accessed time is updated. + * + * Return: + * -1 error + * 0 OK + */ +static int +BTree_split(BTree *self, int index, BTree *next) +{ + int next_size; + Sized *child; + + if (index < 0 || index >= self->len) + index = self->len / 2; + + next_size = self->len - index; + ASSERT(index > 0, "split creates empty tree", -1); + ASSERT(next_size > 0, "split creates empty tree", -1); + + next->data = BTree_Malloc(sizeof(BTreeItem) * next_size); + if (!next->data) + return -1; + memcpy(next->data, self->data + index, sizeof(BTreeItem) * next_size); + next->size = next_size; /* but don't set len until we succeed */ + + /* Set next's firstbucket. self->firstbucket is still correct. */ + child = next->data[0].child; + if (SameType_Check(self, child)) { + PER_USE_OR_RETURN(child, -1); + next->firstbucket = BTREE(child)->firstbucket; + PER_UNUSE(child); + } + else + next->firstbucket = BUCKET(child); + Py_INCREF(next->firstbucket); + + next->len = next_size; + self->len = index; + return PER_CHANGED(self) >= 0 ? 0 : -1; +} + + +/* Fwd decl -- BTree_grow and BTree_split_root reference each other. */ +static int BTree_grow(BTree *self, int index, int noval); + +/* Split the root. This is a little special because the root isn't a child + * of anything else, and the root needs to retain its object identity. So + * this routine moves the root's data into a new child, and splits the + * latter. This leaves the root with two children. + * + * Return: + * 0 OK + * -1 error + * + * CAUTION: The caller must call PER_CHANGED on self. + */ +static int +BTree_split_root(BTree *self, int noval) +{ + BTree *child; + BTreeItem *d; + + /* Create a child BTree, and a new data vector for self. */ + child = BTREE(PyObject_CallObject(OBJECT(self->ob_type), NULL)); + if (!child) return -1; + + d = BTree_Malloc(sizeof(BTreeItem) * 2); + if (!d) { + Py_DECREF(child); + return -1; + } + + /* Move our data to new BTree. */ + child->size = self->size; + child->len = self->len; + child->data = self->data; + child->firstbucket = self->firstbucket; + Py_INCREF(child->firstbucket); + + /* Point self to child and split the child. */ + self->data = d; + self->len = 1; + self->size = 2; + self->data[0].child = SIZED(child); /* transfers reference ownership */ + return BTree_grow(self, 0, noval); +} + +/* +** BTree_grow +** +** Grow a BTree +** +** Arguments: self The BTree +** index self->data[index].child needs to be split. index +** must be 0 if self is empty (len == 0), and a new +** empty bucket is created then. +** noval Boolean; is this a set (true) or mapping (false)? +** +** Returns: 0 on success +** -1 on failure +** +** CAUTION: If self is empty on entry, this routine adds an empty bucket. +** That isn't a legitimate BTree; if the caller doesn't put something in +** in the bucket (say, because of a later error), the BTree must be cleared +** to get rid of the empty bucket. +*/ +static int +BTree_grow(BTree *self, int index, int noval) +{ + int i; + Sized *v, *e = 0; + BTreeItem *d; + + if (self->len == self->size) { + if (self->size) { + d = BTree_Realloc(self->data, sizeof(BTreeItem) * self->size * 2); + if (d == NULL) + return -1; + self->data = d; + self->size *= 2; + } + else { + d = BTree_Malloc(sizeof(BTreeItem) * 2); + if (d == NULL) + return -1; + self->data = d; + self->size = 2; + } + } + + if (self->len) { + d = self->data + index; + v = d->child; + /* Create a new object of the same type as the target value */ + e = (Sized *)PyObject_CallObject((PyObject *)v->ob_type, NULL); + if (e == NULL) + return -1; + + UNLESS(PER_USE(v)) { + Py_DECREF(e); + return -1; + } + + /* Now split between the original (v) and the new (e) at the midpoint*/ + if (SameType_Check(self, v)) + i = BTree_split((BTree *)v, -1, (BTree *)e); + else + i = bucket_split((Bucket *)v, -1, (Bucket *)e); + PER_ALLOW_DEACTIVATION(v); + + if (i < 0) { + Py_DECREF(e); + assert(PyErr_Occurred()); + return -1; + } + + index++; + d++; + if (self->len > index) /* Shift up the old values one array slot */ + memmove(d+1, d, sizeof(BTreeItem)*(self->len-index)); + + if (SameType_Check(self, v)) { + COPY_KEY(d->key, BTREE(e)->data->key); + + /* We take the unused reference from e, so there's no + reason to INCREF! + */ + /* INCREF_KEY(self->data[1].key); */ + } + else { + COPY_KEY(d->key, BUCKET(e)->keys[0]); + INCREF_KEY(d->key); + } + d->child = e; + self->len++; + + if (self->len >= MAX_BTREE_SIZE(self) * 2) /* the root is huge */ + return BTree_split_root(self, noval); + } + else { + /* The BTree is empty. Create an empty bucket. See CAUTION in + * the comments preceding. + */ + assert(index == 0); + d = self->data; + d->child = BTree_newBucket(self); + if (d->child == NULL) + return -1; + self->len = 1; + Py_INCREF(d->child); + self->firstbucket = (Bucket *)d->child; + } + + return 0; +} + +/* Return the rightmost bucket reachable from following child pointers + * from self. The caller gets a new reference to this bucket. Note that + * bucket 'next' pointers are not followed: if self is an interior node + * of a BTree, this returns the rightmost bucket in that node's subtree. + * In case of error, returns NULL. + * + * self must not be a ghost; this isn't checked. The result may be a ghost. + * + * Pragmatics: Note that the rightmost bucket's last key is the largest + * key in self's subtree. + */ +static Bucket * +BTree_lastBucket(BTree *self) +{ + Sized *pchild; + Bucket *result; + + UNLESS (self->data && self->len) { + IndexError(-1); /* is this the best action to take? */ + return NULL; + } + + pchild = self->data[self->len - 1].child; + if (SameType_Check(self, pchild)) { + self = BTREE(pchild); + PER_USE_OR_RETURN(self, NULL); + result = BTree_lastBucket(self); + PER_UNUSE(self); + } + else { + Py_INCREF(pchild); + result = BUCKET(pchild); + } + return result; +} + +static int +BTree_deleteNextBucket(BTree *self) +{ + Bucket *b; + + UNLESS (PER_USE(self)) return -1; + + b = BTree_lastBucket(self); + if (b == NULL) + goto err; + if (Bucket_deleteNextBucket(b) < 0) + goto err; + + Py_DECREF(b); + PER_UNUSE(self); + + return 0; + + err: + Py_XDECREF(b); + PER_ALLOW_DEACTIVATION(self); + return -1; +} + +/* +** _BTree_clear +** +** Clears out all of the values in the BTree (firstbucket, keys, and children); +** leaving self an empty BTree. +** +** Arguments: self The BTree +** +** Returns: 0 on success +** -1 on failure +** +** Internal: Deallocation order is important. The danger is that a long +** list of buckets may get freed "at once" via decref'ing the first bucket, +** in which case a chain of consequenct Py_DECREF calls may blow the stack. +** Luckily, every bucket has a refcount of at least two, one due to being a +** BTree node's child, and another either because it's not the first bucket in +** the chain (so the preceding bucket points to it), or because firstbucket +** points to it. By clearing in the natural depth-first, left-to-right +** order, the BTree->bucket child pointers prevent Py_DECREF(bucket->next) +** calls from freeing bucket->next, and the maximum stack depth is equal +** to the height of the tree. +**/ +static int +_BTree_clear(BTree *self) +{ + const int len = self->len; + + if (self->firstbucket) { + /* Obscure: The first bucket is pointed to at least by + * self->firstbucket and data[0].child of whichever BTree node it's + * a child of. However, if persistence is enabled then the latter + * BTree node may be a ghost at this point, and so its pointers "don't + * count": we can only rely on self's pointers being intact. + */ +#ifdef PERSISTENT + ASSERT(self->firstbucket->ob_refcnt > 0, + "Invalid firstbucket pointer", -1); +#else + ASSERT(self->firstbucket->ob_refcnt > 1, + "Invalid firstbucket pointer", -1); +#endif + Py_DECREF(self->firstbucket); + self->firstbucket = NULL; + } + + if (self->data) { + int i; + if (len > 0) { /* 0 is special because key 0 is trash */ + Py_DECREF(self->data[0].child); + } + + for (i = 1; i < len; i++) { +#ifdef KEY_TYPE_IS_PYOBJECT + DECREF_KEY(self->data[i].key); +#endif + Py_DECREF(self->data[i].child); + } + free(self->data); + self->data = NULL; + } + + self->len = self->size = 0; + return 0; +} + +/* + Set (value != 0) or delete (value=0) a tree item. + + If unique is non-zero, then only change if the key is + new. + + If noval is non-zero, then don't set a value (the tree + is a set). + + Return: + -1 error + 0 successful, and number of entries didn't change + >0 successful, and number of entries did change + + Internal + There are two distinct return values > 0: + + 1 Successful, number of entries changed, but firstbucket did not go away. + + 2 Successful, number of entries changed, firstbucket did go away. + This can only happen on a delete (value == NULL). The caller may + need to change its own firstbucket pointer, and in any case *someone* + needs to adjust the 'next' pointer of the bucket immediately preceding + the bucket that went away (it needs to point to the bucket immediately + following the bucket that went away). +*/ +static int +_BTree_set(BTree *self, PyObject *keyarg, PyObject *value, + int unique, int noval) +{ + int changed = 0; /* did I mutate? */ + int min; /* index of child I searched */ + BTreeItem *d; /* self->data[min] */ + int childlength; /* len(self->data[min].child) */ + int status; /* our return value; and return value from callee */ + int self_was_empty; /* was self empty at entry? */ + + KEY_TYPE key; + int copied = 1; + + COPY_KEY_FROM_ARG(key, keyarg, copied); + if (!copied) return -1; + + PER_USE_OR_RETURN(self, -1); + + self_was_empty = self->len == 0; + if (self_was_empty) { + /* We're empty. Make room. */ + if (value) { + if (BTree_grow(self, 0, noval) < 0) + goto Error; + } + else { + /* Can't delete a key from an empty BTree. */ + PyErr_SetObject(PyExc_KeyError, keyarg); + goto Error; + } + } + + /* Find the right child to search, and hand the work off to it. */ + BTREE_SEARCH(min, self, key, goto Error); + d = self->data + min; + + if (SameType_Check(self, d->child)) + status = _BTree_set(BTREE(d->child), keyarg, value, unique, noval); + else { + int bucket_changed = 0; + status = _bucket_set(BUCKET(d->child), keyarg, + value, unique, noval, &bucket_changed); +#ifdef PERSISTENT + /* If a BTree contains only a single bucket, BTree.__getstate__() + * includes the bucket's entire state, and the bucket doesn't get + * an oid of its own. So if we have a single oid-less bucket that + * changed, it's *our* oid that should be marked as changed -- the + * bucket doesn't have one. + */ + if (bucket_changed + && self->len == 1 + && self->data[0].child->oid == NULL) + { + changed = 1; + } +#endif + } + if (status == 0) goto Done; + if (status < 0) goto Error; + assert(status == 1 || status == 2); + + /* The child changed size. Get its new size. Note that since the tree + * rooted at the child changed size, so did the tree rooted at self: + * our status must be >= 1 too. + */ + UNLESS(PER_USE(d->child)) goto Error; + childlength = d->child->len; + PER_UNUSE(d->child); + + if (value) { + /* A bucket got bigger -- if it's "too big", split it. */ + int toobig; + + assert(status == 1); /* can be 2 only on deletes */ + if (SameType_Check(self, d->child)) + toobig = childlength > MAX_BTREE_SIZE(d->child); + else + toobig = childlength > MAX_BUCKET_SIZE(d->child); + + if (toobig) { + if (BTree_grow(self, min, noval) < 0) goto Error; + changed = 1; /* BTree_grow mutated self */ + } + goto Done; /* and status still == 1 */ + } + + /* A bucket got smaller. This is much harder, and despite that we + * don't try to rebalance the tree. + */ + + if (min && childlength) + { /* We removed a key. but the node child is non-empty. If the + deleted key is the node key, then update the node key using + the smallest key of the node child. + + This doesn't apply to the 0th node, whos key is unused. + */ + int _cmp = 1; + TEST_KEY_SET_OR(_cmp, key, d->key) goto Error; + if (_cmp == 0) + { /* Need to replace key with first key from child */ + Bucket *bucket; + + if (SameType_Check(self, d->child)) + { + UNLESS(PER_USE(d->child)) goto Error; + bucket = BTREE(d->child)->firstbucket; + PER_UNUSE(d->child); + } + else + bucket = BUCKET(d->child); + + UNLESS(PER_USE(bucket)) goto Error; + DECREF_KEY(d->key); + COPY_KEY(d->key, bucket->keys[0]); + INCREF_KEY(d->key); + PER_UNUSE(bucket); + if (PER_CHANGED(self) < 0) goto Error; + } + } + + if (status == 2) { /* this is the last reference to child status */ + /* Two problems to solve: May have to adjust our own firstbucket, + * and the bucket that went away needs to get unlinked. + */ + if (min) { + /* This wasn't our firstbucket, so no need to adjust ours (note + * that it can't be the firstbucket of any node above us either). + * Tell "the tree to the left" to do the unlinking. + */ + if (BTree_deleteNextBucket(BTREE(d[-1].child)) < 0) goto Error; + status = 1; /* we solved the child's firstbucket problem */ + } + else { + /* This was our firstbucket. Update to new firstbucket value. */ + Bucket *nextbucket; + UNLESS(PER_USE(d->child)) goto Error; + nextbucket = BTREE(d->child)->firstbucket; + PER_UNUSE(d->child); + + Py_XINCREF(nextbucket); + Py_DECREF(self->firstbucket); + self->firstbucket = nextbucket; + changed = 1; + + /* The caller has to do the unlinking -- we can't. Also, since + * it was our firstbucket, it may also be theirs. + */ + assert(status == 2); + } + } + + /* If the child isn't empty, we're done! We did all that was possible for + * us to do with the firstbucket problems the child gave us, and since the + * child isn't empty don't create any new firstbucket problems of our own. + */ + if (childlength) goto Done; + + /* The child became empty: we need to remove it from self->data. + * But first, if we're a bottom-level node, we've got more bucket-fiddling + * to set up. + */ + if (!SameType_Check(self, d->child)) { + /* We're about to delete a bucket. */ + if (min) { + /* It's not our first bucket, so we can tell the previous + * bucket to adjust its reference to it. It can't be anyone + * else's first bucket either, so the caller needn't do anything. + */ + if (Bucket_deleteNextBucket(BUCKET(d[-1].child)) < 0) goto Error; + /* status should be 1, and already is: if it were 2, the + * block above would have set it to 1 in its min != 0 branch. + */ + assert(status == 1); + } + else { + Bucket *nextbucket; + /* It's our first bucket. We can't unlink it directly. */ + /* 'changed' will be set true by the deletion code following. */ + UNLESS(PER_USE(d->child)) goto Error; + nextbucket = BUCKET(d->child)->next; + PER_UNUSE(d->child); + + Py_XINCREF(nextbucket); + Py_DECREF(self->firstbucket); + self->firstbucket = nextbucket; + + status = 2; /* we're giving our caller a new firstbucket problem */ + } + } + + /* Remove the child from self->data. */ + Py_DECREF(d->child); +#ifdef KEY_TYPE_IS_PYOBJECT + if (min) { + DECREF_KEY(d->key); + } + else if (self->len > 1) { + /* We're deleting the first child of a BTree with more than one + * child. The key at d+1 is about to be shifted into slot 0, + * and hence never to be referenced again (the key in slot 0 is + * trash). + */ + DECREF_KEY((d+1)->key); + } + /* Else min==0 and len==1: we're emptying the BTree entirely, and + * there is no key in need of decrefing. + */ +#endif + --self->len; + if (min < self->len) + memmove(d, d+1, (self->len - min) * sizeof(BTreeItem)); + changed = 1; + + Done: +#ifdef PERSISTENT + if (changed) { + if (PER_CHANGED(self) < 0) goto Error; + } +#endif + PER_UNUSE(self); + return status; + + Error: + assert(PyErr_Occurred()); + if (self_was_empty) { + /* BTree_grow may have left the BTree in an invalid state. Make + * sure the tree is a legitimate empty tree. + */ + _BTree_clear(self); + } + PER_UNUSE(self); + return -1; +} + +/* +** BTree_setitem +** +** wrapper for _BTree_set +** +** Arguments: self The BTree +** key The key to insert +** v The value to insert +** +** Returns -1 on failure +** 0 on success +*/ +static int +BTree_setitem(BTree *self, PyObject *key, PyObject *v) +{ + if (_BTree_set(self, key, v, 0, 0) < 0) + return -1; + return 0; +} + +#ifdef PERSISTENT +static PyObject * +BTree__p_deactivate(BTree *self, PyObject *args, PyObject *keywords) +{ + int ghostify = 1; + PyObject *force = NULL; + + if (args && PyTuple_GET_SIZE(args) > 0) { + PyErr_SetString(PyExc_TypeError, + "_p_deactivate takes not positional arguments"); + return NULL; + } + if (keywords) { + int size = PyDict_Size(keywords); + force = PyDict_GetItemString(keywords, "force"); + if (force) + size--; + if (size) { + PyErr_SetString(PyExc_TypeError, + "_p_deactivate only accepts keyword arg force"); + return NULL; + } + } + + if (self->jar && self->oid) { + ghostify = self->state == cPersistent_UPTODATE_STATE; + if (!ghostify && force) { + if (PyObject_IsTrue(force)) + ghostify = 1; + if (PyErr_Occurred()) + return NULL; + } + if (ghostify) { + if (_BTree_clear(self) < 0) + return NULL; + PER_GHOSTIFY(self); + } + } + + Py_INCREF(Py_None); + return Py_None; +} +#endif + +static PyObject * +BTree_clear(BTree *self) +{ + UNLESS (PER_USE(self)) return NULL; + + if (self->len) + { + if (_BTree_clear(self) < 0) + goto err; + if (PER_CHANGED(self) < 0) + goto err; + } + + PER_UNUSE(self); + + Py_INCREF(Py_None); + return Py_None; + + err: + PER_UNUSE(self); + return NULL; +} + +/* + * Return: + * + * For an empty BTree (self->len == 0), None. + * + * For a BTree with one child (self->len == 1), and that child is a bucket, + * and that bucket has a NULL oid, a one-tuple containing a one-tuple + * containing the bucket's state: + * + * ( + * ( + * child[0].__getstate__(), + * ), + * ) + * + * Else a two-tuple. The first element is a tuple interleaving the BTree's + * keys and direct children, of size 2*self->len - 1 (key[0] is unused and + * is not saved). The second element is the firstbucket: + * + * ( + * (child[0], key[1], child[1], key[2], child[2], ..., + * key[len-1], child[len-1]), + * self->firstbucket + * ) + * + * In the above, key[i] means self->data[i].key, and similarly for child[i]. + */ +static PyObject * +BTree_getstate(BTree *self) +{ + PyObject *r = NULL; + PyObject *o; + int i, l; + + UNLESS (PER_USE(self)) return NULL; + + if (self->len) { + r = PyTuple_New(self->len * 2 - 1); + if (r == NULL) + goto err; + + if (self->len == 1 + && self->data->child->ob_type != self->ob_type +#ifdef PERSISTENT + && BUCKET(self->data->child)->oid == NULL +#endif + ) { + /* We have just one bucket. Save its data directly. */ + o = bucket_getstate((Bucket *)self->data->child); + if (o == NULL) + goto err; + PyTuple_SET_ITEM(r, 0, o); + ASSIGN(r, Py_BuildValue("(O)", r)); + } + else { + for (i=0, l=0; i < self->len; i++) { + if (i) { + COPY_KEY_TO_OBJECT(o, self->data[i].key); + PyTuple_SET_ITEM(r, l, o); + l++; + } + o = (PyObject *)self->data[i].child; + Py_INCREF(o); + PyTuple_SET_ITEM(r,l,o); + l++; + } + ASSIGN(r, Py_BuildValue("OO", r, self->firstbucket)); + } + + } + else { + r = Py_None; + Py_INCREF(r); + } + + PER_UNUSE(self); + + return r; + + err: + PER_UNUSE(self); + Py_XDECREF(r); + return NULL; +} + +static int +_BTree_setstate(BTree *self, PyObject *state, int noval) +{ + PyObject *items, *firstbucket = NULL; + BTreeItem *d; + int len, l, i, copied=1; + + if (_BTree_clear(self) < 0) + return -1; + + /* The state of a BTree can be one of the following: + None -- an empty BTree + A one-tuple -- a single bucket btree + A two-tuple -- a BTree with more than one bucket + See comments for BTree_getstate() for the details. + */ + + if (state == Py_None) + return 0; + + if (!PyArg_ParseTuple(state, "O|O:__setstate__", &items, &firstbucket)) + return -1; + + if (!PyTuple_Check(items)) { + PyErr_SetString(PyExc_TypeError, + "tuple required for first state element"); + return -1; + } + + len = PyTuple_Size(items); + if (len < 0) + return -1; + len = (len + 1) / 2; + + assert(len > 0); /* If the BTree is empty, it's state is None. */ + assert(self->size == 0); /* We called _BTree_clear(). */ + + self->data = BTree_Malloc(sizeof(BTreeItem) * len); + if (self->data == NULL) + return -1; + self->size = len; + + for (i = 0, d = self->data, l = 0; i < len; i++, d++) { + PyObject *v; + if (i) { /* skip the first key slot */ + COPY_KEY_FROM_ARG(d->key, PyTuple_GET_ITEM(items, l), copied); + l++; + if (!copied) + return -1; + INCREF_KEY(d->key); + } + v = PyTuple_GET_ITEM(items, l); + if (PyTuple_Check(v)) { + /* Handle the special case in __getstate__() for a BTree + with a single bucket. */ + d->child = BTree_newBucket(self); + if (!d->child) + return -1; + if (noval) { + if (_set_setstate(BUCKET(d->child), v) < 0) + return -1; + } + else { + if (_bucket_setstate(BUCKET(d->child), v) < 0) + return -1; + } + } + else { + d->child = (Sized *)v; + Py_INCREF(v); + } + l++; + } + + if (!firstbucket) + firstbucket = (PyObject *)self->data->child; + + if (!PyObject_IsInstance(firstbucket, (PyObject *) + (noval ? &SetType : &BucketType))) { + PyErr_SetString(PyExc_TypeError, + "No firstbucket in non-empty BTree"); + return -1; + } + self->firstbucket = BUCKET(firstbucket); + Py_INCREF(firstbucket); +#ifndef PERSISTENT + /* firstbucket is also the child of some BTree node, but that node may + * be a ghost if persistence is enabled. + */ + assert(self->firstbucket->ob_refcnt > 1); +#endif + self->len = len; + + return 0; +} + +static PyObject * +BTree_setstate(BTree *self, PyObject *arg) +{ + int r; + + PER_PREVENT_DEACTIVATION(self); + r = _BTree_setstate(self, arg, 0); + PER_UNUSE(self); + + if (r < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef PERSISTENT + +/* Recognize the special cases of a BTree that's empty or contains a single + * bucket. In the former case, return a borrowed reference to Py_None. + * In this single-bucket case, the bucket state is embedded directly in the + * BTree state, like so: + * + * ( + * ( + * thebucket.__getstate__(), + * ), + * ) + * + * When this obtains, return a borrowed reference to thebucket.__getstate__(). + * Else return NULL with an exception set. The exception should always be + * ConflictError then, but may be TypeError if the state makes no sense at all + * for a BTree (corrupted or hostile state). + */ +PyObject * +get_bucket_state(PyObject *t) +{ + if (t == Py_None) + return Py_None; /* an empty BTree */ + if (! PyTuple_Check(t)) { + PyErr_SetString(PyExc_TypeError, + "_p_resolveConflict: expected tuple or None for state"); + return NULL; + } + + if (PyTuple_GET_SIZE(t) == 2) { + /* A non-degenerate BTree. */ + return merge_error(-1, -1, -1, 11); + } + + /* We're in the one-bucket case. */ + + if (PyTuple_GET_SIZE(t) != 1) { + PyErr_SetString(PyExc_TypeError, + "_p_resolveConflict: expected 1- or 2-tuple for state"); + return NULL; + } + + t = PyTuple_GET_ITEM(t, 0); + if (! PyTuple_Check(t) || PyTuple_GET_SIZE(t) != 1) { + PyErr_SetString(PyExc_TypeError, + "_p_resolveConflict: expected 1-tuple containing " + "bucket state"); + return NULL; + } + + t = PyTuple_GET_ITEM(t, 0); + if (! PyTuple_Check(t)) { + PyErr_SetString(PyExc_TypeError, + "_p_resolveConflict: expected tuple for bucket state"); + return NULL; + } + + return t; +} + +/* Tricky. The only kind of BTree conflict we can actually potentially + * resolve is the special case of a BTree containing a single bucket, + * in which case this becomes a fancy way of calling the bucket conflict + * resolution code. + */ +static PyObject * +BTree__p_resolveConflict(BTree *self, PyObject *args) +{ + PyObject *s[3]; + PyObject *x, *y, *z; + + if (!PyArg_ParseTuple(args, "OOO", &x, &y, &z)) + return NULL; + + s[0] = get_bucket_state(x); + if (s[0] == NULL) + return NULL; + s[1] = get_bucket_state(y); + if (s[1] == NULL) + return NULL; + s[2] = get_bucket_state(z); + if (s[2] == NULL) + return NULL; + + if (PyObject_IsInstance((PyObject *)self, (PyObject *)&BTreeType)) + x = _bucket__p_resolveConflict(OBJECT(&BucketType), s); + else + x = _bucket__p_resolveConflict(OBJECT(&SetType), s); + + if (x == NULL) + return NULL; + + return Py_BuildValue("((N))", x); +} +#endif + +/* + BTree_findRangeEnd -- Find one end, expressed as a bucket and + position, for a range search. + + If low, return bucket and index of the smallest item >= key, + otherwise return bucket and index of the largest item <= key. + + If exclude_equal, exact matches aren't acceptable; if one is found, + move right if low, or left if !low (this is for range searches exclusive + of an endpoint). + + Return: + -1 Error; offset and bucket unchanged + 0 Not found; offset and bucket unchanged + 1 Correct bucket and offset stored; the caller owns a new reference + to the bucket. + + Internal: + We do binary searches in BTree nodes downward, at each step following + C(i) where K(i) <= key < K(i+1). As always, K(i) <= C(i) < K(i+1) too. + (See Maintainer.txt for the meaning of that notation.) That eventually + leads to a bucket where we do Bucket_findRangeEnd. That usually works, + but there are two cases where it can fail to find the correct answer: + + 1. On a low search, we find a bucket with keys >= K(i), but that doesn't + imply there are keys in the bucket >= key. For example, suppose + a bucket has keys in 1..100, its successor's keys are in 200..300, and + we're doing a low search on 150. We'll end up in the first bucket, + but there are no keys >= 150 in it. K(i+1) > key, though, and all + the keys in C(i+1) >= K(i+1) > key, so the first key in the next + bucket (if any) is the correct result. This is easy to find by + following the bucket 'next' pointer. + + 2. On a high search, again that the keys in the bucket are >= K(i) + doesn't imply that any key in the bucket is <= key, but it's harder + for this to fail (and an earlier version of this routine didn't + catch it): if K(i) itself is in the bucket, it works (then + K(i) <= key is *a* key in the bucket that's in the desired range). + But when keys get deleted from buckets, they aren't also deleted from + BTree nodes, so there's no guarantee that K(i) is in the bucket. + For example, delete the smallest key S from some bucket, and S + remains in the interior BTree nodes. Do a high search for S, and + the BTree nodes direct the search to the bucket S used to be in, + but all keys remaining in that bucket are > S. The largest key in + the *preceding* bucket (if any) is < K(i), though, and K(i) <= key, + so the largest key in the preceding bucket is < key and so is the + proper result. + + This is harder to get at efficiently, as buckets are linked only in + the increasing direction. While we're searching downward, + deepest_smaller is set to the node deepest in the tree where + we *could* have gone to the left of C(i). The rightmost bucket in + deepest_smaller's subtree is the bucket preceding the bucket we find + at first. This is clumsy to get at, but efficient. +*/ +static int +BTree_findRangeEnd(BTree *self, PyObject *keyarg, int low, int exclude_equal, + Bucket **bucket, int *offset) { + Sized *deepest_smaller = NULL; /* last possibility to move left */ + int deepest_smaller_is_btree = 0; /* Boolean; if false, it's a bucket */ + Bucket *pbucket; + int self_got_rebound = 0; /* Boolean; when true, deactivate self */ + int result = -1; /* Until proven innocent */ + int i; + KEY_TYPE key; + int copied = 1; + + COPY_KEY_FROM_ARG(key, keyarg, copied); + UNLESS (copied) return -1; + + /* We don't need to: PER_USE_OR_RETURN(self, -1); + because the caller does. */ + UNLESS (self->data && self->len) return 0; + + /* Search downward until hitting a bucket, stored in pbucket. */ + for (;;) { + Sized *pchild; + int pchild_is_btree; + + BTREE_SEARCH(i, self, key, goto Done); + pchild = self->data[i].child; + pchild_is_btree = SameType_Check(self, pchild); + if (i) { + deepest_smaller = self->data[i-1].child; + deepest_smaller_is_btree = pchild_is_btree; + } + + if (pchild_is_btree) { + if (self_got_rebound) { + PER_UNUSE(self); + } + self = BTREE(pchild); + self_got_rebound = 1; + PER_USE_OR_RETURN(self, -1); + } + else { + pbucket = BUCKET(pchild); + break; + } + } + + /* Search the bucket for a suitable key. */ + i = Bucket_findRangeEnd(pbucket, keyarg, low, exclude_equal, offset); + if (i < 0) + goto Done; + if (i > 0) { + Py_INCREF(pbucket); + *bucket = pbucket; + result = 1; + goto Done; + } + /* This may be one of the two difficult cases detailed in the comments. */ + if (low) { + Bucket *next; + + UNLESS(PER_USE(pbucket)) goto Done; + next = pbucket->next; + if (next) { + result = 1; + Py_INCREF(next); + *bucket = next; + *offset = 0; + } + else + result = 0; + PER_UNUSE(pbucket); + } + /* High-end search: if it's possible to go left, do so. */ + else if (deepest_smaller) { + if (deepest_smaller_is_btree) { + UNLESS(PER_USE(deepest_smaller)) goto Done; + /* We own the reference this returns. */ + pbucket = BTree_lastBucket(BTREE(deepest_smaller)); + PER_UNUSE(deepest_smaller); + if (pbucket == NULL) goto Done; /* error */ + } + else { + pbucket = BUCKET(deepest_smaller); + Py_INCREF(pbucket); + } + UNLESS(PER_USE(pbucket)) goto Done; + result = 1; + *bucket = pbucket; /* transfer ownership to caller */ + *offset = pbucket->len - 1; + PER_UNUSE(pbucket); + } + else + result = 0; /* simply not found */ + + Done: + if (self_got_rebound) { + PER_UNUSE(self); + } + return result; +} + +static PyObject * +BTree_maxminKey(BTree *self, PyObject *args, int min) +{ + PyObject *key=0; + Bucket *bucket = NULL; + int offset, rc; + int empty_tree = 1; + + UNLESS (PyArg_ParseTuple(args, "|O", &key)) return NULL; + + UNLESS (PER_USE(self)) return NULL; + + UNLESS (self->data && self->len) goto empty; + + /* Find the range */ + + if (key) + { + if ((rc = BTree_findRangeEnd(self, key, min, 0, &bucket, &offset)) <= 0) + { + if (rc < 0) goto err; + empty_tree = 0; + goto empty; + } + PER_UNUSE(self); + UNLESS (PER_USE(bucket)) + { + Py_DECREF(bucket); + return NULL; + } + } + else if (min) + { + bucket = self->firstbucket; + PER_UNUSE(self); + PER_USE_OR_RETURN(bucket, NULL); + Py_INCREF(bucket); + offset = 0; + } + else + { + bucket = BTree_lastBucket(self); + PER_UNUSE(self); + UNLESS (PER_USE(bucket)) + { + Py_DECREF(bucket); + return NULL; + } + assert(bucket->len); + offset = bucket->len - 1; + } + + COPY_KEY_TO_OBJECT(key, bucket->keys[offset]); + PER_UNUSE(bucket); + Py_DECREF(bucket); + + return key; + + empty: + PyErr_SetString(PyExc_ValueError, + empty_tree ? "empty tree" : + "no key satisfies the conditions"); + err: + PER_UNUSE(self); + if (bucket) + { + PER_UNUSE(bucket); + Py_DECREF(bucket); + } + return NULL; +} + +static PyObject * +BTree_minKey(BTree *self, PyObject *args) +{ + return BTree_maxminKey(self, args, 1); +} + +static PyObject * +BTree_maxKey(BTree *self, PyObject *args) +{ + return BTree_maxminKey(self, args, 0); +} + +/* +** BTree_rangeSearch +** +** Generates a BTreeItems object based on the two indexes passed in, +** being the range between them. +** +*/ +static PyObject * +BTree_rangeSearch(BTree *self, PyObject *args, PyObject *kw, char type) +{ + PyObject *min = Py_None; + PyObject *max = Py_None; + int excludemin = 0; + int excludemax = 0; + int rc; + Bucket *lowbucket = NULL; + Bucket *highbucket = NULL; + int lowoffset; + int highoffset; + PyObject *result; + + if (args) { + if (! PyArg_ParseTupleAndKeywords(args, kw, "|OOii", search_keywords, + &min, + &max, + &excludemin, + &excludemax)) + return NULL; + } + + UNLESS (PER_USE(self)) return NULL; + + UNLESS (self->data && self->len) goto empty; + + /* Find the low range */ + if (min != Py_None) { + if ((rc = BTree_findRangeEnd(self, min, 1, excludemin, + &lowbucket, &lowoffset)) <= 0) { + if (rc < 0) goto err; + goto empty; + } + } + else { + lowbucket = self->firstbucket; + lowoffset = 0; + if (excludemin) { + int bucketlen; + UNLESS (PER_USE(lowbucket)) goto err; + bucketlen = lowbucket->len; + PER_UNUSE(lowbucket); + if (bucketlen > 1) + lowoffset = 1; + else if (self->len < 2) + goto empty; + else { /* move to first item in next bucket */ + Bucket *next; + UNLESS (PER_USE(lowbucket)) goto err; + next = lowbucket->next; + PER_UNUSE(lowbucket); + assert(next != NULL); + lowbucket = next; + /* and lowoffset is still 0 */ + assert(lowoffset == 0); + } + } + Py_INCREF(lowbucket); + } + + /* Find the high range */ + if (max != Py_None) { + if ((rc = BTree_findRangeEnd(self, max, 0, excludemax, + &highbucket, &highoffset)) <= 0) { + Py_DECREF(lowbucket); + if (rc < 0) goto err; + goto empty; + } + } + else { + int bucketlen; + highbucket = BTree_lastBucket(self); + assert(highbucket != NULL); /* we know self isn't empty */ + UNLESS (PER_USE(highbucket)) goto err_and_decref_buckets; + bucketlen = highbucket->len; + PER_UNUSE(highbucket); + highoffset = bucketlen - 1; + if (excludemax) { + if (highoffset > 0) + --highoffset; + else if (self->len < 2) + goto empty_and_decref_buckets; + else { /* move to last item of preceding bucket */ + int status; + assert(highbucket != self->firstbucket); + Py_DECREF(highbucket); + status = PreviousBucket(&highbucket, self->firstbucket); + if (status < 0) { + Py_DECREF(lowbucket); + goto err; + } + assert(status > 0); + Py_INCREF(highbucket); + UNLESS (PER_USE(highbucket)) goto err_and_decref_buckets; + highoffset = highbucket->len - 1; + PER_UNUSE(highbucket); + } + } + assert(highoffset >= 0); + } + + /* It's still possible that the range is empty, even if min < max. For + * example, if min=3 and max=4, and 3 and 4 aren't in the BTree, but 2 and + * 5 are, then the low position points to the 5 now and the high position + * points to the 2 now. They're not necessarily even in the same bucket, + * so there's no trick we can play with pointer compares to get out + * cheap in general. + */ + if (lowbucket == highbucket && lowoffset > highoffset) + goto empty_and_decref_buckets; /* definitely empty */ + + /* The buckets differ, or they're the same and the offsets show a non- + * empty range. + */ + if (min != Py_None && max != Py_None && /* both args user-supplied */ + lowbucket != highbucket) /* and different buckets */ { + KEY_TYPE first; + KEY_TYPE last; + int cmp; + + /* Have to check the hard way: see how the endpoints compare. */ + UNLESS (PER_USE(lowbucket)) goto err_and_decref_buckets; + COPY_KEY(first, lowbucket->keys[lowoffset]); + PER_UNUSE(lowbucket); + + UNLESS (PER_USE(highbucket)) goto err_and_decref_buckets; + COPY_KEY(last, highbucket->keys[highoffset]); + PER_UNUSE(highbucket); + + TEST_KEY_SET_OR(cmp, first, last) goto err_and_decref_buckets; + if (cmp > 0) goto empty_and_decref_buckets; + } + + PER_UNUSE(self); + + result = newBTreeItems(type, lowbucket, lowoffset, highbucket, highoffset); + Py_DECREF(lowbucket); + Py_DECREF(highbucket); + return result; + + err_and_decref_buckets: + Py_DECREF(lowbucket); + Py_DECREF(highbucket); + + err: + PER_UNUSE(self); + return NULL; + + empty_and_decref_buckets: + Py_DECREF(lowbucket); + Py_DECREF(highbucket); + + empty: + PER_UNUSE(self); + return newBTreeItems(type, 0, 0, 0, 0); +} + +/* +** BTree_keys +*/ +static PyObject * +BTree_keys(BTree *self, PyObject *args, PyObject *kw) +{ + return BTree_rangeSearch(self, args, kw, 'k'); +} + +/* +** BTree_values +*/ +static PyObject * +BTree_values(BTree *self, PyObject *args, PyObject *kw) +{ + return BTree_rangeSearch(self, args, kw, 'v'); +} + +/* +** BTree_items +*/ +static PyObject * +BTree_items(BTree *self, PyObject *args, PyObject *kw) +{ + return BTree_rangeSearch(self, args, kw, 'i'); +} + +static PyObject * +BTree_byValue(BTree *self, PyObject *omin) +{ + PyObject *r=0, *o=0, *item=0; + VALUE_TYPE min; + VALUE_TYPE v; + int copied=1; + SetIteration it = {0, 0, 1}; + + UNLESS (PER_USE(self)) return NULL; + + COPY_VALUE_FROM_ARG(min, omin, copied); + UNLESS(copied) return NULL; + + UNLESS (r=PyList_New(0)) goto err; + + it.set=BTree_rangeSearch(self, NULL, NULL, 'i'); + UNLESS(it.set) goto err; + + if (nextBTreeItems(&it) < 0) goto err; + + while (it.position >= 0) + { + if (TEST_VALUE(it.value, min) >= 0) + { + UNLESS (item = PyTuple_New(2)) goto err; + + COPY_KEY_TO_OBJECT(o, it.key); + UNLESS (o) goto err; + PyTuple_SET_ITEM(item, 1, o); + + COPY_VALUE(v, it.value); + NORMALIZE_VALUE(v, min); + COPY_VALUE_TO_OBJECT(o, v); + DECREF_VALUE(v); + UNLESS (o) goto err; + PyTuple_SET_ITEM(item, 0, o); + + if (PyList_Append(r, item) < 0) goto err; + Py_DECREF(item); + item = 0; + } + if (nextBTreeItems(&it) < 0) goto err; + } + + item=PyObject_GetAttr(r,sort_str); + UNLESS (item) goto err; + ASSIGN(item, PyObject_CallObject(item, NULL)); + UNLESS (item) goto err; + ASSIGN(item, PyObject_GetAttr(r, reverse_str)); + UNLESS (item) goto err; + ASSIGN(item, PyObject_CallObject(item, NULL)); + UNLESS (item) goto err; + Py_DECREF(item); + + finiSetIteration(&it); + PER_UNUSE(self); + return r; + + err: + PER_UNUSE(self); + Py_XDECREF(r); + finiSetIteration(&it); + Py_XDECREF(item); + return NULL; +} + +/* +** BTree_getm +*/ +static PyObject * +BTree_getm(BTree *self, PyObject *args) +{ + PyObject *key, *d=Py_None, *r; + + UNLESS (PyArg_ParseTuple(args, "O|O", &key, &d)) return NULL; + if ((r=_BTree_get(self, key, 0))) return r; + UNLESS (PyErr_ExceptionMatches(PyExc_KeyError)) return NULL; + PyErr_Clear(); + Py_INCREF(d); + return d; +} + +static PyObject * +BTree_has_key(BTree *self, PyObject *key) +{ + return _BTree_get(self, key, 1); +} + +static PyObject * +BTree_setdefault(BTree *self, PyObject *args) +{ + PyObject *key; + PyObject *failobj; /* default */ + PyObject *value; /* return value */ + + if (! PyArg_UnpackTuple(args, "setdefault", 2, 2, &key, &failobj)) + return NULL; + + value = _BTree_get(self, key, 0); + if (value != NULL) + return value; + + /* The key isn't in the tree. If that's not due to a KeyError exception, + * pass back the unexpected exception. + */ + if (! PyErr_ExceptionMatches(PyExc_KeyError)) + return NULL; + PyErr_Clear(); + + /* Associate `key` with `failobj` in the tree, and return `failobj`. */ + value = failobj; + if (_BTree_set(self, key, failobj, 0, 0) < 0) + value = NULL; + Py_XINCREF(value); + return value; +} + +/* forward declaration */ +static Py_ssize_t +BTree_length_or_nonzero(BTree *self, int nonzero); + +static PyObject * +BTree_pop(BTree *self, PyObject *args) +{ + PyObject *key; + PyObject *failobj = NULL; /* default */ + PyObject *value; /* return value */ + + if (! PyArg_UnpackTuple(args, "pop", 1, 2, &key, &failobj)) + return NULL; + + value = _BTree_get(self, key, 0); + if (value != NULL) { + /* Delete key and associated value. */ + if (_BTree_set(self, key, NULL, 0, 0) < 0) { + Py_DECREF(value); + return NULL;; + } + return value; + } + + /* The key isn't in the tree. If that's not due to a KeyError exception, + * pass back the unexpected exception. + */ + if (! PyErr_ExceptionMatches(PyExc_KeyError)) + return NULL; + + if (failobj != NULL) { + /* Clear the KeyError and return the explicit default. */ + PyErr_Clear(); + Py_INCREF(failobj); + return failobj; + } + + /* No default given. The only difference in this case is the error + * message, which depends on whether the tree is empty. + */ + if (BTree_length_or_nonzero(self, 1) == 0) /* tree is empty */ + PyErr_SetString(PyExc_KeyError, "pop(): BTree is empty"); + return NULL; +} + +/* Search BTree self for key. This is the sq_contains slot of the + * PySequenceMethods. + * + * Return: + * -1 error + * 0 not found + * 1 found + */ +static int +BTree_contains(BTree *self, PyObject *key) +{ + PyObject *asobj = _BTree_get(self, key, 1); + int result = -1; + + if (asobj != NULL) { + result = PyInt_AsLong(asobj) ? 1 : 0; + Py_DECREF(asobj); + } + return result; +} + +static PyObject * +BTree_addUnique(BTree *self, PyObject *args) +{ + int grew; + PyObject *key, *v; + + UNLESS (PyArg_ParseTuple(args, "OO", &key, &v)) return NULL; + + if ((grew=_BTree_set(self, key, v, 1, 0)) < 0) return NULL; + return PyInt_FromLong(grew); +} + +/**************************************************************************/ +/* Iterator support. */ + +/* A helper to build all the iterators for BTrees and TreeSets. + * If args is NULL, the iterator spans the entire structure. Else it's an + * argument tuple, with optional low and high arguments. + * kind is 'k', 'v' or 'i'. + * Returns a BTreeIter object, or NULL if error. + */ +static PyObject * +buildBTreeIter(BTree *self, PyObject *args, PyObject *kw, char kind) +{ + BTreeIter *result = NULL; + BTreeItems *items = (BTreeItems *)BTree_rangeSearch(self, args, kw, kind); + + if (items) { + result = BTreeIter_new(items); + Py_DECREF(items); + } + return (PyObject *)result; +} + +/* The implementation of iter(BTree_or_TreeSet); the BTree tp_iter slot. */ +static PyObject * +BTree_getiter(BTree *self) +{ + return buildBTreeIter(self, NULL, NULL, 'k'); +} + +/* The implementation of BTree.iterkeys(). */ +static PyObject * +BTree_iterkeys(BTree *self, PyObject *args, PyObject *kw) +{ + return buildBTreeIter(self, args, kw, 'k'); +} + +/* The implementation of BTree.itervalues(). */ +static PyObject * +BTree_itervalues(BTree *self, PyObject *args, PyObject *kw) +{ + return buildBTreeIter(self, args, kw, 'v'); +} + +/* The implementation of BTree.iteritems(). */ +static PyObject * +BTree_iteritems(BTree *self, PyObject *args, PyObject *kw) +{ + return buildBTreeIter(self, args, kw, 'i'); +} + +/* End of iterator support. */ + + +/* Caution: Even though the _firstbucket attribute is read-only, a program + could do arbitrary damage to the btree internals. For example, it could + call clear() on a bucket inside a BTree. + + We need to decide if the convenience for inspecting BTrees is worth + the risk. +*/ + +static struct PyMemberDef BTree_members[] = { + {"_firstbucket", T_OBJECT, offsetof(BTree, firstbucket), RO}, + {NULL} +}; + +static struct PyMethodDef BTree_methods[] = { + {"__getstate__", (PyCFunction) BTree_getstate, METH_NOARGS, + "__getstate__() -> state\n\n" + "Return the picklable state of the BTree."}, + + {"__setstate__", (PyCFunction) BTree_setstate, METH_O, + "__setstate__(state)\n\n" + "Set the state of the BTree."}, + + {"has_key", (PyCFunction) BTree_has_key, METH_O, + "has_key(key)\n\n" + "Return true if the BTree contains the given key."}, + + {"keys", (PyCFunction) BTree_keys, METH_KEYWORDS, + "keys([min, max]) -> list of keys\n\n" + "Returns the keys of the BTree. If min and max are supplied, only\n" + "keys greater than min and less than max are returned."}, + + {"values", (PyCFunction) BTree_values, METH_KEYWORDS, + "values([min, max]) -> list of values\n\n" + "Returns the values of the BTree. If min and max are supplied, only\n" + "values corresponding to keys greater than min and less than max are\n" + "returned."}, + + {"items", (PyCFunction) BTree_items, METH_KEYWORDS, + "items([min, max]) -> -- list of key, value pairs\n\n" + "Returns the items of the BTree. If min and max are supplied, only\n" + "items with keys greater than min and less than max are returned."}, + + {"byValue", (PyCFunction) BTree_byValue, METH_O, + "byValue(min) -> list of value, key pairs\n\n" + "Returns list of value, key pairs where the value is >= min. The\n" + "list is sorted by value. Note that items() returns keys in the\n" + "opposite order."}, + + {"get", (PyCFunction) BTree_getm, METH_VARARGS, + "get(key[, default=None]) -> Value for key or default\n\n" + "Return the value or the default if the key is not found."}, + + {"setdefault", (PyCFunction) BTree_setdefault, METH_VARARGS, + "D.setdefault(k, d) -> D.get(k, d), also set D[k]=d if k not in D.\n\n" + "Return the value like get() except that if key is missing, d is both\n" + "returned and inserted into the BTree as the value of k."}, + + {"pop", (PyCFunction) BTree_pop, METH_VARARGS, + "D.pop(k[, d]) -> v, remove key and return the corresponding value.\n\n" + "If key is not found, d is returned if given, otherwise KeyError\n" + "is raised."}, + + {"maxKey", (PyCFunction) BTree_maxKey, METH_VARARGS, + "maxKey([max]) -> key\n\n" + "Return the largest key in the BTree. If max is specified, return\n" + "the largest key <= max."}, + + {"minKey", (PyCFunction) BTree_minKey, METH_VARARGS, + "minKey([mi]) -> key\n\n" + "Return the smallest key in the BTree. If min is specified, return\n" + "the smallest key >= min."}, + + {"clear", (PyCFunction) BTree_clear, METH_NOARGS, + "clear()\n\nRemove all of the items from the BTree."}, + + {"insert", (PyCFunction)BTree_addUnique, METH_VARARGS, + "insert(key, value) -> 0 or 1\n\n" + "Add an item if the key is not already used. Return 1 if the item was\n" + "added, or 0 otherwise."}, + + {"update", (PyCFunction) Mapping_update, METH_O, + "update(collection)\n\n Add the items from the given collection."}, + + {"iterkeys", (PyCFunction) BTree_iterkeys, METH_KEYWORDS, + "B.iterkeys([min[,max]]) -> an iterator over the keys of B"}, + + {"itervalues", (PyCFunction) BTree_itervalues, METH_KEYWORDS, + "B.itervalues([min[,max]]) -> an iterator over the values of B"}, + + {"iteritems", (PyCFunction) BTree_iteritems, METH_KEYWORDS, + "B.iteritems([min[,max]]) -> an iterator over the (key, value) items of B"}, + + {"_check", (PyCFunction) BTree_check, METH_NOARGS, + "Perform sanity check on BTree, and raise exception if flawed."}, + +#ifdef PERSISTENT + {"_p_resolveConflict", (PyCFunction) BTree__p_resolveConflict, + METH_VARARGS, + "_p_resolveConflict() -- Reinitialize from a newly created copy"}, + + {"_p_deactivate", (PyCFunction) BTree__p_deactivate, METH_KEYWORDS, + "_p_deactivate()\n\nReinitialize from a newly created copy."}, +#endif + {NULL, NULL} +}; + +static int +BTree_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *v = NULL; + + if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "BTree", &v)) + return -1; + + if (v) + return update_from_seq(self, v); + else + return 0; +} + +static void +BTree_dealloc(BTree *self) +{ + if (self->state != cPersistent_GHOST_STATE) + _BTree_clear(self); + cPersistenceCAPI->pertype->tp_dealloc((PyObject *)self); +} + +static int +BTree_traverse(BTree *self, visitproc visit, void *arg) +{ + int err = 0; + int i, len; + +#define VISIT(SLOT) \ + if (SLOT) { \ + err = visit((PyObject *)(SLOT), arg); \ + if (err) \ + goto Done; \ + } + + if (self->ob_type == &BTreeType) + assert(self->ob_type->tp_dictoffset == 0); + + /* Call our base type's traverse function. Because BTrees are + * subclasses of Peristent, there must be one. + */ + err = cPersistenceCAPI->pertype->tp_traverse((PyObject *)self, visit, arg); + if (err) + goto Done; + + /* If this is registered with the persistence system, cleaning up cycles + * is the database's problem. It would be horrid to unghostify BTree + * nodes here just to chase pointers every time gc runs. + */ + if (self->state == cPersistent_GHOST_STATE) + goto Done; + + len = self->len; +#ifdef KEY_TYPE_IS_PYOBJECT + /* Keys are Python objects so need to be traversed. Note that the + * key 0 slot is unused and should not be traversed. + */ + for (i = 1; i < len; i++) + VISIT(self->data[i].key); +#endif + + /* Children are always pointers, and child 0 is legit. */ + for (i = 0; i < len; i++) + VISIT(self->data[i].child); + + VISIT(self->firstbucket); + + Done: + return err; + +#undef VISIT +} + +static int +BTree_tp_clear(BTree *self) +{ + if (self->state != cPersistent_GHOST_STATE) + _BTree_clear(self); + return 0; +} + +/* + * Return the number of elements in a BTree. nonzero is a Boolean, and + * when true requests just a non-empty/empty result. Testing for emptiness + * is efficient (constant-time). Getting the true length takes time + * proportional to the number of leaves (buckets). + * + * Return: + * When nonzero true: + * -1 error + * 0 empty + * 1 not empty + * When nonzero false (possibly expensive!): + * -1 error + * >= 0 number of elements. + */ +static Py_ssize_t +BTree_length_or_nonzero(BTree *self, int nonzero) +{ + int result; + Bucket *b; + Bucket *next; + + PER_USE_OR_RETURN(self, -1); + b = self->firstbucket; + PER_UNUSE(self); + if (nonzero) + return b != NULL; + + result = 0; + while (b) { + PER_USE_OR_RETURN(b, -1); + result += b->len; + next = b->next; + PER_UNUSE(b); + b = next; + } + return result; +} + +static Py_ssize_t +BTree_length(BTree *self) +{ + return BTree_length_or_nonzero(self, 0); +} + +static PyMappingMethods BTree_as_mapping = { + (lenfunc)BTree_length, /*mp_length*/ + (binaryfunc)BTree_get, /*mp_subscript*/ + (objobjargproc)BTree_setitem, /*mp_ass_subscript*/ +}; + +static PySequenceMethods BTree_as_sequence = { + (lenfunc)0, /* sq_length */ + (binaryfunc)0, /* sq_concat */ + (ssizeargfunc)0, /* sq_repeat */ + (ssizeargfunc)0, /* sq_item */ + (ssizessizeargfunc)0, /* sq_slice */ + (ssizeobjargproc)0, /* sq_ass_item */ + (ssizessizeobjargproc)0, /* sq_ass_slice */ + (objobjproc)BTree_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static Py_ssize_t +BTree_nonzero(BTree *self) +{ + return BTree_length_or_nonzero(self, 1); +} + +static PyNumberMethods BTree_as_number_for_nonzero = { + 0,0,0,0,0,0,0,0,0,0, + (inquiry)BTree_nonzero}; + +static PyTypeObject BTreeType = { + PyObject_HEAD_INIT(NULL) /* PyPersist_Type */ + 0, /* ob_size */ + MODULE_NAME MOD_NAME_PREFIX "BTree",/* tp_name */ + sizeof(BTree), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)BTree_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + &BTree_as_number_for_nonzero, /* tp_as_number */ + &BTree_as_sequence, /* tp_as_sequence */ + &BTree_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)BTree_traverse, /* tp_traverse */ + (inquiry)BTree_tp_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)BTree_getiter, /* tp_iter */ + 0, /* tp_iternext */ + BTree_methods, /* tp_methods */ + BTree_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + BTree_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /*PyType_GenericNew,*/ /* tp_new */ +}; diff -Nru zope3-3.4.0/src/BTrees/BucketTemplate.c zope3-3.5~bzr18/src/BTrees/BucketTemplate.c --- zope3-3.4.0/src/BTrees/BucketTemplate.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/BucketTemplate.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1826 @@ +/***************************************************************************** + + Copyright (c) 2001, 2002 Zope Corporation 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 + + ****************************************************************************/ + +#define BUCKETTEMPLATE_C "$Id: BucketTemplate.c 103090 2009-08-22 17:46:16Z jim $\n" + +/* Use BUCKET_SEARCH to find the index at which a key belongs. + * INDEX An int lvalue to hold the index i such that KEY belongs at + * SELF->keys[i]. Note that this will equal SELF->len if KEY + * is larger than the bucket's largest key. Else it's the + * smallest i such that SELF->keys[i] >= KEY. + * ABSENT An int lvalue to hold a Boolean result, true (!= 0) if the + * key is absent, false (== 0) if the key is at INDEX. + * SELF A pointer to a Bucket node. + * KEY The key you're looking for, of type KEY_TYPE. + * ONERROR What to do if key comparison raises an exception; for example, + * perhaps 'return NULL'. + * + * See Maintainer.txt for discussion: this is optimized in subtle ways. + * It's recommended that you call this at the start of a routine, waiting + * to check for self->len == 0 after (if an empty bucket is special in + * context; INDEX becomes 0 and ABSENT becomes true if this macro is run + * with an empty SELF, and that may be all the invoker needs to know). + */ +#define BUCKET_SEARCH(INDEX, ABSENT, SELF, KEY, ONERROR) { \ + int _lo = 0; \ + int _hi = (SELF)->len; \ + int _i; \ + int _cmp = 1; \ + for (_i = _hi >> 1; _lo < _hi; _i = (_lo + _hi) >> 1) { \ + TEST_KEY_SET_OR(_cmp, (SELF)->keys[_i], (KEY)) \ + ONERROR; \ + if (_cmp < 0) _lo = _i + 1; \ + else if (_cmp == 0) break; \ + else _hi = _i; \ + } \ + (INDEX) = _i; \ + (ABSENT) = _cmp; \ +} + +/* +** _bucket_get +** +** Search a bucket for a given key. +** +** Arguments +** self The bucket +** keyarg The key to look for +** has_key Boolean; if true, return a true/false result; else return +** the value associated with the key. +** +** Return +** If has_key: +** Returns the Python int 0 if the key is absent, else returns +** has_key itself as a Python int. A BTree caller generally passes +** the depth of the bucket for has_key, so a true result returns +** the bucket depth then. +** Note that has_key should be true when searching set buckets. +** If not has_key: +** If the key is present, returns the associated value, and the +** caller owns the reference. Else returns NULL and sets KeyError. +** Whether or not has_key: +** If a comparison sets an exception, returns NULL. +*/ +static PyObject * +_bucket_get(Bucket *self, PyObject *keyarg, int has_key) +{ + int i, cmp; + KEY_TYPE key; + PyObject *r = NULL; + int copied = 1; + + COPY_KEY_FROM_ARG(key, keyarg, copied); + UNLESS (copied) return NULL; + + UNLESS (PER_USE(self)) return NULL; + + BUCKET_SEARCH(i, cmp, self, key, goto Done); + if (has_key) + r = PyInt_FromLong(cmp ? 0 : has_key); + else { + if (cmp == 0) { + COPY_VALUE_TO_OBJECT(r, self->values[i]); + } + else + PyErr_SetObject(PyExc_KeyError, keyarg); + } + +Done: + PER_UNUSE(self); + return r; + +} + +static PyObject * +bucket_getitem(Bucket *self, PyObject *key) +{ + return _bucket_get(self, key, 0); +} + +/* +** Bucket_grow +** +** Resize a bucket. +** +** Arguments: self The bucket. +** newsize The new maximum capacity. If < 0, double the +** current size unless the bucket is currently empty, +** in which case use MIN_BUCKET_ALLOC. +** noval Boolean; if true, allocate only key space and not +** value space +** +** Returns: -1 on error, and MemoryError exception is set +** 0 on success +*/ +static int +Bucket_grow(Bucket *self, int newsize, int noval) +{ + KEY_TYPE *keys; + VALUE_TYPE *values; + + if (self->size) { + if (newsize < 0) + newsize = self->size * 2; + if (newsize < 0) /* int overflow */ + goto Overflow; + UNLESS (keys = BTree_Realloc(self->keys, sizeof(KEY_TYPE) * newsize)) + return -1; + + UNLESS (noval) { + values = BTree_Realloc(self->values, sizeof(VALUE_TYPE) * newsize); + if (values == NULL) { + free(keys); + return -1; + } + self->values = values; + } + self->keys = keys; + } + else { + if (newsize < 0) + newsize = MIN_BUCKET_ALLOC; + UNLESS (self->keys = BTree_Malloc(sizeof(KEY_TYPE) * newsize)) + return -1; + UNLESS (noval) { + self->values = BTree_Malloc(sizeof(VALUE_TYPE) * newsize); + if (self->values == NULL) { + free(self->keys); + self->keys = NULL; + return -1; + } + } + } + self->size = newsize; + return 0; + +Overflow: + PyErr_NoMemory(); + return -1; +} + +/* So far, bucket_append is called only by multiunion_m(), so is called + * only when MULTI_INT_UNION is defined. Flavors of BTree/Bucket that + * don't support MULTI_INT_UNION don't call bucket_append (yet), and + * gcc complains if bucket_append is compiled in those cases. So only + * compile bucket_append if it's going to be used. + */ +#ifdef MULTI_INT_UNION +/* + * Append a slice of the "from" bucket to self. + * + * self Append (at least keys) to this bucket. self must be activated + * upon entry, and remains activated at exit. If copyValues + * is true, self must be empty or already have a non-NULL values + * pointer. self's access and modification times aren't updated. + * from The bucket from which to take keys, and possibly values. from + * must be activated upon entry, and remains activated at exit. + * If copyValues is true, from must have a non-NULL values + * pointer. self and from must not be the same. from's access + * time isn't updated. + * i, n The slice from[i : i+n] is appended to self. Must have + * i >= 0, n > 0 and i+n <= from->len. + * copyValues Boolean. If true, copy values from the slice as well as keys. + * In this case, from must have a non-NULL values pointer, and + * self must too (unless self is empty, in which case a values + * vector will be allocated for it). + * overallocate Boolean. If self doesn't have enough room upon entry to hold + * all the appended stuff, then if overallocate is false exactly + * enough room will be allocated to hold the new stuff, else if + * overallocate is true an excess will be allocated. overallocate + * may be a good idea if you expect to append more stuff to self + * later; else overallocate should be false. + * + * CAUTION: If self is empty upon entry (self->size == 0), and copyValues is + * false, then no space for values will get allocated. This can be a trap if + * the caller intends to copy values itself. + * + * Return + * -1 Error. + * 0 OK. + */ +static int +bucket_append(Bucket *self, Bucket *from, int i, int n, + int copyValues, int overallocate) +{ + int newlen; + + assert(self && from && self != from); + assert(i >= 0); + assert(n > 0); + assert(i+n <= from->len); + + /* Make room. */ + newlen = self->len + n; + if (newlen > self->size) { + int newsize = newlen; + if (overallocate) /* boost by 25% -- pretty arbitrary */ + newsize += newsize >> 2; + if (Bucket_grow(self, newsize, ! copyValues) < 0) + return -1; + } + assert(newlen <= self->size); + + /* Copy stuff. */ + memcpy(self->keys + self->len, from->keys + i, n * sizeof(KEY_TYPE)); + if (copyValues) { + assert(self->values); + assert(from->values); + memcpy(self->values + self->len, from->values + i, + n * sizeof(VALUE_TYPE)); + } + self->len = newlen; + + /* Bump refcounts. */ +#ifdef KEY_TYPE_IS_PYOBJECT + { + int j; + PyObject **p = from->keys + i; + for (j = 0; j < n; ++j, ++p) { + Py_INCREF(*p); + } + } +#endif +#ifdef VALUE_TYPE_IS_PYOBJECT + if (copyValues) { + int j; + PyObject **p = from->values + i; + for (j = 0; j < n; ++j, ++p) { + Py_INCREF(*p); + } + } +#endif + return 0; +} +#endif /* MULTI_INT_UNION */ + +/* +** _bucket_set: Assign a value to a key in a bucket, delete a key+value +** pair, or just insert a key. +** +** Arguments +** self The bucket +** keyarg The key to look for +** v The value to associate with key; NULL means delete the key. +** If NULL, it's an error (KeyError) if the key isn't present. +** Note that if this is a set bucket, and you want to insert +** a new set element, v must be non-NULL although its exact +** value will be ignored. Passing Py_None is good for this. +** unique Boolean; when true, don't replace the value if the key is +** already present. +** noval Boolean; when true, operate on keys only (ignore values) +** changed ignored on input +** +** Return +** -1 on error +** 0 on success and the # of bucket entries didn't change +** 1 on success and the # of bucket entries did change +** *changed If non-NULL, set to 1 on any mutation of the bucket. +*/ +static int +_bucket_set(Bucket *self, PyObject *keyarg, PyObject *v, + int unique, int noval, int *changed) +{ + int i, cmp; + KEY_TYPE key; + + /* Subtle: there may or may not be a value. If there is, we need to + * check its type early, so that in case of error we can get out before + * mutating the bucket. But because value isn't used on all paths, if + * we don't initialize value then gcc gives a nuisance complaint that + * value may be used initialized (it can't be, but gcc doesn't know + * that). So we initialize it. However, VALUE_TYPE can be various types, + * including int, PyObject*, and char[6], so it's a puzzle to spell + * initialization. It so happens that {0} is a valid initializer for all + * these types. + */ + VALUE_TYPE value = {0}; /* squash nuisance warning */ + int result = -1; /* until proven innocent */ + int copied = 1; + + COPY_KEY_FROM_ARG(key, keyarg, copied); + UNLESS(copied) return -1; + + /* Copy the value early (if needed), so that in case of error a + * pile of bucket mutations don't need to be undone. + */ + if (v && !noval) { + COPY_VALUE_FROM_ARG(value, v, copied); + UNLESS(copied) return -1; + } + + UNLESS (PER_USE(self)) return -1; + + BUCKET_SEARCH(i, cmp, self, key, goto Done); + if (cmp == 0) { + /* The key exists, at index i. */ + + if (v) { + /* The key exists at index i, and there's a new value. + * If unique, we're not supposed to replace it. If noval, or this + * is a set bucket (self->values is NULL), there's nothing to do. + */ + if (unique || noval || self->values == NULL) { + result = 0; + goto Done; + } + + /* The key exists at index i, and we need to replace the value. */ +#ifdef VALUE_SAME + /* short-circuit if no change */ + if (VALUE_SAME(self->values[i], value)) { + result = 0; + goto Done; + } +#endif + if (changed) + *changed = 1; + DECREF_VALUE(self->values[i]); + COPY_VALUE(self->values[i], value); + INCREF_VALUE(self->values[i]); + if (PER_CHANGED(self) >= 0) + result = 0; + goto Done; + } + + /* The key exists at index i, and should be deleted. */ + DECREF_KEY(self->keys[i]); + self->len--; + if (i < self->len) + memmove(self->keys + i, self->keys + i+1, + sizeof(KEY_TYPE)*(self->len - i)); + + if (self->values) { + DECREF_VALUE(self->values[i]); + if (i < self->len) + memmove(self->values + i, self->values + i+1, + sizeof(VALUE_TYPE)*(self->len - i)); + } + + if (! self->len) { + self->size = 0; + free(self->keys); + self->keys = NULL; + if (self->values) { + free(self->values); + self->values = NULL; + } + } + + if (changed) + *changed = 1; + if (PER_CHANGED(self) >= 0) + result = 1; + goto Done; + } + + /* The key doesn't exist, and belongs at index i. */ + if (!v) { + /* Can't delete a non-existent key. */ + PyErr_SetObject(PyExc_KeyError, keyarg); + goto Done; + } + + /* The key doesn't exist and should be inserted at index i. */ + if (self->len == self->size && Bucket_grow(self, -1, noval) < 0) + goto Done; + + if (self->len > i) { + memmove(self->keys + i + 1, self->keys + i, + sizeof(KEY_TYPE) * (self->len - i)); + if (self->values) { + memmove(self->values + i + 1, self->values + i, + sizeof(VALUE_TYPE) * (self->len - i)); + } + } + + COPY_KEY(self->keys[i], key); + INCREF_KEY(self->keys[i]); + + if (! noval) { + COPY_VALUE(self->values[i], value); + INCREF_VALUE(self->values[i]); + } + + self->len++; + if (changed) + *changed = 1; + if (PER_CHANGED(self) >= 0) + result = 1; + +Done: + PER_UNUSE(self); + return result; +} + +/* +** bucket_setitem +** +** wrapper for _bucket_setitem (eliminates +1 return code) +** +** Arguments: self The bucket +** key The key to insert under +** v The value to insert +** +** Returns 0 on success +** -1 on failure +*/ +static int +bucket_setitem(Bucket *self, PyObject *key, PyObject *v) +{ + if (_bucket_set(self, key, v, 0, 0, 0) < 0) + return -1; + return 0; +} + +/** + ** Accepts a sequence of 2-tuples, or any object with an items() + ** method that returns an iterable object producing 2-tuples. + */ +static int +update_from_seq(PyObject *map, PyObject *seq) +{ + PyObject *iter, *o, *k, *v; + int err = -1; + + /* One path creates a new seq object. The other path has an + INCREF of the seq argument. So seq must always be DECREFed on + the way out. + */ + /* Use items() if it's not a sequence. Alas, PySequence_Check() + * returns true for a PeristentMapping or PersistentDict, and we + * want to use items() in those cases too. + */ + if (!PySequence_Check(seq) || /* or it "looks like a dict" */ + PyObject_HasAttrString(seq, "iteritems")) { + PyObject *items; + items = PyObject_GetAttrString(seq, "items"); + if (items == NULL) + return -1; + seq = PyObject_CallObject(items, NULL); + Py_DECREF(items); + if (seq == NULL) + return -1; + } + else + Py_INCREF(seq); + + iter = PyObject_GetIter(seq); + if (iter == NULL) + goto err; + while (1) { + o = PyIter_Next(iter); + if (o == NULL) { + if (PyErr_Occurred()) + goto err; + else + break; + } + if (!PyTuple_Check(o) || PyTuple_GET_SIZE(o) != 2) { + Py_DECREF(o); + PyErr_SetString(PyExc_TypeError, + "Sequence must contain 2-item tuples"); + goto err; + } + k = PyTuple_GET_ITEM(o, 0); + v = PyTuple_GET_ITEM(o, 1); + if (PyObject_SetItem(map, k, v) < 0) { + Py_DECREF(o); + goto err; + } + Py_DECREF(o); + } + + err = 0; + err: + Py_DECREF(iter); + Py_DECREF(seq); + return err; +} + +static PyObject * +Mapping_update(PyObject *self, PyObject *seq) +{ + if (update_from_seq(self, seq) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +/* +** bucket_split +** +** Splits one bucket into two +** +** Arguments: self The bucket +** index the index of the key to split at (O.O.B use midpoint) +** next the new bucket to split into +** +** Returns: 0 on success +** -1 on failure +*/ +static int +bucket_split(Bucket *self, int index, Bucket *next) +{ + int next_size; + + ASSERT(self->len > 1, "split of empty bucket", -1); + + if (index < 0 || index >= self->len) + index = self->len / 2; + + next_size = self->len - index; + + next->keys = BTree_Malloc(sizeof(KEY_TYPE) * next_size); + if (!next->keys) + return -1; + memcpy(next->keys, self->keys + index, sizeof(KEY_TYPE) * next_size); + if (self->values) { + next->values = BTree_Malloc(sizeof(VALUE_TYPE) * next_size); + if (!next->values) { + free(next->keys); + next->keys = NULL; + return -1; + } + memcpy(next->values, self->values + index, + sizeof(VALUE_TYPE) * next_size); + } + next->size = next_size; + next->len = next_size; + self->len = index; + + next->next = self->next; + + Py_INCREF(next); + self->next = next; + + if (PER_CHANGED(self) < 0) + return -1; + + return 0; +} + +/* Set self->next to self->next->next, i.e. unlink self's successor from + * the chain. + * + * Return: + * -1 error + * 0 OK + */ +static int +Bucket_deleteNextBucket(Bucket *self) +{ + int result = -1; /* until proven innocent */ + Bucket *successor; + + PER_USE_OR_RETURN(self, -1); + successor = self->next; + if (successor) { + Bucket *next; + /* Before: self -> successor -> next + * After: self --------------> next + */ + UNLESS (PER_USE(successor)) goto Done; + next = successor->next; + PER_UNUSE(successor); + + Py_XINCREF(next); /* it may be NULL, of course */ + self->next = next; + Py_DECREF(successor); + if (PER_CHANGED(self) < 0) + goto Done; + } + result = 0; + +Done: + PER_UNUSE(self); + return result; +} + +/* + Bucket_findRangeEnd -- Find the index of a range endpoint + (possibly) contained in a bucket. + + Arguments: self The bucket + keyarg The key to match against + low Boolean; true for low end of range, false for high + exclude_equal Boolean; if true, don't accept an exact match, + and if there is one then move right if low and + left if !low. + offset The output offset + + If low true, *offset <- index of the smallest item >= key, + if low false the index of the largest item <= key. In either case, if there + is no such index, *offset is left alone and 0 is returned. + + Return: + 0 No suitable index exists; *offset has not been changed + 1 The correct index was stored into *offset + -1 Error + + Example: Suppose the keys are [2, 4], and exclude_equal is false. Searching + for 2 sets *offset to 0 and returns 1 regardless of low. Searching for 4 + sets *offset to 1 and returns 1 regardless of low. + Searching for 1: + If low true, sets *offset to 0, returns 1. + If low false, returns 0. + Searching for 3: + If low true, sets *offset to 1, returns 1. + If low false, sets *offset to 0, returns 1. + Searching for 5: + If low true, returns 0. + If low false, sets *offset to 1, returns 1. + + The 1, 3 and 5 examples are the same when exclude_equal is true. + */ +static int +Bucket_findRangeEnd(Bucket *self, PyObject *keyarg, int low, int exclude_equal, + int *offset) +{ + int i, cmp; + int result = -1; /* until proven innocent */ + KEY_TYPE key; + int copied = 1; + + COPY_KEY_FROM_ARG(key, keyarg, copied); + UNLESS (copied) return -1; + + UNLESS (PER_USE(self)) return -1; + + BUCKET_SEARCH(i, cmp, self, key, goto Done); + if (cmp == 0) { + /* exact match at index i */ + if (exclude_equal) { + /* but we don't want an exact match */ + if (low) + ++i; + else + --i; + } + } + /* Else keys[i-1] < key < keys[i], picturing infinities at OOB indices, + * and i has the smallest item > key, which is correct for low. + */ + else if (! low) + /* i-1 has the largest item < key (unless i-1 is 0OB) */ + --i; + + result = 0 <= i && i < self->len; + if (result) + *offset = i; + +Done: + PER_UNUSE(self); + return result; +} + +static PyObject * +Bucket_maxminKey(Bucket *self, PyObject *args, int min) +{ + PyObject *key=0; + int rc, offset = 0; + int empty_bucket = 1; + + if (args && ! PyArg_ParseTuple(args, "|O", &key)) return NULL; + + PER_USE_OR_RETURN(self, NULL); + + UNLESS (self->len) goto empty; + + /* Find the low range */ + if (key) + { + if ((rc = Bucket_findRangeEnd(self, key, min, 0, &offset)) <= 0) + { + if (rc < 0) return NULL; + empty_bucket = 0; + goto empty; + } + } + else if (min) offset = 0; + else offset = self->len -1; + + COPY_KEY_TO_OBJECT(key, self->keys[offset]); + PER_UNUSE(self); + + return key; + + empty: + PyErr_SetString(PyExc_ValueError, + empty_bucket ? "empty bucket" : + "no key satisfies the conditions"); + PER_UNUSE(self); + return NULL; +} + +static PyObject * +Bucket_minKey(Bucket *self, PyObject *args) +{ + return Bucket_maxminKey(self, args, 1); +} + +static PyObject * +Bucket_maxKey(Bucket *self, PyObject *args) +{ + return Bucket_maxminKey(self, args, 0); +} + +static int +Bucket_rangeSearch(Bucket *self, PyObject *args, PyObject *kw, + int *low, int *high) +{ + PyObject *min = Py_None; + PyObject *max = Py_None; + int excludemin = 0; + int excludemax = 0; + int rc; + + if (args) { + if (! PyArg_ParseTupleAndKeywords(args, kw, "|OOii", search_keywords, + &min, + &max, + &excludemin, + &excludemax)) + return -1; + } + + UNLESS (self->len) goto empty; + + /* Find the low range */ + if (min != Py_None) { + rc = Bucket_findRangeEnd(self, min, 1, excludemin, low); + if (rc < 0) + return -1; + if (rc == 0) + goto empty; + } + else { + *low = 0; + if (excludemin) { + if (self->len < 2) + goto empty; + ++*low; + } + } + + /* Find the high range */ + if (max != Py_None) { + rc = Bucket_findRangeEnd(self, max, 0, excludemax, high); + if (rc < 0) + return -1; + if (rc == 0) + goto empty; + } + else { + *high = self->len - 1; + if (excludemax) { + if (self->len < 2) + goto empty; + --*high; + } + } + + /* If min < max to begin with, it's quite possible that low > high now. */ + if (*low <= *high) + return 0; + + empty: + *low = 0; + *high = -1; + return 0; +} + +/* +** bucket_keys +** +** Generate a list of all keys in the bucket +** +** Arguments: self The Bucket +** args (unused) +** +** Returns: list of bucket keys +*/ +static PyObject * +bucket_keys(Bucket *self, PyObject *args, PyObject *kw) +{ + PyObject *r = NULL, *key; + int i, low, high; + + PER_USE_OR_RETURN(self, NULL); + + if (Bucket_rangeSearch(self, args, kw, &low, &high) < 0) + goto err; + + r = PyList_New(high-low+1); + if (r == NULL) + goto err; + + for (i=low; i <= high; i++) { + COPY_KEY_TO_OBJECT(key, self->keys[i]); + if (PyList_SetItem(r, i-low , key) < 0) + goto err; + } + + PER_UNUSE(self); + return r; + + err: + PER_UNUSE(self); + Py_XDECREF(r); + return NULL; +} + +/* +** bucket_values +** +** Generate a list of all values in the bucket +** +** Arguments: self The Bucket +** args (unused) +** +** Returns list of values +*/ +static PyObject * +bucket_values(Bucket *self, PyObject *args, PyObject *kw) +{ + PyObject *r=0, *v; + int i, low, high; + + PER_USE_OR_RETURN(self, NULL); + + if (Bucket_rangeSearch(self, args, kw, &low, &high) < 0) goto err; + + UNLESS (r=PyList_New(high-low+1)) goto err; + + for (i=low; i <= high; i++) + { + COPY_VALUE_TO_OBJECT(v, self->values[i]); + UNLESS (v) goto err; + if (PyList_SetItem(r, i-low, v) < 0) goto err; + } + + PER_UNUSE(self); + return r; + + err: + PER_UNUSE(self); + Py_XDECREF(r); + return NULL; +} + +/* +** bucket_items +** +** Returns a list of all items in a bucket +** +** Arguments: self The Bucket +** args (unused) +** +** Returns: list of all items in the bucket +*/ +static PyObject * +bucket_items(Bucket *self, PyObject *args, PyObject *kw) +{ + PyObject *r=0, *o=0, *item=0; + int i, low, high; + + PER_USE_OR_RETURN(self, NULL); + + if (Bucket_rangeSearch(self, args, kw, &low, &high) < 0) goto err; + + UNLESS (r=PyList_New(high-low+1)) goto err; + + for (i=low; i <= high; i++) + { + UNLESS (item = PyTuple_New(2)) goto err; + + COPY_KEY_TO_OBJECT(o, self->keys[i]); + UNLESS (o) goto err; + PyTuple_SET_ITEM(item, 0, o); + + COPY_VALUE_TO_OBJECT(o, self->values[i]); + UNLESS (o) goto err; + PyTuple_SET_ITEM(item, 1, o); + + if (PyList_SetItem(r, i-low, item) < 0) goto err; + + item = 0; + } + + PER_UNUSE(self); + return r; + + err: + PER_UNUSE(self); + Py_XDECREF(r); + Py_XDECREF(item); + return NULL; +} + +static PyObject * +bucket_byValue(Bucket *self, PyObject *omin) +{ + PyObject *r=0, *o=0, *item=0; + VALUE_TYPE min; + VALUE_TYPE v; + int i, l, copied=1; + + PER_USE_OR_RETURN(self, NULL); + + COPY_VALUE_FROM_ARG(min, omin, copied); + UNLESS(copied) return NULL; + + for (i=0, l=0; i < self->len; i++) + if (TEST_VALUE(self->values[i], min) >= 0) + l++; + + UNLESS (r=PyList_New(l)) goto err; + + for (i=0, l=0; i < self->len; i++) + { + if (TEST_VALUE(self->values[i], min) < 0) continue; + + UNLESS (item = PyTuple_New(2)) goto err; + + COPY_KEY_TO_OBJECT(o, self->keys[i]); + UNLESS (o) goto err; + PyTuple_SET_ITEM(item, 1, o); + + COPY_VALUE(v, self->values[i]); + NORMALIZE_VALUE(v, min); + COPY_VALUE_TO_OBJECT(o, v); + DECREF_VALUE(v); + UNLESS (o) goto err; + PyTuple_SET_ITEM(item, 0, o); + + if (PyList_SetItem(r, l, item) < 0) goto err; + l++; + + item = 0; + } + + item=PyObject_GetAttr(r,sort_str); + UNLESS (item) goto err; + ASSIGN(item, PyObject_CallObject(item, NULL)); + UNLESS (item) goto err; + ASSIGN(item, PyObject_GetAttr(r, reverse_str)); + UNLESS (item) goto err; + ASSIGN(item, PyObject_CallObject(item, NULL)); + UNLESS (item) goto err; + Py_DECREF(item); + + PER_UNUSE(self); + return r; + + err: + PER_UNUSE(self); + Py_XDECREF(r); + Py_XDECREF(item); + return NULL; +} + +static int +_bucket_clear(Bucket *self) +{ + const int len = self->len; + /* Don't declare i at this level. If neither keys nor values are + * PyObject*, i won't be referenced, and you'll get a nuisance compiler + * wng for declaring it here. + */ + self->len = self->size = 0; + + if (self->next) { + Py_DECREF(self->next); + self->next = NULL; + } + + /* Silence compiler warning about unused variable len for the case + when neither key nor value is an object, i.e. II. */ + (void)len; + + if (self->keys) { +#ifdef KEY_TYPE_IS_PYOBJECT + int i; + for (i = 0; i < len; ++i) + DECREF_KEY(self->keys[i]); +#endif + free(self->keys); + self->keys = NULL; + } + + if (self->values) { +#ifdef VALUE_TYPE_IS_PYOBJECT + int i; + for (i = 0; i < len; ++i) + DECREF_VALUE(self->values[i]); +#endif + free(self->values); + self->values = NULL; + } + return 0; +} + +#ifdef PERSISTENT +static PyObject * +bucket__p_deactivate(Bucket *self, PyObject *args, PyObject *keywords) +{ + int ghostify = 1; + PyObject *force = NULL; + + if (args && PyTuple_GET_SIZE(args) > 0) { + PyErr_SetString(PyExc_TypeError, + "_p_deactivate takes not positional arguments"); + return NULL; + } + if (keywords) { + int size = PyDict_Size(keywords); + force = PyDict_GetItemString(keywords, "force"); + if (force) + size--; + if (size) { + PyErr_SetString(PyExc_TypeError, + "_p_deactivate only accepts keyword arg force"); + return NULL; + } + } + + if (self->jar && self->oid) { + ghostify = self->state == cPersistent_UPTODATE_STATE; + if (!ghostify && force) { + if (PyObject_IsTrue(force)) + ghostify = 1; + if (PyErr_Occurred()) + return NULL; + } + if (ghostify) { + if (_bucket_clear(self) < 0) + return NULL; + PER_GHOSTIFY(self); + } + } + Py_INCREF(Py_None); + return Py_None; +} +#endif + +static PyObject * +bucket_clear(Bucket *self, PyObject *args) +{ + PER_USE_OR_RETURN(self, NULL); + + if (self->len) { + if (_bucket_clear(self) < 0) + return NULL; + if (PER_CHANGED(self) < 0) + goto err; + } + PER_UNUSE(self); + Py_INCREF(Py_None); + return Py_None; + +err: + PER_UNUSE(self); + return NULL; +} + +/* + * Return: + * + * For a set bucket (self->values is NULL), a one-tuple or two-tuple. The + * first element is a tuple of keys, of length self->len. The second element + * is the next bucket, present if and only if next is non-NULL: + * + * ( + * (keys[0], keys[1], ..., keys[len-1]), + * next iff non-NULL> + * ) + * + * For a mapping bucket (self->values is not NULL), a one-tuple or two-tuple. + * The first element is a tuple interleaving keys and values, of length + * 2 * self->len. The second element is the next bucket, present iff next is + * non-NULL: + * + * ( + * (keys[0], values[0], keys[1], values[1], ..., + * keys[len-1], values[len-1]), + * next iff non-NULL> + * ) + */ +static PyObject * +bucket_getstate(Bucket *self) +{ + PyObject *o = NULL, *items = NULL, *state; + int i, len, l; + + PER_USE_OR_RETURN(self, NULL); + + len = self->len; + + if (self->values) { /* Bucket */ + items = PyTuple_New(len * 2); + if (items == NULL) + goto err; + for (i = 0, l = 0; i < len; i++) { + COPY_KEY_TO_OBJECT(o, self->keys[i]); + if (o == NULL) + goto err; + PyTuple_SET_ITEM(items, l, o); + l++; + + COPY_VALUE_TO_OBJECT(o, self->values[i]); + if (o == NULL) + goto err; + PyTuple_SET_ITEM(items, l, o); + l++; + } + } else { /* Set */ + items = PyTuple_New(len); + if (items == NULL) + goto err; + for (i = 0; i < len; i++) { + COPY_KEY_TO_OBJECT(o, self->keys[i]); + if (o == NULL) + goto err; + PyTuple_SET_ITEM(items, i, o); + } + } + + if (self->next) + state = Py_BuildValue("OO", items, self->next); + else + state = Py_BuildValue("(O)", items); + Py_DECREF(items); + + PER_UNUSE(self); + return state; + + err: + PER_UNUSE(self); + Py_XDECREF(items); + return NULL; +} + +static int +_bucket_setstate(Bucket *self, PyObject *state) +{ + PyObject *k, *v, *items; + Bucket *next = NULL; + int i, l, len, copied=1; + KEY_TYPE *keys; + VALUE_TYPE *values; + + if (!PyArg_ParseTuple(state, "O|O:__setstate__", &items, &next)) + return -1; + + if (!PyTuple_Check(items)) { + PyErr_SetString(PyExc_TypeError, + "tuple required for first state element"); + return -1; + } + + len = PyTuple_Size(items); + if (len < 0) + return -1; + len /= 2; + + for (i = self->len; --i >= 0; ) { + DECREF_KEY(self->keys[i]); + DECREF_VALUE(self->values[i]); + } + self->len = 0; + + if (self->next) { + Py_DECREF(self->next); + self->next = NULL; + } + + if (len > self->size) { + keys = BTree_Realloc(self->keys, sizeof(KEY_TYPE)*len); + if (keys == NULL) + return -1; + values = BTree_Realloc(self->values, sizeof(VALUE_TYPE)*len); + if (values == NULL) + return -1; + self->keys = keys; + self->values = values; + self->size = len; + } + + for (i=0, l=0; i < len; i++) { + k = PyTuple_GET_ITEM(items, l); + l++; + v = PyTuple_GET_ITEM(items, l); + l++; + + COPY_KEY_FROM_ARG(self->keys[i], k, copied); + if (!copied) + return -1; + COPY_VALUE_FROM_ARG(self->values[i], v, copied); + if (!copied) + return -1; + INCREF_KEY(self->keys[i]); + INCREF_VALUE(self->values[i]); + } + + self->len = len; + + if (next) { + self->next = next; + Py_INCREF(next); + } + + return 0; +} + +static PyObject * +bucket_setstate(Bucket *self, PyObject *state) +{ + int r; + + PER_PREVENT_DEACTIVATION(self); + r = _bucket_setstate(self, state); + PER_UNUSE(self); + + if (r < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +bucket_has_key(Bucket *self, PyObject *key) +{ + return _bucket_get(self, key, 1); +} + +static PyObject * +bucket_setdefault(Bucket *self, PyObject *args) +{ + PyObject *key; + PyObject *failobj; /* default */ + PyObject *value; /* return value */ + int dummy_changed; /* in order to call _bucket_set */ + + if (! PyArg_UnpackTuple(args, "setdefault", 2, 2, &key, &failobj)) + return NULL; + + value = _bucket_get(self, key, 0); + if (value != NULL) + return value; + + /* The key isn't in the bucket. If that's not due to a KeyError exception, + * pass back the unexpected exception. + */ + if (! PyErr_ExceptionMatches(PyExc_KeyError)) + return NULL; + PyErr_Clear(); + + /* Associate `key` with `failobj` in the bucket, and return `failobj`. */ + value = failobj; + if (_bucket_set(self, key, failobj, 0, 0, &dummy_changed) < 0) + value = NULL; + Py_XINCREF(value); + return value; +} + + +/* forward declaration */ +static int +Bucket_length(Bucket *self); + +static PyObject * +bucket_pop(Bucket *self, PyObject *args) +{ + PyObject *key; + PyObject *failobj = NULL; /* default */ + PyObject *value; /* return value */ + int dummy_changed; /* in order to call _bucket_set */ + + if (! PyArg_UnpackTuple(args, "pop", 1, 2, &key, &failobj)) + return NULL; + + value = _bucket_get(self, key, 0); + if (value != NULL) { + /* Delete key and associated value. */ + if (_bucket_set(self, key, NULL, 0, 0, &dummy_changed) < 0) { + Py_DECREF(value); + return NULL; + } + return value; + } + + /* The key isn't in the bucket. If that's not due to a KeyError exception, + * pass back the unexpected exception. + */ + if (! PyErr_ExceptionMatches(PyExc_KeyError)) + return NULL; + + if (failobj != NULL) { + /* Clear the KeyError and return the explicit default. */ + PyErr_Clear(); + Py_INCREF(failobj); + return failobj; + } + + /* No default given. The only difference in this case is the error + * message, which depends on whether the bucket is empty. + */ + if (Bucket_length(self) == 0) + PyErr_SetString(PyExc_KeyError, "pop(): Bucket is empty"); + return NULL; + } + +/* Search bucket self for key. This is the sq_contains slot of the + * PySequenceMethods. + * + * Return: + * -1 error + * 0 not found + * 1 found + */ +static int +bucket_contains(Bucket *self, PyObject *key) +{ + PyObject *asobj = _bucket_get(self, key, 1); + int result = -1; + + if (asobj != NULL) { + result = PyInt_AsLong(asobj) ? 1 : 0; + Py_DECREF(asobj); + } + return result; +} + +/* +** bucket_getm +** +*/ +static PyObject * +bucket_getm(Bucket *self, PyObject *args) +{ + PyObject *key, *d=Py_None, *r; + + if (!PyArg_ParseTuple(args, "O|O:get", &key, &d)) + return NULL; + r = _bucket_get(self, key, 0); + if (r) + return r; + if (!PyErr_ExceptionMatches(PyExc_KeyError)) + return NULL; + PyErr_Clear(); + Py_INCREF(d); + return d; +} + +/**************************************************************************/ +/* Iterator support. */ + +/* A helper to build all the iterators for Buckets and Sets. + * If args is NULL, the iterator spans the entire structure. Else it's an + * argument tuple, with optional low and high arguments. + * kind is 'k', 'v' or 'i'. + * Returns a BTreeIter object, or NULL if error. + */ +static PyObject * +buildBucketIter(Bucket *self, PyObject *args, PyObject *kw, char kind) +{ + BTreeItems *items; + int lowoffset, highoffset; + BTreeIter *result = NULL; + + PER_USE_OR_RETURN(self, NULL); + if (Bucket_rangeSearch(self, args, kw, &lowoffset, &highoffset) < 0) + goto Done; + + items = (BTreeItems *)newBTreeItems(kind, self, lowoffset, + self, highoffset); + if (items == NULL) goto Done; + + result = BTreeIter_new(items); /* win or lose, we're done */ + Py_DECREF(items); + +Done: + PER_UNUSE(self); + return (PyObject *)result; +} + +/* The implementation of iter(Bucket_or_Set); the Bucket tp_iter slot. */ +static PyObject * +Bucket_getiter(Bucket *self) +{ + return buildBucketIter(self, NULL, NULL, 'k'); +} + +/* The implementation of Bucket.iterkeys(). */ +static PyObject * +Bucket_iterkeys(Bucket *self, PyObject *args, PyObject *kw) +{ + return buildBucketIter(self, args, kw, 'k'); +} + +/* The implementation of Bucket.itervalues(). */ +static PyObject * +Bucket_itervalues(Bucket *self, PyObject *args, PyObject *kw) +{ + return buildBucketIter(self, args, kw, 'v'); +} + +/* The implementation of Bucket.iteritems(). */ +static PyObject * +Bucket_iteritems(Bucket *self, PyObject *args, PyObject *kw) +{ + return buildBucketIter(self, args, kw, 'i'); +} + +/* End of iterator support. */ + +#ifdef PERSISTENT +static PyObject *merge_error(int p1, int p2, int p3, int reason); +static PyObject *bucket_merge(Bucket *s1, Bucket *s2, Bucket *s3); + +static PyObject * +_bucket__p_resolveConflict(PyObject *ob_type, PyObject *s[3]) +{ + PyObject *result = NULL; /* guilty until proved innocent */ + Bucket *b[3] = {NULL, NULL, NULL}; + PyObject *meth = NULL; + PyObject *a = NULL; + int i; + + for (i = 0; i < 3; i++) { + PyObject *r; + + b[i] = (Bucket*)PyObject_CallObject((PyObject *)ob_type, NULL); + if (b[i] == NULL) + goto Done; + if (s[i] == Py_None) /* None is equivalent to empty, for BTrees */ + continue; + meth = PyObject_GetAttr((PyObject *)b[i], __setstate___str); + if (meth == NULL) + goto Done; + a = PyTuple_New(1); + if (a == NULL) + goto Done; + PyTuple_SET_ITEM(a, 0, s[i]); + Py_INCREF(s[i]); + r = PyObject_CallObject(meth, a); /* b[i].__setstate__(s[i]) */ + if (r == NULL) + goto Done; + Py_DECREF(r); + Py_DECREF(a); + Py_DECREF(meth); + a = meth = NULL; + } + + if (b[0]->next != b[1]->next || b[0]->next != b[2]->next) + merge_error(-1, -1, -1, 0); + else + result = bucket_merge(b[0], b[1], b[2]); + +Done: + Py_XDECREF(meth); + Py_XDECREF(a); + Py_XDECREF(b[0]); + Py_XDECREF(b[1]); + Py_XDECREF(b[2]); + + return result; +} + +static PyObject * +bucket__p_resolveConflict(Bucket *self, PyObject *args) +{ + PyObject *s[3]; + + if (!PyArg_ParseTuple(args, "OOO", &s[0], &s[1], &s[2])) + return NULL; + + return _bucket__p_resolveConflict((PyObject *)self->ob_type, s); +} +#endif + +/* Caution: Even though the _next attribute is read-only, a program could + do arbitrary damage to the btree internals. For example, it could call + clear() on a bucket inside a BTree. + + We need to decide if the convenience for inspecting BTrees is worth + the risk. +*/ + +static struct PyMemberDef Bucket_members[] = { + {"_next", T_OBJECT, offsetof(Bucket, next)}, + {NULL} +}; + +static struct PyMethodDef Bucket_methods[] = { + {"__getstate__", (PyCFunction) bucket_getstate, METH_NOARGS, + "__getstate__() -- Return the picklable state of the object"}, + + {"__setstate__", (PyCFunction) bucket_setstate, METH_O, + "__setstate__() -- Set the state of the object"}, + + {"keys", (PyCFunction) bucket_keys, METH_KEYWORDS, + "keys([min, max]) -- Return the keys"}, + + {"has_key", (PyCFunction) bucket_has_key, METH_O, + "has_key(key) -- Test whether the bucket contains the given key"}, + + {"clear", (PyCFunction) bucket_clear, METH_VARARGS, + "clear() -- Remove all of the items from the bucket"}, + + {"update", (PyCFunction) Mapping_update, METH_O, + "update(collection) -- Add the items from the given collection"}, + + {"maxKey", (PyCFunction) Bucket_maxKey, METH_VARARGS, + "maxKey([key]) -- Find the maximum key\n\n" + "If an argument is given, find the maximum <= the argument"}, + + {"minKey", (PyCFunction) Bucket_minKey, METH_VARARGS, + "minKey([key]) -- Find the minimum key\n\n" + "If an argument is given, find the minimum >= the argument"}, + + {"values", (PyCFunction) bucket_values, METH_KEYWORDS, + "values([min, max]) -- Return the values"}, + + {"items", (PyCFunction) bucket_items, METH_KEYWORDS, + "items([min, max])) -- Return the items"}, + + {"byValue", (PyCFunction) bucket_byValue, METH_O, + "byValue(min) -- " + "Return value-keys with values >= min and reverse sorted by values"}, + + {"get", (PyCFunction) bucket_getm, METH_VARARGS, + "get(key[,default]) -- Look up a value\n\n" + "Return the default (or None) if the key is not found."}, + + {"setdefault", (PyCFunction) bucket_setdefault, METH_VARARGS, + "D.setdefault(k, d) -> D.get(k, d), also set D[k]=d if k not in D.\n\n" + "Return the value like get() except that if key is missing, d is both\n" + "returned and inserted into the bucket as the value of k."}, + + {"pop", (PyCFunction) bucket_pop, METH_VARARGS, + "D.pop(k[, d]) -> v, remove key and return the corresponding value.\n\n" + "If key is not found, d is returned if given, otherwise KeyError\n" + "is raised."}, + + {"iterkeys", (PyCFunction) Bucket_iterkeys, METH_KEYWORDS, + "B.iterkeys([min[,max]]) -> an iterator over the keys of B"}, + + {"itervalues", (PyCFunction) Bucket_itervalues, METH_KEYWORDS, + "B.itervalues([min[,max]]) -> an iterator over the values of B"}, + + {"iteritems", (PyCFunction) Bucket_iteritems, METH_KEYWORDS, + "B.iteritems([min[,max]]) -> an iterator over the (key, value) items of B"}, + +#ifdef PERSISTENT + {"_p_resolveConflict", (PyCFunction) bucket__p_resolveConflict, + METH_VARARGS, + "_p_resolveConflict() -- Reinitialize from a newly created copy"}, + + {"_p_deactivate", (PyCFunction) bucket__p_deactivate, METH_KEYWORDS, + "_p_deactivate() -- Reinitialize from a newly created copy"}, +#endif + {NULL, NULL} +}; + +static int +Bucket_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *v = NULL; + + if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "Bucket", &v)) + return -1; + + if (v) + return update_from_seq(self, v); + else + return 0; +} + +static void +bucket_dealloc(Bucket *self) +{ + if (self->state != cPersistent_GHOST_STATE) + _bucket_clear(self); + + cPersistenceCAPI->pertype->tp_dealloc((PyObject *)self); +} + +static int +bucket_traverse(Bucket *self, visitproc visit, void *arg) +{ + int err = 0; + int i, len; + +#define VISIT(SLOT) \ + if (SLOT) { \ + err = visit((PyObject *)(SLOT), arg); \ + if (err) \ + goto Done; \ + } + + /* Call our base type's traverse function. Because buckets are + * subclasses of Peristent, there must be one. + */ + err = cPersistenceCAPI->pertype->tp_traverse((PyObject *)self, visit, arg); + if (err) + goto Done; + + /* If this is registered with the persistence system, cleaning up cycles + * is the database's problem. It would be horrid to unghostify buckets + * here just to chase pointers every time gc runs. + */ + if (self->state == cPersistent_GHOST_STATE) + goto Done; + + len = self->len; + (void)i; /* if neither keys nor values are PyObject*, "i" is otherwise + unreferenced and we get a nuisance compiler wng */ +#ifdef KEY_TYPE_IS_PYOBJECT + /* Keys are Python objects so need to be traversed. */ + for (i = 0; i < len; i++) + VISIT(self->keys[i]); +#endif + +#ifdef VALUE_TYPE_IS_PYOBJECT + if (self->values != NULL) { + /* self->values exists (this is a mapping bucket, not a set bucket), + * and are Python objects, so need to be traversed. */ + for (i = 0; i < len; i++) + VISIT(self->values[i]); + } +#endif + + VISIT(self->next); + +Done: + return err; + +#undef VISIT +} + +static int +bucket_tp_clear(Bucket *self) +{ + if (self->state != cPersistent_GHOST_STATE) + _bucket_clear(self); + return 0; +} + +/* Code to access Bucket objects as mappings */ +static int +Bucket_length( Bucket *self) +{ + int r; + UNLESS (PER_USE(self)) return -1; + r = self->len; + PER_UNUSE(self); + return r; +} + +static PyMappingMethods Bucket_as_mapping = { + (lenfunc)Bucket_length, /*mp_length*/ + (binaryfunc)bucket_getitem, /*mp_subscript*/ + (objobjargproc)bucket_setitem, /*mp_ass_subscript*/ +}; + +static PySequenceMethods Bucket_as_sequence = { + (lenfunc)0, /* sq_length */ + (binaryfunc)0, /* sq_concat */ + (ssizeargfunc)0, /* sq_repeat */ + (ssizeargfunc)0, /* sq_item */ + (ssizessizeargfunc)0, /* sq_slice */ + (ssizeobjargproc)0, /* sq_ass_item */ + (ssizessizeobjargproc)0, /* sq_ass_slice */ + (objobjproc)bucket_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyObject * +bucket_repr(Bucket *self) +{ + PyObject *i, *r; + char repr[10000]; + int rv; + + i = bucket_items(self, NULL, NULL); + if (!i) + return NULL; + r = PyObject_Repr(i); + Py_DECREF(i); + if (!r) { + return NULL; + } + rv = PyOS_snprintf(repr, sizeof(repr), + "%s(%s)", self->ob_type->tp_name, + PyString_AS_STRING(r)); + if (rv > 0 && rv < sizeof(repr)) { + Py_DECREF(r); + return PyString_FromStringAndSize(repr, strlen(repr)); + } + else { + /* The static buffer wasn't big enough */ + int size; + PyObject *s; + + /* 3 for the parens and the null byte */ + size = strlen(self->ob_type->tp_name) + PyString_GET_SIZE(r) + 3; + s = PyString_FromStringAndSize(NULL, size); + if (!s) { + Py_DECREF(r); + return r; + } + PyOS_snprintf(PyString_AS_STRING(s), size, + "%s(%s)", self->ob_type->tp_name, PyString_AS_STRING(r)); + Py_DECREF(r); + return s; + } +} + +static PyTypeObject BucketType = { + PyObject_HEAD_INIT(NULL) /* PyPersist_Type */ + 0, /* ob_size */ + MODULE_NAME MOD_NAME_PREFIX "Bucket",/* tp_name */ + sizeof(Bucket), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)bucket_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)bucket_repr, /* tp_repr */ + 0, /* tp_as_number */ + &Bucket_as_sequence, /* tp_as_sequence */ + &Bucket_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)bucket_traverse, /* tp_traverse */ + (inquiry)bucket_tp_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)Bucket_getiter, /* tp_iter */ + 0, /* tp_iternext */ + Bucket_methods, /* tp_methods */ + Bucket_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + Bucket_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /*PyType_GenericNew,*/ /* tp_new */ +}; + +static int +nextBucket(SetIteration *i) +{ + if (i->position >= 0) + { + UNLESS(PER_USE(BUCKET(i->set))) return -1; + + if (i->position) + { + DECREF_KEY(i->key); + DECREF_VALUE(i->value); + } + + if (i->position < BUCKET(i->set)->len) + { + COPY_KEY(i->key, BUCKET(i->set)->keys[i->position]); + INCREF_KEY(i->key); + COPY_VALUE(i->value, BUCKET(i->set)->values[i->position]); + INCREF_VALUE(i->value); + i->position ++; + } + else + { + i->position = -1; + PER_ACCESSED(BUCKET(i->set)); + } + + PER_ALLOW_DEACTIVATION(BUCKET(i->set)); + } + + + return 0; +} diff -Nru zope3-3.4.0/src/BTrees/check.py zope3-3.5~bzr18/src/BTrees/check.py --- zope3-3.4.0/src/BTrees/check.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/check.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,409 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 working with BTrees (TreeSets, Buckets, and Sets) at a low +level. + +The primary function is check(btree), which performs value-based consistency +checks of a kind btree._check() does not perform. See the function docstring +for details. + +display(btree) displays the internal structure of a BTree (TreeSet, etc) to +stdout. + +CAUTION: When a BTree node has only a single bucket child, it can be +impossible to get at the bucket from Python code (__getstate__() may squash +the bucket object out of existence, as a pickling storage optimization). In +such a case, the code here synthesizes a temporary bucket with the same keys +(and values, if the bucket is of a mapping type). This has no first-order +consequences, but can mislead if you pay close attention to reported object +addresses and/or object identity (the synthesized bucket has an address +that doesn't exist in the actual BTree). +""" + +from types import TupleType + +from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet +from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet +from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet +from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet +from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet +from BTrees.OLBTree import OLBTree, OLBucket, OLSet, OLTreeSet +from BTrees.LOBTree import LOBTree, LOBucket, LOSet, LOTreeSet +from BTrees.LLBTree import LLBTree, LLBucket, LLSet, LLTreeSet +from BTrees.LFBTree import LFBTree, LFBucket, LFSet, LFTreeSet + +from ZODB.utils import positive_id, oid_repr + +TYPE_UNKNOWN, TYPE_BTREE, TYPE_BUCKET = range(3) + +_type2kind = {} +for kv in ('OO', + 'II', 'IO', 'OI', 'IF', + 'LL', 'LO', 'OL', 'LF', + ): + for name, kind in ( + ('BTree', (TYPE_BTREE, True)), + ('Bucket', (TYPE_BUCKET, True)), + ('TreeSet', (TYPE_BTREE, False)), + ('Set', (TYPE_BUCKET, False)), + ): + _type2kind[globals()[kv+name]] = kind + +# Return pair +# +# TYPE_BTREE or TYPE_BUCKET, is_mapping + +def classify(obj): + return _type2kind[type(obj)] + + +BTREE_EMPTY, BTREE_ONE, BTREE_NORMAL = range(3) + +# If the BTree is empty, returns +# +# BTREE_EMPTY, [], [] +# +# If the BTree has only one bucket, sometimes returns +# +# BTREE_ONE, bucket_state, None +# +# Else returns +# +# BTREE_NORMAL, list of keys, list of kids +# +# and the list of kids has one more entry than the list of keys. +# +# BTree.__getstate__() docs: +# +# For an empty BTree (self->len == 0), None. +# +# For a BTree with one child (self->len == 1), and that child is a bucket, +# and that bucket has a NULL oid, a one-tuple containing a one-tuple +# containing the bucket's state: +# +# ( +# ( +# child[0].__getstate__(), +# ), +# ) +# +# Else a two-tuple. The first element is a tuple interleaving the BTree's +# keys and direct children, of size 2*self->len - 1 (key[0] is unused and +# is not saved). The second element is the firstbucket: +# +# ( +# (child[0], key[1], child[1], key[2], child[2], ..., +# key[len-1], child[len-1]), +# self->firstbucket +# ) + +_btree2bucket = {} +for kv in ('OO', + 'II', 'IO', 'OI', 'IF', + 'LL', 'LO', 'OL', 'LF', + ): + _btree2bucket[globals()[kv+'BTree']] = globals()[kv+'Bucket'] + _btree2bucket[globals()[kv+'TreeSet']] = globals()[kv+'Set'] + +def crack_btree(t, is_mapping): + state = t.__getstate__() + if state is None: + return BTREE_EMPTY, [], [] + + assert isinstance(state, TupleType) + if len(state) == 1: + state = state[0] + assert isinstance(state, TupleType) and len(state) == 1 + state = state[0] + return BTREE_ONE, state, None + + assert len(state) == 2 + data, firstbucket = state + n = len(data) + assert n & 1 + kids = [] + keys = [] + i = 0 + for x in data: + if i & 1: + keys.append(x) + else: + kids.append(x) + i += 1 + return BTREE_NORMAL, keys, kids + +# Returns +# +# keys, values # for a mapping; len(keys) == len(values) in this case +# or +# keys, [] # for a set +# +# bucket.__getstate__() docs: +# +# For a set bucket (self->values is NULL), a one-tuple or two-tuple. The +# first element is a tuple of keys, of length self->len. The second element +# is the next bucket, present if and only if next is non-NULL: +# +# ( +# (keys[0], keys[1], ..., keys[len-1]), +# next iff non-NULL> +# ) +# +# For a mapping bucket (self->values is not NULL), a one-tuple or two-tuple. +# The first element is a tuple interleaving keys and values, of length +# 2 * self->len. The second element is the next bucket, present iff next is +# non-NULL: +# +# ( +# (keys[0], values[0], keys[1], values[1], ..., +# keys[len-1], values[len-1]), +# next iff non-NULL> +# ) + +def crack_bucket(b, is_mapping): + state = b.__getstate__() + assert isinstance(state, TupleType) + assert 1 <= len(state) <= 2 + data = state[0] + if not is_mapping: + return data, [] + keys = [] + values = [] + n = len(data) + assert n & 1 == 0 + i = 0 + for x in data: + if i & 1: + values.append(x) + else: + keys.append(x) + i += 1 + return keys, values + +def type_and_adr(obj): + if hasattr(obj, '_p_oid'): + oid = oid_repr(obj._p_oid) + else: + oid = 'None' + return "%s (0x%x oid=%s)" % (type(obj).__name__, positive_id(obj), oid) + +# Walker implements a depth-first search of a BTree (or TreeSet or Set or +# Bucket). Subclasses must implement the visit_btree() and visit_bucket() +# methods, and arrange to call the walk() method. walk() calls the +# visit_XYZ() methods once for each node in the tree, in depth-first +# left-to-right order. + +class Walker: + def __init__(self, obj): + self.obj = obj + + # obj is the BTree (BTree or TreeSet). + # path is a list of indices, from the root. For example, if a BTree node + # is child[5] of child[3] of the root BTree, [3, 5]. + # parent is the parent BTree object, or None if this is the root BTree. + # is_mapping is True for a BTree and False for a TreeSet. + # keys is a list of the BTree's internal keys. + # kids is a list of the BTree's children. + # If the BTree is an empty root node, keys == kids == []. + # Else len(kids) == len(keys) + 1. + # lo and hi are slice bounds on the values the elements of keys *should* + # lie in (lo inclusive, hi exclusive). lo is None if there is no lower + # bound known, and hi is None if no upper bound is known. + + def visit_btree(self, obj, path, parent, is_mapping, + keys, kids, lo, hi): + raise NotImplementedError + + # obj is the bucket (Bucket or Set). + # path is a list of indices, from the root. For example, if a bucket + # node is child[5] of child[3] of the root BTree, [3, 5]. + # parent is the parent BTree object. + # is_mapping is True for a Bucket and False for a Set. + # keys is a list of the bucket's keys. + # values is a list of the bucket's values. + # If is_mapping is false, values == []. Else len(keys) == len(values). + # lo and hi are slice bounds on the values the elements of keys *should* + # lie in (lo inclusive, hi exclusive). lo is None if there is no lower + # bound known, and hi is None if no upper bound is known. + + def visit_bucket(self, obj, path, parent, is_mapping, + keys, values, lo, hi): + raise NotImplementedError + + def walk(self): + obj = self.obj + path = [] + stack = [(obj, path, None, None, None)] + while stack: + obj, path, parent, lo, hi = stack.pop() + kind, is_mapping = classify(obj) + if kind is TYPE_BTREE: + bkind, keys, kids = crack_btree(obj, is_mapping) + if bkind is BTREE_NORMAL: + # push the kids, in reverse order (so they're popped off + # the stack in forward order) + n = len(kids) + for i in range(len(kids)-1, -1, -1): + newlo, newhi = lo, hi + if i < n-1: + newhi = keys[i] + if i > 0: + newlo = keys[i-1] + stack.append((kids[i], + path + [i], + obj, + newlo, + newhi)) + + elif bkind is BTREE_EMPTY: + pass + else: + assert bkind is BTREE_ONE + # Yuck. There isn't a bucket object to pass on, as + # the bucket state is embedded directly in the BTree + # state. Synthesize a bucket. + assert kids is None # and "keys" is really the bucket + # state + bucket = _btree2bucket[type(obj)]() + bucket.__setstate__(keys) + stack.append((bucket, + path + [0], + obj, + lo, + hi)) + keys = [] + kids = [bucket] + + self.visit_btree(obj, + path, + parent, + is_mapping, + keys, + kids, + lo, + hi) + else: + assert kind is TYPE_BUCKET + keys, values = crack_bucket(obj, is_mapping) + self.visit_bucket(obj, + path, + parent, + is_mapping, + keys, + values, + lo, + hi) + + +class Checker(Walker): + def __init__(self, obj): + Walker.__init__(self, obj) + self.errors = [] + + def check(self): + self.walk() + if self.errors: + s = "Errors found in %s:" % type_and_adr(self.obj) + self.errors.insert(0, s) + s = "\n".join(self.errors) + raise AssertionError(s) + + def visit_btree(self, obj, path, parent, is_mapping, + keys, kids, lo, hi): + self.check_sorted(obj, path, keys, lo, hi) + + def visit_bucket(self, obj, path, parent, is_mapping, + keys, values, lo, hi): + self.check_sorted(obj, path, keys, lo, hi) + + def check_sorted(self, obj, path, keys, lo, hi): + i, n = 0, len(keys) + for x in keys: + if lo is not None and not lo <= x: + s = "key %r < lower bound %r at index %d" % (x, lo, i) + self.complain(s, obj, path) + if hi is not None and not x < hi: + s = "key %r >= upper bound %r at index %d" % (x, hi, i) + self.complain(s, obj, path) + if i < n-1 and not x < keys[i+1]: + s = "key %r at index %d >= key %r at index %d" % ( + x, i, keys[i+1], i+1) + self.complain(s, obj, path) + i += 1 + + def complain(self, msg, obj, path): + s = "%s, in %s, path from root %s" % ( + msg, + type_and_adr(obj), + ".".join(map(str, path))) + self.errors.append(s) + +class Printer(Walker): + def __init__(self, obj): + Walker.__init__(self, obj) + + def display(self): + self.walk() + + def visit_btree(self, obj, path, parent, is_mapping, + keys, kids, lo, hi): + indent = " " * len(path) + print "%s%s %s with %d children" % ( + indent, + ".".join(map(str, path)), + type_and_adr(obj), + len(kids)) + indent += " " + n = len(keys) + for i in range(n): + print "%skey %d: %r" % (indent, i, keys[i]) + + def visit_bucket(self, obj, path, parent, is_mapping, + keys, values, lo, hi): + indent = " " * len(path) + print "%s%s %s with %d keys" % ( + indent, + ".".join(map(str, path)), + type_and_adr(obj), + len(keys)) + indent += " " + n = len(keys) + for i in range(n): + print "%skey %d: %r" % (indent, i, keys[i]), + if is_mapping: + print "value %r" % (values[i],) + +def check(btree): + """Check internal value-based invariants in a BTree or TreeSet. + + The btree._check() method checks internal C-level pointer consistency. + The check() function here checks value-based invariants: whether the + keys in leaf bucket and internal nodes are in strictly increasing order, + and whether they all lie in their expected range. The latter is a subtle + invariant that can't be checked locally -- it requires propagating + range info down from the root of the tree, and modifying it at each + level for each child. + + Raises AssertionError if anything is wrong, with a string detail + explaining the problems. The entire tree is checked before + AssertionError is raised, and the string detail may be large (depending + on how much went wrong). + """ + + Checker(btree).check() + +def display(btree): + "Display the internal structure of a BTree, Bucket, TreeSet or Set." + Printer(btree).display() diff -Nru zope3-3.4.0/src/BTrees/Development.txt zope3-3.5~bzr18/src/BTrees/Development.txt --- zope3-3.4.0/src/BTrees/Development.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/Development.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,425 @@ +===================== +Developer Information +===================== + +This document provides information for developers who maintain or extend +`BTrees`. + +Macros +====== + +`BTrees` are defined using a "template", roughly akin to a C++ template. To +create a new family of `BTrees`, create a source file that defines macros used +to handle differences in key and value types: + + +Configuration Macros +-------------------- + +``MASTER_ID`` + + A string to hold an RCS/CVS Id key to be included in compiled binaries. + +``MOD_NAME_PREFIX`` + + A string (like "IO" or "OO") that provides the prefix used for the module. + This gets used to generate type names and the internal module name string. + +``DEFAULT_MAX_BUCKET_SIZE`` + + An int giving the maximum bucket size (number of key/value pairs). When a + bucket gets larger than this due to an insertion *into a BTREE*, it + splits. Inserting into a bucket directly doesn't split, and functions + that produce a bucket output (e.g., ``union()``) also have no bound on how + large a bucket may get. Someday this will be tunable on `BTree`. + instances. + +``DEFAULT_MAX_BTREE_SIZE`` + + An ``int`` giving the maximum size (number of children) of an internal + btree node. Someday this will be tunable on ``BTree`` instances. + + +Macros for Keys +--------------- + +``KEY_TYPE`` + + The C type declaration for keys (e.g., ``int`` or ``PyObject*``). + +``KEY_TYPE_IS_PYOBJECT`` + + Define if ``KEY_TYPE`` is a ``PyObject*`, else ``undef``. + +``KEY_CHECK(K)`` + + Tests whether the ``PyObject* K`` can be converted to the (``C``) key type + (``KEY_TYPE``). The macro should return a boolean (zero for false, + non-zero for true). When it returns false, its caller should probably set + a ``TypeError`` exception. + +``TEST_KEY_SET_OR(V, K, T)`` + + Like Python's ``cmp()``. Compares K(ey) to T(arget), where ``K`` + and ``T`` are ``C`` values of type `KEY_TYPE`. ``V`` is assigned an `int` + value depending on the outcome:: + + < 0 if K < T + == 0 if K == T + > 0 if K > T + + This macro acts like an ``if``, where the following statement is executed + only if a Python exception has been raised because the values could not be + compared. + +``DECREF_KEY(K)`` + + ``K`` is a value of ``KEY_TYPE``. If ``KEY_TYPE`` is a flavor of + ``PyObject*``, write this to do ``Py_DECREF(K)``. Else (e.g., + ``KEY_TYPE`` is ``int``) make it a nop. + +``INCREF_KEY(K)`` + + ``K`` is a value of `KEY_TYPE`. If `KEY_TYPE` is a flavor of + ``PyObject*``, write this to do ``Py_INCREF(K)``. Else (e.g., `KEY_TYPE` + is ``int``) make it a nop. + +``COPY_KEY(K, E)`` + + Like ``K=E``. Copy a key from ``E`` to ``K``, both of ``KEY_TYPE``. Note + that this doesn't ``decref K`` or ``incref E`` when ``KEY_TYPE`` is a + ``PyObject*``; the caller is responsible for keeping refcounts straight. + +``COPY_KEY_TO_OBJECT(O, K)`` + + Roughly like ``O=K``. ``O`` is a ``PyObject*``, and the macro must build + a Python object form of ``K``, assign it to ``O``, and ensure that ``O`` + owns the reference to its new value. It may do this by creating a new + Python object based on ``K`` (e.g., ``PyInt_FromLong(K)`` when + ``KEY_TYPE`` is ``int``), or simply by doing ``Py_INCREF(K)`` if + ``KEY_TYPE`` is a ``PyObject*``. + +``COPY_KEY_FROM_ARG(TARGET, ARG, STATUS)`` + + Copy an argument to the target without creating a new reference to + ``ARG``. ``ARG`` is a ``PyObject*``, and ``TARGET`` is of type + ``KEY_TYPE``. If this can't be done (for example, ``KEY_CHECK(ARG)`` + returns false), set a Python error and set status to ``0``. If there is + no error, leave status alone. + + +Macros for Values +----------------- + +``VALUE_TYPE`` + + The C type declaration for values (e.g., ``int`` or ``PyObject*``). + +``VALUE_TYPE_IS_PYOBJECT`` + + Define if ``VALUE_TYPE`` is a ``PyObject*``, else ``undef``. + +``TEST_VALUE(X, Y)`` + + Like Python's ``cmp()``. Compares ``X`` to ``Y``, where ``X`` & ``Y`` are + ``C`` values of type ``VALUE_TYPE``. The macro returns an ``int``, with + value:: + + < 0 if X < Y + == 0 if X == Y + > 0 if X > Y + + Bug: There is no provision for determining whether the comparison attempt + failed (set a Python exception). + +``DECREF_VALUE(K)`` + + Like ``DECREF_KEY``, except applied to values of ``VALUE_TYPE``. + +``INCREF_VALUE(K)`` + + Like ``INCREF_KEY``, except applied to values of ``VALUE_TYPE``. + +``COPY_VALUE(K, E)`` + + Like ``COPY_KEY``, except applied to values of ``VALUE_TYPE``. + +``COPY_VALUE_TO_OBJECT(O, K)`` + + Like ``COPY_KEY_TO_OBJECT``, except applied to values of ``VALUE_TYPE``. + +``COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS)`` + + Like ``COPY_KEY_FROM_ARG``, except applied to values of ``VALUE_TYPE``. + +``NORMALIZE_VALUE(V, MIN)`` + + Normalize the value, ``V``, using the parameter ``MIN``. This is almost + certainly a YAGNI. It is a no-op for most types. For integers, ``V`` is + replaced by ``V/MIN`` only if ``MIN > 0``. + + +Macros for Set Operations +------------------------- + +``MERGE_DEFAULT`` + + A value of ``VALUE_TYPE`` specifying the value to associate with set + elements when sets are merged with mappings via weighed union or weighted + intersection. + +``MERGE(O1, w1, O2, w2)`` + + Performs a weighted merge of two values, ``O1`` and ``O2``, using weights + ``w1`` and ``w2``. The result must be of ``VALUE_TYPE``. Note that + weighted unions and weighted intersections are not enabled if this macro + is left undefined. + +``MERGE_WEIGHT(O, w)`` + + Computes a weighted value for ``O``. The result must be of + ``VALUE_TYPE``. This is used for "filling out" weighted unions, i.e. to + compute a weighted value for keys that appear in only one of the input + mappings. If left undefined, ``MERGE_WEIGHT`` defaults to:: + + #define MERGE_WEIGHT(O, w) (O) + +``MULTI_INT_UNION`` + + The value doesn't matter. If defined, `SetOpTemplate.c` compiles code for + a ``multiunion()`` function (compute a union of many input sets at high + speed). This currently makes sense only for structures with integer keys. + + +BTree Clues +=========== + +More or less random bits of helpful info. + ++ In papers and textbooks, this flavor of BTree is usually called a B+-Tree, + where "+" is a superscript. + ++ All keys and all values live in the bucket leaf nodes. Keys in interior + (BTree) nodes merely serve to guide a search efficiently toward the correct + leaf. + ++ When a key is deleted, it's physically removed from the bucket it's in, but + this doesn't propagate back up the tree: since keys in interior nodes only + serve to guide searches, it's OK-- and saves time --to leave "stale" keys in + interior nodes. + ++ No attempt is made to rebalance the tree after a deletion, unless a bucket + thereby becomes entirely empty. "Classic BTrees" do rebalance, keeping all + buckets at least half full (provided there are enough keys in the entire + tree to fill half a bucket). The tradeoffs are murky. Pathological cases + in the presence of deletion do exist. Pathologies include trees tending + toward only one key per bucket, and buckets at differing depths (all buckets + are at the same depth in a classic BTree). + ++ ``DEFAULT_MAX_BUCKET_SIZE`` and ``DEFAULT_MAX_BTREE_SIZE`` are chosen mostly + to "even out" pickle sizes in storage. That's why, e.g., an `IIBTree` has + larger values than an `OOBTree`: pickles store ints more efficiently than + they can store arbitrary Python objects. + ++ In a non-empty BTree, every bucket node contains at least one key, and every + BTree node contains at least one child and a non-NULL firstbucket pointer. + However, a BTree node may not contain any keys. + ++ An empty BTree consists solely of a BTree node with ``len==0`` and + ``firstbucket==NULL``. + ++ Although a BTree can become unbalanced under a mix of inserts and deletes + (meaning both that there's nothing stronger that can be said about buckets + than that they're not empty, and that buckets can appear at different + depths), a BTree node always has children of the same kind: they're all + buckets, or they're all BTree nodes. + + +The ``BTREE_SEARCH`` Macro +========================== + +For notational ease, consider a fixed BTree node ``x``, and let + +:: + + K(i) mean x->data.key[i] + C(i) mean all the keys reachable from x->data.child[i] + +For each ``i`` in ``0`` to ``x->len-1`` inclusive, + +:: + + K(i) <= C(i) < K(i+1) + +is a BTree node invariant, where we pretend that ``K(0)`` holds a key smaller +than any possible key, and ``K(x->len)`` holds a key larger than any possible +key. (Note that ``K(x->len)`` doesn't actually exist, and ``K(0)`` is never +used although space for it exists in non-empty BTree nodes.) + +When searching for a key ``k``, then, the child pointer we want to follow is +the one at index ``i`` such that ``K(i) <= k < K(i+1)``. There can be at most +one such ``i``, since the ``K(i)`` are strictly increasing. And there is at +least one such ``i`` provided the tree isn't empty (so that ``0 < len``). For +the moment, assume the tree isn't empty (we'll get back to that later). + +The macro's chief loop invariant is + +:: + + K(lo) < k < K(hi) + +This holds trivially at the start, since ``lo`` is set to ``0``, and ``hi`` to +``x->len``, and we pretend ``K(0)`` is minus infinity and ``K(len)`` is plus +infinity. Inside the loop, if ``K(i) < k`` we set ``lo`` to ``i``, and if +``K(i) > k`` we set ``hi`` to ``i``. These obviously preserve the invariant. +If ``K(i) == k``, the loop breaks and sets the result to ``i``, and since +``K(i) == k`` in that case ``i`` is obviously the correct result. + +Other cases depend on how ``i = floor((lo + hi)/2)`` works, exactly. Suppose +``lo + d = hi`` for some ``d >= 0``. Then ``i = floor((lo + lo + d)/2) = +floor(lo + d/2) = lo + floor(d/2)``. So: + +a. ``[d == 0] (lo == i == hi)`` if and only if ``(lo == hi)``. +b. ``[d == 1] (lo == i < hi)`` if and only if ``(lo+1 == hi)``. +c. ``[d > 1] (lo < i < hi)`` if and only if ``(lo+1 < hi)``. + +If the node is empty ``(x->len == 0)``, then ``lo==i==hi==0`` at the start, +and the loop exits immediately (the first ``i > lo`` test fails), without +entering the body. + +Else ``lo < hi`` at the start, and the invariant ``K(lo) < k < K(hi)`` holds. + +If ``lo+1 < hi``, we're in case (c): ``i`` is strictly between ``lo`` and +``hi``, so the loop body is entered, and regardless of whether the body sets +the new ``lo`` or the new ``hi`` to ``i``, the new ``lo`` is strictly less +than the new ``hi``, and the difference between the new ``lo`` and new ``hi`` +is strictly less than the difference between the old ``lo`` and old ``hi``. +So long as the new ``lo + 1`` remains < the new ``hi``, we stay in this case. +We can't stay in this case forever, though: because ``hi-lo`` decreases on +each trip but remains > ``0``, ``lo+1 == hi`` must eventually become true. +(In fact, it becomes true quickly, in about ``log2(x->len)`` trips; the point +is more that ``lo`` doesn't equal ``hi`` when the loop ends, it has to end +with ``lo+1==hi`` and ``i==lo``). + +Then we're in case (b): ``i==lo==hi-1`` then, and the loop exits. The +invariant still holds, with ``lo==i`` and ``hi==lo+1==i+1``:: + + K(i) < k < K(i+1) + +so ``i`` is again the correct answer. + + +Optimization points: +-------------------- + ++ Division by 2 is done via shift rather via "/2". These are signed ints, and + almost all C compilers treat signed int division as truncating, and shifting + is not the same as truncation for signed int division. The compiler has no + way to know these values aren't negative, so has to generate longer-winded + code for "/2". But we know these values aren't negative, and exploit it. + ++ The order of _cmp comparisons matters. We're in an interior BTree node, and + are looking at only a tiny fraction of all the keys that exist. So finding + the key exactly in this node is unlikely, and checking ``_cmp == 0`` is a + waste of time to the same extent. It doesn't matter whether we check for + ``_cmp < 0`` or ``_cmp > 0`` first, so long as we do both before worrying + about equality. + ++ At the start of a routine, it's better to run this macro even if ``x->len`` + is ``0`` (check for that afterwards). We just called a function and so + probably drained the pipeline. If the first thing we do then is read up + ``self->len`` and check it against ``0``, we just sit there waiting for the + data to get read up, and then another immediate test-and-branch, and for a + very unlikely case (BTree nodes are rarely empty). It's better to get into + the loop right away so the normal case makes progress ASAP. + + +The ``BUCKET_SEARCH`` Macro +=========================== + +This has a different job than ``BTREE_SEARCH``: the key ``0`` slot is +legitimate in a bucket, and we want to find the index at which the key +belongs. If the key is larger than the bucket's largest key, a new slot at +index len is where it belongs, else it belongs at the smallest ``i`` with +``keys[i]`` >= the key we're looking for. We also need to know whether or not +the key is present (``BTREE_SEARCH`` didn't care; it only wanted to find the +next node to search). + +The mechanics of the search are quite similar, though. The primary +loop invariant changes to (say we're searching for key ``k``):: + + K(lo-1) < k < K(hi) + +where ``K(i)`` means ``keys[i]``, and we pretend ``K(-1)`` is minus infinity +and ``K(len)`` is plus infinity. + +If the bucket is empty, ``lo=hi=i=0`` at the start, the loop body is never +entered, and the macro sets ``INDEX`` to 0 and ``ABSENT`` to true. That's why +``_cmp`` is initialized to 1 (``_cmp`` becomes ``ABSENT``). + +Else the bucket is not empty, lok``, ``hi`` is set to ``i``, preserving that ``K[hi] = K[i] > k``. + +If the loop exits after either of those, ``_cmp != 0``, so ``ABSENT`` becomes +true. + +If ``K[i]=k``, the loop breaks, so that ``INDEX`` becomes ``i``, and +``ABSENT`` becomes false (``_cmp=0`` in this case). + +The same case analysis for ``BTREE_SEARCH`` on ``lo`` and ``hi`` holds here: + +a. ``(lo == i == hi)`` if and only if ``(lo == hi)``. +b. ``(lo == i < hi)`` if and only if ``(lo+1 == hi)``. +c. ``(lo < i < hi)`` if and only if ``(lo+1 < hi)``. + +So long as ``lo+1 < hi``, we're in case (c), and either break with equality +(in which case the right results are obviously computed) or narrow the range. +If equality doesn't obtain, the range eventually narrows to cases (a) or (b). + +To go from (c) to (a), we must have ``lo+2==hi`` at the start, and +``K[i]=K[lo+1] + key``), because when it pays it narrows the range more (we get a little + boost from setting ``lo=i+1`` in this case; the other case sets ``hi=i``, + which isn't as much of a narrowing). diff -Nru zope3-3.4.0/src/BTrees/floatvaluemacros.h zope3-3.5~bzr18/src/BTrees/floatvaluemacros.h --- zope3-3.4.0/src/BTrees/floatvaluemacros.h 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/floatvaluemacros.h 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ + +#define VALUEMACROS_H "$Id: floatvaluemacros.h 28617 2004-12-09 23:43:03Z tim_one $\n" + +#define VALUE_TYPE float +#undef VALUE_TYPE_IS_PYOBJECT +#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0)) +#define VALUE_SAME(VALUE, TARGET) ( (VALUE) == (TARGET) ) +#define DECLARE_VALUE(NAME) VALUE_TYPE NAME +#define VALUE_PARSE "f" +#define DECREF_VALUE(k) +#define INCREF_VALUE(k) +#define COPY_VALUE(V, E) (V=(E)) +#define COPY_VALUE_TO_OBJECT(O, K) O=PyFloat_FromDouble(K) +#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \ + if (PyFloat_Check(ARG)) TARGET = (float)PyFloat_AsDouble(ARG); \ + else if (PyInt_Check(ARG)) TARGET = (float)PyInt_AsLong(ARG); \ + else { \ + PyErr_SetString(PyExc_TypeError, "expected float or int value"); \ + (STATUS)=0; (TARGET)=0; } + +#define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0 + +#define MERGE_DEFAULT 1.0f +#define MERGE(O1, w1, O2, w2) ((O1)*(w1)+(O2)*(w2)) +#define MERGE_WEIGHT(O, w) ((O)*(w)) diff -Nru zope3-3.4.0/src/BTrees/_fsBTree.c zope3-3.5~bzr18/src/BTrees/_fsBTree.c --- zope3-3.4.0/src/BTrees/_fsBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_fsBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,70 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _fsBTree.c 41599 2006-02-11 21:33:49Z tseaver $\n" + +/* fsBTree - FileStorage index BTree + + This BTree implements a mapping from 2-character strings + to six-character strings. This allows us to efficiently store + a FileStorage index as a nested mapping of 6-character oid prefix + to mapping of 2-character oid suffix to 6-character (byte) file + positions. +*/ + +typedef unsigned char char2[2]; +typedef unsigned char char6[6]; + +/* Setup template macros */ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "fs" +#define INITMODULE init_fsBTree +#define DEFAULT_MAX_BUCKET_SIZE 500 +#define DEFAULT_MAX_BTREE_SIZE 500 + +/*#include "intkeymacros.h"*/ + +#define KEYMACROS_H "$Id: _fsBTree.c 41599 2006-02-11 21:33:49Z tseaver $\n" +#define KEY_TYPE char2 +#undef KEY_TYPE_IS_PYOBJECT +#define KEY_CHECK(K) (PyString_Check(K) && PyString_GET_SIZE(K)==2) +#define TEST_KEY_SET_OR(V, K, T) if ( ( (V) = ((*(K) < *(T) || (*(K) == *(T) && (K)[1] < (T)[1])) ? -1 : ((*(K) == *(T) && (K)[1] == (T)[1]) ? 0 : 1)) ), 0 ) +#define DECREF_KEY(KEY) +#define INCREF_KEY(k) +#define COPY_KEY(KEY, E) (*(KEY)=*(E), (KEY)[1]=(E)[1]) +#define COPY_KEY_TO_OBJECT(O, K) O=PyString_FromStringAndSize((const char*)K,2) +#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \ + if (KEY_CHECK(ARG)) memcpy(TARGET, PyString_AS_STRING(ARG), 2); else { \ + PyErr_SetString(PyExc_TypeError, "expected two-character string key"); \ + (STATUS)=0; } + +/*#include "intvaluemacros.h"*/ +#define VALUEMACROS_H "$Id: _fsBTree.c 41599 2006-02-11 21:33:49Z tseaver $\n" +#define VALUE_TYPE char6 +#undef VALUE_TYPE_IS_PYOBJECT +#define TEST_VALUE(K, T) memcmp(K,T,6) +#define DECREF_VALUE(k) +#define INCREF_VALUE(k) +#define COPY_VALUE(V, E) (memcpy(V, E, 6)) +#define COPY_VALUE_TO_OBJECT(O, K) O=PyString_FromStringAndSize((const char*)K,6) +#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \ + if ((PyString_Check(ARG) && PyString_GET_SIZE(ARG)==6)) \ + memcpy(TARGET, PyString_AS_STRING(ARG), 6); else { \ + PyErr_SetString(PyExc_TypeError, "expected six-character string key"); \ + (STATUS)=0; } + +#define NORMALIZE_VALUE(V, MIN) +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_fsBTree.py zope3-3.5~bzr18/src/BTrees/_fsBTree.py --- zope3-3.4.0/src/BTrees/_fsBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_fsBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_fsBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/fsBTree.py zope3-3.5~bzr18/src/BTrees/fsBTree.py --- zope3-3.4.0/src/BTrees/fsBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/fsBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,19 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 +# +############################################################################## + +# fsBTrees are data structures used for ZODB FileStorage. They are not +# expected to be "public" excpect to FileStorage. + +# hack to overcome dynamic-linking headache. +from _fsBTree import * diff -Nru zope3-3.4.0/src/BTrees/_IFBTree.c zope3-3.5~bzr18/src/BTrees/_IFBTree.c --- zope3-3.4.0/src/BTrees/_IFBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_IFBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _IFBTree.c 67074 2006-04-17 19:13:39Z fdrake $\n" + +/* IFBTree - int key, float value BTree + + Implements a collection using int type keys + and float type values +*/ + +/* Setup template macros */ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "IF" +#define INITMODULE init_IFBTree +#define DEFAULT_MAX_BUCKET_SIZE 120 +#define DEFAULT_MAX_BTREE_SIZE 500 + +#include "intkeymacros.h" +#include "floatvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_IFBTree.py zope3-3.5~bzr18/src/BTrees/_IFBTree.py --- zope3-3.4.0/src/BTrees/_IFBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_IFBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_IFBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/IFBTree.py zope3-3.5~bzr18/src/BTrees/IFBTree.py --- zope3-3.4.0/src/BTrees/IFBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/IFBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _IFBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IIntegerFloatBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/_IIBTree.c zope3-3.5~bzr18/src/BTrees/_IIBTree.c --- zope3-3.4.0/src/BTrees/_IIBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_IIBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _IIBTree.c 25186 2004-06-02 15:07:33Z jim $\n" + +/* IIBTree - int key, int value BTree + + Implements a collection using int type keys + and int type values +*/ + +/* Setup template macros */ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "II" +#define INITMODULE init_IIBTree +#define DEFAULT_MAX_BUCKET_SIZE 120 +#define DEFAULT_MAX_BTREE_SIZE 500 + +#include "intkeymacros.h" +#include "intvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_IIBTree.py zope3-3.5~bzr18/src/BTrees/_IIBTree.py --- zope3-3.4.0/src/BTrees/_IIBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_IIBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_IIBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/IIBTree.py zope3-3.5~bzr18/src/BTrees/IIBTree.py --- zope3-3.4.0/src/BTrees/IIBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/IIBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _IIBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IIntegerIntegerBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/__init__.py zope3-3.5~bzr18/src/BTrees/__init__.py --- zope3-3.4.0/src/BTrees/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ +############################################################################# +# +# Copyright (c) 2007 Zope Corporation 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 zope.interface +import BTrees.Interfaces + + +class _Family(object): + zope.interface.implements(BTrees.Interfaces.IBTreeFamily) + + from BTrees import OOBTree as OO + +class _Family32(_Family): + from BTrees import OIBTree as OI + from BTrees import IIBTree as II + from BTrees import IOBTree as IO + from BTrees import IFBTree as IF + + maxint = int(2**31-1) + minint = -maxint - 1 + + def __reduce__(self): + return _family32, () + +class _Family64(_Family): + from BTrees import OLBTree as OI + from BTrees import LLBTree as II + from BTrees import LOBTree as IO + from BTrees import LFBTree as IF + + maxint = 2**63-1 + minint = -maxint - 1 + + def __reduce__(self): + return _family64, () + +def _family32(): + return family32 +_family32.__safe_for_unpickling__ = True + +def _family64(): + return family64 +_family64.__safe_for_unpickling__ = True + + +family32 = _Family32() +family64 = _Family64() + + +BTrees.family64.IO.family = family64 +BTrees.family64.OI.family = family64 +BTrees.family64.IF.family = family64 +BTrees.family64.II.family = family64 + +BTrees.family32.IO.family = family32 +BTrees.family32.OI.family = family32 +BTrees.family32.IF.family = family32 +BTrees.family32.II.family = family32 diff -Nru zope3-3.4.0/src/BTrees/Interfaces.py zope3-3.5~bzr18/src/BTrees/Interfaces.py --- zope3-3.4.0/src/BTrees/Interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/Interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,519 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 +# +############################################################################## + +from zope.interface import Interface, Attribute + + +class ICollection(Interface): + + def clear(): + """Remove all of the items from the collection.""" + + def __nonzero__(): + """Check if the collection is non-empty. + + Return a true value if the collection is non-empty and a + false value otherwise. + """ + + +class IReadSequence(Interface): + + def __getitem__(index): + """Return the value at the given index. + + An IndexError is raised if the index cannot be found. + """ + + def __getslice__(index1, index2): + """Return a subsequence from the original sequence. + + The subsequence includes the items from index1 up to, but not + including, index2. + """ + +class IKeyed(ICollection): + + def has_key(key): + """Check whether the object has an item with the given key. + + Return a true value if the key is present, else a false value. + """ + + def keys(min=None, max=None, excludemin=False, excludemax=False): + """Return an IReadSequence containing the keys in the collection. + + The type of the IReadSequence is not specified. It could be a list + or a tuple or some other type. + + All arguments are optional, and may be specified as keyword + arguments, or by position. + + If a min is specified, then output is constrained to keys greater + than or equal to the given min, and, if excludemin is specified and + true, is further constrained to keys strictly greater than min. A + min value of None is ignored. If min is None or not specified, and + excludemin is true, the smallest key is excluded. + + If a max is specified, then output is constrained to keys less than + or equal to the given max, and, if excludemax is specified and + true, is further constrained to keys strictly less than max. A max + value of None is ignored. If max is None or not specified, and + excludemax is true, the largest key is excluded. + """ + + def maxKey(key=None): + """Return the maximum key. + + If a key argument if provided and not None, return the largest key + that is less than or equal to the argument. Raise an exception if + no such key exists. + """ + + def minKey(key=None): + """Return the minimum key. + + If a key argument if provided and not None, return the smallest key + that is greater than or equal to the argument. Raise an exception + if no such key exists. + """ + + +class ISetMutable(IKeyed): + + def insert(key): + """Add the key (value) to the set. + + If the key was already in the set, return 0, otherwise return 1. + """ + + def remove(key): + """Remove the key from the set. + + Raises KeyError if key is not in the set. + """ + + def update(seq): + """Add the items from the given sequence to the set.""" + + +class ISized(Interface): + """An object that supports __len__.""" + + def __len__(): + """Return the number of items in the container.""" + + +class IKeySequence(IKeyed, ISized): + + def __getitem__(index): + """Return the key in the given index position. + + This allows iteration with for loops and use in functions, + like map and list, that read sequences. + """ + + +class ISet(IKeySequence, ISetMutable): + pass + + +class ITreeSet(IKeyed, ISetMutable): + pass + +class IMinimalDictionary(ISized, IKeyed): + + def get(key, default): + """Get the value associated with the given key. + + Return the default if has_key(key) is false. + """ + + def __getitem__(key): + """Get the value associated with the given key. + + Raise KeyError if has_key(key) is false. + """ + + def __setitem__(key, value): + """Set the value associated with the given key.""" + + def __delitem__(key): + """Delete the value associated with the given key. + + Raise KeyError if has_key(key) is false. + """ + + def values(min=None, max=None, excludemin=False, excludemax=False): + """Return an IReadSequence containing the values in the collection. + + The type of the IReadSequence is not specified. It could be a list + or a tuple or some other type. + + All arguments are optional, and may be specified as keyword + arguments, or by position. + + If a min is specified, then output is constrained to values whose + keys are greater than or equal to the given min, and, if excludemin + is specified and true, is further constrained to values whose keys + are strictly greater than min. A min value of None is ignored. If + min is None or not specified, and excludemin is true, the value + corresponding to the smallest key is excluded. + + If a max is specified, then output is constrained to values whose + keys are less than or equal to the given max, and, if excludemax is + specified and true, is further constrained to values whose keys are + strictly less than max. A max value of None is ignored. If max is + None or not specified, and excludemax is true, the value + corresponding to the largest key is excluded. + """ + + def items(min=None, max=None, excludemin=False, excludemax=False): + """Return an IReadSequence containing the items in the collection. + + An item is a 2-tuple, a (key, value) pair. + + The type of the IReadSequence is not specified. It could be a list + or a tuple or some other type. + + All arguments are optional, and may be specified as keyword + arguments, or by position. + + If a min is specified, then output is constrained to items whose + keys are greater than or equal to the given min, and, if excludemin + is specified and true, is further constrained to items whose keys + are strictly greater than min. A min value of None is ignored. If + min is None or not specified, and excludemin is true, the item with + the smallest key is excluded. + + If a max is specified, then output is constrained to items whose + keys are less than or equal to the given max, and, if excludemax is + specified and true, is further constrained to items whose keys are + strictly less than max. A max value of None is ignored. If max is + None or not specified, and excludemax is true, the item with the + largest key is excluded. + """ + +class IDictionaryIsh(IMinimalDictionary): + + def update(collection): + """Add the items from the given collection object to the collection. + + The input collection must be a sequence of (key, value) 2-tuples, + or an object with an 'items' method that returns a sequence of + (key, value) pairs. + """ + + def byValue(minValue): + """Return a sequence of (value, key) pairs, sorted by value. + + Values < minValue are omitted and other values are "normalized" by + the minimum value. This normalization may be a noop, but, for + integer values, the normalization is division. + """ + + def setdefault(key, d): + """D.setdefault(k, d) -> D.get(k, d), also set D[k]=d if k not in D. + + Return the value like get() except that if key is missing, d is both + returned and inserted into the dictionary as the value of k. + + Note that, unlike as for Python's dict.setdefault(), d is not + optional. Python defaults d to None, but that doesn't make sense + for mappings that can't have None as a value (for example, an + IIBTree can have only integers as values). + """ + + def pop(key, d): + """D.pop(k[, d]) -> v, remove key and return the corresponding value. + + If key is not found, d is returned if given, otherwise KeyError is + raised. + """ + + +class IBTree(IDictionaryIsh): + + def insert(key, value): + """Insert a key and value into the collection. + + If the key was already in the collection, then there is no + change and 0 is returned. + + If the key was not already in the collection, then the item is + added and 1 is returned. + + This method is here to allow one to generate random keys and + to insert and test whether the key was there in one operation. + + A standard idiom for generating new keys will be:: + + key = generate_key() + while not t.insert(key, value): + key=generate_key() + """ + + +class IMerge(Interface): + """Object with methods for merging sets, buckets, and trees. + + These methods are supplied in modules that define collection + classes with particular key and value types. The operations apply + only to collections from the same module. For example, the + IIBTree.union can only be used with IIBTree.IIBTree, + IIBTree.IIBucket, IIBTree.IISet, and IIBTree.IITreeSet. + + The implementing module has a value type. The IOBTree and OOBTree + modules have object value type. The IIBTree and OIBTree modules + have integer value types. Other modules may be defined in the + future that have other value types. + + The individual types are classified into set (Set and TreeSet) and + mapping (Bucket and BTree) types. + """ + + def difference(c1, c2): + """Return the keys or items in c1 for which there is no key in c2. + + If c1 is None, then None is returned. If c2 is None, then c1 + is returned. + + If neither c1 nor c2 is None, the output is a Set if c1 is a Set or + TreeSet, and is a Bucket if c1 is a Bucket or BTree. + """ + + def union(c1, c2): + """Compute the Union of c1 and c2. + + If c1 is None, then c2 is returned, otherwise, if c2 is None, + then c1 is returned. + + The output is a Set containing keys from the input + collections. + """ + + def intersection(c1, c2): + """Compute the intersection of c1 and c2. + + If c1 is None, then c2 is returned, otherwise, if c2 is None, + then c1 is returned. + + The output is a Set containing matching keys from the input + collections. + """ + + +class IBTreeModule(Interface): + """These are available in all modules (IOBTree, OIBTree, OOBTree, IIBTree, + IFBTree, LFBTree, LOBTree, OLBTree, and LLBTree). + """ + + BTree = Attribute( + """The IBTree for this module. + + Also available as [prefix]BTree, as in IOBTree.""") + + Bucket = Attribute( + """The leaf-node data buckets used by the BTree. + + (IBucket is not currently defined in this file, but is essentially + IDictionaryIsh, with the exception of __nonzero__, as of this + writing.) + + Also available as [prefix]Bucket, as in IOBucket.""") + + TreeSet = Attribute( + """The ITreeSet for this module. + + Also available as [prefix]TreeSet, as in IOTreeSet.""") + + Set = Attribute( + """The ISet for this module: the leaf-node data buckets used by the + TreeSet. + + Also available as [prefix]BTree, as in IOSet.""") + + +class IIMerge(IMerge): + """Merge collections with integer value type. + + A primary intent is to support operations with no or integer + values, which are used as "scores" to rate indiviual keys. That + is, in this context, a BTree or Bucket is viewed as a set with + scored keys, using integer scores. + """ + + def weightedUnion(c1, c2, weight1=1, weight2=1): + """Compute the weighted union of c1 and c2. + + If c1 and c2 are None, the output is (0, None). + + If c1 is None and c2 is not None, the output is (weight2, c2). + + If c1 is not None and c2 is None, the output is (weight1, c1). + + Else, and hereafter, c1 is not None and c2 is not None. + + If c1 and c2 are both sets, the output is 1 and the (unweighted) + union of the sets. + + Else the output is 1 and a Bucket whose keys are the union of c1 and + c2's keys, and whose values are:: + + v1*weight1 + v2*weight2 + + where: + + v1 is 0 if the key is not in c1 + 1 if the key is in c1 and c1 is a set + c1[key] if the key is in c1 and c1 is a mapping + + v2 is 0 if the key is not in c2 + 1 if the key is in c2 and c2 is a set + c2[key] if the key is in c2 and c2 is a mapping + + Note that c1 and c2 must be collections. + """ + + def weightedIntersection(c1, c2, weight1=1, weight2=1): + """Compute the weighted intersection of c1 and c2. + + If c1 and c2 are None, the output is (0, None). + + If c1 is None and c2 is not None, the output is (weight2, c2). + + If c1 is not None and c2 is None, the output is (weight1, c1). + + Else, and hereafter, c1 is not None and c2 is not None. + + If c1 and c2 are both sets, the output is the sum of the weights + and the (unweighted) intersection of the sets. + + Else the output is 1 and a Bucket whose keys are the intersection of + c1 and c2's keys, and whose values are:: + + v1*weight1 + v2*weight2 + + where: + + v1 is 1 if c1 is a set + c1[key] if c1 is a mapping + + v2 is 1 if c2 is a set + c2[key] if c2 is a mapping + + Note that c1 and c2 must be collections. + """ + + +class IMergeIntegerKey(IMerge): + """IMerge-able objects with integer keys. + + Concretely, this means the types in IOBTree and IIBTree. + """ + + def multiunion(seq): + """Return union of (zero or more) integer sets, as an integer set. + + seq is a sequence of objects each convertible to an integer set. + These objects are convertible to an integer set: + + + An integer, which is added to the union. + + + A Set or TreeSet from the same module (for example, an + IIBTree.TreeSet for IIBTree.multiunion()). The elements of the + set are added to the union. + + + A Bucket or BTree from the same module (for example, an + IOBTree.IOBTree for IOBTree.multiunion()). The keys of the + mapping are added to the union. + + The union is returned as a Set from the same module (for example, + IIBTree.multiunion() returns an IIBTree.IISet). + + The point to this method is that it can run much faster than + doing a sequence of two-input union() calls. Under the covers, + all the integers in all the inputs are sorted via a single + linear-time radix sort, then duplicates are removed in a second + linear-time pass. + """ + +class IBTreeFamily(Interface): + """the 64-bit or 32-bit family""" + IO = Attribute('The IIntegerObjectBTreeModule for this family') + OI = Attribute('The IObjectIntegerBTreeModule for this family') + II = Attribute('The IIntegerIntegerBTreeModule for this family') + IF = Attribute('The IIntegerFloatBTreeModule for this family') + OO = Attribute('The IObjectObjectBTreeModule for this family') + maxint = Attribute('The maximum integer storable in this family') + minint = Attribute('The minimum integer storable in this family') + + +class IIntegerObjectBTreeModule(IBTreeModule, IMerge): + """keys, or set values, are integers; values are objects. + + describes IOBTree and LOBTree""" + + family = Attribute('The IBTreeFamily of this module') + + +class IObjectIntegerBTreeModule(IBTreeModule, IIMerge): + """keys, or set values, are objects; values are integers. + + Object keys (and set values) must sort reliably (for instance, *not* on + object id)! Homogenous key types recommended. + + describes OIBTree and LOBTree""" + + family = Attribute('The IBTreeFamily of this module') + + +class IIntegerIntegerBTreeModule(IBTreeModule, IIMerge, IMergeIntegerKey): + """keys, or set values, are integers; values are also integers. + + describes IIBTree and LLBTree""" + + family = Attribute('The IBTreeFamily of this module') + + +class IObjectObjectBTreeModule(IBTreeModule, IMerge): + """keys, or set values, are objects; values are also objects. + + Object keys (and set values) must sort reliably (for instance, *not* on + object id)! Homogenous key types recommended. + + describes OOBTree""" + + # Note that there's no ``family`` attribute; all families include + # the OO flavor of BTrees. + + +class IIntegerFloatBTreeModule(IBTreeModule, IMerge): + """keys, or set values, are integers; values are floats. + + describes IFBTree and LFBTree""" + + family = Attribute('The IBTreeFamily of this module') + + +############################################################### +# IMPORTANT NOTE +# +# Getting the length of a BTree, TreeSet, or output of keys, +# values, or items of same is expensive. If you need to get the +# length, you need to maintain this separately. +# +# Eventually, I need to express this through the interfaces. +# +################################################################ diff -Nru zope3-3.4.0/src/BTrees/intkeymacros.h zope3-3.5~bzr18/src/BTrees/intkeymacros.h --- zope3-3.4.0/src/BTrees/intkeymacros.h 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/intkeymacros.h 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,35 @@ + +#define KEYMACROS_H "$Id: intkeymacros.h 71250 2006-11-21 19:50:51Z fdrake $\n" + +#ifdef ZODB_64BIT_INTS +/* PY_LONG_LONG as key */ +#define NEED_LONG_LONG_SUPPORT +#define KEY_TYPE PY_LONG_LONG +#define KEY_CHECK longlong_check +#define COPY_KEY_TO_OBJECT(O, K) O=longlong_as_object(K) +#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \ + if (PyInt_Check(ARG)) TARGET=PyInt_AS_LONG(ARG); else \ + if (longlong_check(ARG)) TARGET=PyLong_AsLongLong(ARG); else \ + if (PyLong_Check(ARG)) { \ + PyErr_SetString(PyExc_ValueError, "long integer out of range"); \ + (STATUS)=0; (TARGET)=0; } \ + else { \ + PyErr_SetString(PyExc_TypeError, "expected integer key"); \ + (STATUS)=0; (TARGET)=0; } +#else +/* C int as key */ +#define KEY_TYPE int +#define KEY_CHECK PyInt_Check +#define COPY_KEY_TO_OBJECT(O, K) O=PyInt_FromLong(K) +#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \ + if (PyInt_Check(ARG)) TARGET=PyInt_AS_LONG(ARG); else { \ + PyErr_SetString(PyExc_TypeError, "expected integer key"); \ + (STATUS)=0; (TARGET)=0; } +#endif + +#undef KEY_TYPE_IS_PYOBJECT +#define TEST_KEY_SET_OR(V, K, T) if ( ( (V) = (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0)) ) , 0 ) +#define DECREF_KEY(KEY) +#define INCREF_KEY(k) +#define COPY_KEY(KEY, E) (KEY=(E)) +#define MULTI_INT_UNION 1 diff -Nru zope3-3.4.0/src/BTrees/intvaluemacros.h zope3-3.5~bzr18/src/BTrees/intvaluemacros.h --- zope3-3.4.0/src/BTrees/intvaluemacros.h 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/intvaluemacros.h 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + +#define VALUEMACROS_H "$Id: intvaluemacros.h 71250 2006-11-21 19:50:51Z fdrake $\n" + +#ifdef ZODB_64BIT_INTS +#define NEED_LONG_LONG_SUPPORT +#define VALUE_TYPE PY_LONG_LONG +#define VALUE_PARSE "L" +#define COPY_VALUE_TO_OBJECT(O, K) O=longlong_as_object(K) +#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \ + if (PyInt_Check(ARG)) TARGET=PyInt_AS_LONG(ARG); else \ + if (longlong_check(ARG)) TARGET=PyLong_AsLongLong(ARG); else \ + if (PyLong_Check(ARG)) { \ + PyErr_SetString(PyExc_ValueError, "long integer out of range"); \ + (STATUS)=0; (TARGET)=0; } \ + else { \ + PyErr_SetString(PyExc_TypeError, "expected integer value"); \ + (STATUS)=0; (TARGET)=0; } +#else +#define VALUE_TYPE int +#define VALUE_PARSE "i" +#define COPY_VALUE_TO_OBJECT(O, K) O=PyInt_FromLong(K) +#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \ + if (PyInt_Check(ARG)) TARGET=PyInt_AsLong(ARG); else { \ + PyErr_SetString(PyExc_TypeError, "expected integer value"); \ + (STATUS)=0; (TARGET)=0; } +#endif + +#undef VALUE_TYPE_IS_PYOBJECT +#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0)) +#define VALUE_SAME(VALUE, TARGET) ( (VALUE) == (TARGET) ) +#define DECLARE_VALUE(NAME) VALUE_TYPE NAME +#define DECREF_VALUE(k) +#define INCREF_VALUE(k) +#define COPY_VALUE(V, E) (V=(E)) + +#define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0 + +#define MERGE_DEFAULT 1 +#define MERGE(O1, w1, O2, w2) ((O1)*(w1)+(O2)*(w2)) +#define MERGE_WEIGHT(O, w) ((O)*(w)) diff -Nru zope3-3.4.0/src/BTrees/_IOBTree.c zope3-3.5~bzr18/src/BTrees/_IOBTree.c --- zope3-3.4.0/src/BTrees/_IOBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_IOBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _IOBTree.c 25186 2004-06-02 15:07:33Z jim $\n" + +/* IOBTree - int key, object value BTree + + Implements a collection using int type keys + and object type values +*/ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "IO" +#define DEFAULT_MAX_BUCKET_SIZE 60 +#define DEFAULT_MAX_BTREE_SIZE 500 +#define INITMODULE init_IOBTree + +#include "intkeymacros.h" +#include "objectvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_IOBTree.py zope3-3.5~bzr18/src/BTrees/_IOBTree.py --- zope3-3.4.0/src/BTrees/_IOBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_IOBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_IOBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/IOBTree.py zope3-3.5~bzr18/src/BTrees/IOBTree.py --- zope3-3.4.0/src/BTrees/IOBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/IOBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _IOBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IIntegerObjectBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/Length.py zope3-3.5~bzr18/src/BTrees/Length.py --- zope3-3.4.0/src/BTrees/Length.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/Length.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 persistent + +class Length(persistent.Persistent): + """BTree lengths are too expensive to compute + + Objects that use BTrees need to keep track of lengths themselves. + This class provides an object for doing this. + + As a bonus, the object support application-level conflict + resolution. + + It is tempting to to assign length objects to __len__ attributes + to provide instance-specific __len__ methods. However, this no + longer works as expected, because new-style classes cache + class-defined slot methods (like __len__) in C type slots. Thus, + instance-define slot fillers are ignores. + + """ + + def __init__(self, v=0): + self.value = v + + def __getstate__(self): + return self.value + + def __setstate__(self, v): + self.value = v + + def set(self, v): + self.value = v + + def _p_resolveConflict(self, old, s1, s2): + return s1 + s2 - old + + def _p_independent(self): + # My state doesn't depend on or materially effect the state of + # other objects. + return 1 + + def change(self, delta): + self.value += delta + + def __call__(self, *args): + return self.value diff -Nru zope3-3.4.0/src/BTrees/_LFBTree.c zope3-3.5~bzr18/src/BTrees/_LFBTree.c --- zope3-3.4.0/src/BTrees/_LFBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_LFBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,36 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _IFBTree.c 67074 2006-04-17 19:13:39Z fdrake $\n" + +/* IFBTree - int key, float value BTree + + Implements a collection using int type keys + and float type values +*/ + +/* Setup template macros */ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "LF" +#define INITMODULE init_LFBTree +#define DEFAULT_MAX_BUCKET_SIZE 120 +#define DEFAULT_MAX_BTREE_SIZE 500 + +#define ZODB_64BIT_INTS + +#include "intkeymacros.h" +#include "floatvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_LFBTree.py zope3-3.5~bzr18/src/BTrees/_LFBTree.py --- zope3-3.4.0/src/BTrees/_LFBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_LFBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_LFBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/LFBTree.py zope3-3.5~bzr18/src/BTrees/LFBTree.py --- zope3-3.4.0/src/BTrees/LFBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/LFBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _LFBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IIntegerFloatBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/_LLBTree.c zope3-3.5~bzr18/src/BTrees/_LLBTree.c --- zope3-3.4.0/src/BTrees/_LLBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_LLBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,36 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _IIBTree.c 25186 2004-06-02 15:07:33Z jim $\n" + +/* IIBTree - int key, int value BTree + + Implements a collection using int type keys + and int type values +*/ + +/* Setup template macros */ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "LL" +#define INITMODULE init_LLBTree +#define DEFAULT_MAX_BUCKET_SIZE 120 +#define DEFAULT_MAX_BTREE_SIZE 500 + +#define ZODB_64BIT_INTS + +#include "intkeymacros.h" +#include "intvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_LLBTree.py zope3-3.5~bzr18/src/BTrees/_LLBTree.py --- zope3-3.4.0/src/BTrees/_LLBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_LLBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_LLBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/LLBTree.py zope3-3.5~bzr18/src/BTrees/LLBTree.py --- zope3-3.4.0/src/BTrees/LLBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/LLBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _LLBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IIntegerIntegerBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/_LOBTree.c zope3-3.5~bzr18/src/BTrees/_LOBTree.c --- zope3-3.4.0/src/BTrees/_LOBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_LOBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _IOBTree.c 25186 2004-06-02 15:07:33Z jim $\n" + +/* IOBTree - int key, object value BTree + + Implements a collection using int type keys + and object type values +*/ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "LO" +#define DEFAULT_MAX_BUCKET_SIZE 60 +#define DEFAULT_MAX_BTREE_SIZE 500 +#define INITMODULE init_LOBTree + +#define ZODB_64BIT_INTS + +#include "intkeymacros.h" +#include "objectvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_LOBTree.py zope3-3.5~bzr18/src/BTrees/_LOBTree.py --- zope3-3.4.0/src/BTrees/_LOBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_LOBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_LOBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/LOBTree.py zope3-3.5~bzr18/src/BTrees/LOBTree.py --- zope3-3.4.0/src/BTrees/LOBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/LOBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _LOBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IIntegerObjectBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/MergeTemplate.c zope3-3.5~bzr18/src/BTrees/MergeTemplate.c --- zope3-3.4.0/src/BTrees/MergeTemplate.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/MergeTemplate.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,349 @@ +/***************************************************************************** + + Copyright (c) 2001, 2002 Zope Corporation 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 + + ****************************************************************************/ + +#define MERGETEMPLATE_C "$Id: MergeTemplate.c 105216 2009-10-22 14:30:54Z jim $\n" + +/**************************************************************************** + Set operations + ****************************************************************************/ + +static int +merge_output(Bucket *r, SetIteration *i, int mapping) +{ + if (r->len >= r->size && Bucket_grow(r, -1, !mapping) < 0) + return -1; + COPY_KEY(r->keys[r->len], i->key); + INCREF_KEY(r->keys[r->len]); + if (mapping) { + COPY_VALUE(r->values[r->len], i->value); + INCREF_VALUE(r->values[r->len]); + } + r->len++; + return 0; +} + +/* The "reason" argument is a little integer giving "a reason" for the + * error. In the Zope3 codebase, these are mapped to explanatory strings + * via zodb/btrees/interfaces.py. + */ +static PyObject * +merge_error(int p1, int p2, int p3, int reason) +{ + PyObject *r; + + UNLESS (r=Py_BuildValue("iiii", p1, p2, p3, reason)) r=Py_None; + if (ConflictError == NULL) { + ConflictError = PyExc_ValueError; + Py_INCREF(ConflictError); + } + PyErr_SetObject(ConflictError, r); + if (r != Py_None) + { + Py_DECREF(r); + } + + return NULL; +} + +/* It's hard to explain "the rules" for bucket_merge, in large part because + * any automatic conflict-resolution scheme is going to be incorrect for + * some endcases of *some* app. The scheme here is pretty conservative, + * and should be OK for most apps. It's easier to explain what the code + * allows than what it forbids: + * + * Leaving things alone: it's OK if both s2 and s3 leave a piece of s1 + * alone (don't delete the key, and don't change the value). + * + * Key deletion: a transaction (s2 or s3) can delete a key (from s1), but + * only if the other transaction (of s2 and s3) doesn't delete the same key. + * However, it's not OK for s2 and s3 to, between them, end up deleting all + * the keys. This is a higher-level constraint, due to that the caller of + * bucket_merge() doesn't have enough info to unlink the resulting empty + * bucket from its BTree correctly. It's also not OK if s2 or s3 are empty, + * because the transaction that emptied the bucket unlinked the bucket from + * the tree, and nothing we do here can get it linked back in again. + * + * Key insertion: s2 or s3 can add a new key, provided the other transaction + * doesn't insert the same key. It's not OK even if they insert the same + * pair. + * + * Mapping value modification: s2 or s3 can modify the value associated + * with a key in s1, provided the other transaction doesn't make a + * modification of the same key to a different value. It's OK if s2 and s3 + * both give the same new value to the key while it's hard to be precise about + * why, this doesn't seem consistent with that it's *not* OK for both to add + * a new key mapping to the same value). + */ +static PyObject * +bucket_merge(Bucket *s1, Bucket *s2, Bucket *s3) +{ + Bucket *r=0; + PyObject *s; + SetIteration i1 = {0,0,0}, i2 = {0,0,0}, i3 = {0,0,0}; + int cmp12, cmp13, cmp23, mapping, set; + + /* If either "after" bucket is empty, punt. */ + if (s2->len == 0 || s3->len == 0) + { + merge_error(-1, -1, -1, 12); + goto err; + } + + if (initSetIteration(&i1, OBJECT(s1), 1) < 0) + goto err; + if (initSetIteration(&i2, OBJECT(s2), 1) < 0) + goto err; + if (initSetIteration(&i3, OBJECT(s3), 1) < 0) + goto err; + + mapping = i1.usesValue | i2.usesValue | i3.usesValue; + set = !mapping; + + if (mapping) + r = (Bucket *)PyObject_CallObject((PyObject *)&BucketType, NULL); + else + r = (Bucket *)PyObject_CallObject((PyObject *)&SetType, NULL); + if (r == NULL) + goto err; + + if (i1.next(&i1) < 0) + goto err; + if (i2.next(&i2) < 0) + goto err; + if (i3.next(&i3) < 0) + goto err; + + /* Consult zodb/btrees/interfaces.py for the meaning of the last + * argument passed to merge_error(). + */ + /* TODO: This isn't passing on errors raised by value comparisons. */ + while (i1.position >= 0 && i2.position >= 0 && i3.position >= 0) + { + TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err; + TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err; + if (cmp12==0) + { + if (cmp13==0) + { + if (set || (TEST_VALUE(i1.value, i2.value) == 0)) + { /* change in i3 value or all same */ + if (merge_output(r, &i3, mapping) < 0) goto err; + } + else if (set || (TEST_VALUE(i1.value, i3.value) == 0)) + { /* change in i2 value */ + if (merge_output(r, &i2, mapping) < 0) goto err; + } + else + { /* conflicting value changes in i2 and i3 */ + merge_error(i1.position, i2.position, i3.position, 1); + goto err; + } + if (i1.next(&i1) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + else if (cmp13 > 0) + { /* insert i3 */ + if (merge_output(r, &i3, mapping) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + else if (set || (TEST_VALUE(i1.value, i2.value) == 0)) + { /* deleted in i3 */ + if (i3.position == 1) + { + /* Deleted the first item. This will modify the + parent node, so we don't know if merging will be + safe + */ + merge_error(i1.position, i2.position, i3.position, 13); + goto err; + } + if (i1.next(&i1) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + } + else + { /* conflicting del in i3 and change in i2 */ + merge_error(i1.position, i2.position, i3.position, 2); + goto err; + } + } + else if (cmp13 == 0) + { + if (cmp12 > 0) + { /* insert i2 */ + if (merge_output(r, &i2, mapping) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + } + else if (set || (TEST_VALUE(i1.value, i3.value) == 0)) + { /* deleted in i2 */ + if (i2.position == 1) + { + /* Deleted the first item. This will modify the + parent node, so we don't know if merging will be + safe + */ + merge_error(i1.position, i2.position, i3.position, 13); + goto err; + } + if (i1.next(&i1) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + else + { /* conflicting del in i2 and change in i3 */ + merge_error(i1.position, i2.position, i3.position, 3); + goto err; + } + } + else + { /* Both keys changed */ + TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err; + if (cmp23==0) + { /* dueling inserts or deletes */ + merge_error(i1.position, i2.position, i3.position, 4); + goto err; + } + if (cmp12 > 0) + { /* insert i2 */ + if (cmp23 > 0) + { /* insert i3 first */ + if (merge_output(r, &i3, mapping) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + else + { /* insert i2 first */ + if (merge_output(r, &i2, mapping) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + } + } + else if (cmp13 > 0) + { /* Insert i3 */ + if (merge_output(r, &i3, mapping) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + else + { /* 1<2 and 1<3: both deleted 1.key */ + merge_error(i1.position, i2.position, i3.position, 5); + goto err; + } + } + } + + while (i2.position >= 0 && i3.position >= 0) + { /* New inserts */ + TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err; + if (cmp23==0) + { /* dueling inserts */ + merge_error(i1.position, i2.position, i3.position, 6); + goto err; + } + if (cmp23 > 0) + { /* insert i3 */ + if (merge_output(r, &i3, mapping) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + else + { /* insert i2 */ + if (merge_output(r, &i2, mapping) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + } + } + + while (i1.position >= 0 && i2.position >= 0) + { /* remainder of i1 deleted in i3 */ + TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err; + if (cmp12 > 0) + { /* insert i2 */ + if (merge_output(r, &i2, mapping) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + } + else if (cmp12==0 && (set || (TEST_VALUE(i1.value, i2.value) == 0))) + { /* delete i3 */ + if (i1.next(&i1) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + } + else + { /* Dueling deletes or delete and change */ + merge_error(i1.position, i2.position, i3.position, 7); + goto err; + } + } + + while (i1.position >= 0 && i3.position >= 0) + { /* remainder of i1 deleted in i2 */ + TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err; + if (cmp13 > 0) + { /* insert i3 */ + if (merge_output(r, &i3, mapping) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + else if (cmp13==0 && (set || (TEST_VALUE(i1.value, i3.value) == 0))) + { /* delete i2 */ + if (i1.next(&i1) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + else + { /* Dueling deletes or delete and change */ + merge_error(i1.position, i2.position, i3.position, 8); + goto err; + } + } + + if (i1.position >= 0) + { /* Dueling deletes */ + merge_error(i1.position, i2.position, i3.position, 9); + goto err; + } + + while (i2.position >= 0) + { /* Inserting i2 at end */ + if (merge_output(r, &i2, mapping) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + } + + while (i3.position >= 0) + { /* Inserting i3 at end */ + if (merge_output(r, &i3, mapping) < 0) goto err; + if (i3.next(&i3) < 0) goto err; + } + + /* If the output bucket is empty, conflict resolution doesn't have + * enough info to unlink it from its containing BTree correctly. + */ + if (r->len == 0) + { + merge_error(-1, -1, -1, 10); + goto err; + } + + finiSetIteration(&i1); + finiSetIteration(&i2); + finiSetIteration(&i3); + + if (s1->next) + { + Py_INCREF(s1->next); + r->next = s1->next; + } + s = bucket_getstate(r); + Py_DECREF(r); + + return s; + + err: + finiSetIteration(&i1); + finiSetIteration(&i2); + finiSetIteration(&i3); + Py_XDECREF(r); + return NULL; +} diff -Nru zope3-3.4.0/src/BTrees/objectkeymacros.h zope3-3.5~bzr18/src/BTrees/objectkeymacros.h --- zope3-3.4.0/src/BTrees/objectkeymacros.h 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/objectkeymacros.h 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ +#define KEYMACROS_H "$Id: objectkeymacros.h 24682 2004-05-14 19:32:43Z tim_one $\n" +#define KEY_TYPE PyObject * +#define KEY_TYPE_IS_PYOBJECT +#define TEST_KEY_SET_OR(V, KEY, TARGET) if ( ( (V) = PyObject_Compare((KEY),(TARGET)) ), PyErr_Occurred() ) +#define INCREF_KEY(k) Py_INCREF(k) +#define DECREF_KEY(KEY) Py_DECREF(KEY) +#define COPY_KEY(KEY, E) KEY=(E) +#define COPY_KEY_TO_OBJECT(O, K) O=(K); Py_INCREF(O) +#define COPY_KEY_FROM_ARG(TARGET, ARG, S) TARGET=(ARG) diff -Nru zope3-3.4.0/src/BTrees/objectvaluemacros.h zope3-3.5~bzr18/src/BTrees/objectvaluemacros.h --- zope3-3.4.0/src/BTrees/objectvaluemacros.h 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/objectvaluemacros.h 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,13 @@ + +#define VALUEMACROS_H "$Id: objectvaluemacros.h 24682 2004-05-14 19:32:43Z tim_one $\n" + +#define VALUE_TYPE PyObject * +#define VALUE_TYPE_IS_PYOBJECT +#define TEST_VALUE(VALUE, TARGET) PyObject_Compare((VALUE),(TARGET)) +#define DECLARE_VALUE(NAME) VALUE_TYPE NAME +#define INCREF_VALUE(k) Py_INCREF(k) +#define DECREF_VALUE(k) Py_DECREF(k) +#define COPY_VALUE(k,e) k=(e) +#define COPY_VALUE_TO_OBJECT(O, K) O=(K); Py_INCREF(O) +#define COPY_VALUE_FROM_ARG(TARGET, ARG, S) TARGET=(ARG) +#define NORMALIZE_VALUE(V, MIN) Py_INCREF(V) diff -Nru zope3-3.4.0/src/BTrees/_OIBTree.c zope3-3.5~bzr18/src/BTrees/_OIBTree.c --- zope3-3.4.0/src/BTrees/_OIBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_OIBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _OIBTree.c 25186 2004-06-02 15:07:33Z jim $\n" + +/* OIBTree - object key, int value BTree + + Implements a collection using object type keys + and int type values +*/ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "OI" +#define INITMODULE init_OIBTree +#define DEFAULT_MAX_BUCKET_SIZE 60 +#define DEFAULT_MAX_BTREE_SIZE 250 + +#include "objectkeymacros.h" +#include "intvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_OIBTree.py zope3-3.5~bzr18/src/BTrees/_OIBTree.py --- zope3-3.4.0/src/BTrees/_OIBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_OIBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_OIBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/OIBTree.py zope3-3.5~bzr18/src/BTrees/OIBTree.py --- zope3-3.4.0/src/BTrees/OIBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/OIBTree.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _OIBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IObjectIntegerBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/_OLBTree.c zope3-3.5~bzr18/src/BTrees/_OLBTree.c --- zope3-3.4.0/src/BTrees/_OLBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_OLBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _OIBTree.c 25186 2004-06-02 15:07:33Z jim $\n" + +/* OIBTree - object key, int value BTree + + Implements a collection using object type keys + and int type values +*/ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "OL" +#define INITMODULE init_OLBTree +#define DEFAULT_MAX_BUCKET_SIZE 60 +#define DEFAULT_MAX_BTREE_SIZE 250 + +#define ZODB_64BIT_INTS + +#include "objectkeymacros.h" +#include "intvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_OLBTree.py zope3-3.5~bzr18/src/BTrees/_OLBTree.py --- zope3-3.4.0/src/BTrees/_OLBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_OLBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_OLBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/OLBTree.py zope3-3.5~bzr18/src/BTrees/OLBTree.py --- zope3-3.4.0/src/BTrees/OLBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/OLBTree.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _OLBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IObjectIntegerBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/_OOBTree.c zope3-3.5~bzr18/src/BTrees/_OOBTree.c --- zope3-3.4.0/src/BTrees/_OOBTree.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_OOBTree.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################*/ + +#define MASTER_ID "$Id: _OOBTree.c 25186 2004-06-02 15:07:33Z jim $\n" + +/* OOBTree - object key, object value BTree + + Implements a collection using object type keys + and object type values +*/ + +#define PERSISTENT + +#define MOD_NAME_PREFIX "OO" +#define INITMODULE init_OOBTree +#define DEFAULT_MAX_BUCKET_SIZE 30 +#define DEFAULT_MAX_BTREE_SIZE 250 + +#include "objectkeymacros.h" +#include "objectvaluemacros.h" +#include "BTreeModuleTemplate.c" diff -Nru zope3-3.4.0/src/BTrees/_OOBTree.py zope3-3.5~bzr18/src/BTrees/_OOBTree.py --- zope3-3.4.0/src/BTrees/_OOBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/_OOBTree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_OOBTree.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/BTrees/OOBTree.py zope3-3.5~bzr18/src/BTrees/OOBTree.py --- zope3-3.4.0/src/BTrees/OOBTree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/OOBTree.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 zope.interface +import BTrees.Interfaces + +# hack to overcome dynamic-linking headache. +from _OOBTree import * + +zope.interface.moduleProvides(BTrees.Interfaces.IObjectObjectBTreeModule) diff -Nru zope3-3.4.0/src/BTrees/py24compat.h zope3-3.5~bzr18/src/BTrees/py24compat.h --- zope3-3.4.0/src/BTrees/py24compat.h 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/py24compat.h 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ +/* Backport type definitions from Python 2.5's object.h */ +#ifndef BTREE_PY24COMPATH_H +#define BTREE_PY24COMPAT_H +#if PY_VERSION_HEX < 0x02050000 +typedef Py_ssize_t (*lenfunc)(PyObject *); +typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t); +typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t); +typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *); +typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *); +#endif /* PY_VERSION_HEX */ +#endif /* BTREE_PY24COMPAT_H */ diff -Nru zope3-3.4.0/src/BTrees/SetOpTemplate.c zope3-3.5~bzr18/src/BTrees/SetOpTemplate.c --- zope3-3.4.0/src/BTrees/SetOpTemplate.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/SetOpTemplate.c 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,557 @@ +/***************************************************************************** + + Copyright (c) 2001, 2002 Zope Corporation 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 + + ****************************************************************************/ + +/**************************************************************************** + Set operations + ****************************************************************************/ + +#define SETOPTEMPLATE_C "$Id: SetOpTemplate.c 76952 2007-06-23 04:13:28Z gary $\n" + +#ifdef KEY_CHECK +static int +nextKeyAsSet(SetIteration *i) +{ + if (i->position >= 0) { + if (i->position) { + DECREF_KEY(i->key); + i->position = -1; + } + else + i->position = 1; + } + return 0; +} +#endif + +/* initSetIteration + * + * Start the set iteration protocol. See the comments at struct SetIteration. + * + * Arguments + * i The address of a SetIteration control struct. + * s The address of the set, bucket, BTree, ..., to be iterated. + * useValues Boolean; if true, and s has values (is a mapping), copy + * them into i->value each time i->next() is called; else + * ignore s's values even if s is a mapping. + * + * Return + * 0 on success; -1 and an exception set if error. + * i.usesValue is set to 1 (true) if s has values and useValues was + * true; else usesValue is set to 0 (false). + * i.set gets a new reference to s, or to some other object used to + * iterate over s. + * i.position is set to 0. + * i.next is set to an appropriate iteration function. + * i.key and i.value are left alone. + * + * Internal + * i.position < 0 means iteration terminated. + * i.position = 0 means iteration hasn't yet begun (next() hasn't + * been called yet). + * In all other cases, i.key, and possibly i.value, own references. + * These must be cleaned up, either by next() routines, or by + * finiSetIteration. + * next() routines must ensure the above. They should return without + * doing anything when i.position < 0. + * It's the responsibility of {init, fini}setIteration to clean up + * the reference in i.set, and to ensure that no stale references + * live in i.key or i.value if iteration terminates abnormally. + * A SetIteration struct has been cleaned up iff i.set is NULL. + */ +static int +initSetIteration(SetIteration *i, PyObject *s, int useValues) +{ + i->set = NULL; + i->position = -1; /* set to 0 only on normal return */ + i->usesValue = 0; /* assume it's a set or that values aren't iterated */ + + if (PyObject_IsInstance(s, (PyObject *)&BucketType)) + { + i->set = s; + Py_INCREF(s); + + if (useValues) + { + i->usesValue = 1; + i->next = nextBucket; + } + else + i->next = nextSet; + } + else if (PyObject_IsInstance(s, (PyObject *)&SetType)) + { + i->set = s; + Py_INCREF(s); + i->next = nextSet; + } + else if (PyObject_IsInstance(s, (PyObject *)&BTreeType)) + { + i->set = BTree_rangeSearch(BTREE(s), NULL, NULL, 'i'); + UNLESS(i->set) return -1; + + if (useValues) + { + i->usesValue = 1; + i->next = nextBTreeItems; + } + else + i->next = nextTreeSetItems; + } + else if (PyObject_IsInstance(s, (PyObject *)&TreeSetType)) + { + i->set = BTree_rangeSearch(BTREE(s), NULL, NULL, 'k'); + UNLESS(i->set) return -1; + i->next = nextTreeSetItems; + } +#ifdef KEY_CHECK + else if (KEY_CHECK(s)) + { + int copied = 1; + COPY_KEY_FROM_ARG(i->key, s, copied); + UNLESS (copied) return -1; + + INCREF_KEY(i->key); + i->set = s; + Py_INCREF(s); + i->next = nextKeyAsSet; + } +#endif + else + { + PyErr_SetString(PyExc_TypeError, "invalid argument"); + return -1; + } + + i->position = 0; + + return 0; +} + +#ifndef MERGE_WEIGHT +#define MERGE_WEIGHT(O, w) (O) +#endif + +static int +copyRemaining(Bucket *r, SetIteration *i, int merge, + +/* See comment # 42 */ +#ifdef MERGE + VALUE_TYPE w) +#else + int w) +#endif +{ + while (i->position >= 0) + { + if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) return -1; + COPY_KEY(r->keys[r->len], i->key); + INCREF_KEY(r->keys[r->len]); + + if (merge) + { + COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i->value, w)); + INCREF_VALUE(r->values[r->len]); + } + r->len++; + if (i->next(i) < 0) return -1; + } + + return 0; +} + +/* This is the workhorse for all set merge operations: the weighted and + * unweighted flavors of union and intersection, and set difference. The + * algorithm is conceptually simple but the code is complicated due to all + * the options. + * + * s1, s2 + * The input collections to be merged. + * + * usevalues1, usevalues2 + * Booleans. In the output, should values from s1 (or s2) be used? This + * only makes sense when an operation intends to support mapping outputs; + * these should both be false for operations that want pure set outputs. + * + * w1, w2 + * If usevalues1(2) are true, these are the weights to apply to the + * input values. + * + * c1 + * Boolean. Should keys that appear in c1 but not c2 appear in the output? + * c12 + * Boolean. Should keys that appear in both inputs appear in the output? + * c2 + * Boolean. Should keys that appear in c2 but not c1 appear in the output? + * + * Returns NULL if error, else a Set or Bucket, depending on whether a set or + * mapping was requested. + */ +static PyObject * +set_operation(PyObject *s1, PyObject *s2, + int usevalues1, int usevalues2, + +/* Comment # 42 + +The following ifdef works around a template/type problem + +Weights are passed as integers. In particular, the weight passed by +difference is one. This works fine in the int value and float value +cases but makes no sense in the object value case. In the object +value case, we don't do merging, so we don't use the weights, so it +doesn't matter what they are. +*/ +#ifdef MERGE + VALUE_TYPE w1, VALUE_TYPE w2, +#else + int w1, int w2, +#endif + int c1, int c12, int c2) + + +{ + Bucket *r=0; + SetIteration i1 = {0,0,0}, i2 = {0,0,0}; + int cmp, merge; + + if (initSetIteration(&i1, s1, usevalues1) < 0) goto err; + if (initSetIteration(&i2, s2, usevalues2) < 0) goto err; + merge = i1.usesValue | i2.usesValue; + + if (merge) + { +#ifndef MERGE + if (c12 && i1.usesValue && i2.usesValue) goto invalid_set_operation; +#endif + if (! i1.usesValue&& i2.usesValue) + { + SetIteration t; + int i; + +/* See comment # 42 above */ +#ifdef MERGE + VALUE_TYPE v; +#else + int v; +#endif + + t=i1; i1=i2; i2=t; + i=c1; c1=c2; c2=i; + v=w1; w1=w2; w2=v; + } +#ifdef MERGE_DEFAULT + i1.value=MERGE_DEFAULT; + i2.value=MERGE_DEFAULT; +#else + if (i1.usesValue) + { + if (! i2.usesValue && c2) goto invalid_set_operation; + } + else + { + if (c1 || c12) goto invalid_set_operation; + } +#endif + + UNLESS(r=BUCKET(PyObject_CallObject(OBJECT(&BucketType), NULL))) + goto err; + } + else + { + UNLESS(r=BUCKET(PyObject_CallObject(OBJECT(&SetType), NULL))) + goto err; + } + + if (i1.next(&i1) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + + while (i1.position >= 0 && i2.position >= 0) + { + TEST_KEY_SET_OR(cmp, i1.key, i2.key) goto err; + if(cmp < 0) + { + if(c1) + { + if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err; + COPY_KEY(r->keys[r->len], i1.key); + INCREF_KEY(r->keys[r->len]); + if (merge) + { + COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i1.value, w1)); + INCREF_VALUE(r->values[r->len]); + } + r->len++; + } + if (i1.next(&i1) < 0) goto err; + } + else if(cmp==0) + { + if(c12) + { + if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err; + COPY_KEY(r->keys[r->len], i1.key); + INCREF_KEY(r->keys[r->len]); + if (merge) + { +#ifdef MERGE + r->values[r->len] = MERGE(i1.value, w1, i2.value, w2); +#else + COPY_VALUE(r->values[r->len], i1.value); + INCREF_VALUE(r->values[r->len]); +#endif + } + r->len++; + } + if (i1.next(&i1) < 0) goto err; + if (i2.next(&i2) < 0) goto err; + } + else + { + if(c2) + { + if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err; + COPY_KEY(r->keys[r->len], i2.key); + INCREF_KEY(r->keys[r->len]); + if (merge) + { + COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i2.value, w2)); + INCREF_VALUE(r->values[r->len]); + } + r->len++; + } + if (i2.next(&i2) < 0) goto err; + } + } + if(c1 && copyRemaining(r, &i1, merge, w1) < 0) goto err; + if(c2 && copyRemaining(r, &i2, merge, w2) < 0) goto err; + + + finiSetIteration(&i1); + finiSetIteration(&i2); + + return OBJECT(r); + +#ifndef MERGE_DEFAULT +invalid_set_operation: + PyErr_SetString(PyExc_TypeError, "invalid set operation"); +#endif + +err: + finiSetIteration(&i1); + finiSetIteration(&i2); + Py_XDECREF(r); + return NULL; +} + +static PyObject * +difference_m(PyObject *ignored, PyObject *args) +{ + PyObject *o1, *o2; + + UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL; + + + if (o1 == Py_None || o2 == Py_None) + { + /* difference(None, X) -> None; difference(X, None) -> X */ + Py_INCREF(o1); + return o1; + } + + return set_operation(o1, o2, 1, 0, /* preserve values from o1, ignore o2's */ + 1, 0, /* o1's values multiplied by 1 */ + 1, 0, 0); /* take only keys unique to o1 */ +} + +static PyObject * +union_m(PyObject *ignored, PyObject *args) +{ + PyObject *o1, *o2; + + UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL; + + if (o1 == Py_None) + { + Py_INCREF(o2); + return o2; + } + else if (o2 == Py_None) + { + Py_INCREF(o1); + return o1; + } + + return set_operation(o1, o2, 0, 0, /* ignore values in both */ + 1, 1, /* the weights are irrelevant */ + 1, 1, 1); /* take all keys */ +} + +static PyObject * +intersection_m(PyObject *ignored, PyObject *args) +{ + PyObject *o1, *o2; + + UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL; + + if (o1 == Py_None) + { + Py_INCREF(o2); + return o2; + } + else if (o2 == Py_None) + { + Py_INCREF(o1); + return o1; + } + + return set_operation(o1, o2, 0, 0, /* ignore values in both */ + 1, 1, /* the weights are irrelevant */ + 0, 1, 0); /* take only keys common to both */ +} + +#ifdef MERGE + +static PyObject * +wunion_m(PyObject *ignored, PyObject *args) +{ + PyObject *o1, *o2; + VALUE_TYPE w1 = 1, w2 = 1; + + UNLESS(PyArg_ParseTuple(args, "OO|" VALUE_PARSE VALUE_PARSE, + &o1, &o2, &w1, &w2) + ) return NULL; + + if (o1 == Py_None) + return Py_BuildValue(VALUE_PARSE "O", (o2 == Py_None ? 0 : w2), o2); + else if (o2 == Py_None) + return Py_BuildValue(VALUE_PARSE "O", w1, o1); + + o1 = set_operation(o1, o2, 1, 1, w1, w2, 1, 1, 1); + if (o1) + ASSIGN(o1, Py_BuildValue(VALUE_PARSE "O", (VALUE_TYPE)1, o1)); + + return o1; +} + +static PyObject * +wintersection_m(PyObject *ignored, PyObject *args) +{ + PyObject *o1, *o2; + VALUE_TYPE w1 = 1, w2 = 1; + + UNLESS(PyArg_ParseTuple(args, "OO|" VALUE_PARSE VALUE_PARSE, + &o1, &o2, &w1, &w2) + ) return NULL; + + if (o1 == Py_None) + return Py_BuildValue(VALUE_PARSE "O", (o2 == Py_None ? 0 : w2), o2); + else if (o2 == Py_None) + return Py_BuildValue(VALUE_PARSE "O", w1, o1); + + o1 = set_operation(o1, o2, 1, 1, w1, w2, 0, 1, 0); + if (o1) + ASSIGN(o1, Py_BuildValue(VALUE_PARSE "O", + ((o1->ob_type == (PyTypeObject*)(&SetType)) ? w2+w1 : 1), + o1)); + + return o1; +} + +#endif + +#ifdef MULTI_INT_UNION +#include "sorters.c" + +/* Input is a sequence of integer sets (or convertible to sets by the + set iteration protocol). Output is the union of the sets. The point + is to run much faster than doing pairs of unions. +*/ +static PyObject * +multiunion_m(PyObject *ignored, PyObject *args) +{ + PyObject *seq; /* input sequence */ + int n; /* length of input sequence */ + PyObject *set = NULL; /* an element of the input sequence */ + Bucket *result; /* result set */ + SetIteration setiter = {0}; + int i; + + UNLESS(PyArg_ParseTuple(args, "O", &seq)) + return NULL; + + n = PyObject_Length(seq); + if (n < 0) + return NULL; + + /* Construct an empty result set. */ + result = BUCKET(PyObject_CallObject(OBJECT(&SetType), NULL)); + if (result == NULL) + return NULL; + + /* For each set in the input sequence, append its elements to the result + set. At this point, we ignore the possibility of duplicates. */ + for (i = 0; i < n; ++i) { + set = PySequence_GetItem(seq, i); + if (set == NULL) + goto Error; + + /* If set is a bucket, do a straight resize + memcpy. */ + if (set->ob_type == (PyTypeObject*)&SetType || + set->ob_type == (PyTypeObject*)&BucketType) + { + Bucket *b = BUCKET(set); + int status = 0; + + UNLESS (PER_USE(b)) goto Error; + if (b->len) + status = bucket_append(result, b, 0, b->len, 0, i < n-1); + PER_UNUSE(b); + if (status < 0) goto Error; + } + else { + /* No cheap way: iterate over set's elements one at a time. */ + if (initSetIteration(&setiter, set, 0) < 0) goto Error; + if (setiter.next(&setiter) < 0) goto Error; + while (setiter.position >= 0) { + if (result->len >= result->size && Bucket_grow(result, -1, 1) < 0) + goto Error; + COPY_KEY(result->keys[result->len], setiter.key); + ++result->len; + /* We know the key is an int, so no need to incref it. */ + if (setiter.next(&setiter) < 0) goto Error; + } + finiSetIteration(&setiter); + } + Py_DECREF(set); + set = NULL; + } + + /* Combine, sort, remove duplicates, and reset the result's len. + If the set shrinks (which happens if and only if there are + duplicates), no point to realloc'ing the set smaller, as we + expect the result set to be short-lived. + */ + if (result->len > 0) { + size_t newlen; /* number of elements in final result set */ + newlen = sort_int_nodups(result->keys, (size_t)result->len); + result->len = (int)newlen; + } + return (PyObject *)result; + +Error: + Py_DECREF(result); + Py_XDECREF(set); + finiSetIteration(&setiter); + return NULL; +} +#endif diff -Nru zope3-3.4.0/src/BTrees/SetTemplate.c zope3-3.5~bzr18/src/BTrees/SetTemplate.c --- zope3-3.4.0/src/BTrees/SetTemplate.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/SetTemplate.c 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,371 @@ +/***************************************************************************** + + Copyright (c) 2001, 2002 Zope Corporation 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 + + ****************************************************************************/ + +#define SETTEMPLATE_C "$Id: SetTemplate.c 103321 2009-08-27 20:46:38Z jim $\n" + +static PyObject * +Set_insert(Bucket *self, PyObject *args) +{ + PyObject *key; + int i; + + UNLESS (PyArg_ParseTuple(args, "O", &key)) return NULL; + if ( (i=_bucket_set(self, key, Py_None, 1, 1, 0)) < 0) return NULL; + return PyInt_FromLong(i); +} + +/* _Set_update and _TreeSet_update are identical except for the + function they call to add the element to the set. +*/ + +static int +_Set_update(Bucket *self, PyObject *seq) +{ + int n = -1; + PyObject *iter, *v; + int ind; + + iter = PyObject_GetIter(seq); + if (iter == NULL) + return -1; + + while (1) { + v = PyIter_Next(iter); + if (v == NULL) { + if (PyErr_Occurred()) + goto err; + else + break; + } + ind = _bucket_set(self, v, Py_None, 1, 1, 0); + Py_DECREF(v); + if (ind < 0) + goto err; + else + n += ind; + } + /* n starts out at -1, which is the error return value. If + this point is reached, then there is no error. n must be + incremented to account for the initial value of -1 instead of + 0. + */ + n++; + + err: + Py_DECREF(iter); + return n; +} + +static PyObject * +Set_update(Bucket *self, PyObject *args) +{ + PyObject *seq = NULL; + int n = 0; + + if (!PyArg_ParseTuple(args, "|O:update", &seq)) + return NULL; + + if (seq) { + n = _Set_update(self, seq); + if (n < 0) + return NULL; + } + + return PyInt_FromLong(n); +} + +static PyObject * +Set_remove(Bucket *self, PyObject *args) +{ + PyObject *key; + + UNLESS (PyArg_ParseTuple(args, "O", &key)) return NULL; + if (_bucket_set(self, key, NULL, 0, 1, 0) < 0) return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static int +_set_setstate(Bucket *self, PyObject *args) +{ + PyObject *k, *items; + Bucket *next=0; + int i, l, copied=1; + KEY_TYPE *keys; + + UNLESS (PyArg_ParseTuple(args, "O|O", &items, &next)) + return -1; + + if (!PyTuple_Check(items)) { + PyErr_SetString(PyExc_TypeError, + "tuple required for first state element"); + return -1; + } + + if ((l=PyTuple_Size(items)) < 0) return -1; + + for (i=self->len; --i >= 0; ) + { + DECREF_KEY(self->keys[i]); + } + self->len=0; + + if (self->next) + { + Py_DECREF(self->next); + self->next=0; + } + + if (l > self->size) + { + UNLESS (keys=BTree_Realloc(self->keys, sizeof(KEY_TYPE)*l)) return -1; + self->keys=keys; + self->size=l; + } + + for (i=0; ikeys[i], k, copied); + UNLESS (copied) return -1; + INCREF_KEY(self->keys[i]); + } + + self->len=l; + + if (next) + { + self->next=next; + Py_INCREF(next); + } + + return 0; +} + +static PyObject * +set_setstate(Bucket *self, PyObject *args) +{ + int r; + + UNLESS (PyArg_ParseTuple(args, "O", &args)) return NULL; + + PER_PREVENT_DEACTIVATION(self); + r=_set_setstate(self, args); + PER_UNUSE(self); + + if (r < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef Set_methods[] = { + {"__getstate__", (PyCFunction) bucket_getstate, METH_VARARGS, + "__getstate__() -- Return the picklable state of the object"}, + + {"__setstate__", (PyCFunction) set_setstate, METH_VARARGS, + "__setstate__() -- Set the state of the object"}, + + {"keys", (PyCFunction) bucket_keys, METH_KEYWORDS, + "keys() -- Return the keys"}, + + {"has_key", (PyCFunction) bucket_has_key, METH_O, + "has_key(key) -- Test whether the bucket contains the given key"}, + + {"clear", (PyCFunction) bucket_clear, METH_VARARGS, + "clear() -- Remove all of the items from the bucket"}, + + {"maxKey", (PyCFunction) Bucket_maxKey, METH_VARARGS, + "maxKey([key]) -- Find the maximum key\n\n" + "If an argument is given, find the maximum <= the argument"}, + + {"minKey", (PyCFunction) Bucket_minKey, METH_VARARGS, + "minKey([key]) -- Find the minimum key\n\n" + "If an argument is given, find the minimum >= the argument"}, + +#ifdef PERSISTENT + {"_p_resolveConflict", (PyCFunction) bucket__p_resolveConflict, METH_VARARGS, + "_p_resolveConflict() -- Reinitialize from a newly created copy"}, + + {"_p_deactivate", (PyCFunction) bucket__p_deactivate, METH_KEYWORDS, + "_p_deactivate() -- Reinitialize from a newly created copy"}, +#endif + + {"add", (PyCFunction)Set_insert, METH_VARARGS, + "add(id) -- Add a key to the set"}, + + {"insert", (PyCFunction)Set_insert, METH_VARARGS, + "insert(id) -- Add a key to the set"}, + + {"update", (PyCFunction)Set_update, METH_VARARGS, + "update(seq) -- Add the items from the given sequence to the set"}, + + {"remove", (PyCFunction)Set_remove, METH_VARARGS, + "remove(id) -- Remove an id from the set"}, + + {NULL, NULL} /* sentinel */ +}; + +static int +Set_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *v = NULL; + + if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "Set", &v)) + return -1; + + if (v) + return _Set_update((Bucket *)self, v); + else + return 0; +} + + + +static PyObject * +set_repr(Bucket *self) +{ + static PyObject *format; + PyObject *r, *t; + + if (!format) + format = PyString_FromString(MOD_NAME_PREFIX "Set(%s)"); + UNLESS (t = PyTuple_New(1)) return NULL; + UNLESS (r = bucket_keys(self, NULL, NULL)) goto err; + PyTuple_SET_ITEM(t, 0, r); + r = t; + ASSIGN(r, PyString_Format(format, r)); + return r; +err: + Py_DECREF(t); + return NULL; +} + +static Py_ssize_t +set_length(Bucket *self) +{ + int r; + + PER_USE_OR_RETURN(self, -1); + r = self->len; + PER_UNUSE(self); + + return r; +} + +static PyObject * +set_item(Bucket *self, Py_ssize_t index) +{ + PyObject *r=0; + + PER_USE_OR_RETURN(self, NULL); + if (index >= 0 && index < self->len) + { + COPY_KEY_TO_OBJECT(r, self->keys[index]); + } + else + IndexError(index); + + PER_UNUSE(self); + + return r; +} + +static PySequenceMethods set_as_sequence = { + (lenfunc)set_length, /* sq_length */ + (binaryfunc)0, /* sq_concat */ + (ssizeargfunc)0, /* sq_repeat */ + (ssizeargfunc)set_item, /* sq_item */ + (ssizessizeargfunc)0, /* sq_slice */ + (ssizeobjargproc)0, /* sq_ass_item */ + (ssizessizeobjargproc)0, /* sq_ass_slice */ + (objobjproc)bucket_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyTypeObject SetType = { + PyObject_HEAD_INIT(NULL) /* PyPersist_Type */ + 0, /* ob_size */ + MODULE_NAME MOD_NAME_PREFIX "Set", /* tp_name */ + sizeof(Bucket), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)bucket_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)set_repr, /* tp_repr */ + 0, /* tp_as_number */ + &set_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)bucket_traverse, /* tp_traverse */ + (inquiry)bucket_tp_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)Bucket_getiter, /* tp_iter */ + 0, /* tp_iternext */ + Set_methods, /* tp_methods */ + Bucket_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + Set_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /*PyType_GenericNew,*/ /* tp_new */ +}; + +static int +nextSet(SetIteration *i) +{ + + if (i->position >= 0) + { + UNLESS(PER_USE(BUCKET(i->set))) return -1; + + if (i->position) + { + DECREF_KEY(i->key); + } + + if (i->position < BUCKET(i->set)->len) + { + COPY_KEY(i->key, BUCKET(i->set)->keys[i->position]); + INCREF_KEY(i->key); + i->position ++; + } + else + { + i->position = -1; + PER_ACCESSED(BUCKET(i->set)); + } + + PER_ALLOW_DEACTIVATION(BUCKET(i->set)); + } + + + return 0; +} diff -Nru zope3-3.4.0/src/BTrees/sorters.c zope3-3.5~bzr18/src/BTrees/sorters.c --- zope3-3.4.0/src/BTrees/sorters.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/sorters.c 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,542 @@ +/***************************************************************************** + + Copyright (c) 2002 Zope Corporation 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 + + ****************************************************************************/ + +/* Revision information: $Id: sorters.c 71250 2006-11-21 19:50:51Z fdrake $ */ + +/* The only routine here intended to be used outside the file is + size_t sort_int_nodups(int *p, size_t n) + + Sort the array of n ints pointed at by p, in place, and also remove + duplicates. Return the number of unique elements remaining, which occupy + a contiguous and monotonically increasing slice of the array starting at p. + + Example: If the input array is [3, 1, 2, 3, 1, 5, 2], sort_int_nodups + returns 4, and the first 4 elements of the array are changed to + [1, 2, 3, 5]. The content of the remaining array positions is not defined. + + Notes: + + + This is specific to n-byte signed ints, with endianness natural to the + platform. `n` is determined based on ZODB_64BIT_INTS. + + + 4*n bytes of available heap memory are required for best speed + (8*n when ZODB_64BIT_INTS is defined). +*/ + +#include +#include +#include +#include +#include + +/* The type of array elements to be sorted. Most of the routines don't + care about the type, and will work fine for any scalar C type (provided + they're recompiled with element_type appropriately redefined). However, + the radix sort has to know everything about the type's internal + representation. +*/ +typedef KEY_TYPE element_type; + +/* The radixsort is faster than the quicksort for large arrays, but radixsort + has high fixed overhead, making it a poor choice for small arrays. The + crossover point isn't critical, and is sensitive to things like compiler + and machine cache structure, so don't worry much about this. +*/ +#define QUICKSORT_BEATS_RADIXSORT 800U + +/* In turn, the quicksort backs off to an insertion sort for very small + slices. MAX_INSERTION is the largest slice quicksort leaves entirely to + insertion. Because this version of quicksort uses a median-of-3 rule for + selecting a pivot, MAX_INSERTION must be at least 2 (so that quicksort + has at least 3 values to look at in a slice). Again, the exact value here + isn't critical. +*/ +#define MAX_INSERTION 25U + +#if MAX_INSERTION < 2U +# error "MAX_INSERTION must be >= 2" +#endif + +/* LSB-first radix sort of the n elements in 'in'. + 'work' is work storage at least as large as 'in'. Depending on how many + swaps are done internally, the final result may come back in 'in' or 'work'; + and that pointer is returned. + + radixsort_int is specific to signed n-byte ints, with natural machine + endianness. `n` is determined based on ZODB_64BIT_INTS. +*/ +static element_type* +radixsort_int(element_type *in, element_type *work, size_t n) +{ + /* count[i][j] is the number of input elements that have byte value j + in byte position i, where byte position 0 is the LSB. Note that + holding i fixed, the sum of count[i][j] over all j in range(256) + is n. + */ +#ifdef ZODB_64BIT_INTS + size_t count[8][256]; +#else + size_t count[4][256]; +#endif + size_t i; + int offset, offsetinc; + + /* Which byte position are we working on now? 0=LSB, 1, 2, ... */ + int bytenum; + +#ifdef ZODB_64BIT_INTS + assert(sizeof(element_type) == 8); +#else + assert(sizeof(element_type) == 4); +#endif + assert(in); + assert(work); + + /* Compute all of count in one pass. */ + memset(count, 0, sizeof(count)); + for (i = 0; i < n; ++i) { + element_type const x = in[i]; + ++count[0][(x ) & 0xff]; + ++count[1][(x >> 8) & 0xff]; + ++count[2][(x >> 16) & 0xff]; + ++count[3][(x >> 24) & 0xff]; +#ifdef ZODB_64BIT_INTS + ++count[4][(x >> 32) & 0xff]; + ++count[5][(x >> 40) & 0xff]; + ++count[6][(x >> 48) & 0xff]; + ++count[7][(x >> 56) & 0xff]; +#endif + } + + /* For p an element_type* cast to char*, offset is how much farther we + have to go to get to the LSB of the element; this is 0 for little- + endian boxes and sizeof(element_type)-1 for big-endian. + offsetinc is 1 or -1, respectively, telling us which direction to go + from p+offset to get to the element's more-significant bytes. + */ + { + element_type one = 1; + if (*(char*)&one) { + /* Little endian. */ + offset = 0; + offsetinc = 1; + } + else { + /* Big endian. */ + offset = sizeof(element_type) - 1; + offsetinc = -1; + } + } + + /* The radix sort. */ + for (bytenum = 0; + bytenum < sizeof(element_type); + ++bytenum, offset += offsetinc) { + + /* Do a stable distribution sort on byte position bytenum, + from in to work. index[i] tells us the work index at which + to store the next in element with byte value i. pinbyte + points to the correct byte in the input array. + */ + size_t index[256]; + unsigned char* pinbyte; + size_t total = 0; + size_t *pcount = count[bytenum]; + + /* Compute the correct output starting index for each possible + byte value. + */ + if (bytenum < sizeof(element_type) - 1) { + for (i = 0; i < 256; ++i) { + const size_t icount = pcount[i]; + index[i] = total; + total += icount; + if (icount == n) + break; + } + if (i < 256) { + /* All bytes in the current position have value + i, so there's nothing to do on this pass. + */ + continue; + } + } + else { + /* The MSB of signed ints needs to be distributed + differently than the other bytes, in order + 0x80, 0x81, ... 0xff, 0x00, 0x01, ... 0x7f + */ + for (i = 128; i < 256; ++i) { + const size_t icount = pcount[i]; + index[i] = total; + total += icount; + if (icount == n) + break; + } + if (i < 256) + continue; + for (i = 0; i < 128; ++i) { + const size_t icount = pcount[i]; + index[i] = total; + total += icount; + if (icount == n) + break; + } + if (i < 128) + continue; + } + assert(total == n); + + /* Distribute the elements according to byte value. Note that + this is where most of the time is spent. + Note: The loop is unrolled 4x by hand, for speed. This + may be a pessimization someday, but was a significant win + on my MSVC 6.0 timing tests. + */ + pinbyte = (unsigned char *)in + offset; + i = 0; + /* Reduce number of elements to copy to a multiple of 4. */ + while ((n - i) & 0x3) { + unsigned char byte = *pinbyte; + work[index[byte]++] = in[i]; + ++i; + pinbyte += sizeof(element_type); + } + for (; i < n; i += 4, pinbyte += 4 * sizeof(element_type)) { + unsigned char byte1 = *(pinbyte ); + unsigned char byte2 = *(pinbyte + sizeof(element_type)); + unsigned char byte3 = *(pinbyte + 2 * sizeof(element_type)); + unsigned char byte4 = *(pinbyte + 3 * sizeof(element_type)); + + element_type in1 = in[i ]; + element_type in2 = in[i+1]; + element_type in3 = in[i+2]; + element_type in4 = in[i+3]; + + work[index[byte1]++] = in1; + work[index[byte2]++] = in2; + work[index[byte3]++] = in3; + work[index[byte4]++] = in4; + } + /* Swap in and work (just a pointer swap). */ + { + element_type *temp = in; + in = work; + work = temp; + } + } + + return in; +} + +/* Remove duplicates from sorted array in, storing exactly one of each distinct + element value into sorted array out. It's OK (and expected!) for in == out, + but otherwise the n elements beginning at in must not overlap with the n + beginning at out. + Return the number of elements in out. +*/ +static size_t +uniq(element_type *out, element_type *in, size_t n) +{ + size_t i; + element_type lastelt; + element_type *pout; + + assert(out); + assert(in); + if (n == 0) + return 0; + + /* i <- first index in 'in' that contains a duplicate. + in[0], in[1], ... in[i-1] are unique, but in[i-1] == in[i]. + Set i to n if everything is unique. + */ + for (i = 1; i < n; ++i) { + if (in[i-1] == in[i]) + break; + } + + /* in[:i] is unique; copy to out[:i] if needed. */ + assert(i > 0); + if (in != out) + memcpy(out, in, i * sizeof(element_type)); + + pout = out + i; + lastelt = in[i-1]; /* safe even when i == n */ + for (++i; i < n; ++i) { + element_type elt = in[i]; + if (elt != lastelt) + *pout++ = lastelt = elt; + } + return pout - out; +} + +#if 0 +/* insertionsort is no longer referenced directly, but I'd like to keep + * the code here just in case. + */ + +/* Straight insertion sort of the n elements starting at 'in'. */ +static void +insertionsort(element_type *in, size_t n) +{ + element_type *p, *q; + element_type minimum; /* smallest seen so far */ + element_type *plimit = in + n; + + assert(in); + if (n < 2) + return; + + minimum = *in; + for (p = in+1; p < plimit; ++p) { + /* *in <= *(in+1) <= ... <= *(p-1). Slide *p into place. */ + element_type thiselt = *p; + if (thiselt < minimum) { + /* This is a new minimum. This saves p-in compares + when it happens, but should happen so rarely that + it's not worth checking for its own sake: the + point is that the far more popular 'else' branch can + exploit that thiselt is *not* the smallest so far. + */ + memmove(in+1, in, (p - in) * sizeof(*in)); + *in = minimum = thiselt; + } + else { + /* thiselt >= minimum, so the loop will find a q + with *q <= thiselt. This saves testing q >= in + on each trip. It's such a simple loop that saving + a per-trip test is a major speed win. + */ + for (q = p-1; *q > thiselt; --q) + *(q+1) = *q; + *(q+1) = thiselt; + } + } +} +#endif + +/* The maximum number of elements in the pending-work stack quicksort + maintains. The maximum stack depth is approximately log2(n), so + arrays of size up to approximately MAX_INSERTION * 2**STACKSIZE can be + sorted. The memory burden for the stack is small, so better safe than + sorry. +*/ +#define STACKSIZE 60 + +/* A _stacknode remembers a contiguous slice of an array that needs to sorted. + lo must be <= hi, and, unlike Python array slices, this includes both ends. +*/ +struct _stacknode { + element_type *lo; + element_type *hi; +}; + +static void +quicksort(element_type *plo, size_t n) +{ + element_type *phi; + + /* Swap two array elements. */ + element_type _temp; +#define SWAP(P, Q) (_temp = *(P), *(P) = *(Q), *(Q) = _temp) + + /* Stack of pending array slices to be sorted. */ + struct _stacknode stack[STACKSIZE]; + struct _stacknode *stackfree = stack; /* available stack slot */ + + /* Push an array slice on the pending-work stack. */ +#define PUSH(PLO, PHI) \ + do { \ + assert(stackfree - stack < STACKSIZE); \ + assert((PLO) <= (PHI)); \ + stackfree->lo = (PLO); \ + stackfree->hi = (PHI); \ + ++stackfree; \ + } while(0) + + assert(plo); + phi = plo + n - 1; + + for (;;) { + element_type pivot; + element_type *pi, *pj; + + assert(plo <= phi); + n = phi - plo + 1; + if (n <= MAX_INSERTION) { + /* Do a small insertion sort. Contra Knuth, we do + this now instead of waiting until the end, because + this little slice is likely still in cache now. + */ + element_type *p, *q; + element_type minimum = *plo; + + for (p = plo+1; p <= phi; ++p) { + /* *plo <= *(plo+1) <= ... <= *(p-1). + Slide *p into place. */ + element_type thiselt = *p; + if (thiselt < minimum) { + /* New minimum. */ + memmove(plo+1, + plo, + (p - plo) * sizeof(*p)); + *plo = minimum = thiselt; + } + else { + /* thiselt >= minimum, so the loop will + find a q with *q <= thiselt. + */ + for (q = p-1; *q > thiselt; --q) + *(q+1) = *q; + *(q+1) = thiselt; + } + } + + /* Pop another slice off the stack. */ + if (stack == stackfree) + break; /* no more slices -- we're done */ + --stackfree; + plo = stackfree->lo; + phi = stackfree->hi; + continue; + } + + /* Parition the slice. + For pivot, take the median of the leftmost, rightmost, and + middle elements. First sort those three; then the median + is the middle one. For technical reasons, the middle + element is swapped to plo+1 first (see Knuth Vol 3 Ed 2 + section 5.2.2 exercise 55 -- reverse-sorted arrays can + take quadratic time otherwise!). + */ + { + element_type *plop1 = plo + 1; + element_type *pmid = plo + (n >> 1); + + assert(plo < pmid && pmid < phi); + SWAP(plop1, pmid); + + /* Sort plo, plop1, phi. */ + /* Smaller of rightmost two -> middle. */ + if (*plop1 > *phi) + SWAP(plop1, phi); + /* Smallest of all -> left; if plo is already the + smallest, the sort is complete. + */ + if (*plo > *plop1) { + SWAP(plo, plop1); + /* Largest of all -> right. */ + if (*plop1 > *phi) + SWAP(plop1, phi); + } + pivot = *plop1; + pi = plop1; + } + assert(*plo <= pivot); + assert(*pi == pivot); + assert(*phi >= pivot); + pj = phi; + + /* Partition wrt pivot. This is the time-critical part, and + nearly every decision in the routine aims at making this + loop as fast as possible -- even small points like + arranging that all loop tests can be done correctly at the + bottoms of loops instead of the tops, and that pointers can + be derefenced directly as-is (without fiddly +1 or -1). + The aim is to make the C here so simple that a compiler + has a good shot at doing as well as hand-crafted assembler. + */ + for (;;) { + /* Invariants: + 1. pi < pj. + 2. All elements at plo, plo+1 .. pi are <= pivot. + 3. All elements at pj, pj+1 .. phi are >= pivot. + 4. There is an element >= pivot to the right of pi. + 5. There is an element <= pivot to the left of pj. + + Note that #4 and #5 save us from needing to check + that the pointers stay in bounds. + */ + assert(pi < pj); + + do { ++pi; } while (*pi < pivot); + assert(pi <= pj); + + do { --pj; } while (*pj > pivot); + assert(pj >= pi - 1); + + if (pi < pj) + SWAP(pi, pj); + else + break; + } + assert(plo+1 < pi && pi <= phi); + assert(plo < pj && pj < phi); + assert(*pi >= pivot); + assert( (pi == pj && *pj == pivot) || + (pj + 1 == pi && *pj <= pivot) ); + + /* Swap pivot into its final position, pj. */ + assert(plo[1] == pivot); + plo[1] = *pj; + *pj = pivot; + + /* Subfiles are from plo to pj-1 inclusive, and pj+1 to phi + inclusive. Push the larger one, and loop back to do the + smaller one directly. + */ + if (pj - plo >= phi - pj) { + PUSH(plo, pj-1); + plo = pj+1; + } + else { + PUSH(pj+1, phi); + phi = pj-1; + } + } + +#undef PUSH +#undef SWAP +} + +/* Sort p and remove duplicates, as fast as we can. */ +static size_t +sort_int_nodups(KEY_TYPE *p, size_t n) +{ + size_t nunique; + element_type *work; + + assert(sizeof(KEY_TYPE) == sizeof(element_type)); + assert(p); + + /* Use quicksort if the array is small, OR if malloc can't find + enough temp memory for radixsort. + */ + work = NULL; + if (n > QUICKSORT_BEATS_RADIXSORT) + work = (element_type *)malloc(n * sizeof(element_type)); + + if (work) { + element_type *out = radixsort_int(p, work, n); + nunique = uniq(p, out, n); + free(work); + } + else { + quicksort(p, n); + nunique = uniq(p, p, n); + } + + return nunique; +} diff -Nru zope3-3.4.0/src/BTrees/tests/__init__.py zope3-3.5~bzr18/src/BTrees/tests/__init__.py --- zope3-3.4.0/src/BTrees/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# If tests is a package, debugging is a bit easier. diff -Nru zope3-3.4.0/src/BTrees/tests/testBTrees.py zope3-3.5~bzr18/src/BTrees/tests/testBTrees.py --- zope3-3.4.0/src/BTrees/tests/testBTrees.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/testBTrees.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2127 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 gc +import pickle +import random +import StringIO +from unittest import TestCase, TestSuite, TextTestRunner, makeSuite +from types import ClassType +import zope.interface.verify + +from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet +from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet +from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet +from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet +from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet +from BTrees.LOBTree import LOBTree, LOBucket, LOSet, LOTreeSet +from BTrees.LLBTree import LLBTree, LLBucket, LLSet, LLTreeSet +from BTrees.LFBTree import LFBTree, LFBucket, LFSet, LFTreeSet +from BTrees.OLBTree import OLBTree, OLBucket, OLSet, OLTreeSet + +import BTrees + +from BTrees.IIBTree import using64bits +from BTrees.check import check + +import transaction +from ZODB import DB +from ZODB.MappingStorage import MappingStorage + +class Base(TestCase): + """ Tests common to all types: sets, buckets, and BTrees """ + + db = None + + def setUp(self): + self.t = self.t_class() + + def tearDown(self): + if self.db is not None: + self.db.close() + self.t = None + del self.t + + def _getRoot(self): + if self.db is None: + # Unclear: On the next line, the ZODB4 flavor of this routine + # [asses a cache_size argument: + # self.db = DB(MappingStorage(), cache_size=1) + # If that's done here, though, testLoadAndStore() and + # testGhostUnghost() both nail the CPU and seemingly + # never finish. + self.db = DB(MappingStorage()) + return self.db.open().root() + + def _closeRoot(self, root): + root._p_jar.close() + + def testLoadAndStore(self): + for i in 0, 10, 1000: + t = self.t.__class__() + self._populate(t, i) + root = None + root = self._getRoot() + root[i] = t + transaction.commit() + + root2 = self._getRoot() + if hasattr(t, 'items'): + self.assertEqual(list(root2[i].items()) , list(t.items())) + else: + self.assertEqual(list(root2[i].keys()) , list(t.keys())) + + self._closeRoot(root) + self._closeRoot(root2) + + def testSetstateArgumentChecking(self): + try: self.t.__class__().__setstate__(('',)) + except TypeError, v: + self.assertEqual(str(v), 'tuple required for first state element') + else: + raise AssertionError("Expected exception") + + def testGhostUnghost(self): + for i in 0, 10, 1000: + t = self.t.__class__() + self._populate(t, i) + root = self._getRoot() + root[i] = t + transaction.commit() + + root2 = self._getRoot() + root2[i]._p_deactivate() + transaction.commit() + if hasattr(t, 'items'): + self.assertEqual(list(root2[i].items()) , list(t.items())) + else: + self.assertEqual(list(root2[i].keys()) , list(t.keys())) + + self._closeRoot(root) + self._closeRoot(root2) + + def testSimpleExclusiveKeyRange(self): + t = self.t.__class__() + self.assertEqual(list(t.keys()), []) + self.assertEqual(list(t.keys(excludemin=True)), []) + self.assertEqual(list(t.keys(excludemax=True)), []) + self.assertEqual(list(t.keys(excludemin=True, excludemax=True)), []) + + self._populate(t, 1) + self.assertEqual(list(t.keys()), [0]) + self.assertEqual(list(t.keys(excludemin=True)), []) + self.assertEqual(list(t.keys(excludemax=True)), []) + self.assertEqual(list(t.keys(excludemin=True, excludemax=True)), []) + + t.clear() + self._populate(t, 2) + self.assertEqual(list(t.keys()), [0, 1]) + self.assertEqual(list(t.keys(excludemin=True)), [1]) + self.assertEqual(list(t.keys(excludemax=True)), [0]) + self.assertEqual(list(t.keys(excludemin=True, excludemax=True)), []) + + t.clear() + self._populate(t, 3) + self.assertEqual(list(t.keys()), [0, 1, 2]) + self.assertEqual(list(t.keys(excludemin=True)), [1, 2]) + self.assertEqual(list(t.keys(excludemax=True)), [0, 1]) + self.assertEqual(list(t.keys(excludemin=True, excludemax=True)), [1]) + + self.assertEqual(list(t.keys(-1, 3, excludemin=True, excludemax=True)), + [0, 1, 2]) + self.assertEqual(list(t.keys(0, 3, excludemin=True, excludemax=True)), + [1, 2]) + self.assertEqual(list(t.keys(-1, 2, excludemin=True, excludemax=True)), + [0, 1]) + self.assertEqual(list(t.keys(0, 2, excludemin=True, excludemax=True)), + [1]) + +class MappingBase(Base): + """ Tests common to mappings (buckets, btrees) """ + + def _populate(self, t, l): + # Make some data + for i in range(l): t[i]=i + + def testRepr(self): + # test the repr because buckets have a complex repr implementation + # internally the cutoff from a stack allocated buffer to a heap + # allocated buffer is 10000. + for i in range(1000): + self.t[i] = i + r = repr(self.t) + # Make sure the repr is 10000 bytes long for a bucket. + # But since the test is also run for btrees, skip the length + # check if the repr starts with '<' + if not r.startswith('<'): + self.assert_(len(r) > 10000) + + def testGetItemFails(self): + self.assertRaises(KeyError, self._getitemfail) + + def _getitemfail(self): + return self.t[1] + + def testGetReturnsDefault(self): + self.assertEqual(self.t.get(1) , None) + self.assertEqual(self.t.get(1, 'foo') , 'foo') + + def testSetItemGetItemWorks(self): + self.t[1] = 1 + a = self.t[1] + self.assertEqual(a , 1, `a`) + + def testReplaceWorks(self): + self.t[1] = 1 + self.assertEqual(self.t[1] , 1, self.t[1]) + self.t[1] = 2 + self.assertEqual(self.t[1] , 2, self.t[1]) + + def testLen(self): + added = {} + r = range(1000) + for x in r: + k = random.choice(r) + self.t[k] = x + added[k] = x + addl = added.keys() + self.assertEqual(len(self.t) , len(addl), len(self.t)) + + def testHasKeyWorks(self): + self.t[1] = 1 + self.assert_(self.t.has_key(1)) + self.assert_(1 in self.t) + self.assert_(0 not in self.t) + self.assert_(2 not in self.t) + + def testValuesWorks(self): + for x in range(100): + self.t[x] = x*x + v = self.t.values() + for i in range(100): + self.assertEqual(v[i], i*i) + self.assertRaises(IndexError, lambda: v[i+1]) + i = 0 + for value in self.t.itervalues(): + self.assertEqual(value, i*i) + i += 1 + + def testValuesWorks1(self): + for x in range(100): + self.t[99-x] = x + + for x in range(40): + lst = list(self.t.values(0+x,99-x)) + lst.sort() + self.assertEqual(lst,range(0+x,99-x+1)) + + lst = list(self.t.values(max=99-x, min=0+x)) + lst.sort() + self.assertEqual(lst,range(0+x,99-x+1)) + + def testValuesNegativeIndex(self): + L = [-3, 6, -11, 4] + for i in L: + self.t[i] = i + L.sort() + vals = self.t.values() + for i in range(-1, -5, -1): + self.assertEqual(vals[i], L[i]) + self.assertRaises(IndexError, lambda: vals[-5]) + + def testKeysWorks(self): + for x in range(100): + self.t[x] = x + v = self.t.keys() + i = 0 + for x in v: + self.assertEqual(x,i) + i = i + 1 + self.assertRaises(IndexError, lambda: v[i]) + + for x in range(40): + lst = self.t.keys(0+x,99-x) + self.assertEqual(list(lst), range(0+x, 99-x+1)) + + lst = self.t.keys(max=99-x, min=0+x) + self.assertEqual(list(lst), range(0+x, 99-x+1)) + + self.assertEqual(len(v), 100) + + def testKeysNegativeIndex(self): + L = [-3, 6, -11, 4] + for i in L: + self.t[i] = i + L.sort() + keys = self.t.keys() + for i in range(-1, -5, -1): + self.assertEqual(keys[i], L[i]) + self.assertRaises(IndexError, lambda: keys[-5]) + + def testItemsWorks(self): + for x in range(100): + self.t[x] = 2*x + v = self.t.items() + i = 0 + for x in v: + self.assertEqual(x[0], i) + self.assertEqual(x[1], 2*i) + i += 1 + self.assertRaises(IndexError, lambda: v[i+1]) + + i = 0 + for x in self.t.iteritems(): + self.assertEqual(x, (i, 2*i)) + i += 1 + + items = list(self.t.items(min=12, max=20)) + self.assertEqual(items, zip(range(12, 21), range(24, 43, 2))) + + items = list(self.t.iteritems(min=12, max=20)) + self.assertEqual(items, zip(range(12, 21), range(24, 43, 2))) + + def testItemsNegativeIndex(self): + L = [-3, 6, -11, 4] + for i in L: + self.t[i] = i + L.sort() + items = self.t.items() + for i in range(-1, -5, -1): + self.assertEqual(items[i], (L[i], L[i])) + self.assertRaises(IndexError, lambda: items[-5]) + + def testDeleteInvalidKeyRaisesKeyError(self): + self.assertRaises(KeyError, self._deletefail) + + def _deletefail(self): + del self.t[1] + + def testMaxKeyMinKey(self): + self.t[7] = 6 + self.t[3] = 10 + self.t[8] = 12 + self.t[1] = 100 + self.t[5] = 200 + self.t[10] = 500 + self.t[6] = 99 + self.t[4] = 150 + del self.t[7] + t = self.t + self.assertEqual(t.maxKey(), 10) + self.assertEqual(t.maxKey(6), 6) + self.assertEqual(t.maxKey(9), 8) + self.assertEqual(t.minKey(), 1) + self.assertEqual(t.minKey(3), 3) + self.assertEqual(t.minKey(9), 10) + + try: + t.maxKey(t.minKey() - 1) + except ValueError, err: + self.assertEqual(str(err), "no key satisfies the conditions") + else: + self.fail("expected ValueError") + + try: + t.minKey(t.maxKey() + 1) + except ValueError, err: + self.assertEqual(str(err), "no key satisfies the conditions") + else: + self.fail("expected ValueError") + + def testClear(self): + r = range(100) + for x in r: + rnd = random.choice(r) + self.t[rnd] = 0 + self.t.clear() + diff = lsubtract(list(self.t.keys()), []) + self.assertEqual(diff, []) + + def testUpdate(self): + d={} + l=[] + for i in range(10000): + k=random.randrange(-2000, 2001) + d[k]=i + l.append((k, i)) + + items=d.items() + items.sort() + + self.t.update(d) + self.assertEqual(list(self.t.items()), items) + + self.t.clear() + self.assertEqual(list(self.t.items()), []) + + self.t.update(l) + self.assertEqual(list(self.t.items()), items) + + # Before ZODB 3.4.2, update/construction from PersistentMapping failed. + def testUpdateFromPersistentMapping(self): + from persistent.mapping import PersistentMapping + + pm = PersistentMapping({1: 2}) + self.t.update(pm) + self.assertEqual(list(self.t.items()), [(1, 2)]) + + # Construction goes thru the same internals as .update(). + t = self.t.__class__(pm) + self.assertEqual(list(t.items()), [(1, 2)]) + + def testEmptyRangeSearches(self): + t = self.t + t.update([(1,1), (5,5), (9,9)]) + self.assertEqual(list(t.keys(-6,-4)), [], list(t.keys(-6,-4))) + self.assertEqual(list(t.keys(2,4)), [], list(t.keys(2,4))) + self.assertEqual(list(t.keys(6,8)), [], list(t.keys(6,8))) + self.assertEqual(list(t.keys(10,12)), [], list(t.keys(10,12))) + self.assertEqual(list(t.keys(9, 1)), [], list(t.keys(9, 1))) + + # For IITreeSets, this one was returning 31 for len(keys), and + # list(keys) produced a list with 100 elements. + t.clear() + t.update(zip(range(300), range(300))) + keys = t.keys(200, 50) + self.assertEqual(len(keys), 0) + self.assertEqual(list(keys), []) + self.assertEqual(list(t.iterkeys(200, 50)), []) + + keys = t.keys(max=50, min=200) + self.assertEqual(len(keys), 0) + self.assertEqual(list(keys), []) + self.assertEqual(list(t.iterkeys(max=50, min=200)), []) + + def testSlicing(self): + # Test that slicing of .keys()/.values()/.items() works exactly the + # same way as slicing a Python list with the same contents. + # This tests fixes to several bugs in this area, starting with + # http://collector.zope.org/Zope/419, + # "BTreeItems slice contains 1 too many elements". + + t = self.t + for n in range(10): + t.clear() + self.assertEqual(len(t), 0) + + keys = [] + values = [] + items = [] + for key in range(n): + value = -2 * key + t[key] = value + keys.append(key) + values.append(value) + items.append((key, value)) + self.assertEqual(len(t), n) + + kslice = t.keys() + vslice = t.values() + islice = t.items() + self.assertEqual(len(kslice), n) + self.assertEqual(len(vslice), n) + self.assertEqual(len(islice), n) + + # Test whole-structure slices. + x = kslice[:] + self.assertEqual(list(x), keys[:]) + + x = vslice[:] + self.assertEqual(list(x), values[:]) + + x = islice[:] + self.assertEqual(list(x), items[:]) + + for lo in range(-2*n, 2*n+1): + # Test one-sided slices. + x = kslice[:lo] + self.assertEqual(list(x), keys[:lo]) + x = kslice[lo:] + self.assertEqual(list(x), keys[lo:]) + + x = vslice[:lo] + self.assertEqual(list(x), values[:lo]) + x = vslice[lo:] + self.assertEqual(list(x), values[lo:]) + + x = islice[:lo] + self.assertEqual(list(x), items[:lo]) + x = islice[lo:] + self.assertEqual(list(x), items[lo:]) + + for hi in range(-2*n, 2*n+1): + # Test two-sided slices. + x = kslice[lo:hi] + self.assertEqual(list(x), keys[lo:hi]) + + x = vslice[lo:hi] + self.assertEqual(list(x), values[lo:hi]) + + x = islice[lo:hi] + self.assertEqual(list(x), items[lo:hi]) + + # The specific test case from Zope collector 419. + t.clear() + for i in xrange(100): + t[i] = 1 + tslice = t.items()[20:80] + self.assertEqual(len(tslice), 60) + self.assertEqual(list(tslice), zip(range(20, 80), [1]*60)) + + def testIterators(self): + t = self.t + + for keys in [], [-2], [1, 4], range(-170, 2000, 6): + t.clear() + for k in keys: + t[k] = -3 * k + + self.assertEqual(list(t), keys) + + x = [] + for k in t: + x.append(k) + self.assertEqual(x, keys) + + it = iter(t) + self.assert_(it is iter(it)) + x = [] + try: + while 1: + x.append(it.next()) + except StopIteration: + pass + self.assertEqual(x, keys) + + self.assertEqual(list(t.iterkeys()), keys) + self.assertEqual(list(t.itervalues()), list(t.values())) + self.assertEqual(list(t.iteritems()), list(t.items())) + + def testRangedIterators(self): + t = self.t + + for keys in [], [-2], [1, 4], range(-170, 2000, 13): + t.clear() + values = [] + for k in keys: + value = -3 * k + t[k] = value + values.append(value) + items = zip(keys, values) + + self.assertEqual(list(t.iterkeys()), keys) + self.assertEqual(list(t.itervalues()), values) + self.assertEqual(list(t.iteritems()), items) + + if not keys: + continue + + min_mid_max = (keys[0], keys[len(keys) >> 1], keys[-1]) + for key1 in min_mid_max: + for lo in range(key1 - 1, key1 + 2): + # Test one-sided range iterators. + goodkeys = [k for k in keys if lo <= k] + got = t.iterkeys(lo) + self.assertEqual(goodkeys, list(got)) + + goodvalues = [t[k] for k in goodkeys] + got = t.itervalues(lo) + self.assertEqual(goodvalues, list(got)) + + gooditems = zip(goodkeys, goodvalues) + got = t.iteritems(lo) + self.assertEqual(gooditems, list(got)) + + for key2 in min_mid_max: + for hi in range(key2 - 1, key2 + 2): + goodkeys = [k for k in keys if lo <= k <= hi] + got = t.iterkeys(min=lo, max=hi) + self.assertEqual(goodkeys, list(got)) + + goodvalues = [t[k] for k in goodkeys] + got = t.itervalues(lo, max=hi) + self.assertEqual(goodvalues, list(got)) + + gooditems = zip(goodkeys, goodvalues) + got = t.iteritems(max=hi, min=lo) + self.assertEqual(gooditems, list(got)) + + def testBadUpdateTupleSize(self): + # This one silently ignored the excess in Zope3. + try: + self.t.update([(1, 2, 3)]) + except TypeError: + pass + else: + self.fail("update() with 3-tuple didn't complain") + + # This one dumped core in Zope3. + try: + self.t.update([(1,)]) + except TypeError: + pass + else: + self.fail("update() with 1-tuple didn't complain") + + # This one should simply succeed. + self.t.update([(1, 2)]) + self.assertEqual(list(self.t.items()), [(1, 2)]) + + def testSimpleExclusivRanges(self): + def identity(x): + return x + def dup(x): + return [(y, y) for y in x] + + for methodname, f in (("keys", identity), + ("values", identity), + ("items", dup), + ("iterkeys", identity), + ("itervalues", identity), + ("iteritems", dup)): + + t = self.t.__class__() + meth = getattr(t, methodname, None) + if meth is None: + continue + + self.assertEqual(list(meth()), []) + self.assertEqual(list(meth(excludemin=True)), []) + self.assertEqual(list(meth(excludemax=True)), []) + self.assertEqual(list(meth(excludemin=True, excludemax=True)), []) + + self._populate(t, 1) + self.assertEqual(list(meth()), f([0])) + self.assertEqual(list(meth(excludemin=True)), []) + self.assertEqual(list(meth(excludemax=True)), []) + self.assertEqual(list(meth(excludemin=True, excludemax=True)), []) + + t.clear() + self._populate(t, 2) + self.assertEqual(list(meth()), f([0, 1])) + self.assertEqual(list(meth(excludemin=True)), f([1])) + self.assertEqual(list(meth(excludemax=True)), f([0])) + self.assertEqual(list(meth(excludemin=True, excludemax=True)), []) + + t.clear() + self._populate(t, 3) + self.assertEqual(list(meth()), f([0, 1, 2])) + self.assertEqual(list(meth(excludemin=True)), f([1, 2])) + self.assertEqual(list(meth(excludemax=True)), f([0, 1])) + self.assertEqual(list(meth(excludemin=True, excludemax=True)), + f([1])) + self.assertEqual(list(meth(-1, 3, excludemin=True, + excludemax=True)), + f([0, 1, 2])) + self.assertEqual(list(meth(0, 3, excludemin=True, + excludemax=True)), + f([1, 2])) + self.assertEqual(list(meth(-1, 2, excludemin=True, + excludemax=True)), + f([0, 1])) + self.assertEqual(list(meth(0, 2, excludemin=True, + excludemax=True)), + f([1])) + + def testSetdefault(self): + t = self.t + + self.assertEqual(t.setdefault(1, 2), 2) + # That should also have associated 1 with 2 in the tree. + self.assert_(1 in t) + self.assertEqual(t[1], 2) + # And trying to change it again should have no effect. + self.assertEqual(t.setdefault(1, 666), 2) + self.assertEqual(t[1], 2) + + # Not enough arguments. + self.assertRaises(TypeError, t.setdefault) + self.assertRaises(TypeError, t.setdefault, 1) + # Too many arguments. + self.assertRaises(TypeError, t.setdefault, 1, 2, 3) + + + def testPop(self): + t = self.t + + # Empty container. + # If no default given, raises KeyError. + self.assertRaises(KeyError, t.pop, 1) + # But if default given, returns that instead. + self.assertEqual(t.pop(1, 42), 42) + + t[1] = 3 + # KeyError when key is not in container and default is not passed. + self.assertRaises(KeyError, t.pop, 5) + self.assertEqual(list(t.items()), [(1, 3)]) + # If key is in container, returns the value and deletes the key. + self.assertEqual(t.pop(1), 3) + self.assertEqual(len(t), 0) + + # If key is present, return value bypassing default. + t[1] = 3 + self.assertEqual(t.pop(1, 7), 3) + self.assertEqual(len(t), 0) + + # Pop only one item. + t[1] = 3 + t[2] = 4 + self.assertEqual(len(t), 2) + self.assertEqual(t.pop(1), 3) + self.assertEqual(len(t), 1) + self.assertEqual(t[2], 4) + self.assertEqual(t.pop(1, 3), 3) + + # Too few arguments. + self.assertRaises(TypeError, t.pop) + # Too many arguments. + self.assertRaises(TypeError, t.pop, 1, 2, 3) + + +class NormalSetTests(Base): + """ Test common to all set types """ + + def _populate(self, t, l): + # Make some data + t.update(range(l)) + + def testInsertReturnsValue(self): + t = self.t + self.assertEqual(t.insert(5) , 1) + self.assertEqual(t.add(4) , 1) + + def testDuplicateInsert(self): + t = self.t + t.insert(5) + self.assertEqual(t.insert(5) , 0) + self.assertEqual(t.add(5) , 0) + + def testInsert(self): + t = self.t + t.insert(1) + self.assert_(t.has_key(1)) + self.assert_(1 in t) + self.assert_(2 not in t) + + def testBigInsert(self): + t = self.t + r = xrange(10000) + for x in r: + t.insert(x) + for x in r: + self.assert_(t.has_key(x)) + self.assert_(x in t) + + def testRemoveSucceeds(self): + t = self.t + r = xrange(10000) + for x in r: t.insert(x) + for x in r: t.remove(x) + + def testRemoveFails(self): + self.assertRaises(KeyError, self._removenonexistent) + + def _removenonexistent(self): + self.t.remove(1) + + def testHasKeyFails(self): + t = self.t + self.assert_(not t.has_key(1)) + self.assert_(1 not in t) + + def testKeys(self): + t = self.t + r = xrange(1000) + for x in r: + t.insert(x) + diff = lsubtract(t.keys(), r) + self.assertEqual(diff, []) + + + def testClear(self): + t = self.t + r = xrange(1000) + for x in r: t.insert(x) + t.clear() + diff = lsubtract(t.keys(), []) + self.assertEqual(diff , [], diff) + + def testMaxKeyMinKey(self): + t = self.t + t.insert(1) + t.insert(2) + t.insert(3) + t.insert(8) + t.insert(5) + t.insert(10) + t.insert(6) + t.insert(4) + self.assertEqual(t.maxKey() , 10) + self.assertEqual(t.maxKey(6) , 6) + self.assertEqual(t.maxKey(9) , 8) + self.assertEqual(t.minKey() , 1) + self.assertEqual(t.minKey(3) , 3) + self.assertEqual(t.minKey(9) , 10) + self.assert_(t.minKey() in t) + self.assert_(t.minKey()-1 not in t) + self.assert_(t.maxKey() in t) + self.assert_(t.maxKey()+1 not in t) + + try: + t.maxKey(t.minKey() - 1) + except ValueError, err: + self.assertEqual(str(err), "no key satisfies the conditions") + else: + self.fail("expected ValueError") + + try: + t.minKey(t.maxKey() + 1) + except ValueError, err: + self.assertEqual(str(err), "no key satisfies the conditions") + else: + self.fail("expected ValueError") + + def testUpdate(self): + d={} + l=[] + for i in range(10000): + k=random.randrange(-2000, 2001) + d[k]=i + l.append(k) + + items = d.keys() + items.sort() + + self.t.update(l) + self.assertEqual(list(self.t.keys()), items) + + def testEmptyRangeSearches(self): + t = self.t + t.update([1, 5, 9]) + self.assertEqual(list(t.keys(-6,-4)), [], list(t.keys(-6,-4))) + self.assertEqual(list(t.keys(2,4)), [], list(t.keys(2,4))) + self.assertEqual(list(t.keys(6,8)), [], list(t.keys(6,8))) + self.assertEqual(list(t.keys(10,12)), [], list(t.keys(10,12))) + self.assertEqual(list(t.keys(9,1)), [], list(t.keys(9,1))) + + # For IITreeSets, this one was returning 31 for len(keys), and + # list(keys) produced a list with 100 elements. + t.clear() + t.update(range(300)) + keys = t.keys(200, 50) + self.assertEqual(len(keys), 0) + self.assertEqual(list(keys), []) + + keys = t.keys(max=50, min=200) + self.assertEqual(len(keys), 0) + self.assertEqual(list(keys), []) + + def testSlicing(self): + # Test that slicing of .keys() works exactly the same way as slicing + # a Python list with the same contents. + + t = self.t + for n in range(10): + t.clear() + self.assertEqual(len(t), 0) + + keys = range(10*n, 11*n) + t.update(keys) + self.assertEqual(len(t), n) + + kslice = t.keys() + self.assertEqual(len(kslice), n) + + # Test whole-structure slices. + x = kslice[:] + self.assertEqual(list(x), keys[:]) + + for lo in range(-2*n, 2*n+1): + # Test one-sided slices. + x = kslice[:lo] + self.assertEqual(list(x), keys[:lo]) + x = kslice[lo:] + self.assertEqual(list(x), keys[lo:]) + + for hi in range(-2*n, 2*n+1): + # Test two-sided slices. + x = kslice[lo:hi] + self.assertEqual(list(x), keys[lo:hi]) + + def testIterator(self): + t = self.t + + for keys in [], [-2], [1, 4], range(-170, 2000, 6): + t.clear() + t.update(keys) + + self.assertEqual(list(t), keys) + + x = [] + for k in t: + x.append(k) + self.assertEqual(x, keys) + + it = iter(t) + self.assert_(it is iter(it)) + x = [] + try: + while 1: + x.append(it.next()) + except StopIteration: + pass + self.assertEqual(x, keys) + +class ExtendedSetTests(NormalSetTests): + def testLen(self): + t = self.t + r = xrange(10000) + for x in r: t.insert(x) + self.assertEqual(len(t) , 10000, len(t)) + + def testGetItem(self): + t = self.t + r = xrange(10000) + for x in r: t.insert(x) + for x in r: + self.assertEqual(t[x] , x) + +class BTreeTests(MappingBase): + """ Tests common to all BTrees """ + + def tearDown(self): + self.t._check() + check(self.t) + MappingBase.tearDown(self) + + def testDeleteNoChildrenWorks(self): + self.t[5] = 6 + self.t[2] = 10 + self.t[6] = 12 + self.t[1] = 100 + self.t[3] = 200 + self.t[10] = 500 + self.t[4] = 99 + del self.t[4] + diff = lsubtract(self.t.keys(), [1,2,3,5,6,10]) + self.assertEqual(diff , [], diff) + + def testDeleteOneChildWorks(self): + self.t[5] = 6 + self.t[2] = 10 + self.t[6] = 12 + self.t[1] = 100 + self.t[3] = 200 + self.t[10] = 500 + self.t[4] = 99 + del self.t[3] + diff = lsubtract(self.t.keys(), [1,2,4,5,6,10]) + self.assertEqual(diff , [], diff) + + def testDeleteTwoChildrenNoInorderSuccessorWorks(self): + self.t[5] = 6 + self.t[2] = 10 + self.t[6] = 12 + self.t[1] = 100 + self.t[3] = 200 + self.t[10] = 500 + self.t[4] = 99 + del self.t[2] + diff = lsubtract(self.t.keys(), [1,3,4,5,6,10]) + self.assertEqual(diff , [], diff) + + def testDeleteTwoChildrenInorderSuccessorWorks(self): + # 7, 3, 8, 1, 5, 10, 6, 4 -- del 3 + self.t[7] = 6 + self.t[3] = 10 + self.t[8] = 12 + self.t[1] = 100 + self.t[5] = 200 + self.t[10] = 500 + self.t[6] = 99 + self.t[4] = 150 + del self.t[3] + diff = lsubtract(self.t.keys(), [1,4,5,6,7,8,10]) + self.assertEqual(diff , [], diff) + + def testDeleteRootWorks(self): + # 7, 3, 8, 1, 5, 10, 6, 4 -- del 7 + self.t[7] = 6 + self.t[3] = 10 + self.t[8] = 12 + self.t[1] = 100 + self.t[5] = 200 + self.t[10] = 500 + self.t[6] = 99 + self.t[4] = 150 + del self.t[7] + diff = lsubtract(self.t.keys(), [1,3,4,5,6,8,10]) + self.assertEqual(diff , [], diff) + + def testRandomNonOverlappingInserts(self): + added = {} + r = range(100) + for x in r: + k = random.choice(r) + if not added.has_key(k): + self.t[k] = x + added[k] = 1 + addl = added.keys() + addl.sort() + diff = lsubtract(list(self.t.keys()), addl) + self.assertEqual(diff , [], (diff, addl, list(self.t.keys()))) + + def testRandomOverlappingInserts(self): + added = {} + r = range(100) + for x in r: + k = random.choice(r) + self.t[k] = x + added[k] = 1 + addl = added.keys() + addl.sort() + diff = lsubtract(self.t.keys(), addl) + self.assertEqual(diff , [], diff) + + def testRandomDeletes(self): + r = range(1000) + added = [] + for x in r: + k = random.choice(r) + self.t[k] = x + added.append(k) + deleted = [] + for x in r: + k = random.choice(r) + if self.t.has_key(k): + self.assert_(k in self.t) + del self.t[k] + deleted.append(k) + if self.t.has_key(k): + self.fail( "had problems deleting %s" % k ) + badones = [] + for x in deleted: + if self.t.has_key(x): + badones.append(x) + self.assertEqual(badones , [], (badones, added, deleted)) + + def testTargetedDeletes(self): + r = range(1000) + for x in r: + k = random.choice(r) + self.t[k] = x + for x in r: + try: + del self.t[x] + except KeyError: + pass + self.assertEqual(realseq(self.t.keys()) , [], realseq(self.t.keys())) + + def testPathologicalRightBranching(self): + r = range(1000) + for x in r: + self.t[x] = 1 + self.assertEqual(realseq(self.t.keys()) , r, realseq(self.t.keys())) + for x in r: + del self.t[x] + self.assertEqual(realseq(self.t.keys()) , [], realseq(self.t.keys())) + + def testPathologicalLeftBranching(self): + r = range(1000) + revr = r[:] + revr.reverse() + for x in revr: + self.t[x] = 1 + self.assertEqual(realseq(self.t.keys()) , r, realseq(self.t.keys())) + + for x in revr: + del self.t[x] + self.assertEqual(realseq(self.t.keys()) , [], realseq(self.t.keys())) + + def testSuccessorChildParentRewriteExerciseCase(self): + add_order = [ + 85, 73, 165, 273, 215, 142, 233, 67, 86, 166, 235, 225, 255, + 73, 175, 171, 285, 162, 108, 28, 283, 258, 232, 199, 260, + 298, 275, 44, 261, 291, 4, 181, 285, 289, 216, 212, 129, + 243, 97, 48, 48, 159, 22, 285, 92, 110, 27, 55, 202, 294, + 113, 251, 193, 290, 55, 58, 239, 71, 4, 75, 129, 91, 111, + 271, 101, 289, 194, 218, 77, 142, 94, 100, 115, 101, 226, + 17, 94, 56, 18, 163, 93, 199, 286, 213, 126, 240, 245, 190, + 195, 204, 100, 199, 161, 292, 202, 48, 165, 6, 173, 40, 218, + 271, 228, 7, 166, 173, 138, 93, 22, 140, 41, 234, 17, 249, + 215, 12, 292, 246, 272, 260, 140, 58, 2, 91, 246, 189, 116, + 72, 259, 34, 120, 263, 168, 298, 118, 18, 28, 299, 192, 252, + 112, 60, 277, 273, 286, 15, 263, 141, 241, 172, 255, 52, 89, + 127, 119, 255, 184, 213, 44, 116, 231, 173, 298, 178, 196, + 89, 184, 289, 98, 216, 115, 35, 132, 278, 238, 20, 241, 128, + 179, 159, 107, 206, 194, 31, 260, 122, 56, 144, 118, 283, + 183, 215, 214, 87, 33, 205, 183, 212, 221, 216, 296, 40, + 108, 45, 188, 139, 38, 256, 276, 114, 270, 112, 214, 191, + 147, 111, 299, 107, 101, 43, 84, 127, 67, 205, 251, 38, 91, + 297, 26, 165, 187, 19, 6, 73, 4, 176, 195, 90, 71, 30, 82, + 139, 210, 8, 41, 253, 127, 190, 102, 280, 26, 233, 32, 257, + 194, 263, 203, 190, 111, 218, 199, 29, 81, 207, 18, 180, + 157, 172, 192, 135, 163, 275, 74, 296, 298, 265, 105, 191, + 282, 277, 83, 188, 144, 259, 6, 173, 81, 107, 292, 231, + 129, 65, 161, 113, 103, 136, 255, 285, 289, 1 + ] + delete_order = [ + 276, 273, 12, 275, 2, 286, 127, 83, 92, 33, 101, 195, + 299, 191, 22, 232, 291, 226, 110, 94, 257, 233, 215, 184, + 35, 178, 18, 74, 296, 210, 298, 81, 265, 175, 116, 261, + 212, 277, 260, 234, 6, 129, 31, 4, 235, 249, 34, 289, 105, + 259, 91, 93, 119, 7, 183, 240, 41, 253, 290, 136, 75, 292, + 67, 112, 111, 256, 163, 38, 126, 139, 98, 56, 282, 60, 26, + 55, 245, 225, 32, 52, 40, 271, 29, 252, 239, 89, 87, 205, + 213, 180, 97, 108, 120, 218, 44, 187, 196, 251, 202, 203, + 172, 28, 188, 77, 90, 199, 297, 282, 141, 100, 161, 216, + 73, 19, 17, 189, 30, 258 + ] + for x in add_order: + self.t[x] = 1 + for x in delete_order: + try: del self.t[x] + except KeyError: + if self.t.has_key(x): + self.assertEqual(1,2,"failed to delete %s" % x) + + def testRangeSearchAfterSequentialInsert(self): + r = range(100) + for x in r: + self.t[x] = 0 + diff = lsubtract(list(self.t.keys(0, 100)), r) + self.assertEqual(diff , [], diff) + + def testRangeSearchAfterRandomInsert(self): + r = range(100) + a = {} + for x in r: + rnd = random.choice(r) + self.t[rnd] = 0 + a[rnd] = 0 + diff = lsubtract(list(self.t.keys(0, 100)), a.keys()) + self.assertEqual(diff , [], diff) + + def testPathologicalRangeSearch(self): + t = self.t + # Build a 2-level tree with at least two buckets. + for i in range(200): + t[i] = i + items, dummy = t.__getstate__() + self.assert_(len(items) > 2) # at least two buckets and a key + # All values in the first bucket are < firstkey. All in the + # second bucket are >= firstkey, and firstkey is the first key in + # the second bucket. + firstkey = items[1] + therange = t.keys(-1, firstkey) + self.assertEqual(len(therange), firstkey + 1) + self.assertEqual(list(therange), range(firstkey + 1)) + # Now for the tricky part. If we delete firstkey, the second bucket + # loses its smallest key, but firstkey remains in the BTree node. + # If we then do a high-end range search on firstkey, the BTree node + # directs us to look in the second bucket, but there's no longer any + # key <= firstkey in that bucket. The correct answer points to the + # end of the *first* bucket. The algorithm has to be smart enough + # to "go backwards" in the BTree then; if it doesn't, it will + # erroneously claim that the range is empty. + del t[firstkey] + therange = t.keys(min=-1, max=firstkey) + self.assertEqual(len(therange), firstkey) + self.assertEqual(list(therange), range(firstkey)) + + def testInsertMethod(self): + t = self.t + t[0] = 1 + self.assertEqual(t.insert(0, 1) , 0) + self.assertEqual(t.insert(1, 1) , 1) + self.assertEqual(lsubtract(list(t.keys()), [0,1]) , []) + + def testDamagedIterator(self): + # A cute one from Steve Alexander. This caused the BTreeItems + # object to go insane, accessing memory beyond the allocated part + # of the bucket. If it fails, the symptom is either a C-level + # assertion error (if the BTree code was compiled without NDEBUG), + # or most likely a segfault (if the BTree code was compiled with + # NDEBUG). + + t = self.t.__class__() + self._populate(t, 10) + # In order for this to fail, it's important that k be a "lazy" + # iterator, referring to the BTree by indirect position (index) + # instead of a fully materialized list. Then the position can + # end up pointing into trash memory, if the bucket pointed to + # shrinks. + k = t.keys() + for dummy in range(20): + try: + del t[k[0]] + except RuntimeError, detail: + self.assertEqual(str(detail), "the bucket being iterated " + "changed size") + break + + +LARGEST_32_BITS = 2147483647 +SMALLEST_32_BITS = -LARGEST_32_BITS - 1 + +SMALLEST_POSITIVE_33_BITS = LARGEST_32_BITS + 1 +LARGEST_NEGATIVE_33_BITS = SMALLEST_32_BITS - 1 + +LARGEST_64_BITS = 0x7fffffffffffffff +SMALLEST_64_BITS = -LARGEST_64_BITS - 1 + +SMALLEST_POSITIVE_65_BITS = LARGEST_64_BITS + 1 +LARGEST_NEGATIVE_65_BITS = SMALLEST_64_BITS - 1 + +class TestLongIntSupport: + + def getTwoValues(self): + """Return two distinct values; these must compare as un-equal. + + These values must be usable as values. + + """ + return object(), object() + + def getTwoKeys(self): + """Return two distinct values, these must compare as un-equal. + + These values must be usable as keys. + + """ + return 0, 1 + + def _set_value(self, key, value): + self.t[key] = value + +class TestLongIntKeys(TestLongIntSupport): + + def testLongIntKeysWork(self): + o1, o2 = self.getTwoValues() + assert o1 != o2 + + # Test some small key values first: + self.t[0L] = o1 + self.assertEqual(self.t[0], o1) + self.t[0] = o2 + self.assertEqual(self.t[0L], o2) + self.assertEqual(list(self.t.keys()), [0]) + + # Test some large key values too: + k1 = SMALLEST_POSITIVE_33_BITS + k2 = LARGEST_64_BITS + k3 = SMALLEST_64_BITS + self.t[k1] = o1 + self.t[k2] = o2 + self.t[k3] = o1 + self.assertEqual(self.t[k1], o1) + self.assertEqual(self.t[k2], o2) + self.assertEqual(self.t[k3], o1) + self.assertEqual(list(self.t.keys()), [k3, 0, k1, k2]) + + def testLongIntKeysOutOfRange(self): + o1, o2 = self.getTwoValues() + self.assertRaises( + ValueError, + self._set_value, SMALLEST_POSITIVE_65_BITS, o1) + self.assertRaises( + ValueError, + self._set_value, LARGEST_NEGATIVE_65_BITS, o1) + +class TestLongIntValues(TestLongIntSupport): + + def testLongIntValuesWork(self): + keys = list(self.getTwoKeys()) + keys.sort() + k1, k2 = keys + assert k1 != k2 + + # This is the smallest positive integer that requires 33 bits: + v1 = SMALLEST_POSITIVE_33_BITS + v2 = v1 + 1 + + self.t[k1] = v1 + self.t[k2] = v2 + self.assertEqual(self.t[k1], v1) + self.assertEqual(self.t[k2], v2) + self.assertEqual(list(self.t.values()), [v1, v2]) + + def testLongIntValuesOutOfRange(self): + k1, k2 = self.getTwoKeys() + self.assertRaises( + ValueError, + self._set_value, k1, SMALLEST_POSITIVE_65_BITS) + self.assertRaises( + ValueError, + self._set_value, k1, LARGEST_NEGATIVE_65_BITS) + + +if not using64bits: + # We're not using 64-bit ints in this build, so we don't expect + # the long-integer tests to pass. + + class TestLongIntKeys: + pass + + class TestLongIntValues: + pass + + +# tests of various type errors + +class TypeTest(TestCase): + + def testBadTypeRaises(self): + self.assertRaises(TypeError, self._stringraises) + self.assertRaises(TypeError, self._floatraises) + self.assertRaises(TypeError, self._noneraises) + +class TestIOBTrees(TypeTest): + def setUp(self): + self.t = IOBTree() + + def _stringraises(self): + self.t['c'] = 1 + + def _floatraises(self): + self.t[2.5] = 1 + + def _noneraises(self): + self.t[None] = 1 + +class TestOIBTrees(TypeTest): + def setUp(self): + self.t = OIBTree() + + def _stringraises(self): + self.t[1] = 'c' + + def _floatraises(self): + self.t[1] = 1.4 + + def _noneraises(self): + self.t[1] = None + + def testEmptyFirstBucketReportedByGuido(self): + b = self.t + for i in xrange(29972): # reduce to 29971 and it works + b[i] = i + for i in xrange(30): # reduce to 29 and it works + del b[i] + b[i+40000] = i + + self.assertEqual(b.keys()[0], 30) + +class TestIIBTrees(TestCase): + def setUp(self): + self.t = IIBTree() + + def testNonIntegerKeyRaises(self): + self.assertRaises(TypeError, self._stringraiseskey) + self.assertRaises(TypeError, self._floatraiseskey) + self.assertRaises(TypeError, self._noneraiseskey) + + def testNonIntegerValueRaises(self): + self.assertRaises(TypeError, self._stringraisesvalue) + self.assertRaises(TypeError, self._floatraisesvalue) + self.assertRaises(TypeError, self._noneraisesvalue) + + def _stringraiseskey(self): + self.t['c'] = 1 + + def _floatraiseskey(self): + self.t[2.5] = 1 + + def _noneraiseskey(self): + self.t[None] = 1 + + def _stringraisesvalue(self): + self.t[1] = 'c' + + def _floatraisesvalue(self): + self.t[1] = 1.4 + + def _noneraisesvalue(self): + self.t[1] = None + +class TestIFBTrees(TestCase): + def setUp(self): + self.t = IFBTree() + + def testNonIntegerKeyRaises(self): + self.assertRaises(TypeError, self._stringraiseskey) + self.assertRaises(TypeError, self._floatraiseskey) + self.assertRaises(TypeError, self._noneraiseskey) + + def testNonNumericValueRaises(self): + self.assertRaises(TypeError, self._stringraisesvalue) + self.assertRaises(TypeError, self._noneraisesvalue) + self.t[1] = 1 + self.t[1] = 1.0 + + def _stringraiseskey(self): + self.t['c'] = 1 + + def _floatraiseskey(self): + self.t[2.5] = 1 + + def _noneraiseskey(self): + self.t[None] = 1 + + def _stringraisesvalue(self): + self.t[1] = 'c' + + def _floatraisesvalue(self): + self.t[1] = 1.4 + + def _noneraisesvalue(self): + self.t[1] = None + +class TestIOSets(TestCase): + def setUp(self): + self.t = IOSet() + + def testNonIntegerInsertRaises(self): + self.assertRaises(TypeError,self._insertstringraises) + self.assertRaises(TypeError,self._insertfloatraises) + self.assertRaises(TypeError,self._insertnoneraises) + + def _insertstringraises(self): + self.t.insert('a') + + def _insertfloatraises(self): + self.t.insert(1.4) + + def _insertnoneraises(self): + self.t.insert(None) + +class DegenerateBTree(TestCase): + # Build a degenerate tree (set). Boxes are BTree nodes. There are + # 5 leaf buckets, each containing a single int. Keys in the BTree + # nodes don't appear in the buckets. Seven BTree nodes are purely + # indirection nodes (no keys). Buckets aren't all at the same depth: + # + # +------------------------+ + # | 4 | + # +------------------------+ + # | | + # | v + # | +-+ + # | | | + # | +-+ + # | | + # v v + # +-------+ +-------------+ + # | 2 | | 6 10 | + # +-------+ +-------------+ + # | | | | | + # v v v v v + # +-+ +-+ +-+ +-+ +-+ + # | | | | | | | | | | + # +-+ +-+ +-+ +-+ +-+ + # | | | | | + # v v v v v + # 1 3 +-+ 7 11 + # | | + # +-+ + # | + # v + # 5 + # + # This is nasty for many algorithms. Consider a high-end range search + # for 4. The BTree nodes direct it to the 5 bucket, but the correct + # answer is the 3 bucket, which requires going in a different direction + # at the very top node already. Consider a low-end range search for + # 9. The BTree nodes direct it to the 7 bucket, but the correct answer + # is the 11 bucket. This is also a nasty-case tree for deletions. + + def _build_degenerate_tree(self): + # Build the buckets and chain them together. + bucket11 = IISet([11]) + + bucket7 = IISet() + bucket7.__setstate__(((7,), bucket11)) + + bucket5 = IISet() + bucket5.__setstate__(((5,), bucket7)) + + bucket3 = IISet() + bucket3.__setstate__(((3,), bucket5)) + + bucket1 = IISet() + bucket1.__setstate__(((1,), bucket3)) + + # Build the deepest layers of indirection nodes. + ts = IITreeSet + tree1 = ts() + tree1.__setstate__(((bucket1,), bucket1)) + + tree3 = ts() + tree3.__setstate__(((bucket3,), bucket3)) + + tree5lower = ts() + tree5lower.__setstate__(((bucket5,), bucket5)) + tree5 = ts() + tree5.__setstate__(((tree5lower,), bucket5)) + + tree7 = ts() + tree7.__setstate__(((bucket7,), bucket7)) + + tree11 = ts() + tree11.__setstate__(((bucket11,), bucket11)) + + # Paste together the middle layers. + tree13 = ts() + tree13.__setstate__(((tree1, 2, tree3), bucket1)) + + tree5711lower = ts() + tree5711lower.__setstate__(((tree5, 6, tree7, 10, tree11), bucket5)) + tree5711 = ts() + tree5711.__setstate__(((tree5711lower,), bucket5)) + + # One more. + t = ts() + t.__setstate__(((tree13, 4, tree5711), bucket1)) + t._check() + check(t) + return t, [1, 3, 5, 7, 11] + + def testBasicOps(self): + t, keys = self._build_degenerate_tree() + self.assertEqual(len(t), len(keys)) + self.assertEqual(list(t.keys()), keys) + # has_key actually returns the depth of a bucket. + self.assertEqual(t.has_key(1), 4) + self.assertEqual(t.has_key(3), 4) + self.assertEqual(t.has_key(5), 6) + self.assertEqual(t.has_key(7), 5) + self.assertEqual(t.has_key(11), 5) + for i in 0, 2, 4, 6, 8, 9, 10, 12: + self.assert_(i not in t) + + def _checkRanges(self, tree, keys): + self.assertEqual(len(tree), len(keys)) + sorted_keys = keys[:] + sorted_keys.sort() + self.assertEqual(list(tree.keys()), sorted_keys) + for k in keys: + self.assert_(k in tree) + if keys: + lokey = sorted_keys[0] + hikey = sorted_keys[-1] + self.assertEqual(lokey, tree.minKey()) + self.assertEqual(hikey, tree.maxKey()) + else: + lokey = hikey = 42 + + # Try all range searches. + for lo in range(lokey - 1, hikey + 2): + for hi in range(lo - 1, hikey + 2): + for skipmin in False, True: + for skipmax in False, True: + wantlo, wanthi = lo, hi + if skipmin: + wantlo += 1 + if skipmax: + wanthi -= 1 + want = [k for k in keys if wantlo <= k <= wanthi] + got = list(tree.keys(lo, hi, skipmin, skipmax)) + self.assertEqual(want, got) + + def testRanges(self): + t, keys = self._build_degenerate_tree() + self._checkRanges(t, keys) + + def testDeletes(self): + # Delete keys in all possible orders, checking each tree along + # the way. + + # This is a tough test. Previous failure modes included: + # 1. A variety of assertion failures in _checkRanges. + # 2. Assorted "Invalid firstbucket pointer" failures at + # seemingly random times, coming out of the BTree destructor. + # 3. Under Python 2.3 CVS, some baffling + # RuntimeWarning: tp_compare didn't return -1 or -2 for exception + # warnings, possibly due to memory corruption after a BTree + # goes insane. + + t, keys = self._build_degenerate_tree() + for oneperm in permutations(keys): + t, keys = self._build_degenerate_tree() + for key in oneperm: + t.remove(key) + keys.remove(key) + t._check() + check(t) + self._checkRanges(t, keys) + # We removed all the keys, so the tree should be empty now. + self.assertEqual(t.__getstate__(), None) + + # A damaged tree may trigger an "invalid firstbucket pointer" + # failure at the time its destructor is invoked. Try to force + # that to happen now, so it doesn't look like a baffling failure + # at some unrelated line. + del t # trigger destructor + +LP294788_ids = {} + +class ToBeDeleted(object): + def __init__(self, id): + assert type(id) is int #we don't want to store any object ref here + self.id = id + + global LP294788_ids + LP294788_ids[id] = 1 + + def __del__(self): + global LP294788_ids + LP294788_ids.pop(self.id, None) + + def __cmp__(self, other): + return cmp(self.id, other.id) + + def __hash__(self): + return hash(self.id) + +class BugFixes(TestCase): + + # Collector 1843. Error returns were effectively ignored in + # Bucket_rangeSearch(), leading to "delayed" errors, or worse. + def testFixed1843(self): + t = IISet() + t.insert(1) + # This one used to fail to raise the TypeError when it occurred. + self.assertRaises(TypeError, t.keys, "") + # This one used to segfault. + self.assertRaises(TypeError, t.keys, 0, "") + + def test_LP294788(self): + # https://bugs.launchpad.net/bugs/294788 + # BTree keeps some deleted objects referenced + + # The logic here together with the ToBeDeleted class is that + # a separate reference dict is populated on object creation + # and removed in __del__ + # That means what's left in the reference dict is never GC'ed + # therefore referenced somewhere + # To simulate real life, some random data is used to exercise the tree + + t = OOBTree() + + trandom = random.Random('OOBTree') + + global LP294788_ids + + # /// BTree keys are integers, value is an object + LP294788_ids = {} + ids = {} + for i in xrange(1024): + if trandom.random() > 0.1: + #add + id = None + while id is None or id in ids: + id = trandom.randint(0,1000000) + + ids[id] = 1 + t[id] = ToBeDeleted(id) + else: + #del + id = trandom.choice(ids.keys()) + del t[id] + del ids[id] + + ids = ids.keys() + trandom.shuffle(ids) + for id in ids: + del t[id] + ids = None + + #to be on the safe side run a full GC + gc.collect() + + #print LP294788_ids + + self.assertEqual(len(t), 0) + self.assertEqual(len(LP294788_ids), 0) + # \\\ + + # /// BTree keys are integers, value is a tuple having an object + LP294788_ids = {} + ids = {} + for i in xrange(1024): + if trandom.random() > 0.1: + #add + id = None + while id is None or id in ids: + id = trandom.randint(0,1000000) + + ids[id] = 1 + t[id] = (id, ToBeDeleted(id), u'somename') + else: + #del + id = trandom.choice(ids.keys()) + del t[id] + del ids[id] + + ids = ids.keys() + trandom.shuffle(ids) + for id in ids: + del t[id] + ids = None + + #to be on the safe side run a full GC + gc.collect() + + #print LP294788_ids + + self.assertEqual(len(t), 0) + self.assertEqual(len(LP294788_ids), 0) + # \\\ + + + # /// BTree keys are objects, value is an int + t = OOBTree() + LP294788_ids = {} + ids = {} + for i in xrange(1024): + if trandom.random() > 0.1: + #add + id = None + while id is None or id in ids: + id = ToBeDeleted(trandom.randint(0,1000000)) + + ids[id] = 1 + t[id] = 1 + else: + #del + id = trandom.choice(ids.keys()) + del ids[id] + del t[id] + + ids = ids.keys() + trandom.shuffle(ids) + for id in ids: + del t[id] + #release all refs + ids = obj = id = None + + #to be on the safe side run a full GC + gc.collect() + + #print LP294788_ids + + self.assertEqual(len(t), 0) + self.assertEqual(len(LP294788_ids), 0) + + # /// BTree keys are tuples having objects, value is an int + t = OOBTree() + LP294788_ids = {} + ids = {} + for i in xrange(1024): + if trandom.random() > 0.1: + #add + id = None + while id is None or id in ids: + id = trandom.randint(0,1000000) + id = (id, ToBeDeleted(id), u'somename') + + ids[id] = 1 + t[id] = 1 + else: + #del + id = trandom.choice(ids.keys()) + del ids[id] + del t[id] + + ids = ids.keys() + trandom.shuffle(ids) + for id in ids: + del t[id] + #release all refs + ids = id = obj = key = None + + #to be on the safe side run a full GC + gc.collect() + + #print LP294788_ids + + self.assertEqual(len(t), 0) + self.assertEqual(len(LP294788_ids), 0) + + +class IIBTreeTest(BTreeTests): + def setUp(self): + self.t = IIBTree() +class IFBTreeTest(BTreeTests): + def setUp(self): + self.t = IFBTree() +class IOBTreeTest(BTreeTests): + def setUp(self): + self.t = IOBTree() +class OIBTreeTest(BTreeTests): + def setUp(self): + self.t = OIBTree() +class OOBTreeTest(BTreeTests): + def setUp(self): + self.t = OOBTree() + +if using64bits: + class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues): + def setUp(self): + self.t = IIBTree() + def getTwoValues(self): + return 1, 2 + class IFBTreeTest(BTreeTests, TestLongIntKeys): + def setUp(self): + self.t = IFBTree() + def getTwoValues(self): + return 0.5, 1.5 + class IOBTreeTest(BTreeTests, TestLongIntKeys): + def setUp(self): + self.t = IOBTree() + class OIBTreeTest(BTreeTests, TestLongIntValues): + def setUp(self): + self.t = OIBTree() + def getTwoKeys(self): + return object(), object() + +class LLBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues): + def setUp(self): + self.t = LLBTree() + def getTwoValues(self): + return 1, 2 +class LFBTreeTest(BTreeTests, TestLongIntKeys): + def setUp(self): + self.t = LFBTree() + def getTwoValues(self): + return 0.5, 1.5 +class LOBTreeTest(BTreeTests, TestLongIntKeys): + def setUp(self): + self.t = LOBTree() +class OLBTreeTest(BTreeTests, TestLongIntValues): + def setUp(self): + self.t = OLBTree() + def getTwoKeys(self): + return object(), object() +class OOBTreeTest(BTreeTests): + def setUp(self): + self.t = OOBTree() + + +# cmp error propagation tests + +class DoesntLikeBeingCompared: + def __cmp__(self,other): + raise ValueError('incomparable') + +class TestCmpError(TestCase): + def testFoo(self): + t = OOBTree() + t['hello world'] = None + try: + t[DoesntLikeBeingCompared()] = None + except ValueError,e: + self.assertEqual(str(e), 'incomparable') + else: + self.fail('incomarable objects should not be allowed into ' + 'the tree') + +# test for presence of generic names in module + +class ModuleTest(TestCase): + module = None + prefix = None + iface = None + def testNames(self): + for name in ('Bucket', 'BTree', 'Set', 'TreeSet'): + klass = getattr(self.module, name) + self.assertEqual(klass.__module__, self.module.__name__) + self.assert_(klass is getattr(self.module, self.prefix + name)) + + def testModuleProvides(self): + self.assert_( + zope.interface.verify.verifyObject(self.iface, self.module)) + + def testFamily(self): + if self.prefix == 'OO': + self.assert_( + getattr(self.module, 'family', self) is self) + elif 'L' in self.prefix: + self.assert_(self.module.family is BTrees.family64) + elif 'I' in self.prefix: + self.assert_(self.module.family is BTrees.family32) + +class FamilyTest(TestCase): + def test32(self): + self.assert_( + zope.interface.verify.verifyObject( + BTrees.Interfaces.IBTreeFamily, BTrees.family32)) + self.assertEquals( + BTrees.family32.IO, BTrees.IOBTree) + self.assertEquals( + BTrees.family32.OI, BTrees.OIBTree) + self.assertEquals( + BTrees.family32.II, BTrees.IIBTree) + self.assertEquals( + BTrees.family32.IF, BTrees.IFBTree) + self.assertEquals( + BTrees.family32.OO, BTrees.OOBTree) + s = IOTreeSet() + s.insert(BTrees.family32.maxint) + self.assert_(BTrees.family32.maxint in s) + s = IOTreeSet() + s.insert(BTrees.family32.minint) + self.assert_(BTrees.family32.minint in s) + s = IOTreeSet() + # this next bit illustrates an, um, "interesting feature". If + # the characteristics change to match the 64 bit version, please + # feel free to change. + big = BTrees.family32.maxint + 1 + if isinstance(big, long): + self.assertRaises(TypeError, s.insert, big) + self.assertRaises(TypeError, s.insert, BTrees.family32.minint - 1) + else: # 64 bit Python + s.insert(BTrees.family32.maxint + 1) + self.assert_(BTrees.family32.maxint + 1 not in list(s)) + # yeah, it's len of 1 now, and rolled over to the minint... + # don't look...don't look... + s = IOTreeSet() + s.insert(BTrees.family32.minint - 1) + self.assert_(BTrees.family32.minint - 1 not in list(s)) + # similarly, this is a len of 1, rolling over to the maxint... + self.check_pickling(BTrees.family32) + + def test64(self): + self.assert_( + zope.interface.verify.verifyObject( + BTrees.Interfaces.IBTreeFamily, BTrees.family64)) + self.assertEquals( + BTrees.family64.IO, BTrees.LOBTree) + self.assertEquals( + BTrees.family64.OI, BTrees.OLBTree) + self.assertEquals( + BTrees.family64.II, BTrees.LLBTree) + self.assertEquals( + BTrees.family64.IF, BTrees.LFBTree) + self.assertEquals( + BTrees.family64.OO, BTrees.OOBTree) + s = LOTreeSet() + s.insert(BTrees.family64.maxint) + self.assert_(BTrees.family64.maxint in s) + s = LOTreeSet() + s.insert(BTrees.family64.minint) + self.assert_(BTrees.family64.minint in s) + s = LOTreeSet() + self.assertRaises(ValueError, s.insert, BTrees.family64.maxint + 1) + self.assertRaises(ValueError, s.insert, BTrees.family64.minint - 1) + self.check_pickling(BTrees.family64) + + def check_pickling(self, family): + # The "family" objects are singletons; they can be pickled and + # unpickled, and the same instances will always be returned on + # unpickling, whether from the same unpickler or different + # unpicklers. + s = pickle.dumps((family, family)) + (f1, f2) = pickle.loads(s) + self.failUnless(f1 is family) + self.failUnless(f2 is family) + + # Using a single memo across multiple pickles: + sio = StringIO.StringIO() + p = pickle.Pickler(sio) + p.dump(family) + p.dump([family]) + u = pickle.Unpickler(StringIO.StringIO(sio.getvalue())) + f1 = u.load() + f2, = u.load() + self.failUnless(f1 is family) + self.failUnless(f2 is family) + + # Using separate memos for each pickle: + sio = StringIO.StringIO() + p = pickle.Pickler(sio) + p.dump(family) + p.clear_memo() + p.dump([family]) + u = pickle.Unpickler(StringIO.StringIO(sio.getvalue())) + f1 = u.load() + f2, = u.load() + self.failUnless(f1 is family) + self.failUnless(f2 is family) + +class InternalKeysMappingTest(TestCase): + """There must not be any internal keys not in the BTree + """ + + def add_key(self, tree, key): + tree[key] = key + + def test_internal_keys_after_deletion(self): + """Make sure when a key's deleted, it's not an internal key + + We'll leverage __getstate__ to introspect the internal structures. + + We need to check BTrees with BTree children as well as BTrees + with bucket children. + """ + + from ZODB.MappingStorage import DB + db = DB() + conn = db.open() + + tree = conn.root.tree = self.t_class() + i = 0 + + # Grow the btree until we have multiple buckets + while 1: + i += 1 + self.add_key(tree, i) + data = tree.__getstate__()[0] + if len(data) >= 3: + break + + transaction.commit() + + # Now, delete the internal key and make sure it's really gone + key = data[1] + del tree[key] + data = tree.__getstate__()[0] + self.assert_(data[1] != key) + + # The tree should have changed: + self.assert_(tree._p_changed) + + # Grow the btree until we have multiple levels + while 1: + i += 1 + self.add_key(tree, i) + data = tree.__getstate__()[0] + if data[0].__class__ == tree.__class__: + assert len(data[2].__getstate__()[0]) >= 3 + break + + # Now, delete the internal key and make sure it's really gone + key = data[1] + del tree[key] + data = tree.__getstate__()[0] + self.assert_(data[1] != key) + + transaction.abort() + db.close() + +class InternalKeysSetTest: + """There must not be any internal keys not in the TreeSet + """ + + def add_key(self, tree, key): + tree.add(key) + +def test_suite(): + s = TestSuite() + + for kv in ('OO', + 'II', 'IO', 'OI', 'IF', + 'LL', 'LO', 'OL', 'LF', + ): + for name, bases in ( + ('BTree', (InternalKeysMappingTest,)), + ('TreeSet', (InternalKeysSetTest,)), + ): + klass = ClassType(kv + name + 'InternalKeyTest', bases, + dict(t_class=globals()[kv+name])) + s.addTest(makeSuite(klass)) + + for kv in ('OO', + 'II', 'IO', 'OI', 'IF', + 'LL', 'LO', 'OL', 'LF', + ): + for name, bases in ( + ('Bucket', (MappingBase,)), + ('TreeSet', (NormalSetTests,)), + ('Set', (ExtendedSetTests,)), + ): + klass = ClassType(kv + name + 'Test', bases, + dict(t_class=globals()[kv+name])) + s.addTest(makeSuite(klass)) + + for kv, iface in ( + ('OO', BTrees.Interfaces.IObjectObjectBTreeModule), + ('IO', BTrees.Interfaces.IIntegerObjectBTreeModule), + ('LO', BTrees.Interfaces.IIntegerObjectBTreeModule), + ('OI', BTrees.Interfaces.IObjectIntegerBTreeModule), + ('OL', BTrees.Interfaces.IObjectIntegerBTreeModule), + ('II', BTrees.Interfaces.IIntegerIntegerBTreeModule), + ('LL', BTrees.Interfaces.IIntegerIntegerBTreeModule), + ('IF', BTrees.Interfaces.IIntegerFloatBTreeModule), + ('LF', BTrees.Interfaces.IIntegerFloatBTreeModule)): + s.addTest( + makeSuite( + ClassType( + kv + 'ModuleTest', + (ModuleTest,), + dict( + prefix=kv, + module=getattr(BTrees, kv + 'BTree'), + iface=iface)))) + + for klass in ( + IIBTreeTest, IFBTreeTest, IOBTreeTest, OIBTreeTest, + LLBTreeTest, LFBTreeTest, LOBTreeTest, OLBTreeTest, + OOBTreeTest, + + # Note: there is no TestOOBTrees. The next three are + # checking for assorted TypeErrors, and when both keys + # and values are objects (OO), there's nothing to test. + TestIIBTrees, TestIFBTrees, TestIOBTrees, TestOIBTrees, + TestIOSets, + DegenerateBTree, + TestCmpError, + BugFixes, + FamilyTest, + ): + s.addTest(makeSuite(klass)) + + return s + +## utility functions + +def lsubtract(l1, l2): + l1 = list(l1) + l2 = list(l2) + l = filter(lambda x, l1=l1: x not in l1, l2) + l = l + filter(lambda x, l2=l2: x not in l2, l1) + return l + +def realseq(itemsob): + return [x for x in itemsob] + +def permutations(x): + # Return a list of all permutations of list x. + n = len(x) + if n <= 1: + return [x] + result = [] + x0 = x[0] + for i in range(n): + # Build the (n-1)! permutations with x[i] in the first position. + xcopy = x[:] + first, xcopy[i] = xcopy[i], x0 + result.extend([[first] + p for p in permutations(xcopy[1:])]) + return result + + +def main(): + TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/BTrees/tests/test_btreesubclass.py zope3-3.5~bzr18/src/BTrees/tests/test_btreesubclass.py --- zope3-3.4.0/src/BTrees/tests/test_btreesubclass.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/test_btreesubclass.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +from BTrees.OOBTree import OOBTree, OOBucket + +class B(OOBucket): + pass + +class T(OOBTree): + _bucket_type = B + +import unittest + +class SubclassTest(unittest.TestCase): + + def testSubclass(self): + # test that a subclass that defines _bucket_type gets buckets + # of that type + t = T() + + # There's no good way to get a bucket at the moment. + # __getstate__() is as good as it gets, but the default + # getstate explicitly includes the pickle of the bucket + # for small trees, so we have to be clever :-( + + # make sure there is more than one bucket in the tree + for i in range(1000): + t[i] = i + + state = t.__getstate__() + self.assert_(state[0][0].__class__ is B) + +def test_suite(): + return unittest.makeSuite(SubclassTest) diff -Nru zope3-3.4.0/src/BTrees/tests/testBTreesUnicode.py zope3-3.5~bzr18/src/BTrees/tests/testBTreesUnicode.py --- zope3-3.4.0/src/BTrees/tests/testBTreesUnicode.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/testBTreesUnicode.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,76 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 unittest +from BTrees.OOBTree import OOBTree + +# When an OOBtree contains unicode strings as keys, +# it is neccessary accessing non-unicode strings are +# either ascii strings or encoded as unicoded using the +# corresponding encoding + +encoding = 'ISO-8859-1' + +class TestBTreesUnicode(unittest.TestCase): + """ test unicode""" + + def setUp(self): + """setup an OOBTree with some unicode strings""" + + self.s = unicode('dreit\xe4gigen', 'latin1') + + self.data = [('alien', 1), + ('k\xf6nnten', 2), + ('fox', 3), + ('future', 4), + ('quick', 5), + ('zerst\xf6rt', 6), + (unicode('dreit\xe4gigen','latin1'), 7), + ] + + self.tree = OOBTree() + for k, v in self.data: + if isinstance(k, str): + k = unicode(k, 'latin1') + self.tree[k] = v + + def testAllKeys(self): + # check every item of the tree + for k, v in self.data: + if isinstance(k, str): + k = unicode(k, encoding) + self.assert_(self.tree.has_key(k)) + self.assertEqual(self.tree[k], v) + + def testUnicodeKeys(self): + # try to access unicode keys in tree + k, v = self.data[-1] + self.assertEqual(k, self.s) + self.assertEqual(self.tree[k], v) + self.assertEqual(self.tree[self.s], v) + + def testAsciiKeys(self): + # try to access some "plain ASCII" keys in the tree + for k, v in self.data[0], self.data[2]: + self.assert_(isinstance(k, str)) + self.assertEqual(self.tree[k], v) + +def test_suite(): + return unittest.makeSuite(TestBTreesUnicode) + +def main(): + unittest.TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/BTrees/tests/test_check.py zope3-3.5~bzr18/src/BTrees/tests/test_check.py --- zope3-3.4.0/src/BTrees/tests/test_check.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/test_check.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,96 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 the BTree check.check() function.""" + +import unittest + +from BTrees.OOBTree import OOBTree +from BTrees.check import check + +class CheckTest(unittest.TestCase): + + def setUp(self): + self.t = t = OOBTree() + for i in range(31): + t[i] = 2*i + self.state = t.__getstate__() + + def testNormal(self): + s = self.state + # Looks like (state, first_bucket) + # where state looks like (bucket0, 15, bucket1). + self.assertEqual(len(s), 2) + self.assertEqual(len(s[0]), 3) + self.assertEqual(s[0][1], 15) + self.t._check() # shouldn't blow up + check(self.t) # shouldn't blow up + + def testKeyTooLarge(self): + # Damage an invariant by dropping the BTree key to 14. + s = self.state + news = (s[0][0], 14, s[0][2]), s[1] + self.t.__setstate__(news) + self.t._check() # not caught + try: + # Expecting "... key %r >= upper bound %r at index %d" + check(self.t) + except AssertionError, detail: + self.failUnless(str(detail).find(">= upper bound") > 0) + else: + self.fail("expected self.t_check() to catch the problem") + + def testKeyTooSmall(self): + # Damage an invariant by bumping the BTree key to 16. + s = self.state + news = (s[0][0], 16, s[0][2]), s[1] + self.t.__setstate__(news) + self.t._check() # not caught + try: + # Expecting "... key %r < lower bound %r at index %d" + check(self.t) + except AssertionError, detail: + self.failUnless(str(detail).find("< lower bound") > 0) + else: + self.fail("expected self.t_check() to catch the problem") + + def testKeysSwapped(self): + # Damage an invariant by swapping two key/value pairs. + s = self.state + # Looks like (state, first_bucket) + # where state looks like (bucket0, 15, bucket1). + (b0, num, b1), firstbucket = s + self.assertEqual(b0[4], 8) + self.assertEqual(b0[5], 10) + b0state = b0.__getstate__() + self.assertEqual(len(b0state), 2) + # b0state looks like + # ((k0, v0, k1, v1, ...), nextbucket) + pairs, nextbucket = b0state + self.assertEqual(pairs[8], 4) + self.assertEqual(pairs[9], 8) + self.assertEqual(pairs[10], 5) + self.assertEqual(pairs[11], 10) + newpairs = pairs[:8] + (5, 10, 4, 8) + pairs[12:] + b0.__setstate__((newpairs, nextbucket)) + self.t._check() # not caught + try: + check(self.t) + except AssertionError, detail: + self.failUnless(str(detail).find( + "key 5 at index 4 >= key 4 at index 5") > 0) + else: + self.fail("expected self.t_check() to catch the problem") + +def test_suite(): + return unittest.makeSuite(CheckTest) diff -Nru zope3-3.4.0/src/BTrees/tests/test_compare.py zope3-3.5~bzr18/src/BTrees/tests/test_compare.py --- zope3-3.4.0/src/BTrees/tests/test_compare.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/test_compare.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,74 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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 errors during comparison of BTree keys.""" + +import unittest + +from BTrees.OOBTree import OOBucket as Bucket, OOSet as Set + +import transaction +from ZODB.MappingStorage import MappingStorage +from ZODB.DB import DB + +class CompareTest(unittest.TestCase): + + s = "A string with hi-bit-set characters: \700\701" + u = u"A unicode string" + + def setUp(self): + # These defaults only make sense if the default encoding + # prevents s from being promoted to Unicode. + self.assertRaises(UnicodeError, unicode, self.s) + + # An object needs to be added to the database to + self.db = DB(MappingStorage()) + root = self.db.open().root() + self.bucket = root["bucket"] = Bucket() + self.set = root["set"] = Set() + transaction.commit() + + def tearDown(self): + self.assert_(self.bucket._p_changed != 2) + self.assert_(self.set._p_changed != 2) + transaction.abort() + + def assertUE(self, callable, *args): + self.assertRaises(UnicodeError, callable, *args) + + def testBucketGet(self): + self.bucket[self.s] = 1 + self.assertUE(self.bucket.get, self.u) + + def testSetGet(self): + self.set.insert(self.s) + self.assertUE(self.set.remove, self.u) + + def testBucketSet(self): + self.bucket[self.s] = 1 + self.assertUE(self.bucket.__setitem__, self.u, 1) + + def testSetSet(self): + self.set.insert(self.s) + self.assertUE(self.set.insert, self.u) + + def testBucketMinKey(self): + self.bucket[self.s] = 1 + self.assertUE(self.bucket.minKey, self.u) + + def testSetMinKey(self): + self.set.insert(self.s) + self.assertUE(self.set.minKey, self.u) + +def test_suite(): + return unittest.makeSuite(CompareTest) diff -Nru zope3-3.4.0/src/BTrees/tests/testConflict.py zope3-3.5~bzr18/src/BTrees/tests/testConflict.py --- zope3-3.4.0/src/BTrees/tests/testConflict.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/testConflict.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,849 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 os +from unittest import TestCase, TestSuite, makeSuite +from types import ClassType + +from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet +from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet +from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet +from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet +from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet +from BTrees.LOBTree import LOBTree, LOBucket, LOSet, LOTreeSet +from BTrees.LLBTree import LLBTree, LLBucket, LLSet, LLTreeSet +from BTrees.LFBTree import LFBTree, LFBucket, LFSet, LFTreeSet +from BTrees.OLBTree import OLBTree, OLBucket, OLSet, OLTreeSet + +import transaction +from ZODB.POSException import ConflictError + +class Base: + """ Tests common to all types: sets, buckets, and BTrees """ + + storage = None + + def setUp(self): + self.t = self.t_type() + + def tearDown(self): + transaction.abort() + del self.t + if self.storage is not None: + self.storage.close() + self.storage.cleanup() + + def openDB(self): + from ZODB.FileStorage import FileStorage + from ZODB.DB import DB + n = 'fs_tmp__%s' % os.getpid() + self.storage = FileStorage(n) + self.db = DB(self.storage) + return self.db + +class MappingBase(Base): + """ Tests common to mappings (buckets, btrees) """ + + def _deletefail(self): + del self.t[1] + + def _setupConflict(self): + + l=[ -5124, -7377, 2274, 8801, -9901, 7327, 1565, 17, -679, + 3686, -3607, 14, 6419, -5637, 6040, -4556, -8622, 3847, 7191, + -4067] + + + e1=[(-1704, 0), (5420, 1), (-239, 2), (4024, 3), (-6984, 4)] + e2=[(7745, 0), (4868, 1), (-2548, 2), (-2711, 3), (-3154, 4)] + + + base=self.t + base.update([(i, i*i) for i in l[:20]]) + b1=base.__class__(base) + b2=base.__class__(base) + bm=base.__class__(base) + + items=base.items() + + return base, b1, b2, bm, e1, e2, items + + def testSimpleConflict(self): + # Unlike all the other tests, invoke conflict resolution + # by committing a transaction and catching a conflict + # in the storage. + self.openDB() + + r1 = self.db.open().root() + r1["t"] = self.t + transaction.commit() + + r2 = self.db.open().root() + copy = r2["t"] + list(copy) # unghostify + + self.assertEqual(self.t._p_serial, copy._p_serial) + + self.t.update({1:2, 2:3}) + transaction.commit() + + copy.update({3:4}) + transaction.commit() + + + def testMergeDelete(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + del b1[items[1][0]] + del b2[items[5][0]] + del b1[items[-1][0]] + del b2[items[-2][0]] + del bm[items[1][0]] + del bm[items[5][0]] + del bm[items[-1][0]] + del bm[items[-2][0]] + test_merge(base, b1, b2, bm, 'merge delete') + + def testMergeDeleteAndUpdate(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + del b1[items[1][0]] + b2[items[5][0]]=1 + del b1[items[-1][0]] + b2[items[-2][0]]=2 + del bm[items[1][0]] + bm[items[5][0]]=1 + del bm[items[-1][0]] + bm[items[-2][0]]=2 + test_merge(base, b1, b2, bm, 'merge update and delete') + + def testMergeUpdate(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + b1[items[0][0]]=1 + b2[items[5][0]]=2 + b1[items[-1][0]]=3 + b2[items[-2][0]]=4 + bm[items[0][0]]=1 + bm[items[5][0]]=2 + bm[items[-1][0]]=3 + bm[items[-2][0]]=4 + test_merge(base, b1, b2, bm, 'merge update') + + def testFailMergeDelete(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + del b1[items[0][0]] + del b2[items[0][0]] + test_merge(base, b1, b2, bm, 'merge conflicting delete', + should_fail=1) + + def testFailMergeUpdate(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + b1[items[0][0]]=1 + b2[items[0][0]]=2 + test_merge(base, b1, b2, bm, 'merge conflicting update', + should_fail=1) + + def testFailMergeDeleteAndUpdate(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + del b1[items[0][0]] + b2[items[0][0]]=-9 + test_merge(base, b1, b2, bm, 'merge conflicting update and delete', + should_fail=1) + + def testMergeInserts(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + + b1[-99999]=-99999 + b1[e1[0][0]]=e1[0][1] + b2[99999]=99999 + b2[e1[2][0]]=e1[2][1] + + bm[-99999]=-99999 + bm[e1[0][0]]=e1[0][1] + bm[99999]=99999 + bm[e1[2][0]]=e1[2][1] + test_merge(base, b1, b2, bm, 'merge insert') + + def testMergeInsertsFromEmpty(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + + base.clear() + b1.clear() + b2.clear() + bm.clear() + + b1.update(e1) + bm.update(e1) + b2.update(e2) + bm.update(e2) + + test_merge(base, b1, b2, bm, 'merge insert from empty') + + def testFailMergeEmptyAndFill(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + + b1.clear() + bm.clear() + b2.update(e2) + bm.update(e2) + + test_merge(base, b1, b2, bm, 'merge insert from empty', should_fail=1) + + def testMergeEmpty(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + + b1.clear() + bm.clear() + + test_merge(base, b1, b2, bm, 'empty one and not other', should_fail=1) + + def testFailMergeInsert(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + b1[-99999]=-99999 + b1[e1[0][0]]=e1[0][1] + b2[99999]=99999 + b2[e1[0][0]]=e1[0][1] + test_merge(base, b1, b2, bm, 'merge conflicting inserts', + should_fail=1) + +class SetTests(Base): + "Set (as opposed to TreeSet) specific tests." + + def _setupConflict(self): + l=[ -5124, -7377, 2274, 8801, -9901, 7327, 1565, 17, -679, + 3686, -3607, 14, 6419, -5637, 6040, -4556, -8622, 3847, 7191, + -4067] + + e1=[-1704, 5420, -239, 4024, -6984] + e2=[7745, 4868, -2548, -2711, -3154] + + + base=self.t + base.update(l) + b1=base.__class__(base) + b2=base.__class__(base) + bm=base.__class__(base) + + items=base.keys() + + return base, b1, b2, bm, e1, e2, items + + def testMergeDelete(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + b1.remove(items[1]) + b2.remove(items[5]) + b1.remove(items[-1]) + b2.remove(items[-2]) + bm.remove(items[1]) + bm.remove(items[5]) + bm.remove(items[-1]) + bm.remove(items[-2]) + test_merge(base, b1, b2, bm, 'merge delete') + + def testFailMergeDelete(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + b1.remove(items[0]) + b2.remove(items[0]) + test_merge(base, b1, b2, bm, 'merge conflicting delete', + should_fail=1) + + def testMergeInserts(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + + b1.insert(-99999) + b1.insert(e1[0]) + b2.insert(99999) + b2.insert(e1[2]) + + bm.insert(-99999) + bm.insert(e1[0]) + bm.insert(99999) + bm.insert(e1[2]) + test_merge(base, b1, b2, bm, 'merge insert') + + def testMergeInsertsFromEmpty(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + + base.clear() + b1.clear() + b2.clear() + bm.clear() + + b1.update(e1) + bm.update(e1) + b2.update(e2) + bm.update(e2) + + test_merge(base, b1, b2, bm, 'merge insert from empty') + + def testFailMergeEmptyAndFill(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + + b1.clear() + bm.clear() + b2.update(e2) + bm.update(e2) + + test_merge(base, b1, b2, bm, 'merge insert from empty', should_fail=1) + + def testMergeEmpty(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + + b1.clear() + bm.clear() + + test_merge(base, b1, b2, bm, 'empty one and not other', should_fail=1) + + def testFailMergeInsert(self): + base, b1, b2, bm, e1, e2, items = self._setupConflict() + b1.insert(-99999) + b1.insert(e1[0]) + b2.insert(99999) + b2.insert(e1[0]) + test_merge(base, b1, b2, bm, 'merge conflicting inserts', + should_fail=1) + + +def test_merge(o1, o2, o3, expect, message='failed to merge', should_fail=0): + s1 = o1.__getstate__() + s2 = o2.__getstate__() + s3 = o3.__getstate__() + expected = expect.__getstate__() + if expected is None: + expected = ((((),),),) + + if should_fail: + try: + merged = o1._p_resolveConflict(s1, s2, s3) + except ConflictError, err: + pass + else: + assert 0, message + else: + merged = o1._p_resolveConflict(s1, s2, s3) + assert merged == expected, message + +class NastyConfict(Base, TestCase): + + t_type = OOBTree + + # This tests a problem that cropped up while trying to write + # testBucketSplitConflict (below): conflict resolution wasn't + # working at all in non-trivial cases. Symptoms varied from + # strange complaints about pickling (despite that the test isn't + # doing any *directly*), thru SystemErrors from Python and + # AssertionErrors inside the BTree code. + def testResolutionBlowsUp(self): + b = self.t + for i in range(0, 200, 4): + b[i] = i + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 15 values: 60, 64 .. 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # If these fail, the *preconditions* for running the test aren't + # satisfied -- the test itself hasn't been run yet. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + # Invoke conflict resolution by committing a transaction. + self.openDB() + + r1 = self.db.open().root() + r1["t"] = self.t + transaction.commit() + + r2 = self.db.open().root() + copy = r2["t"] + # Make sure all of copy is loaded. + list(copy.values()) + + self.assertEqual(self.t._p_serial, copy._p_serial) + + self.t.update({1:2, 2:3}) + transaction.commit() + + copy.update({3:4}) + transaction.commit() # if this doesn't blow up + list(copy.values()) # and this doesn't either, then fine + + def testBucketSplitConflict(self): + # Tests that a bucket split is viewed as a conflict. + # It's (almost necessarily) a white-box test, and sensitive to + # implementation details. + b = self.t + for i in range(0, 200, 4): + b[i] = i + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 15 values: 60, 64 .. 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # If these fail, the *preconditions* for running the test aren't + # satisfied -- the test itself hasn't been run yet. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + # Invoke conflict resolution by committing a transaction. + self.openDB() + + tm1 = transaction.TransactionManager() + r1 = self.db.open(transaction_manager=tm1).root() + r1["t"] = self.t + tm1.commit() + + tm2 = transaction.TransactionManager() + r2 = self.db.open(transaction_manager=tm2).root() + copy = r2["t"] + # Make sure all of copy is loaded. + list(copy.values()) + + self.assertEqual(self.t._p_serial, copy._p_serial) + + # In one transaction, add 16 new keys to bucket1, to force a bucket + # split. + b = self.t + numtoadd = 16 + candidate = 60 + while numtoadd: + if not b.has_key(candidate): + b[candidate] = candidate + numtoadd -= 1 + candidate += 1 + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 15 values: 60, 61 .. 74 + # bucket 2 has 16 values: [75, 76 .. 81] + [84, 88 ..116] + # bucket 3 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((b0, 60, b1, 75, b2, 120, b3), firstbucket) + # The next block is still verifying preconditions. + self.assertEqual(len(state) , 2) + self.assertEqual(len(state[0]), 7) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 75) + self.assertEqual(state[0][5], 120) + + tm1.commit() + + # In the other transaction, add 3 values near the tail end of bucket1. + # This doesn't cause a split. + b = copy + for i in range(112, 116): + b[i] = i + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 18 values: 60, 64 .. 112, 113, 114, 115, 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # The next block is still verifying preconditions. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + self.assertRaises(ConflictError, tm2.commit) + + def testEmptyBucketConflict(self): + # Tests that an emptied bucket *created by* conflict resolution is + # viewed as a conflict: conflict resolution doesn't have enough + # info to unlink the empty bucket from the BTree correctly. + b = self.t + for i in range(0, 200, 4): + b[i] = i + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 15 values: 60, 64 .. 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # If these fail, the *preconditions* for running the test aren't + # satisfied -- the test itself hasn't been run yet. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + # Invoke conflict resolution by committing a transaction. + self.openDB() + + tm1 = transaction.TransactionManager() + r1 = self.db.open(transaction_manager=tm1).root() + r1["t"] = self.t + tm1.commit() + + tm2 = transaction.TransactionManager() + r2 = self.db.open(transaction_manager=tm2).root() + copy = r2["t"] + # Make sure all of copy is loaded. + list(copy.values()) + + self.assertEqual(self.t._p_serial, copy._p_serial) + + # In one transaction, delete half of bucket 1. + b = self.t + for k in 60, 64, 68, 72, 76, 80, 84, 88: + del b[k] + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 7 values: 92, 96, 100, 104, 108, 112, 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # The next block is still verifying preconditions. + self.assertEqual(len(state) , 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 92) + self.assertEqual(state[0][3], 120) + + tm1.commit() + + # In the other transaction, delete the other half of bucket 1. + b = copy + for k in 92, 96, 100, 104, 108, 112, 116: + del b[k] + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 8 values: 60, 64, 68, 72, 76, 80, 84, 88 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # The next block is still verifying preconditions. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + # Conflict resolution empties bucket1 entirely. This used to + # create an "insane" BTree (a legit BTree cannot contain an empty + # bucket -- it contains NULL pointers the BTree code doesn't + # expect, and segfaults result). + self.assertRaises(ConflictError, tm2.commit) + + + def testEmptyBucketNoConflict(self): + # Tests that a plain empty bucket (on input) is not viewed as a + # conflict. + b = self.t + for i in range(0, 200, 4): + b[i] = i + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 15 values: 60, 64 .. 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # If these fail, the *preconditions* for running the test aren't + # satisfied -- the test itself hasn't been run yet. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + # Invoke conflict resolution by committing a transaction. + self.openDB() + + r1 = self.db.open().root() + r1["t"] = self.t + transaction.commit() + + r2 = self.db.open().root() + copy = r2["t"] + # Make sure all of copy is loaded. + list(copy.values()) + + self.assertEqual(self.t._p_serial, copy._p_serial) + + # In one transaction, just add a key. + b = self.t + b[1] = 1 + # bucket 0 has 16 values: [0, 1] + [4, 8 .. 56] + # bucket 1 has 15 values: 60, 64 .. 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # The next block is still verifying preconditions. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + transaction.commit() + + # In the other transaction, delete bucket 2. + b = copy + for k in range(120, 200, 4): + del b[k] + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 15 values: 60, 64 .. 116 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1), firstbucket) + # The next block is still verifying preconditions. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 3) + self.assertEqual(state[0][1], 60) + + # This shouldn't create a ConflictError. + transaction.commit() + # And the resulting BTree shouldn't have internal damage. + b._check() + + # The snaky control flow in _bucket__p_resolveConflict ended up trying + # to decref a NULL pointer if conflict resolution was fed 3 empty + # buckets. http://collector.zope.org/Zope/553 + def testThreeEmptyBucketsNoSegfault(self): + self.openDB() + + tm1 = transaction.TransactionManager() + r1 = self.db.open(transaction_manager=tm1).root() + self.assertEqual(len(self.t), 0) + r1["t"] = b = self.t # an empty tree + tm1.commit() + + tm2 = transaction.TransactionManager() + r2 = self.db.open(transaction_manager=tm2).root() + copy = r2["t"] + # Make sure all of copy is loaded. + list(copy.values()) + + # In one transaction, add and delete a key. + b[2] = 2 + del b[2] + tm1.commit() + + # In the other transaction, also add and delete a key. + b = copy + b[1] = 1 + del b[1] + # If the commit() segfaults, the C code is still wrong for this case. + self.assertRaises(ConflictError, tm2.commit) + + def testCantResolveBTreeConflict(self): + # Test that a conflict involving two different changes to + # an internal BTree node is unresolvable. An internal node + # only changes when there are enough additions or deletions + # to a child bucket that the bucket is split or removed. + # It's (almost necessarily) a white-box test, and sensitive to + # implementation details. + b = self.t + for i in range(0, 200, 4): + b[i] = i + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 15 values: 60, 64 .. 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # If these fail, the *preconditions* for running the test aren't + # satisfied -- the test itself hasn't been run yet. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + # Set up database connections to provoke conflict. + self.openDB() + tm1 = transaction.TransactionManager() + r1 = self.db.open(transaction_manager=tm1).root() + r1["t"] = self.t + tm1.commit() + + tm2 = transaction.TransactionManager() + r2 = self.db.open(transaction_manager=tm2).root() + copy = r2["t"] + # Make sure all of copy is loaded. + list(copy.values()) + + self.assertEqual(self.t._p_serial, copy._p_serial) + + # Now one transaction should add enough keys to cause a split, + # and another should remove all the keys in one bucket. + + for k in range(200, 300, 4): + self.t[k] = k + tm1.commit() + + for k in range(0, 60, 4): + del copy[k] + + try: + tm2.commit() + except ConflictError, detail: + self.assert_(str(detail).startswith('database conflict error')) + else: + self.fail("expected ConflictError") + + def testConflictWithOneEmptyBucket(self): + # If one transaction empties a bucket, while another adds an item + # to the bucket, all the changes "look resolvable": bucket conflict + # resolution returns a bucket containing (only) the item added by + # the latter transaction, but changes from the former transaction + # removing the bucket are uncontested: the bucket is removed from + # the BTree despite that resolution thinks it's non-empty! This + # was first reported by Dieter Maurer, to zodb-dev on 22 Mar 2005. + b = self.t + for i in range(0, 200, 4): + b[i] = i + # bucket 0 has 15 values: 0, 4 .. 56 + # bucket 1 has 15 values: 60, 64 .. 116 + # bucket 2 has 20 values: 120, 124 .. 196 + state = b.__getstate__() + # Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket) + # If these fail, the *preconditions* for running the test aren't + # satisfied -- the test itself hasn't been run yet. + self.assertEqual(len(state), 2) + self.assertEqual(len(state[0]), 5) + self.assertEqual(state[0][1], 60) + self.assertEqual(state[0][3], 120) + + # Set up database connections to provoke conflict. + self.openDB() + tm1 = transaction.TransactionManager() + r1 = self.db.open(transaction_manager=tm1).root() + r1["t"] = self.t + tm1.commit() + + tm2 = transaction.TransactionManager() + r2 = self.db.open(transaction_manager=tm2).root() + copy = r2["t"] + # Make sure all of copy is loaded. + list(copy.values()) + + self.assertEqual(self.t._p_serial, copy._p_serial) + + # Now one transaction empties the first bucket, and another adds a + # key to the first bucket. + + for k in range(0, 60, 4): + del self.t[k] + tm1.commit() + + copy[1] = 1 + + try: + tm2.commit() + except ConflictError, detail: + self.assert_(str(detail).startswith('database conflict error')) + else: + self.fail("expected ConflictError") + + # Same thing, except commit the transactions in the opposite order. + b = OOBTree() + for i in range(0, 200, 4): + b[i] = i + + tm1 = transaction.TransactionManager() + r1 = self.db.open(transaction_manager=tm1).root() + r1["t"] = b + tm1.commit() + + tm2 = transaction.TransactionManager() + r2 = self.db.open(transaction_manager=tm2).root() + copy = r2["t"] + # Make sure all of copy is loaded. + list(copy.values()) + + self.assertEqual(b._p_serial, copy._p_serial) + + # Now one transaction empties the first bucket, and another adds a + # key to the first bucket. + b[1] = 1 + tm1.commit() + + for k in range(0, 60, 4): + del copy[k] + try: + tm2.commit() + except ConflictError, detail: + self.assert_(str(detail).startswith('database conflict error')) + else: + self.fail("expected ConflictError") + + def testConflictOfInsertAndDeleteOfFirstBucketItem(self): + """ + Recently, BTrees became careful about removing internal keys + (keys in internal aka BTree nodes) when they were deleted from + buckets. This poses a problem for conflict resolution. + + We want to guard against a case in which the first key in a + bucket is removed in one transaction while a key is added + after that key but before the next key in another transaction + with the result that the added key is unreachble + + original: + + Bucket(...), k1, Bucket((k1, v1), (k3, v3), ...) + + tran1 + + Bucket(...), k3, Bucket(k3, v3), ...) + + tran2 + + Bucket(...), k1, Bucket((k1, v1), (k2, v2), (k3, v3), ...) + + where k1 < k2 < k3 + + We don't want: + + Bucket(...), k3, Bucket((k2, v2), (k3, v3), ...) + + as k2 would be unfindable, so we want a conflict. + + """ + mytype = self.t_type + db = self.openDB() + tm1 = transaction.TransactionManager() + conn1 = db.open(tm1) + conn1.root.t = t = mytype() + for i in range(0, 200, 2): + t[i] = i + tm1.commit() + k = t.__getstate__()[0][1] + assert t.__getstate__()[0][2].keys()[0] == k + + tm2 = transaction.TransactionManager() + conn2 = db.open(tm2) + + t[k+1] = k+1 + del conn2.root.t[k] + for i in range(200,300): + conn2.root.t[i] = i + + tm1.commit() + self.assertRaises(ConflictError, tm2.commit) + tm2.abort() + + k = t.__getstate__()[0][1] + t[k+1] = k+1 + del conn2.root.t[k] + + tm2.commit() + self.assertRaises(ConflictError, tm1.commit) + tm1.abort() + +def test_suite(): + suite = TestSuite() + + for kv in ('OO', + 'II', 'IO', 'OI', 'IF', + 'LL', 'LO', 'OL', 'LF', + ): + for name, bases in (('BTree', (MappingBase, TestCase)), + ('Bucket', (MappingBase, TestCase)), + ('TreeSet', (SetTests, TestCase)), + ('Set', (SetTests, TestCase)), + ): + klass = ClassType(kv + name + 'Test', bases, + dict(t_type=globals()[kv+name])) + suite.addTest(makeSuite(klass)) + + suite.addTest(makeSuite(NastyConfict)) + return suite diff -Nru zope3-3.4.0/src/BTrees/tests/testLength.py zope3-3.5~bzr18/src/BTrees/tests/testLength.py --- zope3-3.4.0/src/BTrees/tests/testLength.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/testLength.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2008 Zope Corporation 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 for BTrees.Length module. + +""" +__docformat__ = "reStructuredText" + +import BTrees.Length +import sys +import unittest + + +class LengthTestCase(unittest.TestCase): + + def test_length_overflows_to_long(self): + length = BTrees.Length.Length(sys.maxint) + self.assertEqual(length(), sys.maxint) + self.assert_(type(length()) is int) + length.change(+1) + self.assertEqual(length(), sys.maxint + 1) + self.assert_(type(length()) is long) + + def test_length_underflows_to_long(self): + minint = (-sys.maxint) - 1 + length = BTrees.Length.Length(minint) + self.assertEqual(length(), minint) + self.assert_(type(length()) is int) + length.change(-1) + self.assertEqual(length(), minint - 1) + self.assert_(type(length()) is long) + + +def test_suite(): + return unittest.makeSuite(LengthTestCase) diff -Nru zope3-3.4.0/src/BTrees/tests/testSetOps.py zope3-3.5~bzr18/src/BTrees/tests/testSetOps.py --- zope3-3.4.0/src/BTrees/tests/testSetOps.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/tests/testSetOps.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,578 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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 +# +############################################################################## +from unittest import TestCase, TestSuite, TextTestRunner, makeSuite + +from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet +from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet +from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet +from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet +from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet +from BTrees.LOBTree import LOBTree, LOBucket, LOSet, LOTreeSet +from BTrees.LFBTree import LFBTree, LFBucket, LFSet, LFTreeSet +from BTrees.LLBTree import LLBTree, LLBucket, LLSet, LLTreeSet +from BTrees.OLBTree import OLBTree, OLBucket, OLSet, OLTreeSet + +# Subclasses have to set up: +# builders - functions to build inputs, taking an optional keys arg +# intersection, union, difference - set to the type-correct versions +class SetResult(TestCase): + def setUp(self): + self.Akeys = [1, 3, 5, 6 ] + self.Bkeys = [ 2, 3, 4, 6, 7] + self.As = [makeset(self.Akeys) for makeset in self.builders] + self.Bs = [makeset(self.Bkeys) for makeset in self.builders] + self.emptys = [makeset() for makeset in self.builders] + + # Slow but obviously correct Python implementations of basic ops. + def _union(self, x, y): + result = list(x) + for e in y: + if e not in result: + result.append(e) + result.sort() + return result + + def _intersection(self, x, y): + result = [] + for e in x: + if e in y: + result.append(e) + return result + + def _difference(self, x, y): + result = list(x) + for e in y: + if e in result: + result.remove(e) + # Difference preserves LHS values. + if hasattr(x, "values"): + result = [(k, x[k]) for k in result] + return result + + def testNone(self): + for op in self.union, self.intersection, self.difference: + C = op(None, None) + self.assert_(C is None) + + for op in self.union, self.intersection, self.difference: + for A in self.As: + C = op(A, None) + self.assert_(C is A) + + C = op(None, A) + if op is self.difference: + self.assert_(C is None) + else: + self.assert_(C is A) + + def testEmptyUnion(self): + for A in self.As: + for E in self.emptys: + C = self.union(A, E) + self.assert_(not hasattr(C, "values")) + self.assertEqual(list(C), self.Akeys) + + C = self.union(E, A) + self.assert_(not hasattr(C, "values")) + self.assertEqual(list(C), self.Akeys) + + def testEmptyIntersection(self): + for A in self.As: + for E in self.emptys: + C = self.intersection(A, E) + self.assert_(not hasattr(C, "values")) + self.assertEqual(list(C), []) + + C = self.intersection(E, A) + self.assert_(not hasattr(C, "values")) + self.assertEqual(list(C), []) + + def testEmptyDifference(self): + for A in self.As: + for E in self.emptys: + C = self.difference(A, E) + # Difference preserves LHS values. + self.assertEqual(hasattr(C, "values"), hasattr(A, "values")) + if hasattr(A, "values"): + self.assertEqual(list(C.items()), list(A.items())) + else: + self.assertEqual(list(C), self.Akeys) + + C = self.difference(E, A) + self.assertEqual(hasattr(C, "values"), hasattr(E, "values")) + self.assertEqual(list(C), []) + + def testUnion(self): + inputs = self.As + self.Bs + for A in inputs: + for B in inputs: + C = self.union(A, B) + self.assert_(not hasattr(C, "values")) + self.assertEqual(list(C), self._union(A, B)) + + def testIntersection(self): + inputs = self.As + self.Bs + for A in inputs: + for B in inputs: + C = self.intersection(A, B) + self.assert_(not hasattr(C, "values")) + self.assertEqual(list(C), self._intersection(A, B)) + + def testDifference(self): + inputs = self.As + self.Bs + for A in inputs: + for B in inputs: + C = self.difference(A, B) + # Difference preserves LHS values. + self.assertEqual(hasattr(C, "values"), hasattr(A, "values")) + want = self._difference(A, B) + if hasattr(A, "values"): + self.assertEqual(list(C.items()), want) + else: + self.assertEqual(list(C), want) + + def testLargerInputs(self): + from random import randint + MAXSIZE = 200 + MAXVAL = 400 + for i in range(3): + n = randint(0, MAXSIZE) + Akeys = [randint(1, MAXVAL) for j in range(n)] + As = [makeset(Akeys) for makeset in self.builders] + Akeys = IISet(Akeys) + + n = randint(0, MAXSIZE) + Bkeys = [randint(1, MAXVAL) for j in range(n)] + Bs = [makeset(Bkeys) for makeset in self.builders] + Bkeys = IISet(Bkeys) + + for op, simulator in ((self.union, self._union), + (self.intersection, self._intersection), + (self.difference, self._difference)): + for A in As: + for B in Bs: + got = op(A, B) + want = simulator(Akeys, Bkeys) + self.assertEqual(list(got), want, + (A, B, Akeys, Bkeys, list(got), want)) + +# Given a mapping builder (IIBTree, OOBucket, etc), return a function +# that builds an object of that type given only a list of keys. +def makeBuilder(mapbuilder): + def result(keys=[], mapbuilder=mapbuilder): + return mapbuilder(zip(keys, keys)) + return result + +class PureOO(SetResult): + from BTrees.OOBTree import union, intersection, difference + builders = OOSet, OOTreeSet, makeBuilder(OOBTree), makeBuilder(OOBucket) + +class PureII(SetResult): + from BTrees.IIBTree import union, intersection, difference + builders = IISet, IITreeSet, makeBuilder(IIBTree), makeBuilder(IIBucket) + +class PureIO(SetResult): + from BTrees.IOBTree import union, intersection, difference + builders = IOSet, IOTreeSet, makeBuilder(IOBTree), makeBuilder(IOBucket) + +class PureIF(SetResult): + from BTrees.IFBTree import union, intersection, difference + builders = IFSet, IFTreeSet, makeBuilder(IFBTree), makeBuilder(IFBucket) + +class PureOI(SetResult): + from BTrees.OIBTree import union, intersection, difference + builders = OISet, OITreeSet, makeBuilder(OIBTree), makeBuilder(OIBucket) + +class PureLL(SetResult): + from BTrees.LLBTree import union, intersection, difference + builders = LLSet, LLTreeSet, makeBuilder(LLBTree), makeBuilder(LLBucket) + +class PureLO(SetResult): + from BTrees.LOBTree import union, intersection, difference + builders = LOSet, LOTreeSet, makeBuilder(LOBTree), makeBuilder(LOBucket) + +class PureLF(SetResult): + from BTrees.LFBTree import union, intersection, difference + builders = LFSet, LFTreeSet, makeBuilder(LFBTree), makeBuilder(LFBucket) + +class PureOL(SetResult): + from BTrees.OLBTree import union, intersection, difference + builders = OLSet, OLTreeSet, makeBuilder(OLBTree), makeBuilder(OLBucket) + +# Subclasses must set up (as class variables): +# multiunion, union +# mkset, mktreeset +# mkbucket, mkbtree +class MultiUnion(TestCase): + + def testEmpty(self): + self.assertEqual(len(self.multiunion([])), 0) + + def testOne(self): + for sequence in [3], range(20), range(-10, 0, 2) + range(1, 10, 2): + seq1 = sequence[:] + seq2 = sequence[:] + seq2.reverse() + seqsorted = sequence[:] + seqsorted.sort() + for seq in seq1, seq2, seqsorted: + for builder in self.mkset, self.mktreeset: + input = builder(seq) + output = self.multiunion([input]) + self.assertEqual(len(seq), len(output)) + self.assertEqual(seqsorted, list(output)) + + def testValuesIgnored(self): + for builder in self.mkbucket, self.mkbtree: + input = builder([(1, 2), (3, 4), (5, 6)]) + output = self.multiunion([input]) + self.assertEqual([1, 3, 5], list(output)) + + def testBigInput(self): + N = 100000 + input = self.mkset(range(N)) + output = self.multiunion([input] * 10) + self.assertEqual(len(output), N) + self.assertEqual(output.minKey(), 0) + self.assertEqual(output.maxKey(), N-1) + self.assertEqual(list(output), range(N)) + + def testLotsOfLittleOnes(self): + from random import shuffle + N = 5000 + inputs = [] + mkset, mktreeset = self.mkset, self.mktreeset + for i in range(N): + base = i * 4 - N + inputs.append(mkset([base, base+1])) + inputs.append(mktreeset([base+2, base+3])) + shuffle(inputs) + output = self.multiunion(inputs) + self.assertEqual(len(output), N*4) + self.assertEqual(list(output), range(-N, 3*N)) + + def testFunkyKeyIteration(self): + # The internal set iteration protocol allows "iterating over" a + # a single key as if it were a set. + N = 100 + union, mkset = self.union, self.mkset + slow = mkset() + for i in range(N): + slow = union(slow, mkset([i])) + fast = self.multiunion(range(N)) # acts like N distinct singleton sets + self.assertEqual(len(slow), N) + self.assertEqual(len(fast), N) + self.assertEqual(list(slow), list(fast)) + self.assertEqual(list(fast), range(N)) + +class TestIIMultiUnion(MultiUnion): + from BTrees.IIBTree import multiunion, union + from BTrees.IIBTree import IISet as mkset, IITreeSet as mktreeset + from BTrees.IIBTree import IIBucket as mkbucket, IIBTree as mkbtree + +class TestIOMultiUnion(MultiUnion): + from BTrees.IOBTree import multiunion, union + from BTrees.IOBTree import IOSet as mkset, IOTreeSet as mktreeset + from BTrees.IOBTree import IOBucket as mkbucket, IOBTree as mkbtree + +class TestIFMultiUnion(MultiUnion): + from BTrees.IFBTree import multiunion, union + from BTrees.IFBTree import IFSet as mkset, IFTreeSet as mktreeset + from BTrees.IFBTree import IFBucket as mkbucket, IFBTree as mkbtree + +class TestLLMultiUnion(MultiUnion): + from BTrees.LLBTree import multiunion, union + from BTrees.LLBTree import LLSet as mkset, LLTreeSet as mktreeset + from BTrees.LLBTree import LLBucket as mkbucket, LLBTree as mkbtree + +class TestLOMultiUnion(MultiUnion): + from BTrees.LOBTree import multiunion, union + from BTrees.LOBTree import LOSet as mkset, LOTreeSet as mktreeset + from BTrees.LOBTree import LOBucket as mkbucket, LOBTree as mkbtree + +class TestLFMultiUnion(MultiUnion): + from BTrees.LFBTree import multiunion, union + from BTrees.LFBTree import LFSet as mkset, LFTreeSet as mktreeset + from BTrees.LFBTree import LFBucket as mkbucket, LFBTree as mkbtree + +# Check that various special module functions are and aren't imported from +# the expected BTree modules. +class TestImports(TestCase): + def testWeightedUnion(self): + from BTrees.IIBTree import weightedUnion + from BTrees.OIBTree import weightedUnion + + try: + from BTrees.IOBTree import weightedUnion + except ImportError: + pass + else: + self.fail("IOBTree shouldn't have weightedUnion") + + from BTrees.LLBTree import weightedUnion + from BTrees.OLBTree import weightedUnion + + try: + from BTrees.LOBTree import weightedUnion + except ImportError: + pass + else: + self.fail("LOBTree shouldn't have weightedUnion") + + try: + from BTrees.OOBTree import weightedUnion + except ImportError: + pass + else: + self.fail("OOBTree shouldn't have weightedUnion") + + def testWeightedIntersection(self): + from BTrees.IIBTree import weightedIntersection + from BTrees.OIBTree import weightedIntersection + + try: + from BTrees.IOBTree import weightedIntersection + except ImportError: + pass + else: + self.fail("IOBTree shouldn't have weightedIntersection") + + from BTrees.LLBTree import weightedIntersection + from BTrees.OLBTree import weightedIntersection + + try: + from BTrees.LOBTree import weightedIntersection + except ImportError: + pass + else: + self.fail("LOBTree shouldn't have weightedIntersection") + + try: + from BTrees.OOBTree import weightedIntersection + except ImportError: + pass + else: + self.fail("OOBTree shouldn't have weightedIntersection") + + def testMultiunion(self): + from BTrees.IIBTree import multiunion + from BTrees.IOBTree import multiunion + + try: + from BTrees.OIBTree import multiunion + except ImportError: + pass + else: + self.fail("OIBTree shouldn't have multiunion") + + from BTrees.LLBTree import multiunion + from BTrees.LOBTree import multiunion + + try: + from BTrees.OLBTree import multiunion + except ImportError: + pass + else: + self.fail("OLBTree shouldn't have multiunion") + + try: + from BTrees.OOBTree import multiunion + except ImportError: + pass + else: + self.fail("OOBTree shouldn't have multiunion") + +# Subclasses must set up (as class variables): +# weightedUnion, weightedIntersection +# builders -- sequence of constructors, taking items +# union, intersection -- the module routines of those names +# mkbucket -- the module bucket builder +class Weighted(TestCase): + + def setUp(self): + self.Aitems = [(1, 10), (3, 30), (5, 50), (6, 60)] + self.Bitems = [(2, 21), (3, 31), (4, 41), (6, 61), (7, 71)] + + self.As = [make(self.Aitems) for make in self.builders] + self.Bs = [make(self.Bitems) for make in self.builders] + self.emptys = [make([]) for make in self.builders] + + weights = [] + for w1 in -3, -1, 0, 1, 7: + for w2 in -3, -1, 0, 1, 7: + weights.append((w1, w2)) + self.weights = weights + + def testBothNone(self): + for op in self.weightedUnion, self.weightedIntersection: + w, C = op(None, None) + self.assert_(C is None) + self.assertEqual(w, 0) + + w, C = op(None, None, 42, 666) + self.assert_(C is None) + self.assertEqual(w, 0) + + def testLeftNone(self): + for op in self.weightedUnion, self.weightedIntersection: + for A in self.As + self.emptys: + w, C = op(None, A) + self.assert_(C is A) + self.assertEqual(w, 1) + + w, C = op(None, A, 42, 666) + self.assert_(C is A) + self.assertEqual(w, 666) + + def testRightNone(self): + for op in self.weightedUnion, self.weightedIntersection: + for A in self.As + self.emptys: + w, C = op(A, None) + self.assert_(C is A) + self.assertEqual(w, 1) + + w, C = op(A, None, 42, 666) + self.assert_(C is A) + self.assertEqual(w, 42) + + # If obj is a set, return a bucket with values all 1; else return obj. + def _normalize(self, obj): + if isaset(obj): + obj = self.mkbucket(zip(obj, [1] * len(obj))) + return obj + + # Python simulation of weightedUnion. + def _wunion(self, A, B, w1=1, w2=1): + if isaset(A) and isaset(B): + return 1, self.union(A, B).keys() + A = self._normalize(A) + B = self._normalize(B) + result = [] + for key in self.union(A, B): + v1 = A.get(key, 0) + v2 = B.get(key, 0) + result.append((key, v1*w1 + v2*w2)) + return 1, result + + def testUnion(self): + inputs = self.As + self.Bs + self.emptys + for A in inputs: + for B in inputs: + want_w, want_s = self._wunion(A, B) + got_w, got_s = self.weightedUnion(A, B) + self.assertEqual(got_w, want_w) + if isaset(got_s): + self.assertEqual(got_s.keys(), want_s) + else: + self.assertEqual(got_s.items(), want_s) + + for w1, w2 in self.weights: + want_w, want_s = self._wunion(A, B, w1, w2) + got_w, got_s = self.weightedUnion(A, B, w1, w2) + self.assertEqual(got_w, want_w) + if isaset(got_s): + self.assertEqual(got_s.keys(), want_s) + else: + self.assertEqual(got_s.items(), want_s) + + # Python simulation weightedIntersection. + def _wintersection(self, A, B, w1=1, w2=1): + if isaset(A) and isaset(B): + return w1 + w2, self.intersection(A, B).keys() + A = self._normalize(A) + B = self._normalize(B) + result = [] + for key in self.intersection(A, B): + result.append((key, A[key]*w1 + B[key]*w2)) + return 1, result + + def testIntersection(self): + inputs = self.As + self.Bs + self.emptys + for A in inputs: + for B in inputs: + want_w, want_s = self._wintersection(A, B) + got_w, got_s = self.weightedIntersection(A, B) + self.assertEqual(got_w, want_w) + if isaset(got_s): + self.assertEqual(got_s.keys(), want_s) + else: + self.assertEqual(got_s.items(), want_s) + + for w1, w2 in self.weights: + want_w, want_s = self._wintersection(A, B, w1, w2) + got_w, got_s = self.weightedIntersection(A, B, w1, w2) + self.assertEqual(got_w, want_w) + if isaset(got_s): + self.assertEqual(got_s.keys(), want_s) + else: + self.assertEqual(got_s.items(), want_s) + +# Given a set builder (like OITreeSet or OISet), return a function that +# takes a list of (key, value) pairs and builds a set out of the keys. +def itemsToSet(setbuilder): + def result(items, setbuilder=setbuilder): + return setbuilder([key for key, value in items]) + return result + +class TestWeightedII(Weighted): + from BTrees.IIBTree import weightedUnion, weightedIntersection + from BTrees.IIBTree import union, intersection + from BTrees.IIBTree import IIBucket as mkbucket + builders = IIBucket, IIBTree, itemsToSet(IISet), itemsToSet(IITreeSet) + +class TestWeightedOI(Weighted): + from BTrees.OIBTree import weightedUnion, weightedIntersection + from BTrees.OIBTree import union, intersection + from BTrees.OIBTree import OIBucket as mkbucket + builders = OIBucket, OIBTree, itemsToSet(OISet), itemsToSet(OITreeSet) + +class TestWeightedLL(Weighted): + from BTrees.LLBTree import weightedUnion, weightedIntersection + from BTrees.LLBTree import union, intersection + from BTrees.LLBTree import LLBucket as mkbucket + builders = LLBucket, LLBTree, itemsToSet(LLSet), itemsToSet(LLTreeSet) + +class TestWeightedOL(Weighted): + from BTrees.OLBTree import weightedUnion, weightedIntersection + from BTrees.OLBTree import union, intersection + from BTrees.OLBTree import OLBucket as mkbucket + builders = OLBucket, OLBTree, itemsToSet(OLSet), itemsToSet(OLTreeSet) + + +# 'thing' is a bucket, btree, set or treeset. Return true iff it's one of the +# latter two. +def isaset(thing): + return not hasattr(thing, 'values') + + +def test_suite(): + s = TestSuite() + for klass in ( + TestIIMultiUnion, TestIOMultiUnion, TestIFMultiUnion, + TestLLMultiUnion, TestLOMultiUnion, TestLFMultiUnion, + TestImports, + PureOO, + PureII, PureIO, PureIF, PureOI, + PureLL, PureLO, PureLF, PureOL, + TestWeightedII, TestWeightedOI, + TestWeightedLL, TestWeightedOL, + ): + s.addTest(makeSuite(klass)) + return s + +def main(): + TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/BTrees/TreeSetTemplate.c zope3-3.5~bzr18/src/BTrees/TreeSetTemplate.c --- zope3-3.4.0/src/BTrees/TreeSetTemplate.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/BTrees/TreeSetTemplate.c 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,247 @@ +/***************************************************************************** + + Copyright (c) 2001, 2002 Zope Corporation 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 + + ****************************************************************************/ + +#define TREESETTEMPLATE_C "$Id: TreeSetTemplate.c 103321 2009-08-27 20:46:38Z jim $\n" + +static PyObject * +TreeSet_insert(BTree *self, PyObject *args) +{ + PyObject *key; + int i; + + if (!PyArg_ParseTuple(args, "O:insert", &key)) + return NULL; + i = _BTree_set(self, key, Py_None, 1, 1); + if (i < 0) + return NULL; + return PyInt_FromLong(i); +} + +/* _Set_update and _TreeSet_update are identical except for the + function they call to add the element to the set. +*/ + +static int +_TreeSet_update(BTree *self, PyObject *seq) +{ + int n = -1; + PyObject *iter, *v; + int ind; + + iter = PyObject_GetIter(seq); + if (iter == NULL) + return -1; + + while (1) { + v = PyIter_Next(iter); + if (v == NULL) { + if (PyErr_Occurred()) + goto err; + else + break; + } + ind = _BTree_set(self, v, Py_None, 1, 1); + Py_DECREF(v); + if (ind < 0) + goto err; + else + n += ind; + } + /* n starts out at -1, which is the error return value. If + this point is reached, then there is no error. n must be + incremented to account for the initial value of -1 instead of + 0. + */ + n++; + + err: + Py_DECREF(iter); + return n; +} + +static PyObject * +TreeSet_update(BTree *self, PyObject *args) +{ + PyObject *seq = NULL; + int n = 0; + + if (!PyArg_ParseTuple(args, "|O:update", &seq)) + return NULL; + + if (seq) { + n = _TreeSet_update(self, seq); + if (n < 0) + return NULL; + } + + return PyInt_FromLong(n); +} + + +static PyObject * +TreeSet_remove(BTree *self, PyObject *args) +{ + PyObject *key; + + UNLESS (PyArg_ParseTuple(args, "O", &key)) return NULL; + if (_BTree_set(self, key, NULL, 0, 1) < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +TreeSet_setstate(BTree *self, PyObject *args) +{ + int r; + + if (!PyArg_ParseTuple(args,"O",&args)) return NULL; + + PER_PREVENT_DEACTIVATION(self); + r=_BTree_setstate(self, args, 1); + PER_UNUSE(self); + + if (r < 0) return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef TreeSet_methods[] = { + {"__getstate__", (PyCFunction) BTree_getstate, METH_NOARGS, + "__getstate__() -> state\n\n" + "Return the picklable state of the TreeSet."}, + + {"__setstate__", (PyCFunction) TreeSet_setstate, METH_VARARGS, + "__setstate__(state)\n\n" + "Set the state of the TreeSet."}, + + {"has_key", (PyCFunction) BTree_has_key, METH_O, + "has_key(key)\n\n" + "Return true if the TreeSet contains the given key."}, + + {"keys", (PyCFunction) BTree_keys, METH_KEYWORDS, + "keys([min, max]) -> list of keys\n\n" + "Returns the keys of the TreeSet. If min and max are supplied, only\n" + "keys greater than min and less than max are returned."}, + + {"maxKey", (PyCFunction) BTree_maxKey, METH_VARARGS, + "maxKey([max]) -> key\n\n" + "Return the largest key in the BTree. If max is specified, return\n" + "the largest key <= max."}, + + {"minKey", (PyCFunction) BTree_minKey, METH_VARARGS, + "minKey([mi]) -> key\n\n" + "Return the smallest key in the BTree. If min is specified, return\n" + "the smallest key >= min."}, + + {"clear", (PyCFunction) BTree_clear, METH_NOARGS, + "clear()\n\nRemove all of the items from the BTree."}, + + {"add", (PyCFunction)TreeSet_insert, METH_VARARGS, + "add(id) -- Add an item to the set"}, + + {"insert", (PyCFunction)TreeSet_insert, METH_VARARGS, + "insert(id) -- Add an item to the set"}, + + {"update", (PyCFunction)TreeSet_update, METH_VARARGS, + "update(collection)\n\n Add the items from the given collection."}, + + {"remove", (PyCFunction)TreeSet_remove, METH_VARARGS, + "remove(id) -- Remove a key from the set"}, + + {"_check", (PyCFunction) BTree_check, METH_NOARGS, + "Perform sanity check on TreeSet, and raise exception if flawed."}, + +#ifdef PERSISTENT + {"_p_resolveConflict", (PyCFunction) BTree__p_resolveConflict, METH_VARARGS, + "_p_resolveConflict() -- Reinitialize from a newly created copy"}, + + {"_p_deactivate", (PyCFunction) BTree__p_deactivate, METH_KEYWORDS, + "_p_deactivate()\n\nReinitialize from a newly created copy."}, +#endif + {NULL, NULL} /* sentinel */ +}; + +static PyMappingMethods TreeSet_as_mapping = { + (lenfunc)BTree_length, /*mp_length*/ +}; + +static PySequenceMethods TreeSet_as_sequence = { + (lenfunc)0, /* sq_length */ + (binaryfunc)0, /* sq_concat */ + (ssizeargfunc)0, /* sq_repeat */ + (ssizeargfunc)0, /* sq_item */ + (ssizessizeargfunc)0, /* sq_slice */ + (ssizeobjargproc)0, /* sq_ass_item */ + (ssizessizeobjargproc)0, /* sq_ass_slice */ + (objobjproc)BTree_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static int +TreeSet_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *v = NULL; + + if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "TreeSet", &v)) + return -1; + + if (v) + return _TreeSet_update((BTree *)self, v); + else + return 0; +} + +static PyTypeObject TreeSetType = { + PyObject_HEAD_INIT(NULL) /* PyPersist_Type */ + 0, /* ob_size */ + MODULE_NAME MOD_NAME_PREFIX "TreeSet",/* tp_name */ + sizeof(BTree), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)BTree_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + &BTree_as_number_for_nonzero, /* tp_as_number */ + &TreeSet_as_sequence, /* tp_as_sequence */ + &TreeSet_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)BTree_traverse, /* tp_traverse */ + (inquiry)BTree_tp_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)BTree_getiter, /* tp_iter */ + 0, /* tp_iternext */ + TreeSet_methods, /* tp_methods */ + BTree_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + TreeSet_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /*PyType_GenericNew,*/ /* tp_new */ +}; diff -Nru zope3-3.4.0/src/docutils/_compat.py zope3-3.5~bzr18/src/docutils/_compat.py --- zope3-3.4.0/src/docutils/_compat.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/_compat.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,36 @@ +# $Id: _compat.py 5908 2009-04-21 13:43:23Z goodger $ +# Author: Georg Brandl +# Copyright: This module has been placed in the public domain. + +""" +Python 2/3 compatibility definitions. + +This module currently provides the following helper symbols: + +* bytes (name of byte string type; str in 2.x, bytes in 3.x) +* b (function converting a string literal to an ASCII byte string; + can be also used to convert a Unicode string into a byte string) +* u_prefix (unicode repr prefix, 'u' in 2.x, nothing in 3.x) +* BytesIO (a StringIO class that works with bytestrings) +""" + +import sys + +if sys.version_info < (3,0): + b = bytes = str + u_prefix = 'u' + from StringIO import StringIO as BytesIO +else: + import builtins + bytes = builtins.bytes + u_prefix = '' + def b(s): + if isinstance(s, str): + return s.encode('latin1') + elif isinstance(s, bytes): + return s + else: + raise TypeError("Invalid argument %r for b()" % (s,)) + # using this hack since 2to3 "fixes" the relative import + # when using ``from io import BytesIO`` + BytesIO = __import__('io').BytesIO diff -Nru zope3-3.4.0/src/docutils/core.py zope3-3.5~bzr18/src/docutils/core.py --- zope3-3.4.0/src/docutils/core.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/core.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,647 @@ +# $Id: core.py 6119 2009-09-09 09:21:59Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Calling the ``publish_*`` convenience functions (or instantiating a +`Publisher` object) with component names will result in default +behavior. For custom behavior (setting component options), create +custom component objects first, and pass *them* to +``publish_*``/`Publisher`. See `The Docutils Publisher`_. + +.. _The Docutils Publisher: http://docutils.sf.net/docs/api/publisher.html +""" + +__docformat__ = 'reStructuredText' + +import sys +import pprint +from docutils import __version__, __version_details__, SettingsSpec +from docutils import frontend, io, utils, readers, writers +from docutils.frontend import OptionParser +from docutils.transforms import Transformer +import docutils.readers.doctree + + +class Publisher: + + """ + A facade encapsulating the high-level logic of a Docutils system. + """ + + def __init__(self, reader=None, parser=None, writer=None, + source=None, source_class=io.FileInput, + destination=None, destination_class=io.FileOutput, + settings=None): + """ + Initial setup. If any of `reader`, `parser`, or `writer` are not + specified, the corresponding ``set_...`` method should be called with + a component name (`set_reader` sets the parser as well). + """ + + self.document = None + """The document tree (`docutils.nodes` objects).""" + + self.reader = reader + """A `docutils.readers.Reader` instance.""" + + self.parser = parser + """A `docutils.parsers.Parser` instance.""" + + self.writer = writer + """A `docutils.writers.Writer` instance.""" + + for component in 'reader', 'parser', 'writer': + assert not isinstance(getattr(self, component), str), ( + 'passed string "%s" as "%s" parameter; pass an instance, ' + 'or use the "%s_name" parameter instead (in ' + 'docutils.core.publish_* convenience functions).' + % (getattr(self, component), component, component)) + + self.source = source + """The source of input data, a `docutils.io.Input` instance.""" + + self.source_class = source_class + """The class for dynamically created source objects.""" + + self.destination = destination + """The destination for docutils output, a `docutils.io.Output` + instance.""" + + self.destination_class = destination_class + """The class for dynamically created destination objects.""" + + self.settings = settings + """An object containing Docutils settings as instance attributes. + Set by `self.process_command_line()` or `self.get_settings()`.""" + + def set_reader(self, reader_name, parser, parser_name): + """Set `self.reader` by name.""" + reader_class = readers.get_reader_class(reader_name) + self.reader = reader_class(parser, parser_name) + self.parser = self.reader.parser + + def set_writer(self, writer_name): + """Set `self.writer` by name.""" + writer_class = writers.get_writer_class(writer_name) + self.writer = writer_class() + + def set_components(self, reader_name, parser_name, writer_name): + if self.reader is None: + self.set_reader(reader_name, self.parser, parser_name) + if self.parser is None: + if self.reader.parser is None: + self.reader.set_parser(parser_name) + self.parser = self.reader.parser + if self.writer is None: + self.set_writer(writer_name) + + def setup_option_parser(self, usage=None, description=None, + settings_spec=None, config_section=None, + **defaults): + if config_section: + if not settings_spec: + settings_spec = SettingsSpec() + settings_spec.config_section = config_section + parts = config_section.split() + if len(parts) > 1 and parts[-1] == 'application': + settings_spec.config_section_dependencies = ['applications'] + #@@@ Add self.source & self.destination to components in future? + option_parser = OptionParser( + components=(self.parser, self.reader, self.writer, settings_spec), + defaults=defaults, read_config_files=1, + usage=usage, description=description) + return option_parser + + def get_settings(self, usage=None, description=None, + settings_spec=None, config_section=None, **defaults): + """ + Set and return default settings (overrides in `defaults` dict). + + Set components first (`self.set_reader` & `self.set_writer`). + Explicitly setting `self.settings` disables command line option + processing from `self.publish()`. + """ + option_parser = self.setup_option_parser( + usage, description, settings_spec, config_section, **defaults) + self.settings = option_parser.get_default_values() + return self.settings + + def process_programmatic_settings(self, settings_spec, + settings_overrides, + config_section): + if self.settings is None: + defaults = (settings_overrides or {}).copy() + # Propagate exceptions by default when used programmatically: + defaults.setdefault('traceback', 1) + self.get_settings(settings_spec=settings_spec, + config_section=config_section, + **defaults) + + def process_command_line(self, argv=None, usage=None, description=None, + settings_spec=None, config_section=None, + **defaults): + """ + Pass an empty list to `argv` to avoid reading `sys.argv` (the + default). + + Set components first (`self.set_reader` & `self.set_writer`). + """ + option_parser = self.setup_option_parser( + usage, description, settings_spec, config_section, **defaults) + if argv is None: + argv = sys.argv[1:] + self.settings = option_parser.parse_args(argv) + + def set_io(self, source_path=None, destination_path=None): + if self.source is None: + self.set_source(source_path=source_path) + if self.destination is None: + self.set_destination(destination_path=destination_path) + + def set_source(self, source=None, source_path=None): + if source_path is None: + source_path = self.settings._source + else: + self.settings._source = source_path + self.source = self.source_class( + source=source, source_path=source_path, + encoding=self.settings.input_encoding) + + def set_destination(self, destination=None, destination_path=None): + if destination_path is None: + destination_path = self.settings._destination + else: + self.settings._destination = destination_path + self.destination = self.destination_class( + destination=destination, destination_path=destination_path, + encoding=self.settings.output_encoding, + error_handler=self.settings.output_encoding_error_handler) + + def apply_transforms(self): + self.document.transformer.populate_from_components( + (self.source, self.reader, self.reader.parser, self.writer, + self.destination)) + self.document.transformer.apply_transforms() + + def publish(self, argv=None, usage=None, description=None, + settings_spec=None, settings_overrides=None, + config_section=None, enable_exit_status=None): + """ + Process command line options and arguments (if `self.settings` not + already set), run `self.reader` and then `self.writer`. Return + `self.writer`'s output. + """ + exit = None + try: + if self.settings is None: + self.process_command_line( + argv, usage, description, settings_spec, config_section, + **(settings_overrides or {})) + self.set_io() + self.document = self.reader.read(self.source, self.parser, + self.settings) + self.apply_transforms() + output = self.writer.write(self.document, self.destination) + self.writer.assemble_parts() + except SystemExit, error: + exit = 1 + exit_status = error.code + except Exception, error: + if not self.settings: # exception too early to report nicely + raise + if self.settings.traceback: # Propagate exceptions? + self.debugging_dumps() + raise + self.report_Exception(error) + exit = 1 + exit_status = 1 + self.debugging_dumps() + if (enable_exit_status and self.document + and (self.document.reporter.max_level + >= self.settings.exit_status_level)): + sys.exit(self.document.reporter.max_level + 10) + elif exit: + sys.exit(exit_status) + return output + + def debugging_dumps(self): + if not self.document: + return + if self.settings.dump_settings: + print >>sys.stderr, '\n::: Runtime settings:' + print >>sys.stderr, pprint.pformat(self.settings.__dict__) + if self.settings.dump_internals: + print >>sys.stderr, '\n::: Document internals:' + print >>sys.stderr, pprint.pformat(self.document.__dict__) + if self.settings.dump_transforms: + print >>sys.stderr, '\n::: Transforms applied:' + print >>sys.stderr, (' (priority, transform class, ' + 'pending node details, keyword args)') + print >>sys.stderr, pprint.pformat( + [(priority, '%s.%s' % (xclass.__module__, xclass.__name__), + pending and pending.details, kwargs) + for priority, xclass, pending, kwargs + in self.document.transformer.applied]) + if self.settings.dump_pseudo_xml: + print >>sys.stderr, '\n::: Pseudo-XML:' + print >>sys.stderr, self.document.pformat().encode( + 'raw_unicode_escape') + + def report_Exception(self, error): + if isinstance(error, utils.SystemMessage): + self.report_SystemMessage(error) + elif isinstance(error, UnicodeEncodeError): + self.report_UnicodeError(error) + else: + print >>sys.stderr, '%s: %s' % (error.__class__.__name__, error) + print >>sys.stderr, ("""\ +Exiting due to error. Use "--traceback" to diagnose. +Please report errors to . +Include "--traceback" output, Docutils version (%s [%s]), +Python version (%s), your OS type & version, and the +command line used.""" % (__version__, __version_details__, + sys.version.split()[0])) + + def report_SystemMessage(self, error): + print >>sys.stderr, ('Exiting due to level-%s (%s) system message.' + % (error.level, + utils.Reporter.levels[error.level])) + + def report_UnicodeError(self, error): + sys.stderr.write( + '%s: %s\n' + '\n' + 'The specified output encoding (%s) cannot\n' + 'handle all of the output.\n' + 'Try setting "--output-encoding-error-handler" to\n' + '\n' + '* "xmlcharrefreplace" (for HTML & XML output);\n' + % (error.__class__.__name__, error, + self.settings.output_encoding)) + try: + data = error.object[error.start:error.end] + sys.stderr.write( + ' the output will contain "%s" and should be usable.\n' + '* "backslashreplace" (for other output formats, Python 2.3+);\n' + ' look for "%s" in the output.\n' + % (data.encode('ascii', 'xmlcharrefreplace'), + data.encode('ascii', 'backslashreplace'))) + except AttributeError: + sys.stderr.write(' the output should be usable as-is.\n') + sys.stderr.write( + '* "replace"; look for "?" in the output.\n' + '\n' + '"--output-encoding-error-handler" is currently set to "%s".\n' + '\n' + 'Exiting due to error. Use "--traceback" to diagnose.\n' + 'If the advice above doesn\'t eliminate the error,\n' + 'please report it to .\n' + 'Include "--traceback" output, Docutils version (%s),\n' + 'Python version (%s), your OS type & version, and the\n' + 'command line used.\n' + % (self.settings.output_encoding_error_handler, + __version__, sys.version.split()[0])) + +default_usage = '%prog [options] [ []]' +default_description = ('Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +def publish_cmdline(reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=1, argv=None, + usage=default_usage, description=default_description): + """ + Set up & run a `Publisher` for command-line-based file I/O (input and + output file paths taken automatically from the command line). Return the + encoded string output also. + + Parameters: see `publish_programmatically` for the remainder. + + - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``. + - `usage`: Usage string, output if there's a problem parsing the command + line. + - `description`: Program description, output for the "--help" option + (along with command-line option descriptions). + """ + pub = Publisher(reader, parser, writer, settings=settings) + pub.set_components(reader_name, parser_name, writer_name) + output = pub.publish( + argv, usage, description, settings_spec, settings_overrides, + config_section=config_section, enable_exit_status=enable_exit_status) + return output + +def publish_file(source=None, source_path=None, + destination=None, destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, settings_overrides=None, + config_section=None, enable_exit_status=None): + """ + Set up & run a `Publisher` for programmatic use with file-like I/O. + Return the encoded string output also. + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source_class=io.FileInput, source=source, source_path=source_path, + destination_class=io.FileOutput, + destination=destination, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return output + +def publish_string(source, source_path=None, destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=None): + """ + Set up & run a `Publisher` for programmatic use with string I/O. Return + the encoded string or Unicode string output. + + For encoded string output, be sure to set the 'output_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + output. Here's one way:: + + publish_string(..., settings_overrides={'output_encoding': 'unicode'}) + + Similarly for Unicode string input (`source`):: + + publish_string(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source_class=io.StringInput, source=source, source_path=source_path, + destination_class=io.StringOutput, + destination=None, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return output + +def publish_parts(source, source_path=None, source_class=io.StringInput, + destination_path=None, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=None): + """ + Set up & run a `Publisher`, and return a dictionary of document parts. + Dictionary keys are the names of parts, and values are Unicode strings; + encoding is up to the client. For programmatic use with string I/O. + + For encoded string input, be sure to set the 'input_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + input. Here's how:: + + publish_parts(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + output, pub = publish_programmatically( + source=source, source_path=source_path, source_class=source_class, + destination_class=io.StringOutput, + destination=None, destination_path=destination_path, + reader=reader, reader_name=reader_name, + parser=parser, parser_name=parser_name, + writer=writer, writer_name=writer_name, + settings=settings, settings_spec=settings_spec, + settings_overrides=settings_overrides, + config_section=config_section, + enable_exit_status=enable_exit_status) + return pub.writer.parts + +def publish_doctree(source, source_path=None, + source_class=io.StringInput, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=None): + """ + Set up & run a `Publisher` for programmatic use with string I/O. + Return the document tree. + + For encoded string input, be sure to set the 'input_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + input. Here's one way:: + + publish_doctree(..., settings_overrides={'input_encoding': 'unicode'}) + + Parameters: see `publish_programmatically`. + """ + pub = Publisher(reader=reader, parser=parser, writer=None, + settings=settings, + source_class=source_class, + destination_class=io.NullOutput) + pub.set_components(reader_name, parser_name, 'null') + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_source(source, source_path) + pub.set_destination(None, None) + output = pub.publish(enable_exit_status=enable_exit_status) + return pub.document + +def publish_from_doctree(document, destination_path=None, + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=None): + """ + Set up & run a `Publisher` to render from an existing document + tree data structure, for programmatic use with string I/O. Return + the encoded string output. + + Note that document.settings is overridden; if you want to use the settings + of the original `document`, pass settings=document.settings. + + Also, new document.transformer and document.reporter objects are + generated. + + For encoded string output, be sure to set the 'output_encoding' setting to + the desired encoding. Set it to 'unicode' for unencoded Unicode string + output. Here's one way:: + + publish_from_doctree( + ..., settings_overrides={'output_encoding': 'unicode'}) + + Parameters: `document` is a `docutils.nodes.document` object, an existing + document tree. + + Other parameters: see `publish_programmatically`. + """ + reader = docutils.readers.doctree.Reader(parser_name='null') + pub = Publisher(reader, None, writer, + source=io.DocTreeInput(document), + destination_class=io.StringOutput, settings=settings) + if not writer and writer_name: + pub.set_writer(writer_name) + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_destination(None, destination_path) + return pub.publish(enable_exit_status=enable_exit_status) + +def publish_cmdline_to_binary(reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='pseudoxml', + settings=None, settings_spec=None, + settings_overrides=None, config_section=None, + enable_exit_status=1, argv=None, + usage=default_usage, description=default_description, + destination=None, destination_class=io.BinaryFileOutput + ): + """ + Set up & run a `Publisher` for command-line-based file I/O (input and + output file paths taken automatically from the command line). Return the + encoded string output also. + + This is just like publish_cmdline, except that it uses + io.BinaryFileOutput instead of io.FileOutput. + + Parameters: see `publish_programmatically` for the remainder. + + - `argv`: Command-line argument list to use instead of ``sys.argv[1:]``. + - `usage`: Usage string, output if there's a problem parsing the command + line. + - `description`: Program description, output for the "--help" option + (along with command-line option descriptions). + """ + pub = Publisher(reader, parser, writer, settings=settings, + destination_class=destination_class) + pub.set_components(reader_name, parser_name, writer_name) + output = pub.publish( + argv, usage, description, settings_spec, settings_overrides, + config_section=config_section, enable_exit_status=enable_exit_status) + return output + +def publish_programmatically(source_class, source, source_path, + destination_class, destination, destination_path, + reader, reader_name, + parser, parser_name, + writer, writer_name, + settings, settings_spec, + settings_overrides, config_section, + enable_exit_status): + """ + Set up & run a `Publisher` for custom programmatic use. Return the + encoded string output and the Publisher object. + + Applications should not need to call this function directly. If it does + seem to be necessary to call this function directly, please write to the + Docutils-develop mailing list + . + + Parameters: + + * `source_class` **required**: The class for dynamically created source + objects. Typically `io.FileInput` or `io.StringInput`. + + * `source`: Type depends on `source_class`: + + - If `source_class` is `io.FileInput`: Either a file-like object + (must have 'read' and 'close' methods), or ``None`` + (`source_path` is opened). If neither `source` nor + `source_path` are supplied, `sys.stdin` is used. + + - If `source_class` is `io.StringInput` **required**: The input + string, either an encoded 8-bit string (set the + 'input_encoding' setting to the correct encoding) or a Unicode + string (set the 'input_encoding' setting to 'unicode'). + + * `source_path`: Type depends on `source_class`: + + - `io.FileInput`: Path to the input file, opened if no `source` + supplied. + + - `io.StringInput`: Optional. Path to the file or object that produced + `source`. Only used for diagnostic output. + + * `destination_class` **required**: The class for dynamically created + destination objects. Typically `io.FileOutput` or `io.StringOutput`. + + * `destination`: Type depends on `destination_class`: + + - `io.FileOutput`: Either a file-like object (must have 'write' and + 'close' methods), or ``None`` (`destination_path` is opened). If + neither `destination` nor `destination_path` are supplied, + `sys.stdout` is used. + + - `io.StringOutput`: Not used; pass ``None``. + + * `destination_path`: Type depends on `destination_class`: + + - `io.FileOutput`: Path to the output file. Opened if no `destination` + supplied. + + - `io.StringOutput`: Path to the file or object which will receive the + output; optional. Used for determining relative paths (stylesheets, + source links, etc.). + + * `reader`: A `docutils.readers.Reader` object. + + * `reader_name`: Name or alias of the Reader class to be instantiated if + no `reader` supplied. + + * `parser`: A `docutils.parsers.Parser` object. + + * `parser_name`: Name or alias of the Parser class to be instantiated if + no `parser` supplied. + + * `writer`: A `docutils.writers.Writer` object. + + * `writer_name`: Name or alias of the Writer class to be instantiated if + no `writer` supplied. + + * `settings`: A runtime settings (`docutils.frontend.Values`) object, for + dotted-attribute access to runtime settings. It's the end result of the + `SettingsSpec`, config file, and option processing. If `settings` is + passed, it's assumed to be complete and no further setting/config/option + processing is done. + + * `settings_spec`: A `docutils.SettingsSpec` subclass or object. Provides + extra application-specific settings definitions independently of + components. In other words, the application becomes a component, and + its settings data is processed along with that of the other components. + Used only if no `settings` specified. + + * `settings_overrides`: A dictionary containing application-specific + settings defaults that override the defaults of other components. + Used only if no `settings` specified. + + * `config_section`: A string, the name of the configuration file section + for this application. Overrides the ``config_section`` attribute + defined by `settings_spec`. Used only if no `settings` specified. + + * `enable_exit_status`: Boolean; enable exit status at end of processing? + """ + pub = Publisher(reader, parser, writer, settings=settings, + source_class=source_class, + destination_class=destination_class) + pub.set_components(reader_name, parser_name, writer_name) + pub.process_programmatic_settings( + settings_spec, settings_overrides, config_section) + pub.set_source(source, source_path) + pub.set_destination(destination, destination_path) + output = pub.publish(enable_exit_status=enable_exit_status) + return output, pub diff -Nru zope3-3.4.0/src/docutils/examples.py zope3-3.5~bzr18/src/docutils/examples.py --- zope3-3.4.0/src/docutils/examples.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/examples.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,96 @@ +# $Id: examples.py 4800 2006-11-12 18:02:01Z goodger $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This module contains practical examples of Docutils client code. + +Importing this module from client code is not recommended; its contents are +subject to change in future Docutils releases. Instead, it is recommended +that you copy and paste the parts you need into your own code, modifying as +necessary. +""" + +from docutils import core, io + + +def html_parts(input_string, source_path=None, destination_path=None, + input_encoding='unicode', doctitle=1, initial_header_level=1): + """ + Given an input string, returns a dictionary of HTML document parts. + + Dictionary keys are the names of parts, and values are Unicode strings; + encoding is up to the client. + + Parameters: + + - `input_string`: A multi-line text string; required. + - `source_path`: Path to the source file or object. Optional, but useful + for diagnostic output (system messages). + - `destination_path`: Path to the file or object which will receive the + output; optional. Used for determining relative paths (stylesheets, + source links, etc.). + - `input_encoding`: The encoding of `input_string`. If it is an encoded + 8-bit string, provide the correct encoding. If it is a Unicode string, + use "unicode", the default. + - `doctitle`: Disable the promotion of a lone top-level section title to + document title (and subsequent section title to document subtitle + promotion); enabled by default. + - `initial_header_level`: The initial level for header elements (e.g. 1 + for "

"). + """ + overrides = {'input_encoding': input_encoding, + 'doctitle_xform': doctitle, + 'initial_header_level': initial_header_level} + parts = core.publish_parts( + source=input_string, source_path=source_path, + destination_path=destination_path, + writer_name='html', settings_overrides=overrides) + return parts + +def html_body(input_string, source_path=None, destination_path=None, + input_encoding='unicode', output_encoding='unicode', + doctitle=1, initial_header_level=1): + """ + Given an input string, returns an HTML fragment as a string. + + The return value is the contents of the element. + + Parameters (see `html_parts()` for the remainder): + + - `output_encoding`: The desired encoding of the output. If a Unicode + string is desired, use the default value of "unicode" . + """ + parts = html_parts( + input_string=input_string, source_path=source_path, + destination_path=destination_path, + input_encoding=input_encoding, doctitle=doctitle, + initial_header_level=initial_header_level) + fragment = parts['html_body'] + if output_encoding != 'unicode': + fragment = fragment.encode(output_encoding) + return fragment + +def internals(input_string, source_path=None, destination_path=None, + input_encoding='unicode', settings_overrides=None): + """ + Return the document tree and publisher, for exploring Docutils internals. + + Parameters: see `html_parts()`. + """ + if settings_overrides: + overrides = settings_overrides.copy() + else: + overrides = {} + overrides['input_encoding'] = input_encoding + output, pub = core.publish_programmatically( + source_class=io.StringInput, source=input_string, + source_path=source_path, + destination_class=io.NullOutput, destination=None, + destination_path=destination_path, + reader=None, reader_name='standalone', + parser=None, parser_name='restructuredtext', + writer=None, writer_name='null', + settings=None, settings_spec=None, settings_overrides=overrides, + config_section=None, enable_exit_status=None) + return pub.writer.document, pub diff -Nru zope3-3.4.0/src/docutils/frontend.py zope3-3.5~bzr18/src/docutils/frontend.py --- zope3-3.4.0/src/docutils/frontend.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/frontend.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,761 @@ +# $Id: frontend.py 6154 2009-10-05 19:08:10Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Command-line and common processing for Docutils front-end tools. + +Exports the following classes: + +* `OptionParser`: Standard Docutils command-line processing. +* `Option`: Customized version of `optparse.Option`; validation support. +* `Values`: Runtime settings; objects are simple structs + (``object.attribute``). Supports cumulative list settings (attributes). +* `ConfigParser`: Standard Docutils config file processing. + +Also exports the following functions: + +* Option callbacks: `store_multiple`, `read_config_file`. +* Setting validators: `validate_encoding`, + `validate_encoding_error_handler`, + `validate_encoding_and_error_handler`, `validate_boolean`, + `validate_threshold`, `validate_colon_separated_string_list`, + `validate_dependency_file`. +* `make_paths_absolute`. +""" + +__docformat__ = 'reStructuredText' + +import os +import os.path +import sys +import warnings +import ConfigParser as CP +import codecs +import docutils +import docutils.utils +import docutils.nodes +import optparse +from optparse import SUPPRESS_HELP + + +def store_multiple(option, opt, value, parser, *args, **kwargs): + """ + Store multiple values in `parser.values`. (Option callback.) + + Store `None` for each attribute named in `args`, and store the value for + each key (attribute name) in `kwargs`. + """ + for attribute in args: + setattr(parser.values, attribute, None) + for key, value in kwargs.items(): + setattr(parser.values, key, value) + +def read_config_file(option, opt, value, parser): + """ + Read a configuration file during option processing. (Option callback.) + """ + try: + new_settings = parser.get_config_file_settings(value) + except ValueError, error: + parser.error(error) + parser.values.update(new_settings, parser) + +def validate_encoding(setting, value, option_parser, + config_parser=None, config_section=None): + try: + codecs.lookup(value) + except LookupError: + raise (LookupError('setting "%s": unknown encoding: "%s"' + % (setting, value)), + None, sys.exc_info()[2]) + return value + +def validate_encoding_error_handler(setting, value, option_parser, + config_parser=None, config_section=None): + try: + codecs.lookup_error(value) + except AttributeError: # TODO: remove (only needed prior to Python 2.3) + if value not in ('strict', 'ignore', 'replace', 'xmlcharrefreplace'): + raise (LookupError( + 'unknown encoding error handler: "%s" (choices: ' + '"strict", "ignore", "replace", or "xmlcharrefreplace")' % value), + None, sys.exc_info()[2]) + except LookupError: + raise (LookupError( + 'unknown encoding error handler: "%s" (choices: ' + '"strict", "ignore", "replace", "backslashreplace", ' + '"xmlcharrefreplace", and possibly others; see documentation for ' + 'the Python ``codecs`` module)' % value), + None, sys.exc_info()[2]) + return value + +def validate_encoding_and_error_handler( + setting, value, option_parser, config_parser=None, config_section=None): + """ + Side-effect: if an error handler is included in the value, it is inserted + into the appropriate place as if it was a separate setting/option. + """ + if ':' in value: + encoding, handler = value.split(':') + validate_encoding_error_handler( + setting + '_error_handler', handler, option_parser, + config_parser, config_section) + if config_parser: + config_parser.set(config_section, setting + '_error_handler', + handler) + else: + setattr(option_parser.values, setting + '_error_handler', handler) + else: + encoding = value + validate_encoding(setting, encoding, option_parser, + config_parser, config_section) + return encoding + +def validate_boolean(setting, value, option_parser, + config_parser=None, config_section=None): + if isinstance(value, unicode): + try: + return option_parser.booleans[value.strip().lower()] + except KeyError: + raise (LookupError('unknown boolean value: "%s"' % value), + None, sys.exc_info()[2]) + return value + +def validate_nonnegative_int(setting, value, option_parser, + config_parser=None, config_section=None): + value = int(value) + if value < 0: + raise ValueError('negative value; must be positive or zero') + return value + +def validate_threshold(setting, value, option_parser, + config_parser=None, config_section=None): + try: + return int(value) + except ValueError: + try: + return option_parser.thresholds[value.lower()] + except (KeyError, AttributeError): + raise (LookupError('unknown threshold: %r.' % value), + None, sys.exc_info[2]) + +def validate_colon_separated_string_list( + setting, value, option_parser, config_parser=None, config_section=None): + if isinstance(value, unicode): + value = value.split(':') + else: + last = value.pop() + value.extend(last.split(':')) + return value + +def validate_url_trailing_slash( + setting, value, option_parser, config_parser=None, config_section=None): + if not value: + return './' + elif value.endswith('/'): + return value + else: + return value + '/' + +def validate_dependency_file(setting, value, option_parser, + config_parser=None, config_section=None): + try: + return docutils.utils.DependencyList(value) + except IOError: + return docutils.utils.DependencyList(None) + +def validate_strip_class(setting, value, option_parser, + config_parser=None, config_section=None): + if config_parser: # validate all values + class_values = value + else: # just validate the latest value + class_values = [value[-1]] + for class_value in class_values: + normalized = docutils.nodes.make_id(class_value) + if class_value != normalized: + raise ValueError('invalid class value %r (perhaps %r?)' + % (class_value, normalized)) + return value + +def make_paths_absolute(pathdict, keys, base_path=None): + """ + Interpret filesystem path settings relative to the `base_path` given. + + Paths are values in `pathdict` whose keys are in `keys`. Get `keys` from + `OptionParser.relative_path_settings`. + """ + if base_path is None: + base_path = os.getcwd() + for key in keys: + if key in pathdict: + value = pathdict[key] + if isinstance(value, list): + value = [make_one_path_absolute(base_path, path) + for path in value] + elif value: + value = make_one_path_absolute(base_path, value) + pathdict[key] = value + +def make_one_path_absolute(base_path, path): + return os.path.abspath(os.path.join(base_path, path)) + + +class Values(optparse.Values): + + """ + Updates list attributes by extension rather than by replacement. + Works in conjunction with the `OptionParser.lists` instance attribute. + """ + + def __init__(self, *args, **kwargs): + optparse.Values.__init__(self, *args, **kwargs) + if (not hasattr(self, 'record_dependencies') + or self.record_dependencies is None): + # Set up dependency list, in case it is needed. + self.record_dependencies = docutils.utils.DependencyList() + + def update(self, other_dict, option_parser): + if isinstance(other_dict, Values): + other_dict = other_dict.__dict__ + other_dict = other_dict.copy() + for setting in option_parser.lists.keys(): + if (hasattr(self, setting) and setting in other_dict): + value = getattr(self, setting) + if value: + value += other_dict[setting] + del other_dict[setting] + self._update_loose(other_dict) + + def copy(self): + """Return a shallow copy of `self`.""" + return self.__class__(defaults=self.__dict__) + + +class Option(optparse.Option): + + ATTRS = optparse.Option.ATTRS + ['validator', 'overrides'] + + def process(self, opt, value, values, parser): + """ + Call the validator function on applicable settings and + evaluate the 'overrides' option. + Extends `optparse.Option.process`. + """ + result = optparse.Option.process(self, opt, value, values, parser) + setting = self.dest + if setting: + if self.validator: + value = getattr(values, setting) + try: + new_value = self.validator(setting, value, parser) + except Exception, error: + raise (optparse.OptionValueError( + 'Error in option "%s":\n %s: %s' + % (opt, error.__class__.__name__, error)), + None, sys.exc_info()[2]) + setattr(values, setting, new_value) + if self.overrides: + setattr(values, self.overrides, None) + return result + + +class OptionParser(optparse.OptionParser, docutils.SettingsSpec): + + """ + Parser for command-line and library use. The `settings_spec` + specification here and in other Docutils components are merged to build + the set of command-line options and runtime settings for this process. + + Common settings (defined below) and component-specific settings must not + conflict. Short options are reserved for common settings, and components + are restrict to using long options. + """ + + standard_config_files = [ + '/etc/docutils.conf', # system-wide + './docutils.conf', # project-specific + '~/.docutils'] # user-specific + """Docutils configuration files, using ConfigParser syntax. Filenames + will be tilde-expanded later. Later files override earlier ones.""" + + threshold_choices = 'info 1 warning 2 error 3 severe 4 none 5'.split() + """Possible inputs for for --report and --halt threshold values.""" + + thresholds = {'info': 1, 'warning': 2, 'error': 3, 'severe': 4, 'none': 5} + """Lookup table for --report and --halt threshold values.""" + + booleans={'1': 1, 'on': 1, 'yes': 1, 'true': 1, + '0': 0, 'off': 0, 'no': 0, 'false': 0, '': 0} + """Lookup table for boolean configuration file settings.""" + + try: + default_error_encoding = sys.stderr.encoding or 'ascii' + except AttributeError: + default_error_encoding = 'ascii' + + # TODO: variable no longer needed since 'backslashreplace' is + # part of Python >= 2.3 (required since Docutils 0.6) + if hasattr(codecs, 'backslashreplace_errors'): + default_error_encoding_error_handler = 'backslashreplace' + else: + default_error_encoding_error_handler = 'replace' + + settings_spec = ( + 'General Docutils Options', + None, + (('Specify the document title as metadata.', + ['--title'], {}), + ('Include a "Generated by Docutils" credit and link.', + ['--generator', '-g'], {'action': 'store_true', + 'validator': validate_boolean}), + ('Do not include a generator credit.', + ['--no-generator'], {'action': 'store_false', 'dest': 'generator'}), + ('Include the date at the end of the document (UTC).', + ['--date', '-d'], {'action': 'store_const', 'const': '%Y-%m-%d', + 'dest': 'datestamp'}), + ('Include the time & date (UTC).', + ['--time', '-t'], {'action': 'store_const', + 'const': '%Y-%m-%d %H:%M UTC', + 'dest': 'datestamp'}), + ('Do not include a datestamp of any kind.', + ['--no-datestamp'], {'action': 'store_const', 'const': None, + 'dest': 'datestamp'}), + ('Include a "View document source" link.', + ['--source-link', '-s'], {'action': 'store_true', + 'validator': validate_boolean}), + ('Use for a source link; implies --source-link.', + ['--source-url'], {'metavar': ''}), + ('Do not include a "View document source" link.', + ['--no-source-link'], + {'action': 'callback', 'callback': store_multiple, + 'callback_args': ('source_link', 'source_url')}), + ('Link from section headers to TOC entries. (default)', + ['--toc-entry-backlinks'], + {'dest': 'toc_backlinks', 'action': 'store_const', 'const': 'entry', + 'default': 'entry'}), + ('Link from section headers to the top of the TOC.', + ['--toc-top-backlinks'], + {'dest': 'toc_backlinks', 'action': 'store_const', 'const': 'top'}), + ('Disable backlinks to the table of contents.', + ['--no-toc-backlinks'], + {'dest': 'toc_backlinks', 'action': 'store_false'}), + ('Link from footnotes/citations to references. (default)', + ['--footnote-backlinks'], + {'action': 'store_true', 'default': 1, + 'validator': validate_boolean}), + ('Disable backlinks from footnotes and citations.', + ['--no-footnote-backlinks'], + {'dest': 'footnote_backlinks', 'action': 'store_false'}), + ('Enable section numbering by Docutils. (default)', + ['--section-numbering'], + {'action': 'store_true', 'dest': 'sectnum_xform', + 'default': 1, 'validator': validate_boolean}), + ('Disable section numbering by Docutils.', + ['--no-section-numbering'], + {'action': 'store_false', 'dest': 'sectnum_xform'}), + ('Remove comment elements from the document tree.', + ['--strip-comments'], + {'action': 'store_true', 'validator': validate_boolean}), + ('Leave comment elements in the document tree. (default)', + ['--leave-comments'], + {'action': 'store_false', 'dest': 'strip_comments'}), + ('Remove all elements with classes="" from the document tree. ' + 'Warning: potentially dangerous; use with caution. ' + '(Multiple-use option.)', + ['--strip-elements-with-class'], + {'action': 'append', 'dest': 'strip_elements_with_classes', + 'metavar': '', 'validator': validate_strip_class}), + ('Remove all classes="" attributes from elements in the ' + 'document tree. Warning: potentially dangerous; use with caution. ' + '(Multiple-use option.)', + ['--strip-class'], + {'action': 'append', 'dest': 'strip_classes', + 'metavar': '', 'validator': validate_strip_class}), + ('Report system messages at or higher than : "info" or "1", ' + '"warning"/"2" (default), "error"/"3", "severe"/"4", "none"/"5"', + ['--report', '-r'], {'choices': threshold_choices, 'default': 2, + 'dest': 'report_level', 'metavar': '', + 'validator': validate_threshold}), + ('Report all system messages. (Same as "--report=1".)', + ['--verbose', '-v'], {'action': 'store_const', 'const': 1, + 'dest': 'report_level'}), + ('Report no system messages. (Same as "--report=5".)', + ['--quiet', '-q'], {'action': 'store_const', 'const': 5, + 'dest': 'report_level'}), + ('Halt execution at system messages at or above . ' + 'Levels as in --report. Default: 4 (severe).', + ['--halt'], {'choices': threshold_choices, 'dest': 'halt_level', + 'default': 4, 'metavar': '', + 'validator': validate_threshold}), + ('Halt at the slightest problem. Same as "--halt=info".', + ['--strict'], {'action': 'store_const', 'const': 1, + 'dest': 'halt_level'}), + ('Enable a non-zero exit status for non-halting system messages at ' + 'or above . Default: 5 (disabled).', + ['--exit-status'], {'choices': threshold_choices, + 'dest': 'exit_status_level', + 'default': 5, 'metavar': '', + 'validator': validate_threshold}), + ('Enable debug-level system messages and diagnostics.', + ['--debug'], {'action': 'store_true', 'validator': validate_boolean}), + ('Disable debug output. (default)', + ['--no-debug'], {'action': 'store_false', 'dest': 'debug'}), + ('Send the output of system messages to .', + ['--warnings'], {'dest': 'warning_stream', 'metavar': ''}), + ('Enable Python tracebacks when Docutils is halted.', + ['--traceback'], {'action': 'store_true', 'default': None, + 'validator': validate_boolean}), + ('Disable Python tracebacks. (default)', + ['--no-traceback'], {'dest': 'traceback', 'action': 'store_false'}), + ('Specify the encoding and optionally the ' + 'error handler of input text. Default: :strict.', + ['--input-encoding', '-i'], + {'metavar': '', + 'validator': validate_encoding_and_error_handler}), + ('Specify the error handler for undecodable characters. ' + 'Choices: "strict" (default), "ignore", and "replace".', + ['--input-encoding-error-handler'], + {'default': 'strict', 'validator': validate_encoding_error_handler}), + ('Specify the text encoding and optionally the error handler for ' + 'output. Default: UTF-8:strict.', + ['--output-encoding', '-o'], + {'metavar': '', 'default': 'utf-8', + 'validator': validate_encoding_and_error_handler}), + ('Specify error handler for unencodable output characters; ' + '"strict" (default), "ignore", "replace", ' + '"xmlcharrefreplace", "backslashreplace".', + ['--output-encoding-error-handler'], + {'default': 'strict', 'validator': validate_encoding_error_handler}), + ('Specify text encoding and error handler for error output. ' + 'Default: %s:%s.' + % (default_error_encoding, default_error_encoding_error_handler), + ['--error-encoding', '-e'], + {'metavar': '', 'default': default_error_encoding, + 'validator': validate_encoding_and_error_handler}), + ('Specify the error handler for unencodable characters in ' + 'error output. Default: %s.' + % default_error_encoding_error_handler, + ['--error-encoding-error-handler'], + {'default': default_error_encoding_error_handler, + 'validator': validate_encoding_error_handler}), + ('Specify the language (as 2-letter code). Default: en.', + ['--language', '-l'], {'dest': 'language_code', 'default': 'en', + 'metavar': ''}), + ('Write output file dependencies to .', + ['--record-dependencies'], + {'metavar': '', 'validator': validate_dependency_file, + 'default': None}), # default set in Values class + ('Read configuration settings from , if it exists.', + ['--config'], {'metavar': '', 'type': 'string', + 'action': 'callback', 'callback': read_config_file}), + ("Show this program's version number and exit.", + ['--version', '-V'], {'action': 'version'}), + ('Show this help message and exit.', + ['--help', '-h'], {'action': 'help'}), + # Typically not useful for non-programmatical use: + (SUPPRESS_HELP, ['--id-prefix'], {'default': ''}), + (SUPPRESS_HELP, ['--auto-id-prefix'], {'default': 'id'}), + # Hidden options, for development use only: + (SUPPRESS_HELP, ['--dump-settings'], {'action': 'store_true'}), + (SUPPRESS_HELP, ['--dump-internals'], {'action': 'store_true'}), + (SUPPRESS_HELP, ['--dump-transforms'], {'action': 'store_true'}), + (SUPPRESS_HELP, ['--dump-pseudo-xml'], {'action': 'store_true'}), + (SUPPRESS_HELP, ['--expose-internal-attribute'], + {'action': 'append', 'dest': 'expose_internals', + 'validator': validate_colon_separated_string_list}), + (SUPPRESS_HELP, ['--strict-visitor'], {'action': 'store_true'}), + )) + """Runtime settings and command-line options common to all Docutils front + ends. Setting specs specific to individual Docutils components are also + used (see `populate_from_components()`).""" + + settings_defaults = {'_disable_config': None, + '_source': None, + '_destination': None, + '_config_files': None} + """Defaults for settings that don't have command-line option equivalents.""" + + relative_path_settings = ('warning_stream',) + + config_section = 'general' + + version_template = ('%%prog (Docutils %s [%s], Python %s, on %s)' + % (docutils.__version__, docutils.__version_details__, + sys.version.split()[0], sys.platform)) + """Default version message.""" + + def __init__(self, components=(), defaults=None, read_config_files=None, + *args, **kwargs): + """ + `components` is a list of Docutils components each containing a + ``.settings_spec`` attribute. `defaults` is a mapping of setting + default overrides. + """ + + self.lists = {} + """Set of list-type settings.""" + + self.config_files = [] + """List of paths of applied configuration files.""" + + optparse.OptionParser.__init__( + self, option_class=Option, add_help_option=None, + formatter=optparse.TitledHelpFormatter(width=78), + *args, **kwargs) + if not self.version: + self.version = self.version_template + # Make an instance copy (it will be modified): + self.relative_path_settings = list(self.relative_path_settings) + self.components = (self,) + tuple(components) + self.populate_from_components(self.components) + self.set_defaults_from_dict(defaults or {}) + if read_config_files and not self.defaults['_disable_config']: + try: + config_settings = self.get_standard_config_settings() + except ValueError, error: + self.error(error) + self.set_defaults_from_dict(config_settings.__dict__) + + def populate_from_components(self, components): + """ + For each component, first populate from the `SettingsSpec.settings_spec` + structure, then from the `SettingsSpec.settings_defaults` dictionary. + After all components have been processed, check for and populate from + each component's `SettingsSpec.settings_default_overrides` dictionary. + """ + for component in components: + if component is None: + continue + settings_spec = component.settings_spec + self.relative_path_settings.extend( + component.relative_path_settings) + for i in range(0, len(settings_spec), 3): + title, description, option_spec = settings_spec[i:i+3] + if title: + group = optparse.OptionGroup(self, title, description) + self.add_option_group(group) + else: + group = self # single options + for (help_text, option_strings, kwargs) in option_spec: + option = group.add_option(help=help_text, *option_strings, + **kwargs) + if kwargs.get('action') == 'append': + self.lists[option.dest] = 1 + if component.settings_defaults: + self.defaults.update(component.settings_defaults) + for component in components: + if component and component.settings_default_overrides: + self.defaults.update(component.settings_default_overrides) + + def get_standard_config_files(self): + """Return list of config files, from environment or standard.""" + try: + config_files = os.environ['DOCUTILSCONFIG'].split(os.pathsep) + except KeyError: + config_files = self.standard_config_files + + # If 'HOME' is not set, expandvars() requires the 'pwd' module which is + # not available under certain environments, for example, within + # mod_python. The publisher ends up in here, and we need to publish + # from within mod_python. Therefore we need to avoid expanding when we + # are in those environments. + expand = os.path.expanduser + if 'HOME' not in os.environ: + try: + import pwd + except ImportError: + expand = lambda x: x + return [expand(f) for f in config_files if f.strip()] + + def get_standard_config_settings(self): + settings = Values() + for filename in self.get_standard_config_files(): + settings.update(self.get_config_file_settings(filename), self) + return settings + + def get_config_file_settings(self, config_file): + """Returns a dictionary containing appropriate config file settings.""" + parser = ConfigParser() + parser.read(config_file, self) + self.config_files.extend(parser._files) + base_path = os.path.dirname(config_file) + applied = {} + settings = Values() + for component in self.components: + if not component: + continue + for section in (tuple(component.config_section_dependencies or ()) + + (component.config_section,)): + if section in applied: + continue + applied[section] = 1 + settings.update(parser.get_section(section), self) + make_paths_absolute( + settings.__dict__, self.relative_path_settings, base_path) + return settings.__dict__ + + def check_values(self, values, args): + """Store positional arguments as runtime settings.""" + values._source, values._destination = self.check_args(args) + make_paths_absolute(values.__dict__, self.relative_path_settings, + os.getcwd()) + values._config_files = self.config_files + return values + + def check_args(self, args): + source = destination = None + if args: + source = args.pop(0) + if source == '-': # means stdin + source = None + if args: + destination = args.pop(0) + if destination == '-': # means stdout + destination = None + if args: + self.error('Maximum 2 arguments allowed.') + if source and source == destination: + self.error('Do not specify the same file for both source and ' + 'destination. It will clobber the source file.') + return source, destination + + def set_defaults_from_dict(self, defaults): + self.defaults.update(defaults) + + def get_default_values(self): + """Needed to get custom `Values` instances.""" + defaults = Values(self.defaults) + defaults._config_files = self.config_files + return defaults + + def get_option_by_dest(self, dest): + """ + Get an option by its dest. + + If you're supplying a dest which is shared by several options, + it is undefined which option of those is returned. + + A KeyError is raised if there is no option with the supplied + dest. + """ + for group in self.option_groups + [self]: + for option in group.option_list: + if option.dest == dest: + return option + raise KeyError('No option with dest == %r.' % dest) + + +class ConfigParser(CP.ConfigParser): + + old_settings = { + 'pep_stylesheet': ('pep_html writer', 'stylesheet'), + 'pep_stylesheet_path': ('pep_html writer', 'stylesheet_path'), + 'pep_template': ('pep_html writer', 'template')} + """{old setting: (new section, new setting)} mapping, used by + `handle_old_config`, to convert settings from the old [options] section.""" + + old_warning = """ +The "[option]" section is deprecated. Support for old-format configuration +files may be removed in a future Docutils release. Please revise your +configuration files. See , +section "Old-Format Configuration Files". +""" + + not_utf8_error = """\ +Unable to read configuration file "%s": content not encoded as UTF-8. +Skipping "%s" configuration file. +""" + + def __init__(self, *args, **kwargs): + CP.ConfigParser.__init__(self, *args, **kwargs) + + self._files = [] + """List of paths of configuration files read.""" + + def read(self, filenames, option_parser): + if type(filenames) in (str, unicode): + filenames = [filenames] + for filename in filenames: + try: + # Config files must be UTF-8-encoded: + fp = codecs.open(filename, 'r', 'utf-8') + except IOError: + continue + try: + CP.ConfigParser.readfp(self, fp, filename) + except UnicodeDecodeError: + sys.stderr.write(self.not_utf8_error % (filename, filename)) + fp.close() + continue + fp.close() + self._files.append(filename) + if self.has_section('options'): + self.handle_old_config(filename) + self.validate_settings(filename, option_parser) + + def handle_old_config(self, filename): + warnings.warn_explicit(self.old_warning, ConfigDeprecationWarning, + filename, 0) + options = self.get_section('options') + if not self.has_section('general'): + self.add_section('general') + for key, value in options.items(): + if key in self.old_settings: + section, setting = self.old_settings[key] + if not self.has_section(section): + self.add_section(section) + else: + section = 'general' + setting = key + if not self.has_option(section, setting): + self.set(section, setting, value) + self.remove_section('options') + + def validate_settings(self, filename, option_parser): + """ + Call the validator function and implement overrides on all applicable + settings. + """ + for section in self.sections(): + for setting in self.options(section): + try: + option = option_parser.get_option_by_dest(setting) + except KeyError: + continue + if option.validator: + value = self.get(section, setting, raw=1) + try: + new_value = option.validator( + setting, value, option_parser, + config_parser=self, config_section=section) + except Exception, error: + raise (ValueError( + 'Error in config file "%s", section "[%s]":\n' + ' %s: %s\n %s = %s' + % (filename, section, error.__class__.__name__, + error, setting, value)), None, sys.exc_info()[2]) + self.set(section, setting, new_value) + if option.overrides: + self.set(section, option.overrides, None) + + def optionxform(self, optionstr): + """ + Transform '-' to '_' so the cmdline form of option names can be used. + """ + return optionstr.lower().replace('-', '_') + + def get_section(self, section): + """ + Return a given section as a dictionary (empty if the section + doesn't exist). + """ + section_dict = {} + if self.has_section(section): + for option in self.options(section): + section_dict[option] = self.get(section, option, raw=1) + return section_dict + + +class ConfigDeprecationWarning(DeprecationWarning): + """Warning for deprecated configuration file features.""" diff -Nru zope3-3.4.0/src/docutils/__init__.py zope3-3.5~bzr18/src/docutils/__init__.py --- zope3-3.4.0/src/docutils/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,204 @@ +# $Id: __init__.py 6164 2009-10-11 11:00:11Z grubert $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This is the Docutils (Python Documentation Utilities) package. + +Package Structure +================= + +Modules: + +- __init__.py: Contains component base classes, exception classes, and + Docutils version information. + +- core.py: Contains the ``Publisher`` class and ``publish_*()`` convenience + functions. + +- frontend.py: Runtime settings (command-line interface, configuration files) + processing, for Docutils front-ends. + +- io.py: Provides a uniform API for low-level input and output. + +- nodes.py: Docutils document tree (doctree) node class library. + +- statemachine.py: A finite state machine specialized for + regular-expression-based text filters. + +- urischemes.py: Contains a complete mapping of known URI addressing + scheme names to descriptions. + +- utils.py: Contains the ``Reporter`` system warning class and miscellaneous + utilities. + +Subpackages: + +- languages: Language-specific mappings of terms. + +- parsers: Syntax-specific input parser modules or packages. + +- readers: Context-specific input handlers which understand the data + source and manage a parser. + +- transforms: Modules used by readers and writers to modify DPS + doctrees. + +- writers: Format-specific output translators. +""" + +__docformat__ = 'reStructuredText' + +__version__ = '0.6' +"""``major.minor.micro`` version number. The micro number is bumped for API +changes, for new functionality, and for interim project releases. The minor +number is bumped whenever there is a significant project release. The major +number will be bumped when the project is feature-complete, and perhaps if +there is a major change in the design.""" + +__version_details__ = 'release' +"""Extra version details (e.g. 'snapshot 2005-05-29, r3410', 'repository', +'release'), modified automatically & manually.""" + +class ApplicationError(StandardError): pass +class DataError(ApplicationError): pass + + +class SettingsSpec: + + """ + Runtime setting specification base class. + + SettingsSpec subclass objects used by `docutils.frontend.OptionParser`. + """ + + settings_spec = () + """Runtime settings specification. Override in subclasses. + + Defines runtime settings and associated command-line options, as used by + `docutils.frontend.OptionParser`. This is a tuple of: + + - Option group title (string or `None` which implies no group, just a list + of single options). + + - Description (string or `None`). + + - A sequence of option tuples. Each consists of: + + - Help text (string) + + - List of option strings (e.g. ``['-Q', '--quux']``). + + - Dictionary of keyword arguments sent to the OptionParser/OptionGroup + ``add_option`` method. + + Runtime setting names are derived implicitly from long option names + ('--a-setting' becomes ``settings.a_setting``) or explicitly from the + 'dest' keyword argument. + + Most settings will also have a 'validator' keyword & function. The + validator function validates setting values (from configuration files + and command-line option arguments) and converts them to appropriate + types. For example, the ``docutils.frontend.validate_boolean`` + function, **required by all boolean settings**, converts true values + ('1', 'on', 'yes', and 'true') to 1 and false values ('0', 'off', + 'no', 'false', and '') to 0. Validators need only be set once per + setting. See the `docutils.frontend.validate_*` functions. + + See the optparse docs for more details. + + - More triples of group title, description, options, as many times as + needed. Thus, `settings_spec` tuples can be simply concatenated. + """ + + settings_defaults = None + """A dictionary of defaults for settings not in `settings_spec` (internal + settings, intended to be inaccessible by command-line and config file). + Override in subclasses.""" + + settings_default_overrides = None + """A dictionary of auxiliary defaults, to override defaults for settings + defined in other components. Override in subclasses.""" + + relative_path_settings = () + """Settings containing filesystem paths. Override in subclasses. + Settings listed here are to be interpreted relative to the current working + directory.""" + + config_section = None + """The name of the config file section specific to this component + (lowercase, no brackets). Override in subclasses.""" + + config_section_dependencies = None + """A list of names of config file sections that are to be applied before + `config_section`, in order (from general to specific). In other words, + the settings in `config_section` are to be overlaid on top of the settings + from these sections. The "general" section is assumed implicitly. + Override in subclasses.""" + + +class TransformSpec: + + """ + Runtime transform specification base class. + + TransformSpec subclass objects used by `docutils.transforms.Transformer`. + """ + + def get_transforms(self): + """Transforms required by this class. Override in subclasses.""" + if self.default_transforms != (): + import warnings + warnings.warn('default_transforms attribute deprecated.\n' + 'Use get_transforms() method instead.', + DeprecationWarning) + return list(self.default_transforms) + return [] + + # Deprecated; for compatibility. + default_transforms = () + + unknown_reference_resolvers = () + """List of functions to try to resolve unknown references. Unknown + references have a 'refname' attribute which doesn't correspond to any + target in the document. Called when the transforms in + `docutils.tranforms.references` are unable to find a correct target. The + list should contain functions which will try to resolve unknown + references, with the following signature:: + + def reference_resolver(node): + '''Returns boolean: true if resolved, false if not.''' + + If the function is able to resolve the reference, it should also remove + the 'refname' attribute and mark the node as resolved:: + + del node['refname'] + node.resolved = 1 + + Each function must have a "priority" attribute which will affect the order + the unknown_reference_resolvers are run:: + + reference_resolver.priority = 100 + + Override in subclasses.""" + + +class Component(SettingsSpec, TransformSpec): + + """Base class for Docutils components.""" + + component_type = None + """Name of the component type ('reader', 'parser', 'writer'). Override in + subclasses.""" + + supported = () + """Names for this component. Override in subclasses.""" + + def supports(self, format): + """ + Is `format` supported by this component? + + To be used by transforms to ask the dependent component if it supports + a certain input context or output format. + """ + return format in self.supported diff -Nru zope3-3.4.0/src/docutils/io.py zope3-3.5~bzr18/src/docutils/io.py --- zope3-3.4.0/src/docutils/io.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/io.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,422 @@ +# $Id: io.py 6125 2009-09-11 14:24:35Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +I/O classes provide a uniform API for low-level input and output. Subclasses +will exist for a variety of input/output mechanisms. +""" + +__docformat__ = 'reStructuredText' + +import sys +try: + import locale +except: + pass +import re +from docutils import TransformSpec +from docutils._compat import b + + +class Input(TransformSpec): + + """ + Abstract base class for input wrappers. + """ + + component_type = 'input' + + default_source_path = None + + def __init__(self, source=None, source_path=None, encoding=None, + error_handler='strict'): + self.encoding = encoding + """Text encoding for the input source.""" + + self.error_handler = error_handler + """Text decoding error handler.""" + + self.source = source + """The source of input data.""" + + self.source_path = source_path + """A text reference to the source.""" + + if not source_path: + self.source_path = self.default_source_path + + self.successful_encoding = None + """The encoding that successfully decoded the source data.""" + + def __repr__(self): + return '%s: source=%r, source_path=%r' % (self.__class__, self.source, + self.source_path) + + def read(self): + raise NotImplementedError + + def decode(self, data): + """ + Decode a string, `data`, heuristically. + Raise UnicodeError if unsuccessful. + + The client application should call ``locale.setlocale`` at the + beginning of processing:: + + locale.setlocale(locale.LC_ALL, '') + """ + if self.encoding and self.encoding.lower() == 'unicode': + assert isinstance(data, unicode), ( + 'input encoding is "unicode" ' + 'but input is not a unicode object') + if isinstance(data, unicode): + # Accept unicode even if self.encoding != 'unicode'. + return data + if self.encoding: + # We believe the user/application when the encoding is + # explicitly given. + encodings = [self.encoding] + else: + data_encoding = self.determine_encoding_from_data(data) + if data_encoding: + # If the data declares its encoding (explicitly or via a BOM), + # we believe it. + encodings = [data_encoding] + else: + # Apply heuristics only if no encoding is explicitly given and + # no BOM found. Start with UTF-8, because that only matches + # data that *IS* UTF-8: + encodings = ['utf-8'] + try: + # for Python 2.2 compatibility + encodings.append(locale.nl_langinfo(locale.CODESET)) + except: + pass + try: + encodings.append(locale.getlocale()[1]) + except: + pass + try: + encodings.append(locale.getdefaultlocale()[1]) + except: + pass + # fallback encoding: + encodings.append('latin-1') + error = None + error_details = '' + for enc in encodings: + if not enc: + continue + try: + decoded = unicode(data, enc, self.error_handler) + self.successful_encoding = enc + # Return decoded, removing BOMs. + return decoded.replace(u'\ufeff', u'') + except (UnicodeError, LookupError), tmperror: + error = tmperror # working around Python 3 deleting the + # error variable after the except clause + if error is not None: + error_details = '\n(%s: %s)' % (error.__class__.__name__, error) + raise UnicodeError( + 'Unable to decode input data. Tried the following encodings: ' + '%s.%s' + % (', '.join([repr(enc) for enc in encodings if enc]), + error_details)) + + coding_slug = re.compile(b("coding[:=]\s*([-\w.]+)")) + """Encoding declaration pattern.""" + + byte_order_marks = ((b('\xef\xbb\xbf'), 'utf-8'), + (b('\xfe\xff'), 'utf-16-be'), + (b('\xff\xfe'), 'utf-16-le'),) + """Sequence of (start_bytes, encoding) tuples to for encoding detection. + The first bytes of input data are checked against the start_bytes strings. + A match indicates the given encoding.""" + + def determine_encoding_from_data(self, data): + """ + Try to determine the encoding of `data` by looking *in* `data`. + Check for a byte order mark (BOM) or an encoding declaration. + """ + # check for a byte order mark: + for start_bytes, encoding in self.byte_order_marks: + if data.startswith(start_bytes): + return encoding + # check for an encoding declaration pattern in first 2 lines of file: + for line in data.splitlines()[:2]: + match = self.coding_slug.search(line) + if match: + return match.group(1).decode('ascii') + return None + + +class Output(TransformSpec): + + """ + Abstract base class for output wrappers. + """ + + component_type = 'output' + + default_destination_path = None + + def __init__(self, destination=None, destination_path=None, + encoding=None, error_handler='strict'): + self.encoding = encoding + """Text encoding for the output destination.""" + + self.error_handler = error_handler or 'strict' + """Text encoding error handler.""" + + self.destination = destination + """The destination for output data.""" + + self.destination_path = destination_path + """A text reference to the destination.""" + + if not destination_path: + self.destination_path = self.default_destination_path + + def __repr__(self): + return ('%s: destination=%r, destination_path=%r' + % (self.__class__, self.destination, self.destination_path)) + + def write(self, data): + """`data` is a Unicode string, to be encoded by `self.encode`.""" + raise NotImplementedError + + def encode(self, data): + if self.encoding and self.encoding.lower() == 'unicode': + assert isinstance(data, unicode), ( + 'the encoding given is "unicode" but the output is not ' + 'a Unicode string') + return data + if not isinstance(data, unicode): + # Non-unicode (e.g. binary) output. + return data + else: + return data.encode(self.encoding, self.error_handler) + + +class FileInput(Input): + + """ + Input for single, simple file-like objects. + """ + + def __init__(self, source=None, source_path=None, + encoding=None, error_handler='strict', + autoclose=1, handle_io_errors=1): + """ + :Parameters: + - `source`: either a file-like object (which is read directly), or + `None` (which implies `sys.stdin` if no `source_path` given). + - `source_path`: a path to a file, which is opened and then read. + - `encoding`: the expected text encoding of the input file. + - `error_handler`: the encoding error handler to use. + - `autoclose`: close automatically after read (boolean); always + false if `sys.stdin` is the source. + - `handle_io_errors`: summarize I/O errors here, and exit? + """ + Input.__init__(self, source, source_path, encoding, error_handler) + self.autoclose = autoclose + self.handle_io_errors = handle_io_errors + if source is None: + if source_path: + try: + self.source = open(source_path, 'rb') + except IOError, error: + if not handle_io_errors: + raise + print >>sys.stderr, '%s: %s' % (error.__class__.__name__, + error) + print >>sys.stderr, ('Unable to open source file for ' + "reading ('%s'). Exiting." % + source_path) + sys.exit(1) + else: + self.source = sys.stdin + self.autoclose = None + if not source_path: + try: + self.source_path = self.source.name + except AttributeError: + pass + + def read(self): + """ + Read and decode a single file and return the data (Unicode string). + """ + try: + data = self.source.read() + finally: + if self.autoclose: + self.close() + return self.decode(data) + + def readlines(self): + """ + Return lines of a single file as list of Unicode strings. + """ + try: + lines = self.source.readlines() + finally: + if self.autoclose: + self.close() + return [self.decode(line) for line in lines] + + def close(self): + self.source.close() + + +class FileOutput(Output): + + """ + Output for single, simple file-like objects. + """ + + def __init__(self, destination=None, destination_path=None, + encoding=None, error_handler='strict', autoclose=1, + handle_io_errors=1): + """ + :Parameters: + - `destination`: either a file-like object (which is written + directly) or `None` (which implies `sys.stdout` if no + `destination_path` given). + - `destination_path`: a path to a file, which is opened and then + written. + - `autoclose`: close automatically after write (boolean); always + false if `sys.stdout` is the destination. + """ + Output.__init__(self, destination, destination_path, + encoding, error_handler) + self.opened = 1 + self.autoclose = autoclose + self.handle_io_errors = handle_io_errors + if destination is None: + if destination_path: + self.opened = None + else: + self.destination = sys.stdout + self.autoclose = None + if not destination_path: + try: + self.destination_path = self.destination.name + except AttributeError: + pass + + def open(self): + try: + self.destination = open(self.destination_path, 'w') + except IOError, error: + if not self.handle_io_errors: + raise + print >>sys.stderr, '%s: %s' % (error.__class__.__name__, + error) + print >>sys.stderr, ('Unable to open destination file for writing' + " ('%s'). Exiting." % self.destination_path) + sys.exit(1) + self.opened = 1 + + def write(self, data): + """Encode `data`, write it to a single file, and return it.""" + output = self.encode(data) + if not self.opened: + self.open() + try: + self.destination.write(output) + finally: + if self.autoclose: + self.close() + return output + + def close(self): + self.destination.close() + self.opened = None + + +class BinaryFileOutput(FileOutput): + """ + A version of docutils.io.FileOutput which writes to a binary file. + """ + def open(self): + try: + self.destination = open(self.destination_path, 'wb') + except IOError, error: + if not self.handle_io_errors: + raise + print >>sys.stderr, '%s: %s' % (error.__class__.__name__, + error) + print >>sys.stderr, ('Unable to open destination file for writing ' + "('%s'). Exiting." % self.destination_path) + sys.exit(1) + self.opened = 1 + + +class StringInput(Input): + + """ + Direct string input. + """ + + default_source_path = '' + + def read(self): + """Decode and return the source string.""" + return self.decode(self.source) + + +class StringOutput(Output): + + """ + Direct string output. + """ + + default_destination_path = '' + + def write(self, data): + """Encode `data`, store it in `self.destination`, and return it.""" + self.destination = self.encode(data) + return self.destination + + +class NullInput(Input): + + """ + Degenerate input: read nothing. + """ + + default_source_path = 'null input' + + def read(self): + """Return a null string.""" + return u'' + + +class NullOutput(Output): + + """ + Degenerate output: write nothing. + """ + + default_destination_path = 'null output' + + def write(self, data): + """Do nothing ([don't even] send data to the bit bucket).""" + pass + + +class DocTreeInput(Input): + + """ + Adapter for document tree input. + + The document tree must be passed in the ``source`` parameter. + """ + + default_source_path = 'doctree input' + + def read(self): + """Return the document tree.""" + return self.source diff -Nru zope3-3.4.0/src/docutils/languages/af.py zope3-3.5~bzr18/src/docutils/languages/af.py --- zope3-3.4.0/src/docutils/languages/af.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/af.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +# $Id: af.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Jannie Hofmeyr +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Afrikaans-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + 'author': 'Auteur', + 'authors': 'Auteurs', + 'organization': 'Organisasie', + 'address': 'Adres', + 'contact': 'Kontak', + 'version': 'Weergawe', + 'revision': 'Revisie', + 'status': 'Status', + 'date': 'Datum', + 'copyright': 'Kopiereg', + 'dedication': 'Opdrag', + 'abstract': 'Opsomming', + 'attention': 'Aandag!', + 'caution': 'Wees versigtig!', + 'danger': '!GEVAAR!', + 'error': 'Fout', + 'hint': 'Wenk', + 'important': 'Belangrik', + 'note': 'Nota', + 'tip': 'Tip', # hint and tip both have the same translation: wenk + 'warning': 'Waarskuwing', + 'contents': 'Inhoud'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + 'auteur': 'author', + 'auteurs': 'authors', + 'organisasie': 'organization', + 'adres': 'address', + 'kontak': 'contact', + 'weergawe': 'version', + 'revisie': 'revision', + 'status': 'status', + 'datum': 'date', + 'kopiereg': 'copyright', + 'opdrag': 'dedication', + 'opsomming': 'abstract'} +"""Afrikaans (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/ca.py zope3-3.5~bzr18/src/docutils/languages/ca.py --- zope3-3.4.0/src/docutils/languages/ca.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/ca.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,60 @@ +# $Id: ca.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Ivan Vilata i Balaguer +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Catalan-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'Autor', + 'authors': u'Autors', + 'organization': u'Organitzaci\u00F3', + 'address': u'Adre\u00E7a', + 'contact': u'Contacte', + 'version': u'Versi\u00F3', + 'revision': u'Revisi\u00F3', + 'status': u'Estat', + 'date': u'Data', + 'copyright': u'Copyright', + 'dedication': u'Dedicat\u00F2ria', + 'abstract': u'Resum', + 'attention': u'Atenci\u00F3!', + 'caution': u'Compte!', + 'danger': u'PERILL!', + 'error': u'Error', + 'hint': u'Suggeriment', + 'important': u'Important', + 'note': u'Nota', + 'tip': u'Consell', + 'warning': u'Av\u00EDs', + 'contents': u'Contingut'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'autor': 'author', + u'autors': 'authors', + u'organitzaci\u00F3': 'organization', + u'adre\u00E7a': 'address', + u'contacte': 'contact', + u'versi\u00F3': 'version', + u'revisi\u00F3': 'revision', + u'estat': 'status', + u'data': 'date', + u'copyright': 'copyright', + u'dedicat\u00F2ria': 'dedication', + u'resum': 'abstract'} +"""Catalan (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/cs.py zope3-3.5~bzr18/src/docutils/languages/cs.py --- zope3-3.4.0/src/docutils/languages/cs.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/cs.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,60 @@ +# $Id: cs.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Marek Blaha +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Czech-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'Autor', + 'authors': u'Auto\u0159i', + 'organization': u'Organizace', + 'address': u'Adresa', + 'contact': u'Kontakt', + 'version': u'Verze', + 'revision': u'Revize', + 'status': u'Stav', + 'date': u'Datum', + 'copyright': u'Copyright', + 'dedication': u'V\u011Bnov\u00E1n\u00ED', + 'abstract': u'Abstrakt', + 'attention': u'Pozor!', + 'caution': u'Opatrn\u011B!', + 'danger': u'!NEBEZPE\u010C\u00CD!', + 'error': u'Chyba', + 'hint': u'Rada', + 'important': u'D\u016Fle\u017Eit\u00E9', + 'note': u'Pozn\u00E1mka', + 'tip': u'Tip', + 'warning': u'Varov\u00E1n\u00ED', + 'contents': u'Obsah'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'autor': 'author', + u'auto\u0159i': 'authors', + u'organizace': 'organization', + u'adresa': 'address', + u'kontakt': 'contact', + u'verze': 'version', + u'revize': 'revision', + u'stav': 'status', + u'datum': 'date', + u'copyright': 'copyright', + u'v\u011Bnov\u00E1n\u00ED': 'dedication', + u'abstrakt': 'abstract'} +"""Czech (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/de.py zope3-3.5~bzr18/src/docutils/languages/de.py --- zope3-3.4.0/src/docutils/languages/de.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/de.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +# $Id: de.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Gunnar Schwant +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +German language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + 'author': 'Autor', + 'authors': 'Autoren', + 'organization': 'Organisation', + 'address': 'Adresse', + 'contact': 'Kontakt', + 'version': 'Version', + 'revision': 'Revision', + 'status': 'Status', + 'date': 'Datum', + 'dedication': 'Widmung', + 'copyright': 'Copyright', + 'abstract': 'Zusammenfassung', + 'attention': 'Achtung!', + 'caution': 'Vorsicht!', + 'danger': '!GEFAHR!', + 'error': 'Fehler', + 'hint': 'Hinweis', + 'important': 'Wichtig', + 'note': 'Bemerkung', + 'tip': 'Tipp', + 'warning': 'Warnung', + 'contents': 'Inhalt'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + 'autor': 'author', + 'autoren': 'authors', + 'organisation': 'organization', + 'adresse': 'address', + 'kontakt': 'contact', + 'version': 'version', + 'revision': 'revision', + 'status': 'status', + 'datum': 'date', + 'copyright': 'copyright', + 'widmung': 'dedication', + 'zusammenfassung': 'abstract'} +"""German (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/en.py zope3-3.5~bzr18/src/docutils/languages/en.py --- zope3-3.4.0/src/docutils/languages/en.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/en.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,60 @@ +# $Id: en.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +English-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': 'Author', + 'authors': 'Authors', + 'organization': 'Organization', + 'address': 'Address', + 'contact': 'Contact', + 'version': 'Version', + 'revision': 'Revision', + 'status': 'Status', + 'date': 'Date', + 'copyright': 'Copyright', + 'dedication': 'Dedication', + 'abstract': 'Abstract', + 'attention': 'Attention!', + 'caution': 'Caution!', + 'danger': '!DANGER!', + 'error': 'Error', + 'hint': 'Hint', + 'important': 'Important', + 'note': 'Note', + 'tip': 'Tip', + 'warning': 'Warning', + 'contents': 'Contents'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + 'author': 'author', + 'authors': 'authors', + 'organization': 'organization', + 'address': 'address', + 'contact': 'contact', + 'version': 'version', + 'revision': 'revision', + 'status': 'status', + 'date': 'date', + 'copyright': 'copyright', + 'dedication': 'dedication', + 'abstract': 'abstract'} +"""English (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/eo.py zope3-3.5~bzr18/src/docutils/languages/eo.py --- zope3-3.4.0/src/docutils/languages/eo.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/eo.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,61 @@ +# $Id: eo.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Marcelo Huerta San Martin +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Esperanto-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'A\u016dtoro', + 'authors': u'A\u016dtoroj', + 'organization': u'Organizo', + 'address': u'Adreso', + 'contact': u'Kontakto', + 'version': u'Versio', + 'revision': u'Revido', + 'status': u'Stato', + 'date': u'Dato', + # 'copyright': u'Kopirajto', + 'copyright': u'A\u016dtorrajto', + 'dedication': u'Dedi\u0109o', + 'abstract': u'Resumo', + 'attention': u'Atentu!', + 'caution': u'Zorgu!', + 'danger': u'DAN\u011cERO!', + 'error': u'Eraro', + 'hint': u'Spuro', + 'important': u'Grava', + 'note': u'Noto', + 'tip': u'Helpeto', + 'warning': u'Averto', + 'contents': u'Enhavo'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + 'a\u016dtoro': 'author', + 'a\u016dtoroj': 'authors', + 'organizo': 'organization', + 'adreso': 'address', + 'kontakto': 'contact', + 'versio': 'version', + 'revido': 'revision', + 'stato': 'status', + 'dato': 'date', + 'a\u016dtorrajto': 'copyright', + 'dedi\u0109o': 'dedication', + 'resumo': 'abstract'} +"""Esperanto (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/es.py zope3-3.5~bzr18/src/docutils/languages/es.py --- zope3-3.4.0/src/docutils/languages/es.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/es.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# $Id: es.py 4572 2006-05-25 20:48:37Z richieadler $ +# Author: Marcelo Huerta San Martín +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Spanish-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + 'author': u'Autor', + 'authors': u'Autores', + 'organization': u'Organizaci\u00f3n', + 'address': u'Direcci\u00f3n', + 'contact': u'Contacto', + 'version': u'Versi\u00f3n', + 'revision': u'Revisi\u00f3n', + 'status': u'Estado', + 'date': u'Fecha', + 'copyright': u'Copyright', + 'dedication': u'Dedicatoria', + 'abstract': u'Resumen', + 'attention': u'\u00a1Atenci\u00f3n!', + 'caution': u'\u00a1Precauci\u00f3n!', + 'danger': u'\u00a1PELIGRO!', + 'error': u'Error', + 'hint': u'Sugerencia', + 'important': u'Importante', + 'note': u'Nota', + 'tip': u'Consejo', + 'warning': u'Advertencia', + 'contents': u'Contenido'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + u'autor': 'author', + u'autores': 'authors', + u'organizaci\u00f3n': 'organization', + u'direcci\u00f3n': 'address', + u'contacto': 'contact', + u'versi\u00f3n': 'version', + u'revisi\u00f3n': 'revision', + u'estado': 'status', + u'fecha': 'date', + u'copyright': 'copyright', + u'dedicatoria': 'dedication', + u'resumen': 'abstract'} +"""Spanish (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/fi.py zope3-3.5~bzr18/src/docutils/languages/fi.py --- zope3-3.4.0/src/docutils/languages/fi.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/fi.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,60 @@ +# $Id: fi.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Asko Soukka +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Finnish-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + u'author': u'Tekij\u00e4', + u'authors': u'Tekij\u00e4t', + u'organization': u'Yhteis\u00f6', + u'address': u'Osoite', + u'contact': u'Yhteystiedot', + u'version': u'Versio', + u'revision': u'Vedos', + u'status': u'Tila', + u'date': u'P\u00e4iv\u00e4ys', + u'copyright': u'Tekij\u00e4noikeudet', + u'dedication': u'Omistuskirjoitus', + u'abstract': u'Tiivistelm\u00e4', + u'attention': u'Huomio!', + u'caution': u'Varo!', + u'danger': u'!VAARA!', + u'error': u'Virhe', + u'hint': u'Vihje', + u'important': u'T\u00e4rke\u00e4\u00e4', + u'note': u'Huomautus', + u'tip': u'Neuvo', + u'warning': u'Varoitus', + u'contents': u'Sis\u00e4llys'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'tekij\u00e4': u'author', + u'tekij\u00e4t': u'authors', + u'yhteis\u00f6': u'organization', + u'osoite': u'address', + u'yhteystiedot': u'contact', + u'versio': u'version', + u'vedos': u'revision', + u'tila': u'status', + u'p\u00e4iv\u00e4ys': u'date', + u'tekij\u00e4noikeudet': u'copyright', + u'omistuskirjoitus': u'dedication', + u'tiivistelm\u00e4': u'abstract'} +"""Finnish (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/fr.py zope3-3.5~bzr18/src/docutils/languages/fr.py --- zope3-3.4.0/src/docutils/languages/fr.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/fr.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +# $Id: fr.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Stefane Fermigier +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +French-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + u'author': u'Auteur', + u'authors': u'Auteurs', + u'organization': u'Organisation', + u'address': u'Adresse', + u'contact': u'Contact', + u'version': u'Version', + u'revision': u'R\u00e9vision', + u'status': u'Statut', + u'date': u'Date', + u'copyright': u'Copyright', + u'dedication': u'D\u00e9dicace', + u'abstract': u'R\u00e9sum\u00e9', + u'attention': u'Attention!', + u'caution': u'Avertissement!', + u'danger': u'!DANGER!', + u'error': u'Erreur', + u'hint': u'Indication', + u'important': u'Important', + u'note': u'Note', + u'tip': u'Astuce', + u'warning': u'Avis', + u'contents': u'Sommaire'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + u'auteur': u'author', + u'auteurs': u'authors', + u'organisation': u'organization', + u'adresse': u'address', + u'contact': u'contact', + u'version': u'version', + u'r\u00e9vision': u'revision', + u'statut': u'status', + u'date': u'date', + u'copyright': u'copyright', + u'd\u00e9dicace': u'dedication', + u'r\u00e9sum\u00e9': u'abstract'} +"""French (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/gl.py zope3-3.5~bzr18/src/docutils/languages/gl.py --- zope3-3.4.0/src/docutils/languages/gl.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/gl.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Author: David Goodger +# Contact: goodger@users.sourceforge.net +# Revision: $Revision: 2224 $ +# Date: $Date: 2004-06-05 21:40:46 +0200 (Sat, 05 Jun 2004) $ +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Galician-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'Autor', + 'authors': u'Autores', + 'organization': u'Organizaci\u00f3n', + 'address': u'Enderezo', + 'contact': u'Contacto', + 'version': u'Versi\u00f3n', + 'revision': u'Revisi\u00f3n', + 'status': u'Estado', + 'date': u'Data', + 'copyright': u'Dereitos de copia', + 'dedication': u'Dedicatoria', + 'abstract': u'Abstract', + 'attention': u'Atenci\u00f3n!', + 'caution': u'Advertencia!', + 'danger': u'PERIGO!', + 'error': u'Erro', + 'hint': u'Consello', + 'important': u'Importante', + 'note': u'Nota', + 'tip': u'Suxesti\u00f3n', + 'warning': u'Aviso', + 'contents': u'Contido'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'autor': 'author', + u'autores': 'authors', + u'organizaci\u00f3n': 'organization', + u'enderezo': 'address', + u'contacto': 'contact', + u'versi\u00f3n': 'version', + u'revisi\u00f3n': 'revision', + u'estado': 'status', + u'data': 'date', + u'dereitos de copia': 'copyright', + u'dedicatoria': 'dedication', + u'abstract': 'abstract'} +"""Galician (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/he.py zope3-3.5~bzr18/src/docutils/languages/he.py --- zope3-3.4.0/src/docutils/languages/he.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/he.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,60 @@ +# Author: Meir Kriheli +# Id: $Id: he.py 4837 2006-12-26 09:59:41Z sfcben $ +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Hebrew-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'\u05de\u05d7\u05d1\u05e8', + 'authors': u'\u05de\u05d7\u05d1\u05e8\u05d9', + 'organization': u'\u05d0\u05e8\u05d2\u05d5\u05df', + 'address': u'\u05db\u05ea\u05d5\u05d1\u05ea', + 'contact': u'\u05d0\u05d9\u05e9 \u05e7\u05e9\u05e8', + 'version': u'\u05d2\u05e8\u05e1\u05d4', + 'revision': u'\u05de\u05d4\u05d3\u05d5\u05e8\u05d4', + 'status': u'\u05e1\u05d8\u05d8\u05d5\u05e1', + 'date': u'\u05ea\u05d0\u05e8\u05d9\u05da', + 'copyright': u'\u05d6\u05db\u05d5\u05d9\u05d5\u05ea \u05e9\u05de\u05d5\u05e8\u05d5\u05ea', + 'dedication': u'\u05d4\u05e7\u05d3\u05e9\u05d4', + 'abstract': u'\u05ea\u05e7\u05e6\u05d9\u05e8', + 'attention': u'\u05ea\u05e9\u05d5\u05de\u05ea \u05dc\u05d1', + 'caution': u'\u05d6\u05d4\u05d9\u05e8\u05d5\u05ea', + 'danger': u'\u05e1\u05db\u05e0\u05d4', + 'error': u'\u05e9\u05d2\u05d9\u05d0\u05d4' , + 'hint': u'\u05e8\u05de\u05d6', + 'important': u'\u05d7\u05e9\u05d5\u05d1', + 'note': u'\u05d4\u05e2\u05e8\u05d4', + 'tip': u'\u05d8\u05d9\u05e4', + 'warning': u'\u05d0\u05d6\u05d4\u05e8\u05d4', + 'contents': u'\u05ea\u05d5\u05db\u05df'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'\u05de\u05d7\u05d1\u05e8': 'author', + u'\u05de\u05d7\u05d1\u05e8\u05d9': 'authors', + u'\u05d0\u05e8\u05d2\u05d5\u05df': 'organization', + u'\u05db\u05ea\u05d5\u05d1\u05ea': 'address', + u'\u05d0\u05d9\u05e9 \u05e7\u05e9\u05e8': 'contact', + u'\u05d2\u05e8\u05e1\u05d4': 'version', + u'\u05de\u05d4\u05d3\u05d5\u05e8\u05d4': 'revision', + u'\u05e1\u05d8\u05d8\u05d5\u05e1': 'status', + u'\u05ea\u05d0\u05e8\u05d9\u05da': 'date', + u'\u05d6\u05db\u05d5\u05d9\u05d5\u05ea \u05e9\u05de\u05d5\u05e8\u05d5\u05ea': 'copyright', + u'\u05d4\u05e7\u05d3\u05e9\u05d4': 'dedication', + u'\u05ea\u05e7\u05e6\u05d9\u05e8': 'abstract'} +"""Hebrew to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/__init__.py zope3-3.5~bzr18/src/docutils/languages/__init__.py --- zope3-3.4.0/src/docutils/languages/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +# $Id: __init__.py 5618 2008-07-28 08:37:32Z strank $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +# Internationalization details are documented in +# . + +""" +This package contains modules for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +_languages = {} + +def get_language(language_code): + if language_code in _languages: + return _languages[language_code] + module = __import__(language_code, globals(), locals()) + _languages[language_code] = module + return module diff -Nru zope3-3.4.0/src/docutils/languages/it.py zope3-3.5~bzr18/src/docutils/languages/it.py --- zope3-3.4.0/src/docutils/languages/it.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/it.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +# $Id: it.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Nicola Larosa +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Italian-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + 'author': 'Autore', + 'authors': 'Autori', + 'organization': 'Organizzazione', + 'address': 'Indirizzo', + 'contact': 'Contatti', + 'version': 'Versione', + 'revision': 'Revisione', + 'status': 'Status', + 'date': 'Data', + 'copyright': 'Copyright', + 'dedication': 'Dedica', + 'abstract': 'Riassunto', + 'attention': 'Attenzione!', + 'caution': 'Cautela!', + 'danger': '!PERICOLO!', + 'error': 'Errore', + 'hint': 'Suggerimento', + 'important': 'Importante', + 'note': 'Nota', + 'tip': 'Consiglio', + 'warning': 'Avvertenza', + 'contents': 'Indice'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + 'autore': 'author', + 'autori': 'authors', + 'organizzazione': 'organization', + 'indirizzo': 'address', + 'contatto': 'contact', + 'versione': 'version', + 'revisione': 'revision', + 'status': 'status', + 'data': 'date', + 'copyright': 'copyright', + 'dedica': 'dedication', + 'riassunto': 'abstract'} +"""Italian (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/ja.py zope3-3.5~bzr18/src/docutils/languages/ja.py --- zope3-3.4.0/src/docutils/languages/ja.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/ja.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# $Id: ja.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Hisashi Morita +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Japanese-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'著者', + 'authors': u'著者', + 'organization': u'組織', + 'address': u'住所', + 'contact': u'連絡先', + 'version': u'バージョン', + 'revision': u'リビジョン', + 'status': u'ステータス', + 'date': u'日付', + 'copyright': u'著作権', + 'dedication': u'献辞', + 'abstract': u'概要', + 'attention': u'注目!', + 'caution': u'注意!', + 'danger': u'!危険!', + 'error': u'エラー', + 'hint': u'ヒント', + 'important': u'重要', + 'note': u'備考', + 'tip': u'通報', + 'warning': u'警告', + 'contents': u'目次'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'著者': 'author', + u' n/a': 'authors', + u'組織': 'organization', + u'住所': 'address', + u'連絡先': 'contact', + u'バージョン': 'version', + u'リビジョン': 'revision', + u'ステータス': 'status', + u'日付': 'date', + u'著作権': 'copyright', + u'献辞': 'dedication', + u'概要': 'abstract'} +"""Japanese (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/nl.py zope3-3.5~bzr18/src/docutils/languages/nl.py --- zope3-3.4.0/src/docutils/languages/nl.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/nl.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,60 @@ +# $Id: nl.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Martijn Pieters +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Dutch-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': 'Auteur', + 'authors': 'Auteurs', + 'organization': 'Organisatie', + 'address': 'Adres', + 'contact': 'Contact', + 'version': 'Versie', + 'revision': 'Revisie', + 'status': 'Status', + 'date': 'Datum', + 'copyright': 'Copyright', + 'dedication': 'Toewijding', + 'abstract': 'Samenvatting', + 'attention': 'Attentie!', + 'caution': 'Let op!', + 'danger': '!GEVAAR!', + 'error': 'Fout', + 'hint': 'Hint', + 'important': 'Belangrijk', + 'note': 'Opmerking', + 'tip': 'Tip', + 'warning': 'Waarschuwing', + 'contents': 'Inhoud'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + 'auteur': 'author', + 'auteurs': 'authors', + 'organisatie': 'organization', + 'adres': 'address', + 'contact': 'contact', + 'versie': 'version', + 'revisie': 'revision', + 'status': 'status', + 'datum': 'date', + 'copyright': 'copyright', + 'toewijding': 'dedication', + 'samenvatting': 'abstract'} +"""Dutch (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/pl.py zope3-3.5~bzr18/src/docutils/languages/pl.py --- zope3-3.4.0/src/docutils/languages/pl.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/pl.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,62 @@ +# $Id$ +# Author: Robert Wojciechowicz +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Polish-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'Autor', + 'authors': u'Autorzy', + 'organization': u'Organizacja', + 'address': u'Adres', + 'contact': u'Kontakt', + 'version': u'Wersja', + 'revision': u'Korekta', + 'status': u'Status', + 'date': u'Data', + 'copyright': u'Copyright', + 'dedication': u'Dedykacja', + 'abstract': u'Streszczenie', + 'attention': u'Uwaga!', + 'caution': u'Ostro\u017cnie!', + 'danger': u'!Niebezpiecze\u0144stwo!', + 'error': u'B\u0142\u0105d', + 'hint': u'Wskaz\u00f3wka', + 'important': u'Wa\u017cne', + 'note': u'Przypis', + 'tip': u'Rada', + 'warning': u'Ostrze\u017cenie', + 'contents': u'Tre\u015b\u0107'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'autor': 'author', + u'autorzy': 'authors', + u'organizacja': 'organization', + u'adres': 'address', + u'kontakt': 'contact', + u'wersja': 'version', + u'korekta': 'revision', + u'status': 'status', + u'data': 'date', + u'copyright': 'copyright', + u'dedykacja': 'dedication', + u'streszczenie': 'abstract'} +"""Polish (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" + + diff -Nru zope3-3.4.0/src/docutils/languages/pt_br.py zope3-3.5~bzr18/src/docutils/languages/pt_br.py --- zope3-3.4.0/src/docutils/languages/pt_br.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/pt_br.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,60 @@ +# $Id: pt_br.py 5567 2008-06-03 01:11:03Z goodger $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Brazilian Portuguese-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'Autor', + 'authors': u'Autores', + 'organization': u'Organiza\u00E7\u00E3o', + 'address': u'Endere\u00E7o', + 'contact': u'Contato', + 'version': u'Vers\u00E3o', + 'revision': u'Revis\u00E3o', + 'status': u'Estado', + 'date': u'Data', + 'copyright': u'Copyright', + 'dedication': u'Dedicat\u00F3ria', + 'abstract': u'Resumo', + 'attention': u'Aten\u00E7\u00E3o!', + 'caution': u'Cuidado!', + 'danger': u'PERIGO!', + 'error': u'Erro', + 'hint': u'Sugest\u00E3o', + 'important': u'Importante', + 'note': u'Nota', + 'tip': u'Dica', + 'warning': u'Aviso', + 'contents': u'Sum\u00E1rio'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'autor': 'author', + u'autores': 'authors', + u'organiza\u00E7\u00E3o': 'organization', + u'endere\u00E7o': 'address', + u'contato': 'contact', + u'vers\u00E3o': 'version', + u'revis\u00E3o': 'revision', + u'estado': 'status', + u'data': 'date', + u'copyright': 'copyright', + u'dedicat\u00F3ria': 'dedication', + u'resumo': 'abstract'} +"""Brazilian Portuguese (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/ru.py zope3-3.5~bzr18/src/docutils/languages/ru.py --- zope3-3.4.0/src/docutils/languages/ru.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/ru.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,66 @@ +# $Id: ru.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Roman Suzi +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Russian-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + u'abstract': u'\u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f', + u'address': u'\u0410\u0434\u0440\u0435\u0441', + u'attention': u'\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435!', + u'author': u'\u0410\u0432\u0442\u043e\u0440', + u'authors': u'\u0410\u0432\u0442\u043e\u0440\u044b', + u'caution': u'\u041e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u043e!', + u'contact': u'\u041a\u043e\u043d\u0442\u0430\u043a\u0442', + u'contents': + u'\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435', + u'copyright': u'\u041f\u0440\u0430\u0432\u0430 ' + u'\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f', + u'danger': u'\u041e\u041f\u0410\u0421\u041d\u041e!', + u'date': u'\u0414\u0430\u0442\u0430', + u'dedication': + u'\u041f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0438\u0435', + u'error': u'\u041e\u0448\u0438\u0431\u043a\u0430', + u'hint': u'\u0421\u043e\u0432\u0435\u0442', + u'important': u'\u0412\u0430\u0436\u043d\u043e', + u'note': u'\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435', + u'organization': + u'\u041e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f', + u'revision': u'\u0420\u0435\u0434\u0430\u043a\u0446\u0438\u044f', + u'status': u'\u0421\u0442\u0430\u0442\u0443\u0441', + u'tip': u'\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0430', + u'version': u'\u0412\u0435\u0440\u0441\u0438\u044f', + u'warning': u'\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436' + u'\u0434\u0435\u043d\u0438\u0435'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + u'\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f': u'abstract', + u'\u0430\u0434\u0440\u0435\u0441': u'address', + u'\u0430\u0432\u0442\u043e\u0440': u'author', + u'\u0430\u0432\u0442\u043e\u0440\u044b': u'authors', + u'\u043a\u043e\u043d\u0442\u0430\u043a\u0442': u'contact', + u'\u043f\u0440\u0430\u0432\u0430 \u043a\u043e\u043f\u0438\u0440\u043e' + u'\u0432\u0430\u043d\u0438\u044f': u'copyright', + u'\u0434\u0430\u0442\u0430': u'date', + u'\u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0438\u0435': + u'dedication', + u'\u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f': + u'organization', + u'\u0440\u0435\u0434\u0430\u043a\u0446\u0438\u044f': u'revision', + u'\u0441\u0442\u0430\u0442\u0443\u0441': u'status', + u'\u0432\u0435\u0440\u0441\u0438\u044f': u'version'} +"""Russian (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/sk.py zope3-3.5~bzr18/src/docutils/languages/sk.py --- zope3-3.4.0/src/docutils/languages/sk.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/sk.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,58 @@ +# $Id: sk.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Miroslav Vasko +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Slovak-language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + 'author': u'Autor', + 'authors': u'Autori', + 'organization': u'Organiz\u00E1cia', + 'address': u'Adresa', + 'contact': u'Kontakt', + 'version': u'Verzia', + 'revision': u'Rev\u00EDzia', + 'status': u'Stav', + 'date': u'D\u00E1tum', + 'copyright': u'Copyright', + 'dedication': u'Venovanie', + 'abstract': u'Abstraktne', + 'attention': u'Pozor!', + 'caution': u'Opatrne!', + 'danger': u'!NEBEZPE\u010cENSTVO!', + 'error': u'Chyba', + 'hint': u'Rada', + 'important': u'D\u00F4le\u017Eit\u00E9', + 'note': u'Pozn\u00E1mka', + 'tip': u'Tip', + 'warning': u'Varovanie', + 'contents': u'Obsah'} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + u'autor': 'author', + u'autori': 'authors', + u'organiz\u00E1cia': 'organization', + u'adresa': 'address', + u'kontakt': 'contact', + u'verzia': 'version', + u'rev\u00EDzia': 'revision', + u'stav': 'status', + u'd\u00E1tum': 'date', + u'copyright': 'copyright', + u'venovanie': 'dedication', + u'abstraktne': 'abstract'} +"""Slovak (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/sv.py zope3-3.5~bzr18/src/docutils/languages/sv.py --- zope3-3.4.0/src/docutils/languages/sv.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/sv.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,59 @@ +# $Id: sv.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Adam Chodorowski +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Swedish language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + 'author': u'F\u00f6rfattare', + 'authors': u'F\u00f6rfattare', + 'organization': u'Organisation', + 'address': u'Adress', + 'contact': u'Kontakt', + 'version': u'Version', + 'revision': u'Revision', + 'status': u'Status', + 'date': u'Datum', + 'copyright': u'Copyright', + 'dedication': u'Dedikation', + 'abstract': u'Sammanfattning', + 'attention': u'Observera!', + 'caution': u'Varning!', + 'danger': u'FARA!', + 'error': u'Fel', + 'hint': u'V\u00e4gledning', + 'important': u'Viktigt', + 'note': u'Notera', + 'tip': u'Tips', + 'warning': u'Varning', + 'contents': u'Inneh\u00e5ll' } +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # 'Author' and 'Authors' identical in Swedish; assume the plural: + u'f\u00f6rfattare': 'authors', + u' n/a': 'author', + u'organisation': 'organization', + u'adress': 'address', + u'kontakt': 'contact', + u'version': 'version', + u'revision': 'revision', + u'status': 'status', + u'datum': 'date', + u'copyright': 'copyright', + u'dedikation': 'dedication', + u'sammanfattning': 'abstract' } +"""Swedish (lowcased) to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ','] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/zh_cn.py zope3-3.5~bzr18/src/docutils/languages/zh_cn.py --- zope3-3.4.0/src/docutils/languages/zh_cn.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/zh_cn.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# $Id: zh_cn.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Pan Junyong +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Simplified Chinese language mappings for language-dependent features +of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'作者', + 'authors': u'作者群', + 'organization': u'组织', + 'address': u'地址', + 'contact': u'联系', + 'version': u'版本', + 'revision': u'修订', + 'status': u'状态', + 'date': u'日期', + 'copyright': u'版权', + 'dedication': u'献辞', + 'abstract': u'摘要', + 'attention': u'注意', + 'caution': u'小心', + 'danger': u'危险', + 'error': u'错误', + 'hint': u'提示', + 'important': u'重要', + 'note': u'注解', + 'tip': u'技巧', + 'warning': u'警告', + 'contents': u'目录', +} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + u'作者': 'author', + u'作者群': 'authors', + u'组织': 'organization', + u'地址': 'address', + u'联系': 'contact', + u'版本': 'version', + u'修订': 'revision', + u'状态': 'status', + u'时间': 'date', + u'版权': 'copyright', + u'献辞': 'dedication', + u'摘要': 'abstract'} +"""Simplified Chinese to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ',', + u'\uff1b', # ';' + u'\uff0c', # ',' + u'\u3001', # '、' + ] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/languages/zh_tw.py zope3-3.5~bzr18/src/docutils/languages/zh_tw.py --- zope3-3.4.0/src/docutils/languages/zh_tw.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/languages/zh_tw.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# $Id: zh_tw.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Joe YS Jaw +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Traditional Chinese language mappings for language-dependent features of Docutils. +""" + +__docformat__ = 'reStructuredText' + +labels = { + # fixed: language-dependent + 'author': u'\u4f5c\u8005', # '作者' <-- Chinese word + 'authors': u'\u4f5c\u8005\u7fa4', # '作者群', + 'organization': u'\u7d44\u7e54', # '組織', + 'address': u'\u5730\u5740', # '地址', + 'contact': u'\u9023\u7d61', # '連絡', + 'version': u'\u7248\u672c', # '版本', + 'revision': u'\u4fee\u8a02', # '修訂', + 'status': u'\u72c0\u614b', # '狀態', + 'date': u'\u65e5\u671f', # '日期', + 'copyright': u'\u7248\u6b0a', # '版權', + 'dedication': u'\u984c\u737b', # '題獻', + 'abstract': u'\u6458\u8981', # '摘要', + 'attention': u'\u6ce8\u610f\uff01', # '注意!', + 'caution': u'\u5c0f\u5fc3\uff01', # '小心!', + 'danger': u'\uff01\u5371\u96aa\uff01', # '!危險!', + 'error': u'\u932f\u8aa4', # '錯誤', + 'hint': u'\u63d0\u793a', # '提示', + 'important': u'\u91cd\u8981', # '注意!', + 'note': u'\u8a3b\u91cb', # '註釋', + 'tip': u'\u79d8\u8a23', # '秘訣', + 'warning': u'\u8b66\u544a', # '警告', + 'contents': u'\u76ee\u9304' # '目錄' +} +"""Mapping of node class name to label text.""" + +bibliographic_fields = { + # language-dependent: fixed + 'author (translation required)': 'author', + 'authors (translation required)': 'authors', + 'organization (translation required)': 'organization', + 'address (translation required)': 'address', + 'contact (translation required)': 'contact', + 'version (translation required)': 'version', + 'revision (translation required)': 'revision', + 'status (translation required)': 'status', + 'date (translation required)': 'date', + 'copyright (translation required)': 'copyright', + 'dedication (translation required)': 'dedication', + 'abstract (translation required)': 'abstract'} +"""Traditional Chinese to canonical name mapping for bibliographic fields.""" + +author_separators = [';', ',', + u'\uff1b', # ';' + u'\uff0c', # ',' + u'\u3001', # '、' + ] +"""List of separator strings for the 'Authors' bibliographic field. Tried in +order.""" diff -Nru zope3-3.4.0/src/docutils/nodes.py zope3-3.5~bzr18/src/docutils/nodes.py --- zope3-3.4.0/src/docutils/nodes.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/nodes.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1921 @@ +# $Id: nodes.py 6011 2009-07-09 10:00:07Z gbrandl $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Docutils document tree element class library. + +Classes in CamelCase are abstract base classes or auxiliary classes. The one +exception is `Text`, for a text (PCDATA) node; uppercase is used to +differentiate from element classes. Classes in lower_case_with_underscores +are element classes, matching the XML element generic identifiers in the DTD_. + +The position of each node (the level at which it can occur) is significant and +is represented by abstract base classes (`Root`, `Structural`, `Body`, +`Inline`, etc.). Certain transformations will be easier because we can use +``isinstance(node, base_class)`` to determine the position of the node in the +hierarchy. + +.. _DTD: http://docutils.sourceforge.net/docs/ref/docutils.dtd +""" + +__docformat__ = 'reStructuredText' + +import sys +import os +import re +import warnings +import types +import unicodedata + +# ============================== +# Functional Node Base Classes +# ============================== + +class Node(object): + + """Abstract base class of nodes in a document tree.""" + + parent = None + """Back-reference to the Node immediately containing this Node.""" + + document = None + """The `document` node at the root of the tree containing this Node.""" + + source = None + """Path or description of the input source which generated this Node.""" + + line = None + """The line number (1-based) of the beginning of this Node in `source`.""" + + def __nonzero__(self): + """ + Node instances are always true, even if they're empty. A node is more + than a simple container. Its boolean "truth" does not depend on + having one or more subnodes in the doctree. + + Use `len()` to check node length. Use `None` to represent a boolean + false value. + """ + return True + + if sys.version_info < (3,): + # on 2.x, str(node) will be a byte string with Unicode + # characters > 255 escaped; on 3.x this is no longer necessary + def __str__(self): + return unicode(self).encode('raw_unicode_escape') + + def asdom(self, dom=None): + """Return a DOM **fragment** representation of this Node.""" + if dom is None: + import xml.dom.minidom as dom + domroot = dom.Document() + return self._dom_node(domroot) + + def pformat(self, indent=' ', level=0): + """ + Return an indented pseudo-XML representation, for test purposes. + + Override in subclasses. + """ + raise NotImplementedError + + def copy(self): + """Return a copy of self.""" + raise NotImplementedError + + def deepcopy(self): + """Return a deep copy of self (also copying children).""" + raise NotImplementedError + + def setup_child(self, child): + child.parent = self + if self.document: + child.document = self.document + if child.source is None: + child.source = self.document.current_source + if child.line is None: + child.line = self.document.current_line + + def walk(self, visitor): + """ + Traverse a tree of `Node` objects, calling the + `dispatch_visit()` method of `visitor` when entering each + node. (The `walkabout()` method is similar, except it also + calls the `dispatch_departure()` method before exiting each + node.) + + This tree traversal supports limited in-place tree + modifications. Replacing one node with one or more nodes is + OK, as is removing an element. However, if the node removed + or replaced occurs after the current node, the old node will + still be traversed, and any new nodes will not. + + Within ``visit`` methods (and ``depart`` methods for + `walkabout()`), `TreePruningException` subclasses may be raised + (`SkipChildren`, `SkipSiblings`, `SkipNode`, `SkipDeparture`). + + Parameter `visitor`: A `NodeVisitor` object, containing a + ``visit`` implementation for each `Node` subclass encountered. + + Return true if we should stop the traversal. + """ + stop = 0 + visitor.document.reporter.debug( + 'docutils.nodes.Node.walk calling dispatch_visit for %s' + % self.__class__.__name__) + try: + try: + visitor.dispatch_visit(self) + except (SkipChildren, SkipNode): + return stop + except SkipDeparture: # not applicable; ignore + pass + children = self.children + try: + for child in children[:]: + if child.walk(visitor): + stop = 1 + break + except SkipSiblings: + pass + except StopTraversal: + stop = 1 + return stop + + def walkabout(self, visitor): + """ + Perform a tree traversal similarly to `Node.walk()` (which + see), except also call the `dispatch_departure()` method + before exiting each node. + + Parameter `visitor`: A `NodeVisitor` object, containing a + ``visit`` and ``depart`` implementation for each `Node` + subclass encountered. + + Return true if we should stop the traversal. + """ + call_depart = 1 + stop = 0 + visitor.document.reporter.debug( + 'docutils.nodes.Node.walkabout calling dispatch_visit for %s' + % self.__class__.__name__) + try: + try: + visitor.dispatch_visit(self) + except SkipNode: + return stop + except SkipDeparture: + call_depart = 0 + children = self.children + try: + for child in children[:]: + if child.walkabout(visitor): + stop = 1 + break + except SkipSiblings: + pass + except SkipChildren: + pass + except StopTraversal: + stop = 1 + if call_depart: + visitor.document.reporter.debug( + 'docutils.nodes.Node.walkabout calling dispatch_departure ' + 'for %s' % self.__class__.__name__) + visitor.dispatch_departure(self) + return stop + + def _fast_traverse(self, cls): + """Specialized traverse() that only supports instance checks.""" + result = [] + if isinstance(self, cls): + result.append(self) + for child in self.children: + result.extend(child._fast_traverse(cls)) + return result + + def _all_traverse(self): + """Specialized traverse() that doesn't check for a condition.""" + result = [] + result.append(self) + for child in self.children: + result.extend(child._all_traverse()) + return result + + def traverse(self, condition=None, + include_self=1, descend=1, siblings=0, ascend=0): + """ + Return an iterable containing + + * self (if include_self is true) + * all descendants in tree traversal order (if descend is true) + * all siblings (if siblings is true) and their descendants (if + also descend is true) + * the siblings of the parent (if ascend is true) and their + descendants (if also descend is true), and so on + + If `condition` is not None, the iterable contains only nodes + for which ``condition(node)`` is true. If `condition` is a + node class ``cls``, it is equivalent to a function consisting + of ``return isinstance(node, cls)``. + + If ascend is true, assume siblings to be true as well. + + For example, given the following tree:: + + + <--- emphasis.traverse() and + <--- strong.traverse() are called. + Foo + Bar + + Baz + + Then list(emphasis.traverse()) equals :: + + [, , <#text: Foo>, <#text: Bar>] + + and list(strong.traverse(ascend=1)) equals :: + + [, <#text: Foo>, <#text: Bar>, , <#text: Baz>] + """ + if ascend: + siblings=1 + # Check for special argument combinations that allow using an + # optimized version of traverse() + if include_self and descend and not siblings: + if condition is None: + return self._all_traverse() + elif isinstance(condition, (types.ClassType, type)): + return self._fast_traverse(condition) + # Check if `condition` is a class (check for TypeType for Python + # implementations that use only new-style classes, like PyPy). + if isinstance(condition, (types.ClassType, type)): + node_class = condition + def condition(node, node_class=node_class): + return isinstance(node, node_class) + r = [] + if include_self and (condition is None or condition(self)): + r.append(self) + if descend and len(self.children): + for child in self: + r.extend(child.traverse( + include_self=1, descend=1, siblings=0, ascend=0, + condition=condition)) + if siblings or ascend: + node = self + while node.parent: + index = node.parent.index(node) + for sibling in node.parent[index+1:]: + r.extend(sibling.traverse(include_self=1, descend=descend, + siblings=0, ascend=0, + condition=condition)) + if not ascend: + break + else: + node = node.parent + return r + + def next_node(self, condition=None, + include_self=0, descend=1, siblings=0, ascend=0): + """ + Return the first node in the iterable returned by traverse(), + or None if the iterable is empty. + + Parameter list is the same as of traverse. Note that + include_self defaults to 0, though. + """ + iterable = self.traverse(condition=condition, + include_self=include_self, descend=descend, + siblings=siblings, ascend=ascend) + try: + return iterable[0] + except IndexError: + return None + +if sys.version_info < (3,): + class reprunicode(unicode): + """ + A class that removes the initial u from unicode's repr. + """ + + def __repr__(self): + return unicode.__repr__(self)[1:] +else: + reprunicode = unicode + + +class Text(Node, reprunicode): + + """ + Instances are terminal nodes (leaves) containing text only; no child + nodes or attributes. Initialize by passing a string to the constructor. + Access the text itself with the `astext` method. + """ + + tagname = '#text' + + children = () + """Text nodes have no children, and cannot have children.""" + + if sys.version_info > (3,): + def __new__(cls, data, rawsource=None): + """Prevent the rawsource argument from propagating to str.""" + if isinstance(data, bytes): + raise TypeError('expecting str data, not bytes') + return reprunicode.__new__(cls, data) + else: + def __new__(cls, data, rawsource=None): + """Prevent the rawsource argument from propagating to str.""" + return reprunicode.__new__(cls, data) + + def __init__(self, data, rawsource=''): + + self.rawsource = rawsource + """The raw text from which this element was constructed.""" + + def __repr__(self): + data = reprunicode.__repr__(self) + if len(data) > 70: + data = reprunicode.__repr__(self[:64] + ' ...') + return '<%s: %s>' % (self.tagname, data) + + def shortrepr(self): + data = reprunicode.__repr__(self) + if len(data) > 20: + data = reprunicode.__repr__(self[:16] + ' ...') + return '<%s: %s>' % (self.tagname, data) + + def _dom_node(self, domroot): + return domroot.createTextNode(unicode(self)) + + def astext(self): + return reprunicode(self) + + # Note about __unicode__: The implementation of __unicode__ here, + # and the one raising NotImplemented in the superclass Node had + # to be removed when changing Text to a subclass of unicode instead + # of UserString, since there is no way to delegate the __unicode__ + # call to the superclass unicode: + # unicode itself does not have __unicode__ method to delegate to + # and calling unicode(self) or unicode.__new__ directly creates + # an infinite loop + + def copy(self): + return self.__class__(reprunicode(self), rawsource=self.rawsource) + + def deepcopy(self): + return self.copy() + + def pformat(self, indent=' ', level=0): + result = [] + indent = indent * level + for line in self.splitlines(): + result.append(indent + line + '\n') + return ''.join(result) + + # rstrip and lstrip are used by substitution definitions where + # they are expected to return a Text instance, this was formerly + # taken care of by UserString. Note that then and now the + # rawsource member is lost. + + def rstrip(self, chars=None): + return self.__class__(reprunicode.rstrip(self, chars)) + def lstrip(self, chars=None): + return self.__class__(reprunicode.lstrip(self, chars)) + +class Element(Node): + + """ + `Element` is the superclass to all specific elements. + + Elements contain attributes and child nodes. Elements emulate + dictionaries for attributes, indexing by attribute name (a string). To + set the attribute 'att' to 'value', do:: + + element['att'] = 'value' + + There are two special attributes: 'ids' and 'names'. Both are + lists of unique identifiers, and names serve as human interfaces + to IDs. Names are case- and whitespace-normalized (see the + fully_normalize_name() function), and IDs conform to the regular + expression ``[a-z](-?[a-z0-9]+)*`` (see the make_id() function). + + Elements also emulate lists for child nodes (element nodes and/or text + nodes), indexing by integer. To get the first child node, use:: + + element[0] + + Elements may be constructed using the ``+=`` operator. To add one new + child node to element, do:: + + element += node + + This is equivalent to ``element.append(node)``. + + To add a list of multiple child nodes at once, use the same ``+=`` + operator:: + + element += [node1, node2] + + This is equivalent to ``element.extend([node1, node2])``. + """ + + list_attributes = ('ids', 'classes', 'names', 'dupnames', 'backrefs') + """List attributes, automatically initialized to empty lists for + all nodes.""" + + tagname = None + """The element generic identifier. If None, it is set as an instance + attribute to the name of the class.""" + + child_text_separator = '\n\n' + """Separator for child nodes, used by `astext()` method.""" + + def __init__(self, rawsource='', *children, **attributes): + self.rawsource = rawsource + """The raw text from which this element was constructed.""" + + self.children = [] + """List of child nodes (elements and/or `Text`).""" + + self.extend(children) # maintain parent info + + self.attributes = {} + """Dictionary of attribute {name: value}.""" + + # Initialize list attributes. + for att in self.list_attributes: + self.attributes[att] = [] + + for att, value in attributes.items(): + att = att.lower() + if att in self.list_attributes: + # mutable list; make a copy for this node + self.attributes[att] = value[:] + else: + self.attributes[att] = value + + if self.tagname is None: + self.tagname = self.__class__.__name__ + + def _dom_node(self, domroot): + element = domroot.createElement(self.tagname) + for attribute, value in self.attlist(): + if isinstance(value, list): + value = ' '.join([serial_escape('%s' % v) for v in value]) + element.setAttribute(attribute, '%s' % value) + for child in self.children: + element.appendChild(child._dom_node(domroot)) + return element + + def __repr__(self): + data = '' + for c in self.children: + data += c.shortrepr() + if len(data) > 60: + data = data[:56] + ' ...' + break + if self['names']: + return '<%s "%s": %s>' % (self.__class__.__name__, + '; '.join(self['names']), data) + else: + return '<%s: %s>' % (self.__class__.__name__, data) + + def shortrepr(self): + if self['names']: + return '<%s "%s"...>' % (self.__class__.__name__, + '; '.join(self['names'])) + else: + return '<%s...>' % self.tagname + + def __unicode__(self): + if self.children: + return u'%s%s%s' % (self.starttag(), + ''.join([unicode(c) for c in self.children]), + self.endtag()) + else: + return self.emptytag() + + if sys.version_info > (3,): + # 2to3 doesn't convert __unicode__ to __str__ + __str__ = __unicode__ + + def starttag(self): + parts = [self.tagname] + for name, value in self.attlist(): + if value is None: # boolean attribute + parts.append(name) + elif isinstance(value, list): + values = [serial_escape('%s' % v) for v in value] + parts.append('%s="%s"' % (name, ' '.join(values))) + else: + parts.append('%s="%s"' % (name, value)) + return '<%s>' % ' '.join(parts) + + def endtag(self): + return '' % self.tagname + + def emptytag(self): + return u'<%s/>' % ' '.join([self.tagname] + + ['%s="%s"' % (n, v) + for n, v in self.attlist()]) + + def __len__(self): + return len(self.children) + + def __contains__(self, key): + # support both membership test for children and attributes + # (has_key is translated to "in" by 2to3) + if isinstance(key, basestring): + return key in self.attributes + return key in self.children + + def __getitem__(self, key): + if isinstance(key, basestring): + return self.attributes[key] + elif isinstance(key, int): + return self.children[key] + elif isinstance(key, types.SliceType): + assert key.step in (None, 1), 'cannot handle slice with stride' + return self.children[key.start:key.stop] + else: + raise TypeError, ('element index must be an integer, a slice, or ' + 'an attribute name string') + + def __setitem__(self, key, item): + if isinstance(key, basestring): + self.attributes[str(key)] = item + elif isinstance(key, int): + self.setup_child(item) + self.children[key] = item + elif isinstance(key, types.SliceType): + assert key.step in (None, 1), 'cannot handle slice with stride' + for node in item: + self.setup_child(node) + self.children[key.start:key.stop] = item + else: + raise TypeError, ('element index must be an integer, a slice, or ' + 'an attribute name string') + + def __delitem__(self, key): + if isinstance(key, basestring): + del self.attributes[key] + elif isinstance(key, int): + del self.children[key] + elif isinstance(key, types.SliceType): + assert key.step in (None, 1), 'cannot handle slice with stride' + del self.children[key.start:key.stop] + else: + raise TypeError, ('element index must be an integer, a simple ' + 'slice, or an attribute name string') + + def __add__(self, other): + return self.children + other + + def __radd__(self, other): + return other + self.children + + def __iadd__(self, other): + """Append a node or a list of nodes to `self.children`.""" + if isinstance(other, Node): + self.append(other) + elif other is not None: + self.extend(other) + return self + + def astext(self): + return self.child_text_separator.join( + [child.astext() for child in self.children]) + + def non_default_attributes(self): + atts = {} + for key, value in self.attributes.items(): + if self.is_not_default(key): + atts[key] = value + return atts + + def attlist(self): + attlist = self.non_default_attributes().items() + attlist.sort() + return attlist + + def get(self, key, failobj=None): + return self.attributes.get(key, failobj) + + def hasattr(self, attr): + return attr in self.attributes + + def delattr(self, attr): + if attr in self.attributes: + del self.attributes[attr] + + def setdefault(self, key, failobj=None): + return self.attributes.setdefault(key, failobj) + + has_key = hasattr + + # support operator in + __contains__ = hasattr + + def append(self, item): + self.setup_child(item) + self.children.append(item) + + def extend(self, item): + for node in item: + self.append(node) + + def insert(self, index, item): + if isinstance(item, Node): + self.setup_child(item) + self.children.insert(index, item) + elif item is not None: + self[index:index] = item + + def pop(self, i=-1): + return self.children.pop(i) + + def remove(self, item): + self.children.remove(item) + + def index(self, item): + return self.children.index(item) + + def is_not_default(self, key): + if self[key] == [] and key in self.list_attributes: + return 0 + else: + return 1 + + def update_basic_atts(self, dict): + """ + Update basic attributes ('ids', 'names', 'classes', + 'dupnames', but not 'source') from node or dictionary `dict`. + """ + if isinstance(dict, Node): + dict = dict.attributes + for att in ('ids', 'classes', 'names', 'dupnames'): + for value in dict.get(att, []): + if not value in self[att]: + self[att].append(value) + + def clear(self): + self.children = [] + + def replace(self, old, new): + """Replace one child `Node` with another child or children.""" + index = self.index(old) + if isinstance(new, Node): + self.setup_child(new) + self[index] = new + elif new is not None: + self[index:index+1] = new + + def replace_self(self, new): + """ + Replace `self` node with `new`, where `new` is a node or a + list of nodes. + """ + update = new + if not isinstance(new, Node): + # `new` is a list; update first child. + try: + update = new[0] + except IndexError: + update = None + if isinstance(update, Element): + update.update_basic_atts(self) + else: + # `update` is a Text node or `new` is an empty list. + # Assert that we aren't losing any attributes. + for att in ('ids', 'names', 'classes', 'dupnames'): + assert not self[att], \ + 'Losing "%s" attribute: %s' % (att, self[att]) + self.parent.replace(self, new) + + def first_child_matching_class(self, childclass, start=0, end=sys.maxint): + """ + Return the index of the first child whose class exactly matches. + + Parameters: + + - `childclass`: A `Node` subclass to search for, or a tuple of `Node` + classes. If a tuple, any of the classes may match. + - `start`: Initial index to check. + - `end`: Initial index to *not* check. + """ + if not isinstance(childclass, tuple): + childclass = (childclass,) + for index in range(start, min(len(self), end)): + for c in childclass: + if isinstance(self[index], c): + return index + return None + + def first_child_not_matching_class(self, childclass, start=0, + end=sys.maxint): + """ + Return the index of the first child whose class does *not* match. + + Parameters: + + - `childclass`: A `Node` subclass to skip, or a tuple of `Node` + classes. If a tuple, none of the classes may match. + - `start`: Initial index to check. + - `end`: Initial index to *not* check. + """ + if not isinstance(childclass, tuple): + childclass = (childclass,) + for index in range(start, min(len(self), end)): + for c in childclass: + if isinstance(self.children[index], c): + break + else: + return index + return None + + def pformat(self, indent=' ', level=0): + return ''.join(['%s%s\n' % (indent * level, self.starttag())] + + [child.pformat(indent, level+1) + for child in self.children]) + + def copy(self): + return self.__class__(**self.attributes) + + def deepcopy(self): + copy = self.copy() + copy.extend([child.deepcopy() for child in self.children]) + return copy + + def set_class(self, name): + """Add a new class to the "classes" attribute.""" + warnings.warn('docutils.nodes.Element.set_class deprecated; ' + "append to Element['classes'] list attribute directly", + DeprecationWarning, stacklevel=2) + assert ' ' not in name + self['classes'].append(name.lower()) + + def note_referenced_by(self, name=None, id=None): + """Note that this Element has been referenced by its name + `name` or id `id`.""" + self.referenced = 1 + # Element.expect_referenced_by_* dictionaries map names or ids + # to nodes whose ``referenced`` attribute is set to true as + # soon as this node is referenced by the given name or id. + # Needed for target propagation. + by_name = getattr(self, 'expect_referenced_by_name', {}).get(name) + by_id = getattr(self, 'expect_referenced_by_id', {}).get(id) + if by_name: + assert name is not None + by_name.referenced = 1 + if by_id: + assert id is not None + by_id.referenced = 1 + + +class TextElement(Element): + + """ + An element which directly contains text. + + Its children are all `Text` or `Inline` subclass nodes. You can + check whether an element's context is inline simply by checking whether + its immediate parent is a `TextElement` instance (including subclasses). + This is handy for nodes like `image` that can appear both inline and as + standalone body elements. + + If passing children to `__init__()`, make sure to set `text` to + ``''`` or some other suitable value. + """ + + child_text_separator = '' + """Separator for child nodes, used by `astext()` method.""" + + def __init__(self, rawsource='', text='', *children, **attributes): + if text != '': + textnode = Text(text) + Element.__init__(self, rawsource, textnode, *children, + **attributes) + else: + Element.__init__(self, rawsource, *children, **attributes) + + +class FixedTextElement(TextElement): + + """An element which directly contains preformatted text.""" + + def __init__(self, rawsource='', text='', *children, **attributes): + TextElement.__init__(self, rawsource, text, *children, **attributes) + self.attributes['xml:space'] = 'preserve' + + +# ======== +# Mixins +# ======== + +class Resolvable: + + resolved = 0 + + +class BackLinkable: + + def add_backref(self, refid): + self['backrefs'].append(refid) + + +# ==================== +# Element Categories +# ==================== + +class Root: pass + +class Titular: pass + +class PreBibliographic: + """Category of Node which may occur before Bibliographic Nodes.""" + +class Bibliographic: pass + +class Decorative(PreBibliographic): pass + +class Structural: pass + +class Body: pass + +class General(Body): pass + +class Sequential(Body): + """List-like elements.""" + +class Admonition(Body): pass + +class Special(Body): + """Special internal body elements.""" + +class Invisible(PreBibliographic): + """Internal elements that don't appear in output.""" + +class Part: pass + +class Inline: pass + +class Referential(Resolvable): pass + + +class Targetable(Resolvable): + + referenced = 0 + + indirect_reference_name = None + """Holds the whitespace_normalized_name (contains mixed case) of a target. + Required for MoinMoin/reST compatibility.""" + + +class Labeled: + """Contains a `label` as its first element.""" + + +# ============== +# Root Element +# ============== + +class document(Root, Structural, Element): + + """ + The document root element. + + Do not instantiate this class directly; use + `docutils.utils.new_document()` instead. + """ + + def __init__(self, settings, reporter, *args, **kwargs): + Element.__init__(self, *args, **kwargs) + + self.current_source = None + """Path to or description of the input source being processed.""" + + self.current_line = None + """Line number (1-based) of `current_source`.""" + + self.settings = settings + """Runtime settings data record.""" + + self.reporter = reporter + """System message generator.""" + + self.indirect_targets = [] + """List of indirect target nodes.""" + + self.substitution_defs = {} + """Mapping of substitution names to substitution_definition nodes.""" + + self.substitution_names = {} + """Mapping of case-normalized substitution names to case-sensitive + names.""" + + self.refnames = {} + """Mapping of names to lists of referencing nodes.""" + + self.refids = {} + """Mapping of ids to lists of referencing nodes.""" + + self.nameids = {} + """Mapping of names to unique id's.""" + + self.nametypes = {} + """Mapping of names to hyperlink type (boolean: True => explicit, + False => implicit.""" + + self.ids = {} + """Mapping of ids to nodes.""" + + self.footnote_refs = {} + """Mapping of footnote labels to lists of footnote_reference nodes.""" + + self.citation_refs = {} + """Mapping of citation labels to lists of citation_reference nodes.""" + + self.autofootnotes = [] + """List of auto-numbered footnote nodes.""" + + self.autofootnote_refs = [] + """List of auto-numbered footnote_reference nodes.""" + + self.symbol_footnotes = [] + """List of symbol footnote nodes.""" + + self.symbol_footnote_refs = [] + """List of symbol footnote_reference nodes.""" + + self.footnotes = [] + """List of manually-numbered footnote nodes.""" + + self.citations = [] + """List of citation nodes.""" + + self.autofootnote_start = 1 + """Initial auto-numbered footnote number.""" + + self.symbol_footnote_start = 0 + """Initial symbol footnote symbol index.""" + + self.id_start = 1 + """Initial ID number.""" + + self.parse_messages = [] + """System messages generated while parsing.""" + + self.transform_messages = [] + """System messages generated while applying transforms.""" + + import docutils.transforms + self.transformer = docutils.transforms.Transformer(self) + """Storage for transforms to be applied to this document.""" + + self.decoration = None + """Document's `decoration` node.""" + + self.document = self + + def __getstate__(self): + """ + Return dict with unpicklable references removed. + """ + state = self.__dict__.copy() + state['reporter'] = None + state['transformer'] = None + return state + + def asdom(self, dom=None): + """Return a DOM representation of this document.""" + if dom is None: + import xml.dom.minidom as dom + domroot = dom.Document() + domroot.appendChild(self._dom_node(domroot)) + return domroot + + def set_id(self, node, msgnode=None): + for id in node['ids']: + if id in self.ids and self.ids[id] is not node: + msg = self.reporter.severe('Duplicate ID: "%s".' % id) + if msgnode != None: + msgnode += msg + if not node['ids']: + for name in node['names']: + id = self.settings.id_prefix + make_id(name) + if id and id not in self.ids: + break + else: + id = '' + while not id or id in self.ids: + id = (self.settings.id_prefix + + self.settings.auto_id_prefix + str(self.id_start)) + self.id_start += 1 + node['ids'].append(id) + self.ids[id] = node + return id + + def set_name_id_map(self, node, id, msgnode=None, explicit=None): + """ + `self.nameids` maps names to IDs, while `self.nametypes` maps names to + booleans representing hyperlink type (True==explicit, + False==implicit). This method updates the mappings. + + The following state transition table shows how `self.nameids` ("ids") + and `self.nametypes` ("types") change with new input (a call to this + method), and what actions are performed ("implicit"-type system + messages are INFO/1, and "explicit"-type system messages are ERROR/3): + + ==== ===== ======== ======== ======= ==== ===== ===== + Old State Input Action New State Notes + ----------- -------- ----------------- ----------- ----- + ids types new type sys.msg. dupname ids types + ==== ===== ======== ======== ======= ==== ===== ===== + - - explicit - - new True + - - implicit - - new False + None False explicit - - new True + old False explicit implicit old new True + None True explicit explicit new None True + old True explicit explicit new,old None True [#]_ + None False implicit implicit new None False + old False implicit implicit new,old None False + None True implicit implicit new None True + old True implicit implicit new old True + ==== ===== ======== ======== ======= ==== ===== ===== + + .. [#] Do not clear the name-to-id map or invalidate the old target if + both old and new targets are external and refer to identical URIs. + The new target is invalidated regardless. + """ + for name in node['names']: + if name in self.nameids: + self.set_duplicate_name_id(node, id, name, msgnode, explicit) + else: + self.nameids[name] = id + self.nametypes[name] = explicit + + def set_duplicate_name_id(self, node, id, name, msgnode, explicit): + old_id = self.nameids[name] + old_explicit = self.nametypes[name] + self.nametypes[name] = old_explicit or explicit + if explicit: + if old_explicit: + level = 2 + if old_id is not None: + old_node = self.ids[old_id] + if 'refuri' in node: + refuri = node['refuri'] + if old_node['names'] \ + and 'refuri' in old_node \ + and old_node['refuri'] == refuri: + level = 1 # just inform if refuri's identical + if level > 1: + dupname(old_node, name) + self.nameids[name] = None + msg = self.reporter.system_message( + level, 'Duplicate explicit target name: "%s".' % name, + backrefs=[id], base_node=node) + if msgnode != None: + msgnode += msg + dupname(node, name) + else: + self.nameids[name] = id + if old_id is not None: + old_node = self.ids[old_id] + dupname(old_node, name) + else: + if old_id is not None and not old_explicit: + self.nameids[name] = None + old_node = self.ids[old_id] + dupname(old_node, name) + dupname(node, name) + if not explicit or (not old_explicit and old_id is not None): + msg = self.reporter.info( + 'Duplicate implicit target name: "%s".' % name, + backrefs=[id], base_node=node) + if msgnode != None: + msgnode += msg + + def has_name(self, name): + return name in self.nameids + + # "note" here is an imperative verb: "take note of". + def note_implicit_target(self, target, msgnode=None): + id = self.set_id(target, msgnode) + self.set_name_id_map(target, id, msgnode, explicit=None) + + def note_explicit_target(self, target, msgnode=None): + id = self.set_id(target, msgnode) + self.set_name_id_map(target, id, msgnode, explicit=1) + + def note_refname(self, node): + self.refnames.setdefault(node['refname'], []).append(node) + + def note_refid(self, node): + self.refids.setdefault(node['refid'], []).append(node) + + def note_indirect_target(self, target): + self.indirect_targets.append(target) + if target['names']: + self.note_refname(target) + + def note_anonymous_target(self, target): + self.set_id(target) + + def note_autofootnote(self, footnote): + self.set_id(footnote) + self.autofootnotes.append(footnote) + + def note_autofootnote_ref(self, ref): + self.set_id(ref) + self.autofootnote_refs.append(ref) + + def note_symbol_footnote(self, footnote): + self.set_id(footnote) + self.symbol_footnotes.append(footnote) + + def note_symbol_footnote_ref(self, ref): + self.set_id(ref) + self.symbol_footnote_refs.append(ref) + + def note_footnote(self, footnote): + self.set_id(footnote) + self.footnotes.append(footnote) + + def note_footnote_ref(self, ref): + self.set_id(ref) + self.footnote_refs.setdefault(ref['refname'], []).append(ref) + self.note_refname(ref) + + def note_citation(self, citation): + self.citations.append(citation) + + def note_citation_ref(self, ref): + self.set_id(ref) + self.citation_refs.setdefault(ref['refname'], []).append(ref) + self.note_refname(ref) + + def note_substitution_def(self, subdef, def_name, msgnode=None): + name = whitespace_normalize_name(def_name) + if name in self.substitution_defs: + msg = self.reporter.error( + 'Duplicate substitution definition name: "%s".' % name, + base_node=subdef) + if msgnode != None: + msgnode += msg + oldnode = self.substitution_defs[name] + dupname(oldnode, name) + # keep only the last definition: + self.substitution_defs[name] = subdef + # case-insensitive mapping: + self.substitution_names[fully_normalize_name(name)] = name + + def note_substitution_ref(self, subref, refname): + subref['refname'] = whitespace_normalize_name(refname) + + def note_pending(self, pending, priority=None): + self.transformer.add_pending(pending, priority) + + def note_parse_message(self, message): + self.parse_messages.append(message) + + def note_transform_message(self, message): + self.transform_messages.append(message) + + def note_source(self, source, offset): + self.current_source = source + if offset is None: + self.current_line = offset + else: + self.current_line = offset + 1 + + def copy(self): + return self.__class__(self.settings, self.reporter, + **self.attributes) + + def get_decoration(self): + if not self.decoration: + self.decoration = decoration() + index = self.first_child_not_matching_class(Titular) + if index is None: + self.append(self.decoration) + else: + self.insert(index, self.decoration) + return self.decoration + + +# ================ +# Title Elements +# ================ + +class title(Titular, PreBibliographic, TextElement): pass +class subtitle(Titular, PreBibliographic, TextElement): pass +class rubric(Titular, TextElement): pass + + +# ======================== +# Bibliographic Elements +# ======================== + +class docinfo(Bibliographic, Element): pass +class author(Bibliographic, TextElement): pass +class authors(Bibliographic, Element): pass +class organization(Bibliographic, TextElement): pass +class address(Bibliographic, FixedTextElement): pass +class contact(Bibliographic, TextElement): pass +class version(Bibliographic, TextElement): pass +class revision(Bibliographic, TextElement): pass +class status(Bibliographic, TextElement): pass +class date(Bibliographic, TextElement): pass +class copyright(Bibliographic, TextElement): pass + + +# ===================== +# Decorative Elements +# ===================== + +class decoration(Decorative, Element): + + def get_header(self): + if not len(self.children) or not isinstance(self.children[0], header): + self.insert(0, header()) + return self.children[0] + + def get_footer(self): + if not len(self.children) or not isinstance(self.children[-1], footer): + self.append(footer()) + return self.children[-1] + + +class header(Decorative, Element): pass +class footer(Decorative, Element): pass + + +# ===================== +# Structural Elements +# ===================== + +class section(Structural, Element): pass + + +class topic(Structural, Element): + + """ + Topics are terminal, "leaf" mini-sections, like block quotes with titles, + or textual figures. A topic is just like a section, except that it has no + subsections, and it doesn't have to conform to section placement rules. + + Topics are allowed wherever body elements (list, table, etc.) are allowed, + but only at the top level of a section or document. Topics cannot nest + inside topics, sidebars, or body elements; you can't have a topic inside a + table, list, block quote, etc. + """ + + +class sidebar(Structural, Element): + + """ + Sidebars are like miniature, parallel documents that occur inside other + documents, providing related or reference material. A sidebar is + typically offset by a border and "floats" to the side of the page; the + document's main text may flow around it. Sidebars can also be likened to + super-footnotes; their content is outside of the flow of the document's + main text. + + Sidebars are allowed wherever body elements (list, table, etc.) are + allowed, but only at the top level of a section or document. Sidebars + cannot nest inside sidebars, topics, or body elements; you can't have a + sidebar inside a table, list, block quote, etc. + """ + + +class transition(Structural, Element): pass + + +# =============== +# Body Elements +# =============== + +class paragraph(General, TextElement): pass +class compound(General, Element): pass +class container(General, Element): pass +class bullet_list(Sequential, Element): pass +class enumerated_list(Sequential, Element): pass +class list_item(Part, Element): pass +class definition_list(Sequential, Element): pass +class definition_list_item(Part, Element): pass +class term(Part, TextElement): pass +class classifier(Part, TextElement): pass +class definition(Part, Element): pass +class field_list(Sequential, Element): pass +class field(Part, Element): pass +class field_name(Part, TextElement): pass +class field_body(Part, Element): pass + + +class option(Part, Element): + + child_text_separator = '' + + +class option_argument(Part, TextElement): + + def astext(self): + return self.get('delimiter', ' ') + TextElement.astext(self) + + +class option_group(Part, Element): + + child_text_separator = ', ' + + +class option_list(Sequential, Element): pass + + +class option_list_item(Part, Element): + + child_text_separator = ' ' + + +class option_string(Part, TextElement): pass +class description(Part, Element): pass +class literal_block(General, FixedTextElement): pass +class doctest_block(General, FixedTextElement): pass +class line_block(General, Element): pass + + +class line(Part, TextElement): + + indent = None + + +class block_quote(General, Element): pass +class attribution(Part, TextElement): pass +class attention(Admonition, Element): pass +class caution(Admonition, Element): pass +class danger(Admonition, Element): pass +class error(Admonition, Element): pass +class important(Admonition, Element): pass +class note(Admonition, Element): pass +class tip(Admonition, Element): pass +class hint(Admonition, Element): pass +class warning(Admonition, Element): pass +class admonition(Admonition, Element): pass +class comment(Special, Invisible, FixedTextElement): pass +class substitution_definition(Special, Invisible, TextElement): pass +class target(Special, Invisible, Inline, TextElement, Targetable): pass +class footnote(General, BackLinkable, Element, Labeled, Targetable): pass +class citation(General, BackLinkable, Element, Labeled, Targetable): pass +class label(Part, TextElement): pass +class figure(General, Element): pass +class caption(Part, TextElement): pass +class legend(Part, Element): pass +class table(General, Element): pass +class tgroup(Part, Element): pass +class colspec(Part, Element): pass +class thead(Part, Element): pass +class tbody(Part, Element): pass +class row(Part, Element): pass +class entry(Part, Element): pass + + +class system_message(Special, BackLinkable, PreBibliographic, Element): + + """ + System message element. + + Do not instantiate this class directly; use + ``document.reporter.info/warning/error/severe()`` instead. + """ + + def __init__(self, message=None, *children, **attributes): + if message: + p = paragraph('', message) + children = (p,) + children + try: + Element.__init__(self, '', *children, **attributes) + except: + print 'system_message: children=%r' % (children,) + raise + + def astext(self): + line = self.get('line', '') + return u'%s:%s: (%s/%s) %s' % (self['source'], line, self['type'], + self['level'], Element.astext(self)) + + +class pending(Special, Invisible, Element): + + """ + The "pending" element is used to encapsulate a pending operation: the + operation (transform), the point at which to apply it, and any data it + requires. Only the pending operation's location within the document is + stored in the public document tree (by the "pending" object itself); the + operation and its data are stored in the "pending" object's internal + instance attributes. + + For example, say you want a table of contents in your reStructuredText + document. The easiest way to specify where to put it is from within the + document, with a directive:: + + .. contents:: + + But the "contents" directive can't do its work until the entire document + has been parsed and possibly transformed to some extent. So the directive + code leaves a placeholder behind that will trigger the second phase of its + processing, something like this:: + + + internal attributes + + Use `document.note_pending()` so that the + `docutils.transforms.Transformer` stage of processing can run all pending + transforms. + """ + + def __init__(self, transform, details=None, + rawsource='', *children, **attributes): + Element.__init__(self, rawsource, *children, **attributes) + + self.transform = transform + """The `docutils.transforms.Transform` class implementing the pending + operation.""" + + self.details = details or {} + """Detail data (dictionary) required by the pending operation.""" + + def pformat(self, indent=' ', level=0): + internals = [ + '.. internal attributes:', + ' .transform: %s.%s' % (self.transform.__module__, + self.transform.__name__), + ' .details:'] + details = self.details.items() + details.sort() + for key, value in details: + if isinstance(value, Node): + internals.append('%7s%s:' % ('', key)) + internals.extend(['%9s%s' % ('', line) + for line in value.pformat().splitlines()]) + elif value and isinstance(value, list) \ + and isinstance(value[0], Node): + internals.append('%7s%s:' % ('', key)) + for v in value: + internals.extend(['%9s%s' % ('', line) + for line in v.pformat().splitlines()]) + else: + internals.append('%7s%s: %r' % ('', key, value)) + return (Element.pformat(self, indent, level) + + ''.join([(' %s%s\n' % (indent * level, line)) + for line in internals])) + + def copy(self): + return self.__class__(self.transform, self.details, self.rawsource, + **self.attributes) + + +class raw(Special, Inline, PreBibliographic, FixedTextElement): + + """ + Raw data that is to be passed untouched to the Writer. + """ + + pass + + +# ================= +# Inline Elements +# ================= + +class emphasis(Inline, TextElement): pass +class strong(Inline, TextElement): pass +class literal(Inline, TextElement): pass +class reference(General, Inline, Referential, TextElement): pass +class footnote_reference(Inline, Referential, TextElement): pass +class citation_reference(Inline, Referential, TextElement): pass +class substitution_reference(Inline, TextElement): pass +class title_reference(Inline, TextElement): pass +class abbreviation(Inline, TextElement): pass +class acronym(Inline, TextElement): pass +class superscript(Inline, TextElement): pass +class subscript(Inline, TextElement): pass + + +class image(General, Inline, Element): + + def astext(self): + return self.get('alt', '') + + +class inline(Inline, TextElement): pass +class problematic(Inline, TextElement): pass +class generated(Inline, TextElement): pass + + +# ======================================== +# Auxiliary Classes, Functions, and Data +# ======================================== + +node_class_names = """ + Text + abbreviation acronym address admonition attention attribution author + authors + block_quote bullet_list + caption caution citation citation_reference classifier colspec comment + compound contact container copyright + danger date decoration definition definition_list definition_list_item + description docinfo doctest_block document + emphasis entry enumerated_list error + field field_body field_list field_name figure footer + footnote footnote_reference + generated + header hint + image important inline + label legend line line_block list_item literal literal_block + note + option option_argument option_group option_list option_list_item + option_string organization + paragraph pending problematic + raw reference revision row rubric + section sidebar status strong subscript substitution_definition + substitution_reference subtitle superscript system_message + table target tbody term tgroup thead tip title title_reference topic + transition + version + warning""".split() +"""A list of names of all concrete Node subclasses.""" + + +class NodeVisitor: + + """ + "Visitor" pattern [GoF95]_ abstract superclass implementation for + document tree traversals. + + Each node class has corresponding methods, doing nothing by + default; override individual methods for specific and useful + behaviour. The `dispatch_visit()` method is called by + `Node.walk()` upon entering a node. `Node.walkabout()` also calls + the `dispatch_departure()` method before exiting a node. + + The dispatch methods call "``visit_`` + node class name" or + "``depart_`` + node class name", resp. + + This is a base class for visitors whose ``visit_...`` & ``depart_...`` + methods should be implemented for *all* node types encountered (such as + for `docutils.writers.Writer` subclasses). Unimplemented methods will + raise exceptions. + + For sparse traversals, where only certain node types are of interest, + subclass `SparseNodeVisitor` instead. When (mostly or entirely) uniform + processing is desired, subclass `GenericNodeVisitor`. + + .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of + Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA, + 1995. + """ + + optional = () + """ + Tuple containing node class names (as strings). + + No exception will be raised if writers do not implement visit + or departure functions for these node classes. + + Used to ensure transitional compatibility with existing 3rd-party writers. + """ + + def __init__(self, document): + self.document = document + + def dispatch_visit(self, node): + """ + Call self."``visit_`` + node class name" with `node` as + parameter. If the ``visit_...`` method does not exist, call + self.unknown_visit. + """ + node_name = node.__class__.__name__ + method = getattr(self, 'visit_' + node_name, self.unknown_visit) + self.document.reporter.debug( + 'docutils.nodes.NodeVisitor.dispatch_visit calling %s for %s' + % (method.__name__, node_name)) + return method(node) + + def dispatch_departure(self, node): + """ + Call self."``depart_`` + node class name" with `node` as + parameter. If the ``depart_...`` method does not exist, call + self.unknown_departure. + """ + node_name = node.__class__.__name__ + method = getattr(self, 'depart_' + node_name, self.unknown_departure) + self.document.reporter.debug( + 'docutils.nodes.NodeVisitor.dispatch_departure calling %s for %s' + % (method.__name__, node_name)) + return method(node) + + def unknown_visit(self, node): + """ + Called when entering unknown `Node` types. + + Raise an exception unless overridden. + """ + if (self.document.settings.strict_visitor + or node.__class__.__name__ not in self.optional): + raise NotImplementedError( + '%s visiting unknown node type: %s' + % (self.__class__, node.__class__.__name__)) + + def unknown_departure(self, node): + """ + Called before exiting unknown `Node` types. + + Raise exception unless overridden. + """ + if (self.document.settings.strict_visitor + or node.__class__.__name__ not in self.optional): + raise NotImplementedError( + '%s departing unknown node type: %s' + % (self.__class__, node.__class__.__name__)) + + +class SparseNodeVisitor(NodeVisitor): + + """ + Base class for sparse traversals, where only certain node types are of + interest. When ``visit_...`` & ``depart_...`` methods should be + implemented for *all* node types (such as for `docutils.writers.Writer` + subclasses), subclass `NodeVisitor` instead. + """ + + +class GenericNodeVisitor(NodeVisitor): + + """ + Generic "Visitor" abstract superclass, for simple traversals. + + Unless overridden, each ``visit_...`` method calls `default_visit()`, and + each ``depart_...`` method (when using `Node.walkabout()`) calls + `default_departure()`. `default_visit()` (and `default_departure()`) must + be overridden in subclasses. + + Define fully generic visitors by overriding `default_visit()` (and + `default_departure()`) only. Define semi-generic visitors by overriding + individual ``visit_...()`` (and ``depart_...()``) methods also. + + `NodeVisitor.unknown_visit()` (`NodeVisitor.unknown_departure()`) should + be overridden for default behavior. + """ + + def default_visit(self, node): + """Override for generic, uniform traversals.""" + raise NotImplementedError + + def default_departure(self, node): + """Override for generic, uniform traversals.""" + raise NotImplementedError + +def _call_default_visit(self, node): + self.default_visit(node) + +def _call_default_departure(self, node): + self.default_departure(node) + +def _nop(self, node): + pass + +def _add_node_class_names(names): + """Save typing with dynamic assignments:""" + for _name in names: + setattr(GenericNodeVisitor, "visit_" + _name, _call_default_visit) + setattr(GenericNodeVisitor, "depart_" + _name, _call_default_departure) + setattr(SparseNodeVisitor, 'visit_' + _name, _nop) + setattr(SparseNodeVisitor, 'depart_' + _name, _nop) + +_add_node_class_names(node_class_names) + + +class TreeCopyVisitor(GenericNodeVisitor): + + """ + Make a complete copy of a tree or branch, including element attributes. + """ + + def __init__(self, document): + GenericNodeVisitor.__init__(self, document) + self.parent_stack = [] + self.parent = [] + + def get_tree_copy(self): + return self.parent[0] + + def default_visit(self, node): + """Copy the current node, and make it the new acting parent.""" + newnode = node.copy() + self.parent.append(newnode) + self.parent_stack.append(self.parent) + self.parent = newnode + + def default_departure(self, node): + """Restore the previous acting parent.""" + self.parent = self.parent_stack.pop() + + +class TreePruningException(Exception): + + """ + Base class for `NodeVisitor`-related tree pruning exceptions. + + Raise subclasses from within ``visit_...`` or ``depart_...`` methods + called from `Node.walk()` and `Node.walkabout()` tree traversals to prune + the tree traversed. + """ + + pass + + +class SkipChildren(TreePruningException): + + """ + Do not visit any children of the current node. The current node's + siblings and ``depart_...`` method are not affected. + """ + + pass + + +class SkipSiblings(TreePruningException): + + """ + Do not visit any more siblings (to the right) of the current node. The + current node's children and its ``depart_...`` method are not affected. + """ + + pass + + +class SkipNode(TreePruningException): + + """ + Do not visit the current node's children, and do not call the current + node's ``depart_...`` method. + """ + + pass + + +class SkipDeparture(TreePruningException): + + """ + Do not call the current node's ``depart_...`` method. The current node's + children and siblings are not affected. + """ + + pass + + +class NodeFound(TreePruningException): + + """ + Raise to indicate that the target of a search has been found. This + exception must be caught by the client; it is not caught by the traversal + code. + """ + + pass + + +class StopTraversal(TreePruningException): + + """ + Stop the traversal alltogether. The current node's ``depart_...`` method + is not affected. The parent nodes ``depart_...`` methods are also called + as usual. No other nodes are visited. This is an alternative to + NodeFound that does not cause exception handling to trickle up to the + caller. + """ + + pass + + +def make_id(string): + """ + Convert `string` into an identifier and return it. + + Docutils identifiers will conform to the regular expression + ``[a-z](-?[a-z0-9]+)*``. For CSS compatibility, identifiers (the "class" + and "id" attributes) should have no underscores, colons, or periods. + Hyphens may be used. + + - The `HTML 4.01 spec`_ defines identifiers based on SGML tokens: + + ID and NAME tokens must begin with a letter ([A-Za-z]) and may be + followed by any number of letters, digits ([0-9]), hyphens ("-"), + underscores ("_"), colons (":"), and periods ("."). + + - However the `CSS1 spec`_ defines identifiers based on the "name" token, + a tighter interpretation ("flex" tokenizer notation; "latin1" and + "escape" 8-bit characters have been replaced with entities):: + + unicode \\[0-9a-f]{1,4} + latin1 [¡-ÿ] + escape {unicode}|\\[ -~¡-ÿ] + nmchar [-a-z0-9]|{latin1}|{escape} + name {nmchar}+ + + The CSS1 "nmchar" rule does not include underscores ("_"), colons (":"), + or periods ("."), therefore "class" and "id" attributes should not contain + these characters. They should be replaced with hyphens ("-"). Combined + with HTML's requirements (the first character must be a letter; no + "unicode", "latin1", or "escape" characters), this results in the + ``[a-z](-?[a-z0-9]+)*`` pattern. + + .. _HTML 4.01 spec: http://www.w3.org/TR/html401 + .. _CSS1 spec: http://www.w3.org/TR/REC-CSS1 + """ + id = string.lower() + if not isinstance(id, unicode): + id = id.decode() + id = id.translate(_non_id_translate_digraphs) + id = id.translate(_non_id_translate) + # get rid of non-ascii characters + id = unicodedata.normalize('NFKD', id).\ + encode('ASCII', 'ignore').decode('ASCII') + # shrink runs of whitespace and replace by hyphen + id = _non_id_chars.sub('-', ' '.join(id.split())) + id = _non_id_at_ends.sub('', id) + return str(id) + +_non_id_chars = re.compile('[^a-z0-9]+') +_non_id_at_ends = re.compile('^[-0-9]+|-+$') +_non_id_translate = { + 0x00f8: u'o', # o with stroke + 0x0111: u'd', # d with stroke + 0x0127: u'h', # h with stroke + 0x0131: u'i', # dotless i + 0x0142: u'l', # l with stroke + 0x0167: u't', # t with stroke + 0x0180: u'b', # b with stroke + 0x0183: u'b', # b with topbar + 0x0188: u'c', # c with hook + 0x018c: u'd', # d with topbar + 0x0192: u'f', # f with hook + 0x0199: u'k', # k with hook + 0x019a: u'l', # l with bar + 0x019e: u'n', # n with long right leg + 0x01a5: u'p', # p with hook + 0x01ab: u't', # t with palatal hook + 0x01ad: u't', # t with hook + 0x01b4: u'y', # y with hook + 0x01b6: u'z', # z with stroke + 0x01e5: u'g', # g with stroke + 0x0225: u'z', # z with hook + 0x0234: u'l', # l with curl + 0x0235: u'n', # n with curl + 0x0236: u't', # t with curl + 0x0237: u'j', # dotless j + 0x023c: u'c', # c with stroke + 0x023f: u's', # s with swash tail + 0x0240: u'z', # z with swash tail + 0x0247: u'e', # e with stroke + 0x0249: u'j', # j with stroke + 0x024b: u'q', # q with hook tail + 0x024d: u'r', # r with stroke + 0x024f: u'y', # y with stroke +} +_non_id_translate_digraphs = { + 0x00df: u'sz', # ligature sz + 0x00e6: u'ae', # ae + 0x0153: u'oe', # ligature oe + 0x0238: u'db', # db digraph + 0x0239: u'qp', # qp digraph +} + +def dupname(node, name): + node['dupnames'].append(name) + node['names'].remove(name) + # Assume that this method is referenced, even though it isn't; we + # don't want to throw unnecessary system_messages. + node.referenced = 1 + +def fully_normalize_name(name): + """Return a case- and whitespace-normalized name.""" + return ' '.join(name.lower().split()) + +def whitespace_normalize_name(name): + """Return a whitespace-normalized name.""" + return ' '.join(name.split()) + +def serial_escape(value): + """Escape string values that are elements of a list, for serialization.""" + return value.replace('\\', r'\\').replace(' ', r'\ ') + +# +# +# Local Variables: +# indent-tabs-mode: nil +# sentence-end-double-space: t +# fill-column: 78 +# End: diff -Nru zope3-3.4.0/src/docutils/parsers/__init__.py zope3-3.5~bzr18/src/docutils/parsers/__init__.py --- zope3-3.4.0/src/docutils/parsers/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ +# $Id: __init__.py 5618 2008-07-28 08:37:32Z strank $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This package contains Docutils parser modules. +""" + +__docformat__ = 'reStructuredText' + +from docutils import Component + + +class Parser(Component): + + component_type = 'parser' + config_section = 'parsers' + + def parse(self, inputstring, document): + """Override to parse `inputstring` into document tree `document`.""" + raise NotImplementedError('subclass must override this method') + + def setup_parse(self, inputstring, document): + """Initial parse setup. Call at start of `self.parse()`.""" + self.inputstring = inputstring + self.document = document + document.reporter.attach_observer(document.note_parse_message) + + def finish_parse(self): + """Finalize parse details. Call at end of `self.parse()`.""" + self.document.reporter.detach_observer( + self.document.note_parse_message) + + +_parser_aliases = { + 'restructuredtext': 'rst', + 'rest': 'rst', + 'restx': 'rst', + 'rtxt': 'rst',} + +def get_parser_class(parser_name): + """Return the Parser class from the `parser_name` module.""" + parser_name = parser_name.lower() + if parser_name in _parser_aliases: + parser_name = _parser_aliases[parser_name] + module = __import__(parser_name, globals(), locals()) + return module.Parser diff -Nru zope3-3.4.0/src/docutils/parsers/null.py zope3-3.5~bzr18/src/docutils/parsers/null.py --- zope3-3.4.0/src/docutils/parsers/null.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/null.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,20 @@ +# $Id: null.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Martin Blais +# Copyright: This module has been placed in the public domain. + +"""A do-nothing parser.""" + +from docutils import parsers + + +class Parser(parsers.Parser): + + """A do-nothing parser.""" + + supported = ('null',) + + config_section = 'null parser' + config_section_dependencies = ('parsers',) + + def parse(self, inputstring, document): + pass diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/admonitions.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/admonitions.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/admonitions.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/admonitions.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,97 @@ +# $Id: admonitions.py 5618 2008-07-28 08:37:32Z strank $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Admonition directives. +""" + +__docformat__ = 'reStructuredText' + + +from docutils.parsers.rst import Directive +from docutils.parsers.rst import states, directives +from docutils import nodes + + +class BaseAdmonition(Directive): + + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {} + has_content = True + + node_class = None + """Subclasses must set this to the appropriate admonition node class.""" + + def run(self): + self.assert_has_content() + text = '\n'.join(self.content) + admonition_node = self.node_class(text) + if self.arguments: + title_text = self.arguments[0] + textnodes, messages = self.state.inline_text(title_text, + self.lineno) + admonition_node += nodes.title(title_text, '', *textnodes) + admonition_node += messages + if 'class' in self.options: + classes = self.options['class'] + else: + classes = ['admonition-' + nodes.make_id(title_text)] + admonition_node['classes'] += classes + self.state.nested_parse(self.content, self.content_offset, + admonition_node) + return [admonition_node] + + +class Admonition(BaseAdmonition): + + required_arguments = 1 + option_spec = {'class': directives.class_option} + node_class = nodes.admonition + + +class Attention(BaseAdmonition): + + node_class = nodes.attention + + +class Caution(BaseAdmonition): + + node_class = nodes.caution + + +class Danger(BaseAdmonition): + + node_class = nodes.danger + + +class Error(BaseAdmonition): + + node_class = nodes.error + + +class Hint(BaseAdmonition): + + node_class = nodes.hint + + +class Important(BaseAdmonition): + + node_class = nodes.important + + +class Note(BaseAdmonition): + + node_class = nodes.note + + +class Tip(BaseAdmonition): + + node_class = nodes.tip + + +class Warning(BaseAdmonition): + + node_class = nodes.warning diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/body.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/body.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/body.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/body.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,192 @@ +# $Id: body.py 5618 2008-07-28 08:37:32Z strank $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Directives for additional body elements. + +See `docutils.parsers.rst.directives` for API details. +""" + +__docformat__ = 'reStructuredText' + + +import sys +from docutils import nodes +from docutils.parsers.rst import Directive +from docutils.parsers.rst import directives +from docutils.parsers.rst.roles import set_classes + + +class BasePseudoSection(Directive): + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'class': directives.class_option} + has_content = True + + node_class = None + """Node class to be used (must be set in subclasses).""" + + def run(self): + if not (self.state_machine.match_titles + or isinstance(self.state_machine.node, nodes.sidebar)): + raise self.error('The "%s" directive may not be used within ' + 'topics or body elements.' % self.name) + self.assert_has_content() + title_text = self.arguments[0] + textnodes, messages = self.state.inline_text(title_text, self.lineno) + titles = [nodes.title(title_text, '', *textnodes)] + # Sidebar uses this code. + if 'subtitle' in self.options: + textnodes, more_messages = self.state.inline_text( + self.options['subtitle'], self.lineno) + titles.append(nodes.subtitle(self.options['subtitle'], '', + *textnodes)) + messages.extend(more_messages) + text = '\n'.join(self.content) + node = self.node_class(text, *(titles + messages)) + node['classes'] += self.options.get('class', []) + if text: + self.state.nested_parse(self.content, self.content_offset, node) + return [node] + + +class Topic(BasePseudoSection): + + node_class = nodes.topic + + +class Sidebar(BasePseudoSection): + + node_class = nodes.sidebar + + option_spec = BasePseudoSection.option_spec.copy() + option_spec['subtitle'] = directives.unchanged_required + + def run(self): + if isinstance(self.state_machine.node, nodes.sidebar): + raise self.error('The "%s" directive may not be used within a ' + 'sidebar element.' % self.name) + return BasePseudoSection.run(self) + + +class LineBlock(Directive): + + option_spec = {'class': directives.class_option} + has_content = True + + def run(self): + self.assert_has_content() + block = nodes.line_block(classes=self.options.get('class', [])) + node_list = [block] + for line_text in self.content: + text_nodes, messages = self.state.inline_text( + line_text.strip(), self.lineno + self.content_offset) + line = nodes.line(line_text, '', *text_nodes) + if line_text.strip(): + line.indent = len(line_text) - len(line_text.lstrip()) + block += line + node_list.extend(messages) + self.content_offset += 1 + self.state.nest_line_block_lines(block) + return node_list + + +class ParsedLiteral(Directive): + + option_spec = {'class': directives.class_option} + has_content = True + + def run(self): + set_classes(self.options) + self.assert_has_content() + text = '\n'.join(self.content) + text_nodes, messages = self.state.inline_text(text, self.lineno) + node = nodes.literal_block(text, '', *text_nodes, **self.options) + node.line = self.content_offset + 1 + return [node] + messages + + +class Rubric(Directive): + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'class': directives.class_option} + + def run(self): + set_classes(self.options) + rubric_text = self.arguments[0] + textnodes, messages = self.state.inline_text(rubric_text, self.lineno) + rubric = nodes.rubric(rubric_text, '', *textnodes, **self.options) + return [rubric] + messages + + +class BlockQuote(Directive): + + has_content = True + classes = [] + + def run(self): + self.assert_has_content() + elements = self.state.block_quote(self.content, self.content_offset) + for element in elements: + if isinstance(element, nodes.block_quote): + element['classes'] += self.classes + return elements + + +class Epigraph(BlockQuote): + + classes = ['epigraph'] + + +class Highlights(BlockQuote): + + classes = ['highlights'] + + +class PullQuote(BlockQuote): + + classes = ['pull-quote'] + + +class Compound(Directive): + + option_spec = {'class': directives.class_option} + has_content = True + + def run(self): + self.assert_has_content() + text = '\n'.join(self.content) + node = nodes.compound(text) + node['classes'] += self.options.get('class', []) + self.state.nested_parse(self.content, self.content_offset, node) + return [node] + + +class Container(Directive): + + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + has_content = True + + def run(self): + self.assert_has_content() + text = '\n'.join(self.content) + try: + if self.arguments: + classes = directives.class_option(self.arguments[0]) + else: + classes = [] + except ValueError: + raise self.error( + 'Invalid class attribute value for "%s" directive: "%s".' + % (self.name, self.arguments[0])) + node = nodes.container(text) + node['classes'].extend(classes) + self.state.nested_parse(self.content, self.content_offset, node) + return [node] diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/html.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/html.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/html.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/html.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,88 @@ +# $Id: html.py 4667 2006-07-12 21:40:56Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Directives for typically HTML-specific constructs. +""" + +__docformat__ = 'reStructuredText' + +import sys +from docutils import nodes, utils +from docutils.parsers.rst import Directive +from docutils.parsers.rst import states +from docutils.transforms import components + + +class MetaBody(states.SpecializedBody): + + class meta(nodes.Special, nodes.PreBibliographic, nodes.Element): + """HTML-specific "meta" element.""" + pass + + def field_marker(self, match, context, next_state): + """Meta element.""" + node, blank_finish = self.parsemeta(match) + self.parent += node + return [], next_state, [] + + def parsemeta(self, match): + name = self.parse_field_marker(match) + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end()) + node = self.meta() + pending = nodes.pending(components.Filter, + {'component': 'writer', + 'format': 'html', + 'nodes': [node]}) + node['content'] = ' '.join(indented) + if not indented: + line = self.state_machine.line + msg = self.reporter.info( + 'No content for meta tag "%s".' % name, + nodes.literal_block(line, line), + line=self.state_machine.abs_line_number()) + return msg, blank_finish + tokens = name.split() + try: + attname, val = utils.extract_name_value(tokens[0])[0] + node[attname.lower()] = val + except utils.NameValueError: + node['name'] = tokens[0] + for token in tokens[1:]: + try: + attname, val = utils.extract_name_value(token)[0] + node[attname.lower()] = val + except utils.NameValueError, detail: + line = self.state_machine.line + msg = self.reporter.error( + 'Error parsing meta tag attribute "%s": %s.' + % (token, detail), nodes.literal_block(line, line), + line=self.state_machine.abs_line_number()) + return msg, blank_finish + self.document.note_pending(pending) + return pending, blank_finish + + +class Meta(Directive): + + has_content = True + + SMkwargs = {'state_classes': (MetaBody,)} + + def run(self): + self.assert_has_content() + node = nodes.Element() + new_line_offset, blank_finish = self.state.nested_list_parse( + self.content, self.content_offset, node, + initial_state='MetaBody', blank_finish=1, + state_machine_kwargs=self.SMkwargs) + if (new_line_offset - self.content_offset) != len(self.content): + # incomplete parse of block? + error = self.state_machine.reporter.error( + 'Invalid meta directive.', + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + node += error + return node.children diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/images.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/images.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/images.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/images.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,154 @@ +# $Id: images.py 5952 2009-05-19 08:45:27Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Directives for figures and simple images. +""" + +__docformat__ = 'reStructuredText' + + +import sys +from docutils import nodes, utils +from docutils.parsers.rst import Directive +from docutils.parsers.rst import directives, states +from docutils.nodes import fully_normalize_name, whitespace_normalize_name +from docutils.parsers.rst.roles import set_classes + +try: + import Image as PIL # PIL +except ImportError: + PIL = None + + +class Image(Directive): + + align_h_values = ('left', 'center', 'right') + align_v_values = ('top', 'middle', 'bottom') + align_values = align_v_values + align_h_values + + def align(argument): + # This is not callable as self.align. We cannot make it a + # staticmethod because we're saving an unbound method in + # option_spec below. + return directives.choice(argument, Image.align_values) + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'alt': directives.unchanged, + 'height': directives.length_or_unitless, + 'width': directives.length_or_percentage_or_unitless, + 'scale': directives.percentage, + 'align': align, + 'target': directives.unchanged_required, + 'class': directives.class_option} + + def run(self): + if 'align' in self.options: + if isinstance(self.state, states.SubstitutionDef): + # Check for align_v_values. + if self.options['align'] not in self.align_v_values: + raise self.error( + 'Error in "%s" directive: "%s" is not a valid value ' + 'for the "align" option within a substitution ' + 'definition. Valid values for "align" are: "%s".' + % (self.name, self.options['align'], + '", "'.join(self.align_v_values))) + elif self.options['align'] not in self.align_h_values: + raise self.error( + 'Error in "%s" directive: "%s" is not a valid value for ' + 'the "align" option. Valid values for "align" are: "%s".' + % (self.name, self.options['align'], + '", "'.join(self.align_h_values))) + messages = [] + reference = directives.uri(self.arguments[0]) + self.options['uri'] = reference + reference_node = None + if 'target' in self.options: + block = states.escape2null( + self.options['target']).splitlines() + block = [line for line in block] + target_type, data = self.state.parse_target( + block, self.block_text, self.lineno) + if target_type == 'refuri': + reference_node = nodes.reference(refuri=data) + elif target_type == 'refname': + reference_node = nodes.reference( + refname=fully_normalize_name(data), + name=whitespace_normalize_name(data)) + reference_node.indirect_reference_name = data + self.state.document.note_refname(reference_node) + else: # malformed target + messages.append(data) # data is a system message + del self.options['target'] + set_classes(self.options) + image_node = nodes.image(self.block_text, **self.options) + if reference_node: + reference_node += image_node + return messages + [reference_node] + else: + return messages + [image_node] + + +class Figure(Image): + + def align(argument): + return directives.choice(argument, Figure.align_h_values) + + def figwidth_value(argument): + if argument.lower() == 'image': + return 'image' + else: + return directives.length_or_percentage_or_unitless(argument, 'px') + + option_spec = Image.option_spec.copy() + option_spec['figwidth'] = figwidth_value + option_spec['figclass'] = directives.class_option + option_spec['align'] = align + has_content = True + + def run(self): + figwidth = self.options.pop('figwidth', None) + figclasses = self.options.pop('figclass', None) + align = self.options.pop('align', None) + (image_node,) = Image.run(self) + if isinstance(image_node, nodes.system_message): + return [image_node] + figure_node = nodes.figure('', image_node) + if figwidth == 'image': + if PIL and self.state.document.settings.file_insertion_enabled: + # PIL doesn't like Unicode paths: + try: + i = PIL.open(str(image_node['uri'])) + except (IOError, UnicodeError): + pass + else: + self.state.document.settings.record_dependencies.add( + image_node['uri']) + figure_node['width'] = i.size[0] + elif figwidth is not None: + figure_node['width'] = figwidth + if figclasses: + figure_node['classes'] += figclasses + if align: + figure_node['align'] = align + if self.content: + node = nodes.Element() # anonymous container for parsing + self.state.nested_parse(self.content, self.content_offset, node) + first_node = node[0] + if isinstance(first_node, nodes.paragraph): + caption = nodes.caption(first_node.rawsource, '', + *first_node.children) + figure_node += caption + elif not (isinstance(first_node, nodes.comment) + and len(first_node) == 0): + error = self.state_machine.reporter.error( + 'Figure caption must be a paragraph or empty comment.', + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + return [figure_node, error] + if len(node) > 1: + figure_node += nodes.legend('', *node[1:]) + return [figure_node] diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/__init__.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/__init__.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,395 @@ +# $Id: __init__.py 5952 2009-05-19 08:45:27Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This package contains directive implementation modules. +""" + +__docformat__ = 'reStructuredText' + +import re +import codecs +from docutils import nodes +from docutils.parsers.rst.languages import en as _fallback_language_module + + +_directive_registry = { + 'attention': ('admonitions', 'Attention'), + 'caution': ('admonitions', 'Caution'), + 'danger': ('admonitions', 'Danger'), + 'error': ('admonitions', 'Error'), + 'important': ('admonitions', 'Important'), + 'note': ('admonitions', 'Note'), + 'tip': ('admonitions', 'Tip'), + 'hint': ('admonitions', 'Hint'), + 'warning': ('admonitions', 'Warning'), + 'admonition': ('admonitions', 'Admonition'), + 'sidebar': ('body', 'Sidebar'), + 'topic': ('body', 'Topic'), + 'line-block': ('body', 'LineBlock'), + 'parsed-literal': ('body', 'ParsedLiteral'), + 'rubric': ('body', 'Rubric'), + 'epigraph': ('body', 'Epigraph'), + 'highlights': ('body', 'Highlights'), + 'pull-quote': ('body', 'PullQuote'), + 'compound': ('body', 'Compound'), + 'container': ('body', 'Container'), + #'questions': ('body', 'question_list'), + 'table': ('tables', 'RSTTable'), + 'csv-table': ('tables', 'CSVTable'), + 'list-table': ('tables', 'ListTable'), + 'image': ('images', 'Image'), + 'figure': ('images', 'Figure'), + 'contents': ('parts', 'Contents'), + 'sectnum': ('parts', 'Sectnum'), + 'header': ('parts', 'Header'), + 'footer': ('parts', 'Footer'), + #'footnotes': ('parts', 'footnotes'), + #'citations': ('parts', 'citations'), + 'target-notes': ('references', 'TargetNotes'), + 'meta': ('html', 'Meta'), + #'imagemap': ('html', 'imagemap'), + 'raw': ('misc', 'Raw'), + 'include': ('misc', 'Include'), + 'replace': ('misc', 'Replace'), + 'unicode': ('misc', 'Unicode'), + 'class': ('misc', 'Class'), + 'role': ('misc', 'Role'), + 'default-role': ('misc', 'DefaultRole'), + 'title': ('misc', 'Title'), + 'date': ('misc', 'Date'), + 'restructuredtext-test-directive': ('misc', 'TestDirective'),} +"""Mapping of directive name to (module name, class name). The +directive name is canonical & must be lowercase. Language-dependent +names are defined in the ``language`` subpackage.""" + +_directives = {} +"""Cache of imported directives.""" + +def directive(directive_name, language_module, document): + """ + Locate and return a directive function from its language-dependent name. + If not found in the current language, check English. Return None if the + named directive cannot be found. + """ + normname = directive_name.lower() + messages = [] + msg_text = [] + if normname in _directives: + return _directives[normname], messages + canonicalname = None + try: + canonicalname = language_module.directives[normname] + except AttributeError, error: + msg_text.append('Problem retrieving directive entry from language ' + 'module %r: %s.' % (language_module, error)) + except KeyError: + msg_text.append('No directive entry for "%s" in module "%s".' + % (directive_name, language_module.__name__)) + if not canonicalname: + try: + canonicalname = _fallback_language_module.directives[normname] + msg_text.append('Using English fallback for directive "%s".' + % directive_name) + except KeyError: + msg_text.append('Trying "%s" as canonical directive name.' + % directive_name) + # The canonical name should be an English name, but just in case: + canonicalname = normname + if msg_text: + message = document.reporter.info( + '\n'.join(msg_text), line=document.current_line) + messages.append(message) + try: + modulename, classname = _directive_registry[canonicalname] + except KeyError: + # Error handling done by caller. + return None, messages + try: + module = __import__(modulename, globals(), locals()) + except ImportError, detail: + messages.append(document.reporter.error( + 'Error importing directive module "%s" (directive "%s"):\n%s' + % (modulename, directive_name, detail), + line=document.current_line)) + return None, messages + try: + directive = getattr(module, classname) + _directives[normname] = directive + except AttributeError: + messages.append(document.reporter.error( + 'No directive class "%s" in module "%s" (directive "%s").' + % (classname, modulename, directive_name), + line=document.current_line)) + return None, messages + return directive, messages + +def register_directive(name, directive): + """ + Register a nonstandard application-defined directive function. + Language lookups are not needed for such functions. + """ + _directives[name] = directive + +def flag(argument): + """ + Check for a valid flag option (no argument) and return ``None``. + (Directive option conversion function.) + + Raise ``ValueError`` if an argument is found. + """ + if argument and argument.strip(): + raise ValueError('no argument is allowed; "%s" supplied' % argument) + else: + return None + +def unchanged_required(argument): + """ + Return the argument text, unchanged. + (Directive option conversion function.) + + Raise ``ValueError`` if no argument is found. + """ + if argument is None: + raise ValueError('argument required but none supplied') + else: + return argument # unchanged! + +def unchanged(argument): + """ + Return the argument text, unchanged. + (Directive option conversion function.) + + No argument implies empty string (""). + """ + if argument is None: + return u'' + else: + return argument # unchanged! + +def path(argument): + """ + Return the path argument unwrapped (with newlines removed). + (Directive option conversion function.) + + Raise ``ValueError`` if no argument is found. + """ + if argument is None: + raise ValueError('argument required but none supplied') + else: + path = ''.join([s.strip() for s in argument.splitlines()]) + return path + +def uri(argument): + """ + Return the URI argument with whitespace removed. + (Directive option conversion function.) + + Raise ``ValueError`` if no argument is found. + """ + if argument is None: + raise ValueError('argument required but none supplied') + else: + uri = ''.join(argument.split()) + return uri + +def nonnegative_int(argument): + """ + Check for a nonnegative integer argument; raise ``ValueError`` if not. + (Directive option conversion function.) + """ + value = int(argument) + if value < 0: + raise ValueError('negative value; must be positive or zero') + return value + +def percentage(argument): + """ + Check for an integer percentage value with optional percent sign. + """ + try: + argument = argument.rstrip(' %') + except AttributeError: + pass + return nonnegative_int(argument) + +length_units = ['em', 'ex', 'px', 'in', 'cm', 'mm', 'pt', 'pc'] + +def get_measure(argument, units): + """ + Check for a positive argument of one of the units and return a + normalized string of the form "" (without space in + between). + + To be called from directive option conversion functions. + """ + match = re.match(r'^([0-9.]+) *(%s)$' % '|'.join(units), argument) + try: + assert match is not None + float(match.group(1)) + except (AssertionError, ValueError): + raise ValueError( + 'not a positive measure of one of the following units:\n%s' + % ' '.join(['"%s"' % i for i in units])) + return match.group(1) + match.group(2) + +def length_or_unitless(argument): + return get_measure(argument, length_units + ['']) + +def length_or_percentage_or_unitless(argument, default=''): + """ + Return normalized string of a length or percentage unit. + + Add if there is no unit. Raise ValueError if the argument is not + a positive measure of one of the valid CSS units (or without unit). + + >>> length_or_percentage_or_unitless('3 pt') + '3pt' + >>> length_or_percentage_or_unitless('3%', 'em') + '3%' + >>> length_or_percentage_or_unitless('3') + '3' + >>> length_or_percentage_or_unitless('3', 'px') + '3px' + """ + try: + return get_measure(argument, length_units + ['%']) + except ValueError: + return get_measure(argument, ['']) + default + +def class_option(argument): + """ + Convert the argument into a list of ID-compatible strings and return it. + (Directive option conversion function.) + + Raise ``ValueError`` if no argument is found. + """ + if argument is None: + raise ValueError('argument required but none supplied') + names = argument.split() + class_names = [] + for name in names: + class_name = nodes.make_id(name) + if not class_name: + raise ValueError('cannot make "%s" into a class name' % name) + class_names.append(class_name) + return class_names + +unicode_pattern = re.compile( + r'(?:0x|x|\\x|U\+?|\\u)([0-9a-f]+)$|&#x([0-9a-f]+);$', re.IGNORECASE) + +def unicode_code(code): + r""" + Convert a Unicode character code to a Unicode character. + (Directive option conversion function.) + + Codes may be decimal numbers, hexadecimal numbers (prefixed by ``0x``, + ``x``, ``\x``, ``U+``, ``u``, or ``\u``; e.g. ``U+262E``), or XML-style + numeric character entities (e.g. ``☮``). Other text remains as-is. + + Raise ValueError for illegal Unicode code values. + """ + try: + if code.isdigit(): # decimal number + return unichr(int(code)) + else: + match = unicode_pattern.match(code) + if match: # hex number + value = match.group(1) or match.group(2) + return unichr(int(value, 16)) + else: # other text + return code + except OverflowError, detail: + raise ValueError('code too large (%s)' % detail) + +def single_char_or_unicode(argument): + """ + A single character is returned as-is. Unicode characters codes are + converted as in `unicode_code`. (Directive option conversion function.) + """ + char = unicode_code(argument) + if len(char) > 1: + raise ValueError('%r invalid; must be a single character or ' + 'a Unicode code' % char) + return char + +def single_char_or_whitespace_or_unicode(argument): + """ + As with `single_char_or_unicode`, but "tab" and "space" are also supported. + (Directive option conversion function.) + """ + if argument == 'tab': + char = '\t' + elif argument == 'space': + char = ' ' + else: + char = single_char_or_unicode(argument) + return char + +def positive_int(argument): + """ + Converts the argument into an integer. Raises ValueError for negative, + zero, or non-integer values. (Directive option conversion function.) + """ + value = int(argument) + if value < 1: + raise ValueError('negative or zero value; must be positive') + return value + +def positive_int_list(argument): + """ + Converts a space- or comma-separated list of values into a Python list + of integers. + (Directive option conversion function.) + + Raises ValueError for non-positive-integer values. + """ + if ',' in argument: + entries = argument.split(',') + else: + entries = argument.split() + return [positive_int(entry) for entry in entries] + +def encoding(argument): + """ + Verfies the encoding argument by lookup. + (Directive option conversion function.) + + Raises ValueError for unknown encodings. + """ + try: + codecs.lookup(argument) + except LookupError: + raise ValueError('unknown encoding: "%s"' % argument) + return argument + +def choice(argument, values): + """ + Directive option utility function, supplied to enable options whose + argument must be a member of a finite set of possible values (must be + lower case). A custom conversion function must be written to use it. For + example:: + + from docutils.parsers.rst import directives + + def yesno(argument): + return directives.choice(argument, ('yes', 'no')) + + Raise ``ValueError`` if no argument is found or if the argument's value is + not valid (not an entry in the supplied list). + """ + try: + value = argument.lower().strip() + except AttributeError: + raise ValueError('must supply an argument; choose from %s' + % format_values(values)) + if value in values: + return value + else: + raise ValueError('"%s" unknown; choose from %s' + % (argument, format_values(values))) + +def format_values(values): + return '%s, or "%s"' % (', '.join(['"%s"' % s for s in values[:-1]]), + values[-1]) diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/misc.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/misc.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/misc.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/misc.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,485 @@ +# $Id: misc.py 6142 2009-09-25 18:51:01Z milde $ +# Authors: David Goodger ; Dethe Elza +# Copyright: This module has been placed in the public domain. + +"""Miscellaneous directives.""" + +__docformat__ = 'reStructuredText' + +import sys +import os.path +import re +import time +from docutils import io, nodes, statemachine, utils +from docutils.parsers.rst import Directive, convert_directive_function +from docutils.parsers.rst import directives, roles, states +from docutils.transforms import misc + +class Include(Directive): + + """ + Include content read from a separate source file. + + Content may be parsed by the parser, or included as a literal + block. The encoding of the included file can be specified. Only + a part of the given file argument may be included by specifying + start and end line or text to match before and/or after the text + to be used. + """ + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'literal': directives.flag, + 'encoding': directives.encoding, + 'tab-width': int, + 'start-line': int, + 'end-line': int, + 'start-after': directives.unchanged_required, + 'end-before': directives.unchanged_required} + + standard_include_path = os.path.join(os.path.dirname(states.__file__), + 'include') + + def run(self): + """Include a reST file as part of the content of this reST file.""" + if not self.state.document.settings.file_insertion_enabled: + raise self.warning('"%s" directive disabled.' % self.name) + source = self.state_machine.input_lines.source( + self.lineno - self.state_machine.input_offset - 1) + source_dir = os.path.dirname(os.path.abspath(source)) + path = directives.path(self.arguments[0]) + if path.startswith('<') and path.endswith('>'): + path = os.path.join(self.standard_include_path, path[1:-1]) + path = os.path.normpath(os.path.join(source_dir, path)) + path = utils.relative_path(None, path) + path = nodes.reprunicode(path) + encoding = self.options.get( + 'encoding', self.state.document.settings.input_encoding) + tab_width = self.options.get( + 'tab-width', self.state.document.settings.tab_width) + try: + self.state.document.settings.record_dependencies.add(path) + include_file = io.FileInput( + source_path=path, encoding=encoding, + error_handler=(self.state.document.settings.\ + input_encoding_error_handler), + handle_io_errors=None) + except IOError, error: + raise self.severe('Problems with "%s" directive path:\n%s: %s.' % + (self.name, error.__class__.__name__, str(error))) + # Hack: Since Python 2.6, the string interpolation returns a + # unicode object if one of the supplied %s replacements is a + # unicode object. IOError has no `__unicode__` method and the + # fallback `__repr__` does not report the file name. Explicitely + # converting to str fixes this for now:: + # print '%s\n%s\n%s\n' %(error, str(error), repr(error)) + startline = self.options.get('start-line', None) + endline = self.options.get('end-line', None) + try: + if startline or (endline is not None): + include_lines = include_file.readlines() + include_text = ''.join(include_lines[startline:endline]) + else: + include_text = include_file.read() + except UnicodeError, error: + raise self.severe( + 'Problem with "%s" directive:\n%s: %s' + % (self.name, error.__class__.__name__, error)) + # start-after/end-before: no restrictions on newlines in match-text, + # and no restrictions on matching inside lines vs. line boundaries + after_text = self.options.get('start-after', None) + if after_text: + # skip content in include_text before *and incl.* a matching text + after_index = include_text.find(after_text) + if after_index < 0: + raise self.severe('Problem with "start-after" option of "%s" ' + 'directive:\nText not found.' % self.name) + include_text = include_text[after_index + len(after_text):] + before_text = self.options.get('end-before', None) + if before_text: + # skip content in include_text after *and incl.* a matching text + before_index = include_text.find(before_text) + if before_index < 0: + raise self.severe('Problem with "end-before" option of "%s" ' + 'directive:\nText not found.' % self.name) + include_text = include_text[:before_index] + if 'literal' in self.options: + # Convert tabs to spaces, if `tab_width` is positive. + if tab_width >= 0: + text = include_text.expandtabs(tab_width) + else: + text = include_text + literal_block = nodes.literal_block(include_text, text, + source=path) + literal_block.line = 1 + return [literal_block] + else: + include_lines = statemachine.string2lines( + include_text, tab_width, convert_whitespace=1) + self.state_machine.insert_input(include_lines, path) + return [] + + +class Raw(Directive): + + """ + Pass through content unchanged + + Content is included in output based on type argument + + Content may be included inline (content section of directive) or + imported from a file or url. + """ + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'file': directives.path, + 'url': directives.uri, + 'encoding': directives.encoding} + has_content = True + + def run(self): + if (not self.state.document.settings.raw_enabled + or (not self.state.document.settings.file_insertion_enabled + and ('file' in self.options + or 'url' in self.options))): + raise self.warning('"%s" directive disabled.' % self.name) + attributes = {'format': ' '.join(self.arguments[0].lower().split())} + encoding = self.options.get( + 'encoding', self.state.document.settings.input_encoding) + if self.content: + if 'file' in self.options or 'url' in self.options: + raise self.error( + '"%s" directive may not both specify an external file ' + 'and have content.' % self.name) + text = '\n'.join(self.content) + elif 'file' in self.options: + if 'url' in self.options: + raise self.error( + 'The "file" and "url" options may not be simultaneously ' + 'specified for the "%s" directive.' % self.name) + source_dir = os.path.dirname( + os.path.abspath(self.state.document.current_source)) + path = os.path.normpath(os.path.join(source_dir, + self.options['file'])) + path = utils.relative_path(None, path) + try: + self.state.document.settings.record_dependencies.add(path) + raw_file = io.FileInput( + source_path=path, encoding=encoding, + error_handler=(self.state.document.settings.\ + input_encoding_error_handler), + handle_io_errors=None) + except IOError, error: + raise self.severe('Problems with "%s" directive path:\n%s.' + % (self.name, error)) + try: + text = raw_file.read() + except UnicodeError, error: + raise self.severe( + 'Problem with "%s" directive:\n%s: %s' + % (self.name, error.__class__.__name__, error)) + attributes['source'] = path + elif 'url' in self.options: + source = self.options['url'] + # Do not import urllib2 at the top of the module because + # it may fail due to broken SSL dependencies, and it takes + # about 0.15 seconds to load. + import urllib2 + try: + raw_text = urllib2.urlopen(source).read() + except (urllib2.URLError, IOError, OSError), error: + raise self.severe( + 'Problems with "%s" directive URL "%s":\n%s.' + % (self.name, self.options['url'], error)) + raw_file = io.StringInput( + source=raw_text, source_path=source, encoding=encoding, + error_handler=(self.state.document.settings.\ + input_encoding_error_handler)) + try: + text = raw_file.read() + except UnicodeError, error: + raise self.severe( + 'Problem with "%s" directive:\n%s: %s' + % (self.name, error.__class__.__name__, error)) + attributes['source'] = source + else: + # This will always fail because there is no content. + self.assert_has_content() + raw_node = nodes.raw('', text, **attributes) + return [raw_node] + + +class Replace(Directive): + + has_content = True + + def run(self): + if not isinstance(self.state, states.SubstitutionDef): + raise self.error( + 'Invalid context: the "%s" directive can only be used within ' + 'a substitution definition.' % self.name) + self.assert_has_content() + text = '\n'.join(self.content) + element = nodes.Element(text) + self.state.nested_parse(self.content, self.content_offset, + element) + if ( len(element) != 1 + or not isinstance(element[0], nodes.paragraph)): + messages = [] + for node in element: + if isinstance(node, nodes.system_message): + node['backrefs'] = [] + messages.append(node) + error = self.state_machine.reporter.error( + 'Error in "%s" directive: may contain a single paragraph ' + 'only.' % (self.name), line=self.lineno) + messages.append(error) + return messages + else: + return element[0].children + + +class Unicode(Directive): + + r""" + Convert Unicode character codes (numbers) to characters. Codes may be + decimal numbers, hexadecimal numbers (prefixed by ``0x``, ``x``, ``\x``, + ``U+``, ``u``, or ``\u``; e.g. ``U+262E``), or XML-style numeric character + entities (e.g. ``☮``). Text following ".." is a comment and is + ignored. Spaces are ignored, and any other text remains as-is. + """ + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'trim': directives.flag, + 'ltrim': directives.flag, + 'rtrim': directives.flag} + + comment_pattern = re.compile(r'( |\n|^)\.\. ') + + def run(self): + if not isinstance(self.state, states.SubstitutionDef): + raise self.error( + 'Invalid context: the "%s" directive can only be used within ' + 'a substitution definition.' % self.name) + substitution_definition = self.state_machine.node + if 'trim' in self.options: + substitution_definition.attributes['ltrim'] = 1 + substitution_definition.attributes['rtrim'] = 1 + if 'ltrim' in self.options: + substitution_definition.attributes['ltrim'] = 1 + if 'rtrim' in self.options: + substitution_definition.attributes['rtrim'] = 1 + codes = self.comment_pattern.split(self.arguments[0])[0].split() + element = nodes.Element() + for code in codes: + try: + decoded = directives.unicode_code(code) + except ValueError, err: + raise self.error( + 'Invalid character code: %s\n%s: %s' + % (code, err.__class__.__name__, err)) + element += nodes.Text(decoded) + return element.children + + +class Class(Directive): + + """ + Set a "class" attribute on the directive content or the next element. + When applied to the next element, a "pending" element is inserted, and a + transform does the work later. + """ + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + has_content = True + + def run(self): + try: + class_value = directives.class_option(self.arguments[0]) + except ValueError: + raise self.error( + 'Invalid class attribute value for "%s" directive: "%s".' + % (self.name, self.arguments[0])) + node_list = [] + if self.content: + container = nodes.Element() + self.state.nested_parse(self.content, self.content_offset, + container) + for node in container: + node['classes'].extend(class_value) + node_list.extend(container.children) + else: + pending = nodes.pending( + misc.ClassAttribute, + {'class': class_value, 'directive': self.name}, + self.block_text) + self.state_machine.document.note_pending(pending) + node_list.append(pending) + return node_list + + +class Role(Directive): + + has_content = True + + argument_pattern = re.compile(r'(%s)\s*(\(\s*(%s)\s*\)\s*)?$' + % ((states.Inliner.simplename,) * 2)) + + def run(self): + """Dynamically create and register a custom interpreted text role.""" + if self.content_offset > self.lineno or not self.content: + raise self.error('"%s" directive requires arguments on the first ' + 'line.' % self.name) + args = self.content[0] + match = self.argument_pattern.match(args) + if not match: + raise self.error('"%s" directive arguments not valid role names: ' + '"%s".' % (self.name, args)) + new_role_name = match.group(1) + base_role_name = match.group(3) + messages = [] + if base_role_name: + base_role, messages = roles.role( + base_role_name, self.state_machine.language, self.lineno, + self.state.reporter) + if base_role is None: + error = self.state.reporter.error( + 'Unknown interpreted text role "%s".' % base_role_name, + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + return messages + [error] + else: + base_role = roles.generic_custom_role + assert not hasattr(base_role, 'arguments'), ( + 'Supplemental directive arguments for "%s" directive not ' + 'supported (specified by "%r" role).' % (self.name, base_role)) + try: + converted_role = convert_directive_function(base_role) + (arguments, options, content, content_offset) = ( + self.state.parse_directive_block( + self.content[1:], self.content_offset, converted_role, + option_presets={})) + except states.MarkupError, detail: + error = self.state_machine.reporter.error( + 'Error in "%s" directive:\n%s.' % (self.name, detail), + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + return messages + [error] + if 'class' not in options: + try: + options['class'] = directives.class_option(new_role_name) + except ValueError, detail: + error = self.state_machine.reporter.error( + 'Invalid argument for "%s" directive:\n%s.' + % (self.name, detail), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + return messages + [error] + role = roles.CustomRole(new_role_name, base_role, options, content) + roles.register_local_role(new_role_name, role) + return messages + + +class DefaultRole(Directive): + + """Set the default interpreted text role.""" + + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = False + + def run(self): + if not self.arguments: + if '' in roles._roles: + # restore the "default" default role + del roles._roles[''] + return [] + role_name = self.arguments[0] + role, messages = roles.role(role_name, self.state_machine.language, + self.lineno, self.state.reporter) + if role is None: + error = self.state.reporter.error( + 'Unknown interpreted text role "%s".' % role_name, + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + return messages + [error] + roles._roles[''] = role + # @@@ should this be local to the document, not the parser? + return messages + + +class Title(Directive): + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + + def run(self): + self.state_machine.document['title'] = self.arguments[0] + return [] + + +class Date(Directive): + + has_content = True + + def run(self): + if not isinstance(self.state, states.SubstitutionDef): + raise self.error( + 'Invalid context: the "%s" directive can only be used within ' + 'a substitution definition.' % self.name) + format = '\n'.join(self.content) or '%Y-%m-%d' + text = time.strftime(format) + return [nodes.Text(text)] + + +class TestDirective(Directive): + + """This directive is useful only for testing purposes.""" + + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {'option': directives.unchanged_required} + has_content = True + + def run(self): + if self.content: + text = '\n'.join(self.content) + info = self.state_machine.reporter.info( + 'Directive processed. Type="%s", arguments=%r, options=%r, ' + 'content:' % (self.name, self.arguments, self.options), + nodes.literal_block(text, text), line=self.lineno) + else: + info = self.state_machine.reporter.info( + 'Directive processed. Type="%s", arguments=%r, options=%r, ' + 'content: None' % (self.name, self.arguments, self.options), + line=self.lineno) + return [info] + +# Old-style, functional definition: +# +# def directive_test_function(name, arguments, options, content, lineno, +# content_offset, block_text, state, state_machine): +# """This directive is useful only for testing purposes.""" +# if content: +# text = '\n'.join(content) +# info = state_machine.reporter.info( +# 'Directive processed. Type="%s", arguments=%r, options=%r, ' +# 'content:' % (name, arguments, options), +# nodes.literal_block(text, text), line=lineno) +# else: +# info = state_machine.reporter.info( +# 'Directive processed. Type="%s", arguments=%r, options=%r, ' +# 'content: None' % (name, arguments, options), line=lineno) +# return [info] +# +# directive_test_function.arguments = (0, 1, 1) +# directive_test_function.options = {'option': directives.unchanged_required} +# directive_test_function.content = 1 diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/parts.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/parts.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/parts.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/parts.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,123 @@ +# $Id: parts.py 5618 2008-07-28 08:37:32Z strank $ +# Authors: David Goodger ; Dmitry Jemerov +# Copyright: This module has been placed in the public domain. + +""" +Directives for document parts. +""" + +__docformat__ = 'reStructuredText' + +from docutils import nodes, languages +from docutils.transforms import parts +from docutils.parsers.rst import Directive +from docutils.parsers.rst import directives + + +class Contents(Directive): + + """ + Table of contents. + + The table of contents is generated in two passes: initial parse and + transform. During the initial parse, a 'pending' element is generated + which acts as a placeholder, storing the TOC title and any options + internally. At a later stage in the processing, the 'pending' element is + replaced by a 'topic' element, a title and the table of contents proper. + """ + + backlinks_values = ('top', 'entry', 'none') + + def backlinks(arg): + value = directives.choice(arg, Contents.backlinks_values) + if value == 'none': + return None + else: + return value + + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {'depth': directives.nonnegative_int, + 'local': directives.flag, + 'backlinks': backlinks, + 'class': directives.class_option} + + def run(self): + if not (self.state_machine.match_titles + or isinstance(self.state_machine.node, nodes.sidebar)): + raise self.error('The "%s" directive may not be used within ' + 'topics or body elements.' % self.name) + document = self.state_machine.document + language = languages.get_language(document.settings.language_code) + if self.arguments: + title_text = self.arguments[0] + text_nodes, messages = self.state.inline_text(title_text, + self.lineno) + title = nodes.title(title_text, '', *text_nodes) + else: + messages = [] + if 'local' in self.options: + title = None + else: + title = nodes.title('', language.labels['contents']) + topic = nodes.topic(classes=['contents']) + topic['classes'] += self.options.get('class', []) + if 'local' in self.options: + topic['classes'].append('local') + if title: + name = title.astext() + topic += title + else: + name = language.labels['contents'] + name = nodes.fully_normalize_name(name) + if not document.has_name(name): + topic['names'].append(name) + document.note_implicit_target(topic) + pending = nodes.pending(parts.Contents, rawsource=self.block_text) + pending.details.update(self.options) + document.note_pending(pending) + topic += pending + return [topic] + messages + + +class Sectnum(Directive): + + """Automatic section numbering.""" + + option_spec = {'depth': int, + 'start': int, + 'prefix': directives.unchanged_required, + 'suffix': directives.unchanged_required} + + def run(self): + pending = nodes.pending(parts.SectNum) + pending.details.update(self.options) + self.state_machine.document.note_pending(pending) + return [pending] + + +class Header(Directive): + + """Contents of document header.""" + + has_content = True + + def run(self): + self.assert_has_content() + header = self.state_machine.document.get_decoration().get_header() + self.state.nested_parse(self.content, self.content_offset, header) + return [] + + +class Footer(Directive): + + """Contents of document footer.""" + + has_content = True + + def run(self): + self.assert_has_content() + footer = self.state_machine.document.get_decoration().get_footer() + self.state.nested_parse(self.content, self.content_offset, footer) + return [] diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/references.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/references.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/references.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/references.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,28 @@ +# $Id: references.py 4667 2006-07-12 21:40:56Z wiemann $ +# Authors: David Goodger ; Dmitry Jemerov +# Copyright: This module has been placed in the public domain. + +""" +Directives for references and targets. +""" + +__docformat__ = 'reStructuredText' + +from docutils import nodes +from docutils.transforms import references +from docutils.parsers.rst import Directive +from docutils.parsers.rst import directives + + +class TargetNotes(Directive): + + """Target footnote generation.""" + + option_spec = {'class': directives.class_option} + + def run(self): + pending = nodes.pending(references.TargetNotes) + pending.details.update(self.options) + self.state_machine.document.note_pending(pending) + nodelist = [pending] + return nodelist diff -Nru zope3-3.4.0/src/docutils/parsers/rst/directives/tables.py zope3-3.5~bzr18/src/docutils/parsers/rst/directives/tables.py --- zope3-3.4.0/src/docutils/parsers/rst/directives/tables.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/directives/tables.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,447 @@ +# $Id: tables.py 6107 2009-08-31 02:29:08Z goodger $ +# Authors: David Goodger ; David Priest +# Copyright: This module has been placed in the public domain. + +""" +Directives for table elements. +""" + +__docformat__ = 'reStructuredText' + + +import sys +import os.path +import csv + +from docutils import io, nodes, statemachine, utils +from docutils.utils import SystemMessagePropagation +from docutils.parsers.rst import Directive +from docutils.parsers.rst import directives + + +class Table(Directive): + + """ + Generic table base class. + """ + + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {'class': directives.class_option} + has_content = True + + def make_title(self): + if self.arguments: + title_text = self.arguments[0] + text_nodes, messages = self.state.inline_text(title_text, + self.lineno) + title = nodes.title(title_text, '', *text_nodes) + else: + title = None + messages = [] + return title, messages + + def process_header_option(self): + source = self.state_machine.get_source(self.lineno - 1) + table_head = [] + max_header_cols = 0 + if 'header' in self.options: # separate table header in option + rows, max_header_cols = self.parse_csv_data_into_rows( + self.options['header'].split('\n'), self.HeaderDialect(), + source) + table_head.extend(rows) + return table_head, max_header_cols + + def check_table_dimensions(self, rows, header_rows, stub_columns): + if len(rows) < header_rows: + error = self.state_machine.reporter.error( + '%s header row(s) specified but only %s row(s) of data ' + 'supplied ("%s" directive).' + % (header_rows, len(rows), self.name), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + if len(rows) == header_rows > 0: + error = self.state_machine.reporter.error( + 'Insufficient data supplied (%s row(s)); no data remaining ' + 'for table body, required by "%s" directive.' + % (len(rows), self.name), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + for row in rows: + if len(row) < stub_columns: + error = self.state_machine.reporter.error( + '%s stub column(s) specified but only %s columns(s) of ' + 'data supplied ("%s" directive).' % + (stub_columns, len(row), self.name), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + if len(row) == stub_columns > 0: + error = self.state_machine.reporter.error( + 'Insufficient data supplied (%s columns(s)); no data remaining ' + 'for table body, required by "%s" directive.' + % (len(row), self.name), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + + def get_column_widths(self, max_cols): + if 'widths' in self.options: + col_widths = self.options['widths'] + if len(col_widths) != max_cols: + error = self.state_machine.reporter.error( + '"%s" widths do not match the number of columns in table ' + '(%s).' % (self.name, max_cols), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + elif max_cols: + col_widths = [100 // max_cols] * max_cols + else: + error = self.state_machine.reporter.error( + 'No table data detected in CSV file.', nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + return col_widths + + def extend_short_rows_with_empty_cells(self, columns, parts): + for part in parts: + for row in part: + if len(row) < columns: + row.extend([(0, 0, 0, [])] * (columns - len(row))) + + +class RSTTable(Table): + + def run(self): + if not self.content: + warning = self.state_machine.reporter.warning( + 'Content block expected for the "%s" directive; none found.' + % self.name, nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + return [warning] + title, messages = self.make_title() + node = nodes.Element() # anonymous container for parsing + self.state.nested_parse(self.content, self.content_offset, node) + if len(node) != 1 or not isinstance(node[0], nodes.table): + error = self.state_machine.reporter.error( + 'Error parsing content block for the "%s" directive: exactly ' + 'one table expected.' % self.name, nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + return [error] + table_node = node[0] + table_node['classes'] += self.options.get('class', []) + if title: + table_node.insert(0, title) + return [table_node] + messages + + +class CSVTable(Table): + + option_spec = {'header-rows': directives.nonnegative_int, + 'stub-columns': directives.nonnegative_int, + 'header': directives.unchanged, + 'widths': directives.positive_int_list, + 'file': directives.path, + 'url': directives.uri, + 'encoding': directives.encoding, + 'class': directives.class_option, + # field delimiter char + 'delim': directives.single_char_or_whitespace_or_unicode, + # treat whitespace after delimiter as significant + 'keepspace': directives.flag, + # text field quote/unquote char: + 'quote': directives.single_char_or_unicode, + # char used to escape delim & quote as-needed: + 'escape': directives.single_char_or_unicode,} + + class DocutilsDialect(csv.Dialect): + + """CSV dialect for `csv_table` directive.""" + + delimiter = ',' + quotechar = '"' + doublequote = True + skipinitialspace = True + lineterminator = '\n' + quoting = csv.QUOTE_MINIMAL + + def __init__(self, options): + if 'delim' in options: + self.delimiter = str(options['delim']) + if 'keepspace' in options: + self.skipinitialspace = False + if 'quote' in options: + self.quotechar = str(options['quote']) + if 'escape' in options: + self.doublequote = False + self.escapechar = str(options['escape']) + csv.Dialect.__init__(self) + + + class HeaderDialect(csv.Dialect): + + """CSV dialect to use for the "header" option data.""" + + delimiter = ',' + quotechar = '"' + escapechar = '\\' + doublequote = False + skipinitialspace = True + lineterminator = '\n' + quoting = csv.QUOTE_MINIMAL + + def check_requirements(self): + pass + + def run(self): + try: + if (not self.state.document.settings.file_insertion_enabled + and ('file' in self.options + or 'url' in self.options)): + warning = self.state_machine.reporter.warning( + 'File and URL access deactivated; ignoring "%s" ' + 'directive.' % self.name, nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + return [warning] + self.check_requirements() + title, messages = self.make_title() + csv_data, source = self.get_csv_data() + table_head, max_header_cols = self.process_header_option() + rows, max_cols = self.parse_csv_data_into_rows( + csv_data, self.DocutilsDialect(self.options), source) + max_cols = max(max_cols, max_header_cols) + header_rows = self.options.get('header-rows', 0) + stub_columns = self.options.get('stub-columns', 0) + self.check_table_dimensions(rows, header_rows, stub_columns) + table_head.extend(rows[:header_rows]) + table_body = rows[header_rows:] + col_widths = self.get_column_widths(max_cols) + self.extend_short_rows_with_empty_cells(max_cols, + (table_head, table_body)) + except SystemMessagePropagation, detail: + return [detail.args[0]] + except csv.Error, detail: + error = self.state_machine.reporter.error( + 'Error with CSV data in "%s" directive:\n%s' + % (self.name, detail), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + return [error] + table = (col_widths, table_head, table_body) + table_node = self.state.build_table(table, self.content_offset, + stub_columns) + table_node['classes'] += self.options.get('class', []) + if title: + table_node.insert(0, title) + return [table_node] + messages + + def get_csv_data(self): + """ + Get CSV data from the directive content, from an external + file, or from a URL reference. + """ + encoding = self.options.get( + 'encoding', self.state.document.settings.input_encoding) + if self.content: + # CSV data is from directive content. + if 'file' in self.options or 'url' in self.options: + error = self.state_machine.reporter.error( + '"%s" directive may not both specify an external file and' + ' have content.' % self.name, nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + source = self.content.source(0) + csv_data = self.content + elif 'file' in self.options: + # CSV data is from an external file. + if 'url' in self.options: + error = self.state_machine.reporter.error( + 'The "file" and "url" options may not be simultaneously' + ' specified for the "%s" directive.' % self.name, + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + raise SystemMessagePropagation(error) + source_dir = os.path.dirname( + os.path.abspath(self.state.document.current_source)) + source = os.path.normpath(os.path.join(source_dir, + self.options['file'])) + source = utils.relative_path(None, source) + try: + self.state.document.settings.record_dependencies.add(source) + csv_file = io.FileInput( + source_path=source, encoding=encoding, + error_handler=(self.state.document.settings.\ + input_encoding_error_handler), + handle_io_errors=None) + csv_data = csv_file.read().splitlines() + except IOError, error: + severe = self.state_machine.reporter.severe( + 'Problems with "%s" directive path:\n%s.' + % (self.name, error), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(severe) + elif 'url' in self.options: + # CSV data is from a URL. + # Do not import urllib2 at the top of the module because + # it may fail due to broken SSL dependencies, and it takes + # about 0.15 seconds to load. + import urllib2 + source = self.options['url'] + try: + csv_text = urllib2.urlopen(source).read() + except (urllib2.URLError, IOError, OSError, ValueError), error: + severe = self.state_machine.reporter.severe( + 'Problems with "%s" directive URL "%s":\n%s.' + % (self.name, self.options['url'], error), + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + raise SystemMessagePropagation(severe) + csv_file = io.StringInput( + source=csv_text, source_path=source, encoding=encoding, + error_handler=(self.state.document.settings.\ + input_encoding_error_handler)) + csv_data = csv_file.read().splitlines() + else: + error = self.state_machine.reporter.warning( + 'The "%s" directive requires content; none supplied.' + % self.name, nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + return csv_data, source + + if sys.version_info < (3,): + # 2.x csv module doesn't do Unicode + def decode_from_csv(s): + return s.decode('utf-8') + def encode_for_csv(s): + return s.encode('utf-8') + else: + def decode_from_csv(s): + return s + def encode_for_csv(s): + return s + decode_from_csv = staticmethod(decode_from_csv) + encode_for_csv = staticmethod(encode_for_csv) + + def parse_csv_data_into_rows(self, csv_data, dialect, source): + # csv.py doesn't do Unicode; encode temporarily as UTF-8 + csv_reader = csv.reader([self.encode_for_csv(line + '\n') + for line in csv_data], + dialect=dialect) + rows = [] + max_cols = 0 + for row in csv_reader: + row_data = [] + for cell in row: + # decode UTF-8 back to Unicode + cell_text = self.decode_from_csv(cell) + cell_data = (0, 0, 0, statemachine.StringList( + cell_text.splitlines(), source=source)) + row_data.append(cell_data) + rows.append(row_data) + max_cols = max(max_cols, len(row)) + return rows, max_cols + + +class ListTable(Table): + + """ + Implement tables whose data is encoded as a uniform two-level bullet list. + For further ideas, see + http://docutils.sf.net/docs/dev/rst/alternatives.html#list-driven-tables + """ + + option_spec = {'header-rows': directives.nonnegative_int, + 'stub-columns': directives.nonnegative_int, + 'widths': directives.positive_int_list, + 'class': directives.class_option} + + def run(self): + if not self.content: + error = self.state_machine.reporter.error( + 'The "%s" directive is empty; content required.' % self.name, + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + return [error] + title, messages = self.make_title() + node = nodes.Element() # anonymous container for parsing + self.state.nested_parse(self.content, self.content_offset, node) + try: + num_cols, col_widths = self.check_list_content(node) + table_data = [[item.children for item in row_list[0]] + for row_list in node[0]] + header_rows = self.options.get('header-rows', 0) + stub_columns = self.options.get('stub-columns', 0) + self.check_table_dimensions(table_data, header_rows, stub_columns) + except SystemMessagePropagation, detail: + return [detail.args[0]] + table_node = self.build_table_from_list(table_data, col_widths, + header_rows, stub_columns) + table_node['classes'] += self.options.get('class', []) + if title: + table_node.insert(0, title) + return [table_node] + messages + + def check_list_content(self, node): + if len(node) != 1 or not isinstance(node[0], nodes.bullet_list): + error = self.state_machine.reporter.error( + 'Error parsing content block for the "%s" directive: ' + 'exactly one bullet list expected.' % self.name, + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + raise SystemMessagePropagation(error) + list_node = node[0] + # Check for a uniform two-level bullet list: + for item_index in range(len(list_node)): + item = list_node[item_index] + if len(item) != 1 or not isinstance(item[0], nodes.bullet_list): + error = self.state_machine.reporter.error( + 'Error parsing content block for the "%s" directive: ' + 'two-level bullet list expected, but row %s does not ' + 'contain a second-level bullet list.' + % (self.name, item_index + 1), nodes.literal_block( + self.block_text, self.block_text), line=self.lineno) + raise SystemMessagePropagation(error) + elif item_index: + # ATTN pychecker users: num_cols is guaranteed to be set in the + # "else" clause below for item_index==0, before this branch is + # triggered. + if len(item[0]) != num_cols: + error = self.state_machine.reporter.error( + 'Error parsing content block for the "%s" directive: ' + 'uniform two-level bullet list expected, but row %s ' + 'does not contain the same number of items as row 1 ' + '(%s vs %s).' + % (self.name, item_index + 1, len(item[0]), num_cols), + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + raise SystemMessagePropagation(error) + else: + num_cols = len(item[0]) + col_widths = self.get_column_widths(num_cols) + return num_cols, col_widths + + def build_table_from_list(self, table_data, col_widths, header_rows, stub_columns): + table = nodes.table() + tgroup = nodes.tgroup(cols=len(col_widths)) + table += tgroup + for col_width in col_widths: + colspec = nodes.colspec(colwidth=col_width) + if stub_columns: + colspec.attributes['stub'] = 1 + stub_columns -= 1 + tgroup += colspec + rows = [] + for row in table_data: + row_node = nodes.row() + for cell in row: + entry = nodes.entry() + entry += cell + row_node += entry + rows.append(row_node) + if header_rows: + thead = nodes.thead() + thead.extend(rows[:header_rows]) + tgroup += thead + tbody = nodes.tbody() + tbody.extend(rows[header_rows:]) + tgroup += tbody + return table diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isoamsa.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsa.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isoamsa.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsa.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,162 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |angzarr| unicode:: U+0237C .. RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW +.. |cirmid| unicode:: U+02AEF .. VERTICAL LINE WITH CIRCLE ABOVE +.. |cudarrl| unicode:: U+02938 .. RIGHT-SIDE ARC CLOCKWISE ARROW +.. |cudarrr| unicode:: U+02935 .. ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS +.. |cularr| unicode:: U+021B6 .. ANTICLOCKWISE TOP SEMICIRCLE ARROW +.. |cularrp| unicode:: U+0293D .. TOP ARC ANTICLOCKWISE ARROW WITH PLUS +.. |curarr| unicode:: U+021B7 .. CLOCKWISE TOP SEMICIRCLE ARROW +.. |curarrm| unicode:: U+0293C .. TOP ARC CLOCKWISE ARROW WITH MINUS +.. |Darr| unicode:: U+021A1 .. DOWNWARDS TWO HEADED ARROW +.. |dArr| unicode:: U+021D3 .. DOWNWARDS DOUBLE ARROW +.. |darr2| unicode:: U+021CA .. DOWNWARDS PAIRED ARROWS +.. |ddarr| unicode:: U+021CA .. DOWNWARDS PAIRED ARROWS +.. |DDotrahd| unicode:: U+02911 .. RIGHTWARDS ARROW WITH DOTTED STEM +.. |dfisht| unicode:: U+0297F .. DOWN FISH TAIL +.. |dHar| unicode:: U+02965 .. DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT +.. |dharl| unicode:: U+021C3 .. DOWNWARDS HARPOON WITH BARB LEFTWARDS +.. |dharr| unicode:: U+021C2 .. DOWNWARDS HARPOON WITH BARB RIGHTWARDS +.. |dlarr| unicode:: U+02199 .. SOUTH WEST ARROW +.. |drarr| unicode:: U+02198 .. SOUTH EAST ARROW +.. |duarr| unicode:: U+021F5 .. DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW +.. |duhar| unicode:: U+0296F .. DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT +.. |dzigrarr| unicode:: U+027FF .. LONG RIGHTWARDS SQUIGGLE ARROW +.. |erarr| unicode:: U+02971 .. EQUALS SIGN ABOVE RIGHTWARDS ARROW +.. |hArr| unicode:: U+021D4 .. LEFT RIGHT DOUBLE ARROW +.. |harr| unicode:: U+02194 .. LEFT RIGHT ARROW +.. |harrcir| unicode:: U+02948 .. LEFT RIGHT ARROW THROUGH SMALL CIRCLE +.. |harrw| unicode:: U+021AD .. LEFT RIGHT WAVE ARROW +.. |hoarr| unicode:: U+021FF .. LEFT RIGHT OPEN-HEADED ARROW +.. |imof| unicode:: U+022B7 .. IMAGE OF +.. |lAarr| unicode:: U+021DA .. LEFTWARDS TRIPLE ARROW +.. |Larr| unicode:: U+0219E .. LEFTWARDS TWO HEADED ARROW +.. |larr2| unicode:: U+021C7 .. LEFTWARDS PAIRED ARROWS +.. |larrbfs| unicode:: U+0291F .. LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND +.. |larrfs| unicode:: U+0291D .. LEFTWARDS ARROW TO BLACK DIAMOND +.. |larrhk| unicode:: U+021A9 .. LEFTWARDS ARROW WITH HOOK +.. |larrlp| unicode:: U+021AB .. LEFTWARDS ARROW WITH LOOP +.. |larrpl| unicode:: U+02939 .. LEFT-SIDE ARC ANTICLOCKWISE ARROW +.. |larrsim| unicode:: U+02973 .. LEFTWARDS ARROW ABOVE TILDE OPERATOR +.. |larrtl| unicode:: U+021A2 .. LEFTWARDS ARROW WITH TAIL +.. |lAtail| unicode:: U+0291B .. LEFTWARDS DOUBLE ARROW-TAIL +.. |latail| unicode:: U+02919 .. LEFTWARDS ARROW-TAIL +.. |lBarr| unicode:: U+0290E .. LEFTWARDS TRIPLE DASH ARROW +.. |lbarr| unicode:: U+0290C .. LEFTWARDS DOUBLE DASH ARROW +.. |ldca| unicode:: U+02936 .. ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS +.. |ldrdhar| unicode:: U+02967 .. LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN +.. |ldrushar| unicode:: U+0294B .. LEFT BARB DOWN RIGHT BARB UP HARPOON +.. |ldsh| unicode:: U+021B2 .. DOWNWARDS ARROW WITH TIP LEFTWARDS +.. |lfisht| unicode:: U+0297C .. LEFT FISH TAIL +.. |lHar| unicode:: U+02962 .. LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN +.. |lhard| unicode:: U+021BD .. LEFTWARDS HARPOON WITH BARB DOWNWARDS +.. |lharu| unicode:: U+021BC .. LEFTWARDS HARPOON WITH BARB UPWARDS +.. |lharul| unicode:: U+0296A .. LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH +.. |llarr| unicode:: U+021C7 .. LEFTWARDS PAIRED ARROWS +.. |llhard| unicode:: U+0296B .. LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH +.. |loarr| unicode:: U+021FD .. LEFTWARDS OPEN-HEADED ARROW +.. |lrarr| unicode:: U+021C6 .. LEFTWARDS ARROW OVER RIGHTWARDS ARROW +.. |lrarr2| unicode:: U+021C6 .. LEFTWARDS ARROW OVER RIGHTWARDS ARROW +.. |lrhar| unicode:: U+021CB .. LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON +.. |lrhar2| unicode:: U+021CB .. LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON +.. |lrhard| unicode:: U+0296D .. RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH +.. |lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS +.. |lurdshar| unicode:: U+0294A .. LEFT BARB UP RIGHT BARB DOWN HARPOON +.. |luruhar| unicode:: U+02966 .. LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP +.. |Map| unicode:: U+02905 .. RIGHTWARDS TWO-HEADED ARROW FROM BAR +.. |map| unicode:: U+021A6 .. RIGHTWARDS ARROW FROM BAR +.. |midcir| unicode:: U+02AF0 .. VERTICAL LINE WITH CIRCLE BELOW +.. |mumap| unicode:: U+022B8 .. MULTIMAP +.. |nearhk| unicode:: U+02924 .. NORTH EAST ARROW WITH HOOK +.. |neArr| unicode:: U+021D7 .. NORTH EAST DOUBLE ARROW +.. |nearr| unicode:: U+02197 .. NORTH EAST ARROW +.. |nesear| unicode:: U+02928 .. NORTH EAST ARROW AND SOUTH EAST ARROW +.. |nhArr| unicode:: U+021CE .. LEFT RIGHT DOUBLE ARROW WITH STROKE +.. |nharr| unicode:: U+021AE .. LEFT RIGHT ARROW WITH STROKE +.. |nlArr| unicode:: U+021CD .. LEFTWARDS DOUBLE ARROW WITH STROKE +.. |nlarr| unicode:: U+0219A .. LEFTWARDS ARROW WITH STROKE +.. |nrArr| unicode:: U+021CF .. RIGHTWARDS DOUBLE ARROW WITH STROKE +.. |nrarr| unicode:: U+0219B .. RIGHTWARDS ARROW WITH STROKE +.. |nrarrc| unicode:: U+02933 U+00338 .. WAVE ARROW POINTING DIRECTLY RIGHT with slash +.. |nrarrw| unicode:: U+0219D U+00338 .. RIGHTWARDS WAVE ARROW with slash +.. |nvHarr| unicode:: U+02904 .. LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE +.. |nvlArr| unicode:: U+02902 .. LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE +.. |nvrArr| unicode:: U+02903 .. RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE +.. |nwarhk| unicode:: U+02923 .. NORTH WEST ARROW WITH HOOK +.. |nwArr| unicode:: U+021D6 .. NORTH WEST DOUBLE ARROW +.. |nwarr| unicode:: U+02196 .. NORTH WEST ARROW +.. |nwnear| unicode:: U+02927 .. NORTH WEST ARROW AND NORTH EAST ARROW +.. |olarr| unicode:: U+021BA .. ANTICLOCKWISE OPEN CIRCLE ARROW +.. |orarr| unicode:: U+021BB .. CLOCKWISE OPEN CIRCLE ARROW +.. |origof| unicode:: U+022B6 .. ORIGINAL OF +.. |rAarr| unicode:: U+021DB .. RIGHTWARDS TRIPLE ARROW +.. |Rarr| unicode:: U+021A0 .. RIGHTWARDS TWO HEADED ARROW +.. |rarr2| unicode:: U+021C9 .. RIGHTWARDS PAIRED ARROWS +.. |rarrap| unicode:: U+02975 .. RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO +.. |rarrbfs| unicode:: U+02920 .. RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND +.. |rarrc| unicode:: U+02933 .. WAVE ARROW POINTING DIRECTLY RIGHT +.. |rarrfs| unicode:: U+0291E .. RIGHTWARDS ARROW TO BLACK DIAMOND +.. |rarrhk| unicode:: U+021AA .. RIGHTWARDS ARROW WITH HOOK +.. |rarrlp| unicode:: U+021AC .. RIGHTWARDS ARROW WITH LOOP +.. |rarrpl| unicode:: U+02945 .. RIGHTWARDS ARROW WITH PLUS BELOW +.. |rarrsim| unicode:: U+02974 .. RIGHTWARDS ARROW ABOVE TILDE OPERATOR +.. |Rarrtl| unicode:: U+02916 .. RIGHTWARDS TWO-HEADED ARROW WITH TAIL +.. |rarrtl| unicode:: U+021A3 .. RIGHTWARDS ARROW WITH TAIL +.. |rarrw| unicode:: U+0219D .. RIGHTWARDS WAVE ARROW +.. |rAtail| unicode:: U+0291C .. RIGHTWARDS DOUBLE ARROW-TAIL +.. |ratail| unicode:: U+0291A .. RIGHTWARDS ARROW-TAIL +.. |RBarr| unicode:: U+02910 .. RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW +.. |rBarr| unicode:: U+0290F .. RIGHTWARDS TRIPLE DASH ARROW +.. |rbarr| unicode:: U+0290D .. RIGHTWARDS DOUBLE DASH ARROW +.. |rdca| unicode:: U+02937 .. ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS +.. |rdldhar| unicode:: U+02969 .. RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN +.. |rdsh| unicode:: U+021B3 .. DOWNWARDS ARROW WITH TIP RIGHTWARDS +.. |rfisht| unicode:: U+0297D .. RIGHT FISH TAIL +.. |rHar| unicode:: U+02964 .. RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN +.. |rhard| unicode:: U+021C1 .. RIGHTWARDS HARPOON WITH BARB DOWNWARDS +.. |rharu| unicode:: U+021C0 .. RIGHTWARDS HARPOON WITH BARB UPWARDS +.. |rharul| unicode:: U+0296C .. RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH +.. |rlarr| unicode:: U+021C4 .. RIGHTWARDS ARROW OVER LEFTWARDS ARROW +.. |rlarr2| unicode:: U+021C4 .. RIGHTWARDS ARROW OVER LEFTWARDS ARROW +.. |rlhar| unicode:: U+021CC .. RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON +.. |rlhar2| unicode:: U+021CC .. RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON +.. |roarr| unicode:: U+021FE .. RIGHTWARDS OPEN-HEADED ARROW +.. |rrarr| unicode:: U+021C9 .. RIGHTWARDS PAIRED ARROWS +.. |rsh| unicode:: U+021B1 .. UPWARDS ARROW WITH TIP RIGHTWARDS +.. |ruluhar| unicode:: U+02968 .. RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP +.. |searhk| unicode:: U+02925 .. SOUTH EAST ARROW WITH HOOK +.. |seArr| unicode:: U+021D8 .. SOUTH EAST DOUBLE ARROW +.. |searr| unicode:: U+02198 .. SOUTH EAST ARROW +.. |seswar| unicode:: U+02929 .. SOUTH EAST ARROW AND SOUTH WEST ARROW +.. |simrarr| unicode:: U+02972 .. TILDE OPERATOR ABOVE RIGHTWARDS ARROW +.. |slarr| unicode:: U+02190 .. LEFTWARDS ARROW +.. |srarr| unicode:: U+02192 .. RIGHTWARDS ARROW +.. |swarhk| unicode:: U+02926 .. SOUTH WEST ARROW WITH HOOK +.. |swArr| unicode:: U+021D9 .. SOUTH WEST DOUBLE ARROW +.. |swarr| unicode:: U+02199 .. SOUTH WEST ARROW +.. |swnwar| unicode:: U+0292A .. SOUTH WEST ARROW AND NORTH WEST ARROW +.. |Uarr| unicode:: U+0219F .. UPWARDS TWO HEADED ARROW +.. |uArr| unicode:: U+021D1 .. UPWARDS DOUBLE ARROW +.. |uarr2| unicode:: U+021C8 .. UPWARDS PAIRED ARROWS +.. |Uarrocir| unicode:: U+02949 .. UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE +.. |udarr| unicode:: U+021C5 .. UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW +.. |udhar| unicode:: U+0296E .. UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT +.. |ufisht| unicode:: U+0297E .. UP FISH TAIL +.. |uHar| unicode:: U+02963 .. UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT +.. |uharl| unicode:: U+021BF .. UPWARDS HARPOON WITH BARB LEFTWARDS +.. |uharr| unicode:: U+021BE .. UPWARDS HARPOON WITH BARB RIGHTWARDS +.. |uuarr| unicode:: U+021C8 .. UPWARDS PAIRED ARROWS +.. |vArr| unicode:: U+021D5 .. UP DOWN DOUBLE ARROW +.. |varr| unicode:: U+02195 .. UP DOWN ARROW +.. |xhArr| unicode:: U+027FA .. LONG LEFT RIGHT DOUBLE ARROW +.. |xharr| unicode:: U+027F7 .. LONG LEFT RIGHT ARROW +.. |xlArr| unicode:: U+027F8 .. LONG LEFTWARDS DOUBLE ARROW +.. |xlarr| unicode:: U+027F5 .. LONG LEFTWARDS ARROW +.. |xmap| unicode:: U+027FC .. LONG RIGHTWARDS ARROW FROM BAR +.. |xrArr| unicode:: U+027F9 .. LONG RIGHTWARDS DOUBLE ARROW +.. |xrarr| unicode:: U+027F6 .. LONG RIGHTWARDS ARROW +.. |zigrarr| unicode:: U+021DD .. RIGHTWARDS SQUIGGLE ARROW diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isoamsb.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsb.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isoamsb.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsb.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,126 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |ac| unicode:: U+0223E .. INVERTED LAZY S +.. |acE| unicode:: U+0223E U+00333 .. INVERTED LAZY S with double underline +.. |amalg| unicode:: U+02A3F .. AMALGAMATION OR COPRODUCT +.. |barvee| unicode:: U+022BD .. NOR +.. |Barwed| unicode:: U+02306 .. PERSPECTIVE +.. |barwed| unicode:: U+02305 .. PROJECTIVE +.. |bsolb| unicode:: U+029C5 .. SQUARED FALLING DIAGONAL SLASH +.. |Cap| unicode:: U+022D2 .. DOUBLE INTERSECTION +.. |capand| unicode:: U+02A44 .. INTERSECTION WITH LOGICAL AND +.. |capbrcup| unicode:: U+02A49 .. INTERSECTION ABOVE BAR ABOVE UNION +.. |capcap| unicode:: U+02A4B .. INTERSECTION BESIDE AND JOINED WITH INTERSECTION +.. |capcup| unicode:: U+02A47 .. INTERSECTION ABOVE UNION +.. |capdot| unicode:: U+02A40 .. INTERSECTION WITH DOT +.. |caps| unicode:: U+02229 U+0FE00 .. INTERSECTION with serifs +.. |ccaps| unicode:: U+02A4D .. CLOSED INTERSECTION WITH SERIFS +.. |ccups| unicode:: U+02A4C .. CLOSED UNION WITH SERIFS +.. |ccupssm| unicode:: U+02A50 .. CLOSED UNION WITH SERIFS AND SMASH PRODUCT +.. |coprod| unicode:: U+02210 .. N-ARY COPRODUCT +.. |Cup| unicode:: U+022D3 .. DOUBLE UNION +.. |cupbrcap| unicode:: U+02A48 .. UNION ABOVE BAR ABOVE INTERSECTION +.. |cupcap| unicode:: U+02A46 .. UNION ABOVE INTERSECTION +.. |cupcup| unicode:: U+02A4A .. UNION BESIDE AND JOINED WITH UNION +.. |cupdot| unicode:: U+0228D .. MULTISET MULTIPLICATION +.. |cupor| unicode:: U+02A45 .. UNION WITH LOGICAL OR +.. |cups| unicode:: U+0222A U+0FE00 .. UNION with serifs +.. |cuvee| unicode:: U+022CE .. CURLY LOGICAL OR +.. |cuwed| unicode:: U+022CF .. CURLY LOGICAL AND +.. |Dagger| unicode:: U+02021 .. DOUBLE DAGGER +.. |dagger| unicode:: U+02020 .. DAGGER +.. |diam| unicode:: U+022C4 .. DIAMOND OPERATOR +.. |divonx| unicode:: U+022C7 .. DIVISION TIMES +.. |eplus| unicode:: U+02A71 .. EQUALS SIGN ABOVE PLUS SIGN +.. |hercon| unicode:: U+022B9 .. HERMITIAN CONJUGATE MATRIX +.. |intcal| unicode:: U+022BA .. INTERCALATE +.. |iprod| unicode:: U+02A3C .. INTERIOR PRODUCT +.. |loplus| unicode:: U+02A2D .. PLUS SIGN IN LEFT HALF CIRCLE +.. |lotimes| unicode:: U+02A34 .. MULTIPLICATION SIGN IN LEFT HALF CIRCLE +.. |lthree| unicode:: U+022CB .. LEFT SEMIDIRECT PRODUCT +.. |ltimes| unicode:: U+022C9 .. LEFT NORMAL FACTOR SEMIDIRECT PRODUCT +.. |midast| unicode:: U+0002A .. ASTERISK +.. |minusb| unicode:: U+0229F .. SQUARED MINUS +.. |minusd| unicode:: U+02238 .. DOT MINUS +.. |minusdu| unicode:: U+02A2A .. MINUS SIGN WITH DOT BELOW +.. |ncap| unicode:: U+02A43 .. INTERSECTION WITH OVERBAR +.. |ncup| unicode:: U+02A42 .. UNION WITH OVERBAR +.. |oast| unicode:: U+0229B .. CIRCLED ASTERISK OPERATOR +.. |ocir| unicode:: U+0229A .. CIRCLED RING OPERATOR +.. |odash| unicode:: U+0229D .. CIRCLED DASH +.. |odiv| unicode:: U+02A38 .. CIRCLED DIVISION SIGN +.. |odot| unicode:: U+02299 .. CIRCLED DOT OPERATOR +.. |odsold| unicode:: U+029BC .. CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN +.. |ofcir| unicode:: U+029BF .. CIRCLED BULLET +.. |ogt| unicode:: U+029C1 .. CIRCLED GREATER-THAN +.. |ohbar| unicode:: U+029B5 .. CIRCLE WITH HORIZONTAL BAR +.. |olcir| unicode:: U+029BE .. CIRCLED WHITE BULLET +.. |olt| unicode:: U+029C0 .. CIRCLED LESS-THAN +.. |omid| unicode:: U+029B6 .. CIRCLED VERTICAL BAR +.. |ominus| unicode:: U+02296 .. CIRCLED MINUS +.. |opar| unicode:: U+029B7 .. CIRCLED PARALLEL +.. |operp| unicode:: U+029B9 .. CIRCLED PERPENDICULAR +.. |oplus| unicode:: U+02295 .. CIRCLED PLUS +.. |osol| unicode:: U+02298 .. CIRCLED DIVISION SLASH +.. |Otimes| unicode:: U+02A37 .. MULTIPLICATION SIGN IN DOUBLE CIRCLE +.. |otimes| unicode:: U+02297 .. CIRCLED TIMES +.. |otimesas| unicode:: U+02A36 .. CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT +.. |ovbar| unicode:: U+0233D .. APL FUNCTIONAL SYMBOL CIRCLE STILE +.. |plusacir| unicode:: U+02A23 .. PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE +.. |plusb| unicode:: U+0229E .. SQUARED PLUS +.. |pluscir| unicode:: U+02A22 .. PLUS SIGN WITH SMALL CIRCLE ABOVE +.. |plusdo| unicode:: U+02214 .. DOT PLUS +.. |plusdu| unicode:: U+02A25 .. PLUS SIGN WITH DOT BELOW +.. |pluse| unicode:: U+02A72 .. PLUS SIGN ABOVE EQUALS SIGN +.. |plussim| unicode:: U+02A26 .. PLUS SIGN WITH TILDE BELOW +.. |plustwo| unicode:: U+02A27 .. PLUS SIGN WITH SUBSCRIPT TWO +.. |prod| unicode:: U+0220F .. N-ARY PRODUCT +.. |race| unicode:: U+029DA .. LEFT DOUBLE WIGGLY FENCE +.. |roplus| unicode:: U+02A2E .. PLUS SIGN IN RIGHT HALF CIRCLE +.. |rotimes| unicode:: U+02A35 .. MULTIPLICATION SIGN IN RIGHT HALF CIRCLE +.. |rthree| unicode:: U+022CC .. RIGHT SEMIDIRECT PRODUCT +.. |rtimes| unicode:: U+022CA .. RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT +.. |sdot| unicode:: U+022C5 .. DOT OPERATOR +.. |sdotb| unicode:: U+022A1 .. SQUARED DOT OPERATOR +.. |setmn| unicode:: U+02216 .. SET MINUS +.. |simplus| unicode:: U+02A24 .. PLUS SIGN WITH TILDE ABOVE +.. |smashp| unicode:: U+02A33 .. SMASH PRODUCT +.. |solb| unicode:: U+029C4 .. SQUARED RISING DIAGONAL SLASH +.. |sqcap| unicode:: U+02293 .. SQUARE CAP +.. |sqcaps| unicode:: U+02293 U+0FE00 .. SQUARE CAP with serifs +.. |sqcup| unicode:: U+02294 .. SQUARE CUP +.. |sqcups| unicode:: U+02294 U+0FE00 .. SQUARE CUP with serifs +.. |ssetmn| unicode:: U+02216 .. SET MINUS +.. |sstarf| unicode:: U+022C6 .. STAR OPERATOR +.. |subdot| unicode:: U+02ABD .. SUBSET WITH DOT +.. |sum| unicode:: U+02211 .. N-ARY SUMMATION +.. |supdot| unicode:: U+02ABE .. SUPERSET WITH DOT +.. |timesb| unicode:: U+022A0 .. SQUARED TIMES +.. |timesbar| unicode:: U+02A31 .. MULTIPLICATION SIGN WITH UNDERBAR +.. |timesd| unicode:: U+02A30 .. MULTIPLICATION SIGN WITH DOT ABOVE +.. |top| unicode:: U+022A4 .. DOWN TACK +.. |tridot| unicode:: U+025EC .. WHITE UP-POINTING TRIANGLE WITH DOT +.. |triminus| unicode:: U+02A3A .. MINUS SIGN IN TRIANGLE +.. |triplus| unicode:: U+02A39 .. PLUS SIGN IN TRIANGLE +.. |trisb| unicode:: U+029CD .. TRIANGLE WITH SERIFS AT BOTTOM +.. |tritime| unicode:: U+02A3B .. MULTIPLICATION SIGN IN TRIANGLE +.. |uplus| unicode:: U+0228E .. MULTISET UNION +.. |veebar| unicode:: U+022BB .. XOR +.. |wedbar| unicode:: U+02A5F .. LOGICAL AND WITH UNDERBAR +.. |wreath| unicode:: U+02240 .. WREATH PRODUCT +.. |xcap| unicode:: U+022C2 .. N-ARY INTERSECTION +.. |xcirc| unicode:: U+025EF .. LARGE CIRCLE +.. |xcup| unicode:: U+022C3 .. N-ARY UNION +.. |xdtri| unicode:: U+025BD .. WHITE DOWN-POINTING TRIANGLE +.. |xodot| unicode:: U+02A00 .. N-ARY CIRCLED DOT OPERATOR +.. |xoplus| unicode:: U+02A01 .. N-ARY CIRCLED PLUS OPERATOR +.. |xotime| unicode:: U+02A02 .. N-ARY CIRCLED TIMES OPERATOR +.. |xsqcup| unicode:: U+02A06 .. N-ARY SQUARE UNION OPERATOR +.. |xuplus| unicode:: U+02A04 .. N-ARY UNION OPERATOR WITH PLUS +.. |xutri| unicode:: U+025B3 .. WHITE UP-POINTING TRIANGLE +.. |xvee| unicode:: U+022C1 .. N-ARY LOGICAL OR +.. |xwedge| unicode:: U+022C0 .. N-ARY LOGICAL AND diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isoamsc.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsc.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isoamsc.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsc.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,29 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |dlcorn| unicode:: U+0231E .. BOTTOM LEFT CORNER +.. |drcorn| unicode:: U+0231F .. BOTTOM RIGHT CORNER +.. |gtlPar| unicode:: U+02995 .. DOUBLE LEFT ARC GREATER-THAN BRACKET +.. |langd| unicode:: U+02991 .. LEFT ANGLE BRACKET WITH DOT +.. |lbrke| unicode:: U+0298B .. LEFT SQUARE BRACKET WITH UNDERBAR +.. |lbrksld| unicode:: U+0298F .. LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER +.. |lbrkslu| unicode:: U+0298D .. LEFT SQUARE BRACKET WITH TICK IN TOP CORNER +.. |lceil| unicode:: U+02308 .. LEFT CEILING +.. |lfloor| unicode:: U+0230A .. LEFT FLOOR +.. |lmoust| unicode:: U+023B0 .. UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION +.. |lpargt| unicode:: U+029A0 .. SPHERICAL ANGLE OPENING LEFT +.. |lparlt| unicode:: U+02993 .. LEFT ARC LESS-THAN BRACKET +.. |ltrPar| unicode:: U+02996 .. DOUBLE RIGHT ARC LESS-THAN BRACKET +.. |rangd| unicode:: U+02992 .. RIGHT ANGLE BRACKET WITH DOT +.. |rbrke| unicode:: U+0298C .. RIGHT SQUARE BRACKET WITH UNDERBAR +.. |rbrksld| unicode:: U+0298E .. RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER +.. |rbrkslu| unicode:: U+02990 .. RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER +.. |rceil| unicode:: U+02309 .. RIGHT CEILING +.. |rfloor| unicode:: U+0230B .. RIGHT FLOOR +.. |rmoust| unicode:: U+023B1 .. UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION +.. |rpargt| unicode:: U+02994 .. RIGHT ARC GREATER-THAN BRACKET +.. |ulcorn| unicode:: U+0231C .. TOP LEFT CORNER +.. |urcorn| unicode:: U+0231D .. TOP RIGHT CORNER diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isoamsn.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsn.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isoamsn.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsn.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,96 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |gnap| unicode:: U+02A8A .. GREATER-THAN AND NOT APPROXIMATE +.. |gnE| unicode:: U+02269 .. GREATER-THAN BUT NOT EQUAL TO +.. |gne| unicode:: U+02A88 .. GREATER-THAN AND SINGLE-LINE NOT EQUAL TO +.. |gnsim| unicode:: U+022E7 .. GREATER-THAN BUT NOT EQUIVALENT TO +.. |gvnE| unicode:: U+02269 U+0FE00 .. GREATER-THAN BUT NOT EQUAL TO - with vertical stroke +.. |lnap| unicode:: U+02A89 .. LESS-THAN AND NOT APPROXIMATE +.. |lnE| unicode:: U+02268 .. LESS-THAN BUT NOT EQUAL TO +.. |lne| unicode:: U+02A87 .. LESS-THAN AND SINGLE-LINE NOT EQUAL TO +.. |lnsim| unicode:: U+022E6 .. LESS-THAN BUT NOT EQUIVALENT TO +.. |lvnE| unicode:: U+02268 U+0FE00 .. LESS-THAN BUT NOT EQUAL TO - with vertical stroke +.. |nap| unicode:: U+02249 .. NOT ALMOST EQUAL TO +.. |napE| unicode:: U+02A70 U+00338 .. APPROXIMATELY EQUAL OR EQUAL TO with slash +.. |napid| unicode:: U+0224B U+00338 .. TRIPLE TILDE with slash +.. |ncong| unicode:: U+02247 .. NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO +.. |ncongdot| unicode:: U+02A6D U+00338 .. CONGRUENT WITH DOT ABOVE with slash +.. |nequiv| unicode:: U+02262 .. NOT IDENTICAL TO +.. |ngE| unicode:: U+02267 U+00338 .. GREATER-THAN OVER EQUAL TO with slash +.. |nge| unicode:: U+02271 .. NEITHER GREATER-THAN NOR EQUAL TO +.. |nges| unicode:: U+02A7E U+00338 .. GREATER-THAN OR SLANTED EQUAL TO with slash +.. |nGg| unicode:: U+022D9 U+00338 .. VERY MUCH GREATER-THAN with slash +.. |ngsim| unicode:: U+02275 .. NEITHER GREATER-THAN NOR EQUIVALENT TO +.. |nGt| unicode:: U+0226B U+020D2 .. MUCH GREATER THAN with vertical line +.. |ngt| unicode:: U+0226F .. NOT GREATER-THAN +.. |nGtv| unicode:: U+0226B U+00338 .. MUCH GREATER THAN with slash +.. |nlE| unicode:: U+02266 U+00338 .. LESS-THAN OVER EQUAL TO with slash +.. |nle| unicode:: U+02270 .. NEITHER LESS-THAN NOR EQUAL TO +.. |nles| unicode:: U+02A7D U+00338 .. LESS-THAN OR SLANTED EQUAL TO with slash +.. |nLl| unicode:: U+022D8 U+00338 .. VERY MUCH LESS-THAN with slash +.. |nlsim| unicode:: U+02274 .. NEITHER LESS-THAN NOR EQUIVALENT TO +.. |nLt| unicode:: U+0226A U+020D2 .. MUCH LESS THAN with vertical line +.. |nlt| unicode:: U+0226E .. NOT LESS-THAN +.. |nltri| unicode:: U+022EA .. NOT NORMAL SUBGROUP OF +.. |nltrie| unicode:: U+022EC .. NOT NORMAL SUBGROUP OF OR EQUAL TO +.. |nLtv| unicode:: U+0226A U+00338 .. MUCH LESS THAN with slash +.. |nmid| unicode:: U+02224 .. DOES NOT DIVIDE +.. |npar| unicode:: U+02226 .. NOT PARALLEL TO +.. |npr| unicode:: U+02280 .. DOES NOT PRECEDE +.. |nprcue| unicode:: U+022E0 .. DOES NOT PRECEDE OR EQUAL +.. |npre| unicode:: U+02AAF U+00338 .. PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash +.. |nrtri| unicode:: U+022EB .. DOES NOT CONTAIN AS NORMAL SUBGROUP +.. |nrtrie| unicode:: U+022ED .. DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL +.. |nsc| unicode:: U+02281 .. DOES NOT SUCCEED +.. |nsccue| unicode:: U+022E1 .. DOES NOT SUCCEED OR EQUAL +.. |nsce| unicode:: U+02AB0 U+00338 .. SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash +.. |nsim| unicode:: U+02241 .. NOT TILDE +.. |nsime| unicode:: U+02244 .. NOT ASYMPTOTICALLY EQUAL TO +.. |nsmid| unicode:: U+02224 .. DOES NOT DIVIDE +.. |nspar| unicode:: U+02226 .. NOT PARALLEL TO +.. |nsqsube| unicode:: U+022E2 .. NOT SQUARE IMAGE OF OR EQUAL TO +.. |nsqsupe| unicode:: U+022E3 .. NOT SQUARE ORIGINAL OF OR EQUAL TO +.. |nsub| unicode:: U+02284 .. NOT A SUBSET OF +.. |nsubE| unicode:: U+02AC5 U+00338 .. SUBSET OF ABOVE EQUALS SIGN with slash +.. |nsube| unicode:: U+02288 .. NEITHER A SUBSET OF NOR EQUAL TO +.. |nsup| unicode:: U+02285 .. NOT A SUPERSET OF +.. |nsupE| unicode:: U+02AC6 U+00338 .. SUPERSET OF ABOVE EQUALS SIGN with slash +.. |nsupe| unicode:: U+02289 .. NEITHER A SUPERSET OF NOR EQUAL TO +.. |ntgl| unicode:: U+02279 .. NEITHER GREATER-THAN NOR LESS-THAN +.. |ntlg| unicode:: U+02278 .. NEITHER LESS-THAN NOR GREATER-THAN +.. |nvap| unicode:: U+0224D U+020D2 .. EQUIVALENT TO with vertical line +.. |nVDash| unicode:: U+022AF .. NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE +.. |nVdash| unicode:: U+022AE .. DOES NOT FORCE +.. |nvDash| unicode:: U+022AD .. NOT TRUE +.. |nvdash| unicode:: U+022AC .. DOES NOT PROVE +.. |nvge| unicode:: U+02265 U+020D2 .. GREATER-THAN OR EQUAL TO with vertical line +.. |nvgt| unicode:: U+0003E U+020D2 .. GREATER-THAN SIGN with vertical line +.. |nvle| unicode:: U+02264 U+020D2 .. LESS-THAN OR EQUAL TO with vertical line +.. |nvlt| unicode:: U+0003C U+020D2 .. LESS-THAN SIGN with vertical line +.. |nvltrie| unicode:: U+022B4 U+020D2 .. NORMAL SUBGROUP OF OR EQUAL TO with vertical line +.. |nvrtrie| unicode:: U+022B5 U+020D2 .. CONTAINS AS NORMAL SUBGROUP OR EQUAL TO with vertical line +.. |nvsim| unicode:: U+0223C U+020D2 .. TILDE OPERATOR with vertical line +.. |parsim| unicode:: U+02AF3 .. PARALLEL WITH TILDE OPERATOR +.. |prnap| unicode:: U+02AB9 .. PRECEDES ABOVE NOT ALMOST EQUAL TO +.. |prnE| unicode:: U+02AB5 .. PRECEDES ABOVE NOT EQUAL TO +.. |prnsim| unicode:: U+022E8 .. PRECEDES BUT NOT EQUIVALENT TO +.. |rnmid| unicode:: U+02AEE .. DOES NOT DIVIDE WITH REVERSED NEGATION SLASH +.. |scnap| unicode:: U+02ABA .. SUCCEEDS ABOVE NOT ALMOST EQUAL TO +.. |scnE| unicode:: U+02AB6 .. SUCCEEDS ABOVE NOT EQUAL TO +.. |scnsim| unicode:: U+022E9 .. SUCCEEDS BUT NOT EQUIVALENT TO +.. |simne| unicode:: U+02246 .. APPROXIMATELY BUT NOT ACTUALLY EQUAL TO +.. |solbar| unicode:: U+0233F .. APL FUNCTIONAL SYMBOL SLASH BAR +.. |subnE| unicode:: U+02ACB .. SUBSET OF ABOVE NOT EQUAL TO +.. |subne| unicode:: U+0228A .. SUBSET OF WITH NOT EQUAL TO +.. |supnE| unicode:: U+02ACC .. SUPERSET OF ABOVE NOT EQUAL TO +.. |supne| unicode:: U+0228B .. SUPERSET OF WITH NOT EQUAL TO +.. |vnsub| unicode:: U+02282 U+020D2 .. SUBSET OF with vertical line +.. |vnsup| unicode:: U+02283 U+020D2 .. SUPERSET OF with vertical line +.. |vsubnE| unicode:: U+02ACB U+0FE00 .. SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members +.. |vsubne| unicode:: U+0228A U+0FE00 .. SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members +.. |vsupnE| unicode:: U+02ACC U+0FE00 .. SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members +.. |vsupne| unicode:: U+0228B U+0FE00 .. SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isoamso.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamso.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isoamso.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamso.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,62 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |ang| unicode:: U+02220 .. ANGLE +.. |ange| unicode:: U+029A4 .. ANGLE WITH UNDERBAR +.. |angmsd| unicode:: U+02221 .. MEASURED ANGLE +.. |angmsdaa| unicode:: U+029A8 .. MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT +.. |angmsdab| unicode:: U+029A9 .. MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT +.. |angmsdac| unicode:: U+029AA .. MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT +.. |angmsdad| unicode:: U+029AB .. MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT +.. |angmsdae| unicode:: U+029AC .. MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP +.. |angmsdaf| unicode:: U+029AD .. MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP +.. |angmsdag| unicode:: U+029AE .. MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN +.. |angmsdah| unicode:: U+029AF .. MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN +.. |angrtvb| unicode:: U+022BE .. RIGHT ANGLE WITH ARC +.. |angrtvbd| unicode:: U+0299D .. MEASURED RIGHT ANGLE WITH DOT +.. |bbrk| unicode:: U+023B5 .. BOTTOM SQUARE BRACKET +.. |bbrktbrk| unicode:: U+023B6 .. BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET +.. |bemptyv| unicode:: U+029B0 .. REVERSED EMPTY SET +.. |beth| unicode:: U+02136 .. BET SYMBOL +.. |boxbox| unicode:: U+029C9 .. TWO JOINED SQUARES +.. |bprime| unicode:: U+02035 .. REVERSED PRIME +.. |bsemi| unicode:: U+0204F .. REVERSED SEMICOLON +.. |cemptyv| unicode:: U+029B2 .. EMPTY SET WITH SMALL CIRCLE ABOVE +.. |cirE| unicode:: U+029C3 .. CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT +.. |cirscir| unicode:: U+029C2 .. CIRCLE WITH SMALL CIRCLE TO THE RIGHT +.. |comp| unicode:: U+02201 .. COMPLEMENT +.. |daleth| unicode:: U+02138 .. DALET SYMBOL +.. |demptyv| unicode:: U+029B1 .. EMPTY SET WITH OVERBAR +.. |ell| unicode:: U+02113 .. SCRIPT SMALL L +.. |empty| unicode:: U+02205 .. EMPTY SET +.. |emptyv| unicode:: U+02205 .. EMPTY SET +.. |gimel| unicode:: U+02137 .. GIMEL SYMBOL +.. |iiota| unicode:: U+02129 .. TURNED GREEK SMALL LETTER IOTA +.. |image| unicode:: U+02111 .. BLACK-LETTER CAPITAL I +.. |imath| unicode:: U+00131 .. LATIN SMALL LETTER DOTLESS I +.. |inodot| unicode:: U+00131 .. LATIN SMALL LETTER DOTLESS I +.. |jmath| unicode:: U+0006A .. LATIN SMALL LETTER J +.. |jnodot| unicode:: U+0006A .. LATIN SMALL LETTER J +.. |laemptyv| unicode:: U+029B4 .. EMPTY SET WITH LEFT ARROW ABOVE +.. |lltri| unicode:: U+025FA .. LOWER LEFT TRIANGLE +.. |lrtri| unicode:: U+022BF .. RIGHT TRIANGLE +.. |mho| unicode:: U+02127 .. INVERTED OHM SIGN +.. |nang| unicode:: U+02220 U+020D2 .. ANGLE with vertical line +.. |nexist| unicode:: U+02204 .. THERE DOES NOT EXIST +.. |oS| unicode:: U+024C8 .. CIRCLED LATIN CAPITAL LETTER S +.. |planck| unicode:: U+0210F .. PLANCK CONSTANT OVER TWO PI +.. |plankv| unicode:: U+0210F .. PLANCK CONSTANT OVER TWO PI +.. |raemptyv| unicode:: U+029B3 .. EMPTY SET WITH RIGHT ARROW ABOVE +.. |range| unicode:: U+029A5 .. REVERSED ANGLE WITH UNDERBAR +.. |real| unicode:: U+0211C .. BLACK-LETTER CAPITAL R +.. |sbsol| unicode:: U+0FE68 .. SMALL REVERSE SOLIDUS +.. |tbrk| unicode:: U+023B4 .. TOP SQUARE BRACKET +.. |trpezium| unicode:: U+0FFFD .. REPLACEMENT CHARACTER +.. |ultri| unicode:: U+025F8 .. UPPER LEFT TRIANGLE +.. |urtri| unicode:: U+025F9 .. UPPER RIGHT TRIANGLE +.. |vprime| unicode:: U+02032 .. PRIME +.. |vzigzag| unicode:: U+0299A .. VERTICAL ZIGZAG LINE +.. |weierp| unicode:: U+02118 .. SCRIPT CAPITAL P diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isoamsr.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsr.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isoamsr.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isoamsr.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,191 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |apE| unicode:: U+02A70 .. APPROXIMATELY EQUAL OR EQUAL TO +.. |ape| unicode:: U+0224A .. ALMOST EQUAL OR EQUAL TO +.. |apid| unicode:: U+0224B .. TRIPLE TILDE +.. |asymp| unicode:: U+02248 .. ALMOST EQUAL TO +.. |Barv| unicode:: U+02AE7 .. SHORT DOWN TACK WITH OVERBAR +.. |bcong| unicode:: U+0224C .. ALL EQUAL TO +.. |bepsi| unicode:: U+003F6 .. GREEK REVERSED LUNATE EPSILON SYMBOL +.. |bowtie| unicode:: U+022C8 .. BOWTIE +.. |bsim| unicode:: U+0223D .. REVERSED TILDE +.. |bsime| unicode:: U+022CD .. REVERSED TILDE EQUALS +.. |bsolhsub| unicode:: U+0005C U+02282 .. REVERSE SOLIDUS, SUBSET OF +.. |bump| unicode:: U+0224E .. GEOMETRICALLY EQUIVALENT TO +.. |bumpE| unicode:: U+02AAE .. EQUALS SIGN WITH BUMPY ABOVE +.. |bumpe| unicode:: U+0224F .. DIFFERENCE BETWEEN +.. |cire| unicode:: U+02257 .. RING EQUAL TO +.. |Colon| unicode:: U+02237 .. PROPORTION +.. |Colone| unicode:: U+02A74 .. DOUBLE COLON EQUAL +.. |colone| unicode:: U+02254 .. COLON EQUALS +.. |congdot| unicode:: U+02A6D .. CONGRUENT WITH DOT ABOVE +.. |csub| unicode:: U+02ACF .. CLOSED SUBSET +.. |csube| unicode:: U+02AD1 .. CLOSED SUBSET OR EQUAL TO +.. |csup| unicode:: U+02AD0 .. CLOSED SUPERSET +.. |csupe| unicode:: U+02AD2 .. CLOSED SUPERSET OR EQUAL TO +.. |cuepr| unicode:: U+022DE .. EQUAL TO OR PRECEDES +.. |cuesc| unicode:: U+022DF .. EQUAL TO OR SUCCEEDS +.. |cupre| unicode:: U+0227C .. PRECEDES OR EQUAL TO +.. |Dashv| unicode:: U+02AE4 .. VERTICAL BAR DOUBLE LEFT TURNSTILE +.. |dashv| unicode:: U+022A3 .. LEFT TACK +.. |easter| unicode:: U+02A6E .. EQUALS WITH ASTERISK +.. |ecir| unicode:: U+02256 .. RING IN EQUAL TO +.. |ecolon| unicode:: U+02255 .. EQUALS COLON +.. |eDDot| unicode:: U+02A77 .. EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW +.. |eDot| unicode:: U+02251 .. GEOMETRICALLY EQUAL TO +.. |efDot| unicode:: U+02252 .. APPROXIMATELY EQUAL TO OR THE IMAGE OF +.. |eg| unicode:: U+02A9A .. DOUBLE-LINE EQUAL TO OR GREATER-THAN +.. |egs| unicode:: U+02A96 .. SLANTED EQUAL TO OR GREATER-THAN +.. |egsdot| unicode:: U+02A98 .. SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE +.. |el| unicode:: U+02A99 .. DOUBLE-LINE EQUAL TO OR LESS-THAN +.. |els| unicode:: U+02A95 .. SLANTED EQUAL TO OR LESS-THAN +.. |elsdot| unicode:: U+02A97 .. SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE +.. |equest| unicode:: U+0225F .. QUESTIONED EQUAL TO +.. |equivDD| unicode:: U+02A78 .. EQUIVALENT WITH FOUR DOTS ABOVE +.. |erDot| unicode:: U+02253 .. IMAGE OF OR APPROXIMATELY EQUAL TO +.. |esdot| unicode:: U+02250 .. APPROACHES THE LIMIT +.. |Esim| unicode:: U+02A73 .. EQUALS SIGN ABOVE TILDE OPERATOR +.. |esim| unicode:: U+02242 .. MINUS TILDE +.. |fork| unicode:: U+022D4 .. PITCHFORK +.. |forkv| unicode:: U+02AD9 .. ELEMENT OF OPENING DOWNWARDS +.. |frown| unicode:: U+02322 .. FROWN +.. |gap| unicode:: U+02A86 .. GREATER-THAN OR APPROXIMATE +.. |gE| unicode:: U+02267 .. GREATER-THAN OVER EQUAL TO +.. |gEl| unicode:: U+02A8C .. GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN +.. |gel| unicode:: U+022DB .. GREATER-THAN EQUAL TO OR LESS-THAN +.. |ges| unicode:: U+02A7E .. GREATER-THAN OR SLANTED EQUAL TO +.. |gescc| unicode:: U+02AA9 .. GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL +.. |gesdot| unicode:: U+02A80 .. GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE +.. |gesdoto| unicode:: U+02A82 .. GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE +.. |gesdotol| unicode:: U+02A84 .. GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT +.. |gesl| unicode:: U+022DB U+0FE00 .. GREATER-THAN slanted EQUAL TO OR LESS-THAN +.. |gesles| unicode:: U+02A94 .. GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL +.. |Gg| unicode:: U+022D9 .. VERY MUCH GREATER-THAN +.. |gl| unicode:: U+02277 .. GREATER-THAN OR LESS-THAN +.. |gla| unicode:: U+02AA5 .. GREATER-THAN BESIDE LESS-THAN +.. |glE| unicode:: U+02A92 .. GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL +.. |glj| unicode:: U+02AA4 .. GREATER-THAN OVERLAPPING LESS-THAN +.. |gsdot| unicode:: U+022D7 .. GREATER-THAN WITH DOT +.. |gsim| unicode:: U+02273 .. GREATER-THAN OR EQUIVALENT TO +.. |gsime| unicode:: U+02A8E .. GREATER-THAN ABOVE SIMILAR OR EQUAL +.. |gsiml| unicode:: U+02A90 .. GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN +.. |Gt| unicode:: U+0226B .. MUCH GREATER-THAN +.. |gtcc| unicode:: U+02AA7 .. GREATER-THAN CLOSED BY CURVE +.. |gtcir| unicode:: U+02A7A .. GREATER-THAN WITH CIRCLE INSIDE +.. |gtdot| unicode:: U+022D7 .. GREATER-THAN WITH DOT +.. |gtquest| unicode:: U+02A7C .. GREATER-THAN WITH QUESTION MARK ABOVE +.. |gtrarr| unicode:: U+02978 .. GREATER-THAN ABOVE RIGHTWARDS ARROW +.. |homtht| unicode:: U+0223B .. HOMOTHETIC +.. |lap| unicode:: U+02A85 .. LESS-THAN OR APPROXIMATE +.. |lat| unicode:: U+02AAB .. LARGER THAN +.. |late| unicode:: U+02AAD .. LARGER THAN OR EQUAL TO +.. |lates| unicode:: U+02AAD U+0FE00 .. LARGER THAN OR slanted EQUAL +.. |ldot| unicode:: U+022D6 .. LESS-THAN WITH DOT +.. |lE| unicode:: U+02266 .. LESS-THAN OVER EQUAL TO +.. |lEg| unicode:: U+02A8B .. LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN +.. |leg| unicode:: U+022DA .. LESS-THAN EQUAL TO OR GREATER-THAN +.. |les| unicode:: U+02A7D .. LESS-THAN OR SLANTED EQUAL TO +.. |lescc| unicode:: U+02AA8 .. LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL +.. |lesdot| unicode:: U+02A7F .. LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE +.. |lesdoto| unicode:: U+02A81 .. LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE +.. |lesdotor| unicode:: U+02A83 .. LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT +.. |lesg| unicode:: U+022DA U+0FE00 .. LESS-THAN slanted EQUAL TO OR GREATER-THAN +.. |lesges| unicode:: U+02A93 .. LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL +.. |lg| unicode:: U+02276 .. LESS-THAN OR GREATER-THAN +.. |lgE| unicode:: U+02A91 .. LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL +.. |Ll| unicode:: U+022D8 .. VERY MUCH LESS-THAN +.. |lsim| unicode:: U+02272 .. LESS-THAN OR EQUIVALENT TO +.. |lsime| unicode:: U+02A8D .. LESS-THAN ABOVE SIMILAR OR EQUAL +.. |lsimg| unicode:: U+02A8F .. LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN +.. |Lt| unicode:: U+0226A .. MUCH LESS-THAN +.. |ltcc| unicode:: U+02AA6 .. LESS-THAN CLOSED BY CURVE +.. |ltcir| unicode:: U+02A79 .. LESS-THAN WITH CIRCLE INSIDE +.. |ltdot| unicode:: U+022D6 .. LESS-THAN WITH DOT +.. |ltlarr| unicode:: U+02976 .. LESS-THAN ABOVE LEFTWARDS ARROW +.. |ltquest| unicode:: U+02A7B .. LESS-THAN WITH QUESTION MARK ABOVE +.. |ltrie| unicode:: U+022B4 .. NORMAL SUBGROUP OF OR EQUAL TO +.. |mcomma| unicode:: U+02A29 .. MINUS SIGN WITH COMMA ABOVE +.. |mDDot| unicode:: U+0223A .. GEOMETRIC PROPORTION +.. |mid| unicode:: U+02223 .. DIVIDES +.. |mlcp| unicode:: U+02ADB .. TRANSVERSAL INTERSECTION +.. |models| unicode:: U+022A7 .. MODELS +.. |mstpos| unicode:: U+0223E .. INVERTED LAZY S +.. |Pr| unicode:: U+02ABB .. DOUBLE PRECEDES +.. |pr| unicode:: U+0227A .. PRECEDES +.. |prap| unicode:: U+02AB7 .. PRECEDES ABOVE ALMOST EQUAL TO +.. |prcue| unicode:: U+0227C .. PRECEDES OR EQUAL TO +.. |prE| unicode:: U+02AB3 .. PRECEDES ABOVE EQUALS SIGN +.. |pre| unicode:: U+02AAF .. PRECEDES ABOVE SINGLE-LINE EQUALS SIGN +.. |prsim| unicode:: U+0227E .. PRECEDES OR EQUIVALENT TO +.. |prurel| unicode:: U+022B0 .. PRECEDES UNDER RELATION +.. |ratio| unicode:: U+02236 .. RATIO +.. |rtrie| unicode:: U+022B5 .. CONTAINS AS NORMAL SUBGROUP OR EQUAL TO +.. |rtriltri| unicode:: U+029CE .. RIGHT TRIANGLE ABOVE LEFT TRIANGLE +.. |samalg| unicode:: U+02210 .. N-ARY COPRODUCT +.. |Sc| unicode:: U+02ABC .. DOUBLE SUCCEEDS +.. |sc| unicode:: U+0227B .. SUCCEEDS +.. |scap| unicode:: U+02AB8 .. SUCCEEDS ABOVE ALMOST EQUAL TO +.. |sccue| unicode:: U+0227D .. SUCCEEDS OR EQUAL TO +.. |scE| unicode:: U+02AB4 .. SUCCEEDS ABOVE EQUALS SIGN +.. |sce| unicode:: U+02AB0 .. SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN +.. |scsim| unicode:: U+0227F .. SUCCEEDS OR EQUIVALENT TO +.. |sdote| unicode:: U+02A66 .. EQUALS SIGN WITH DOT BELOW +.. |sfrown| unicode:: U+02322 .. FROWN +.. |simg| unicode:: U+02A9E .. SIMILAR OR GREATER-THAN +.. |simgE| unicode:: U+02AA0 .. SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN +.. |siml| unicode:: U+02A9D .. SIMILAR OR LESS-THAN +.. |simlE| unicode:: U+02A9F .. SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN +.. |smid| unicode:: U+02223 .. DIVIDES +.. |smile| unicode:: U+02323 .. SMILE +.. |smt| unicode:: U+02AAA .. SMALLER THAN +.. |smte| unicode:: U+02AAC .. SMALLER THAN OR EQUAL TO +.. |smtes| unicode:: U+02AAC U+0FE00 .. SMALLER THAN OR slanted EQUAL +.. |spar| unicode:: U+02225 .. PARALLEL TO +.. |sqsub| unicode:: U+0228F .. SQUARE IMAGE OF +.. |sqsube| unicode:: U+02291 .. SQUARE IMAGE OF OR EQUAL TO +.. |sqsup| unicode:: U+02290 .. SQUARE ORIGINAL OF +.. |sqsupe| unicode:: U+02292 .. SQUARE ORIGINAL OF OR EQUAL TO +.. |ssmile| unicode:: U+02323 .. SMILE +.. |Sub| unicode:: U+022D0 .. DOUBLE SUBSET +.. |subE| unicode:: U+02AC5 .. SUBSET OF ABOVE EQUALS SIGN +.. |subedot| unicode:: U+02AC3 .. SUBSET OF OR EQUAL TO WITH DOT ABOVE +.. |submult| unicode:: U+02AC1 .. SUBSET WITH MULTIPLICATION SIGN BELOW +.. |subplus| unicode:: U+02ABF .. SUBSET WITH PLUS SIGN BELOW +.. |subrarr| unicode:: U+02979 .. SUBSET ABOVE RIGHTWARDS ARROW +.. |subsim| unicode:: U+02AC7 .. SUBSET OF ABOVE TILDE OPERATOR +.. |subsub| unicode:: U+02AD5 .. SUBSET ABOVE SUBSET +.. |subsup| unicode:: U+02AD3 .. SUBSET ABOVE SUPERSET +.. |Sup| unicode:: U+022D1 .. DOUBLE SUPERSET +.. |supdsub| unicode:: U+02AD8 .. SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET +.. |supE| unicode:: U+02AC6 .. SUPERSET OF ABOVE EQUALS SIGN +.. |supedot| unicode:: U+02AC4 .. SUPERSET OF OR EQUAL TO WITH DOT ABOVE +.. |suphsol| unicode:: U+02283 U+0002F .. SUPERSET OF, SOLIDUS +.. |suphsub| unicode:: U+02AD7 .. SUPERSET BESIDE SUBSET +.. |suplarr| unicode:: U+0297B .. SUPERSET ABOVE LEFTWARDS ARROW +.. |supmult| unicode:: U+02AC2 .. SUPERSET WITH MULTIPLICATION SIGN BELOW +.. |supplus| unicode:: U+02AC0 .. SUPERSET WITH PLUS SIGN BELOW +.. |supsim| unicode:: U+02AC8 .. SUPERSET OF ABOVE TILDE OPERATOR +.. |supsub| unicode:: U+02AD4 .. SUPERSET ABOVE SUBSET +.. |supsup| unicode:: U+02AD6 .. SUPERSET ABOVE SUPERSET +.. |thkap| unicode:: U+02248 .. ALMOST EQUAL TO +.. |thksim| unicode:: U+0223C .. TILDE OPERATOR +.. |topfork| unicode:: U+02ADA .. PITCHFORK WITH TEE TOP +.. |trie| unicode:: U+0225C .. DELTA EQUAL TO +.. |twixt| unicode:: U+0226C .. BETWEEN +.. |Vbar| unicode:: U+02AEB .. DOUBLE UP TACK +.. |vBar| unicode:: U+02AE8 .. SHORT UP TACK WITH UNDERBAR +.. |vBarv| unicode:: U+02AE9 .. SHORT UP TACK ABOVE SHORT DOWN TACK +.. |VDash| unicode:: U+022AB .. DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE +.. |Vdash| unicode:: U+022A9 .. FORCES +.. |vDash| unicode:: U+022A8 .. TRUE +.. |vdash| unicode:: U+022A2 .. RIGHT TACK +.. |Vdashl| unicode:: U+02AE6 .. LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL +.. |veebar| unicode:: U+022BB .. XOR +.. |vltri| unicode:: U+022B2 .. NORMAL SUBGROUP OF +.. |vprop| unicode:: U+0221D .. PROPORTIONAL TO +.. |vrtri| unicode:: U+022B3 .. CONTAINS AS NORMAL SUBGROUP +.. |Vvdash| unicode:: U+022AA .. TRIPLE VERTICAL BAR RIGHT TURNSTILE diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isobox.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isobox.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isobox.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isobox.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,46 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |boxDL| unicode:: U+02557 .. BOX DRAWINGS DOUBLE DOWN AND LEFT +.. |boxDl| unicode:: U+02556 .. BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +.. |boxdL| unicode:: U+02555 .. BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +.. |boxdl| unicode:: U+02510 .. BOX DRAWINGS LIGHT DOWN AND LEFT +.. |boxDR| unicode:: U+02554 .. BOX DRAWINGS DOUBLE DOWN AND RIGHT +.. |boxDr| unicode:: U+02553 .. BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +.. |boxdR| unicode:: U+02552 .. BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +.. |boxdr| unicode:: U+0250C .. BOX DRAWINGS LIGHT DOWN AND RIGHT +.. |boxH| unicode:: U+02550 .. BOX DRAWINGS DOUBLE HORIZONTAL +.. |boxh| unicode:: U+02500 .. BOX DRAWINGS LIGHT HORIZONTAL +.. |boxHD| unicode:: U+02566 .. BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +.. |boxHd| unicode:: U+02564 .. BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +.. |boxhD| unicode:: U+02565 .. BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +.. |boxhd| unicode:: U+0252C .. BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +.. |boxHU| unicode:: U+02569 .. BOX DRAWINGS DOUBLE UP AND HORIZONTAL +.. |boxHu| unicode:: U+02567 .. BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +.. |boxhU| unicode:: U+02568 .. BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +.. |boxhu| unicode:: U+02534 .. BOX DRAWINGS LIGHT UP AND HORIZONTAL +.. |boxUL| unicode:: U+0255D .. BOX DRAWINGS DOUBLE UP AND LEFT +.. |boxUl| unicode:: U+0255C .. BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +.. |boxuL| unicode:: U+0255B .. BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +.. |boxul| unicode:: U+02518 .. BOX DRAWINGS LIGHT UP AND LEFT +.. |boxUR| unicode:: U+0255A .. BOX DRAWINGS DOUBLE UP AND RIGHT +.. |boxUr| unicode:: U+02559 .. BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +.. |boxuR| unicode:: U+02558 .. BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +.. |boxur| unicode:: U+02514 .. BOX DRAWINGS LIGHT UP AND RIGHT +.. |boxV| unicode:: U+02551 .. BOX DRAWINGS DOUBLE VERTICAL +.. |boxv| unicode:: U+02502 .. BOX DRAWINGS LIGHT VERTICAL +.. |boxVH| unicode:: U+0256C .. BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +.. |boxVh| unicode:: U+0256B .. BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +.. |boxvH| unicode:: U+0256A .. BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +.. |boxvh| unicode:: U+0253C .. BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +.. |boxVL| unicode:: U+02563 .. BOX DRAWINGS DOUBLE VERTICAL AND LEFT +.. |boxVl| unicode:: U+02562 .. BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +.. |boxvL| unicode:: U+02561 .. BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +.. |boxvl| unicode:: U+02524 .. BOX DRAWINGS LIGHT VERTICAL AND LEFT +.. |boxVR| unicode:: U+02560 .. BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +.. |boxVr| unicode:: U+0255F .. BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +.. |boxvR| unicode:: U+0255E .. BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +.. |boxvr| unicode:: U+0251C .. BOX DRAWINGS LIGHT VERTICAL AND RIGHT diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isocyr1.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isocyr1.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isocyr1.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isocyr1.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,73 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Acy| unicode:: U+00410 .. CYRILLIC CAPITAL LETTER A +.. |acy| unicode:: U+00430 .. CYRILLIC SMALL LETTER A +.. |Bcy| unicode:: U+00411 .. CYRILLIC CAPITAL LETTER BE +.. |bcy| unicode:: U+00431 .. CYRILLIC SMALL LETTER BE +.. |CHcy| unicode:: U+00427 .. CYRILLIC CAPITAL LETTER CHE +.. |chcy| unicode:: U+00447 .. CYRILLIC SMALL LETTER CHE +.. |Dcy| unicode:: U+00414 .. CYRILLIC CAPITAL LETTER DE +.. |dcy| unicode:: U+00434 .. CYRILLIC SMALL LETTER DE +.. |Ecy| unicode:: U+0042D .. CYRILLIC CAPITAL LETTER E +.. |ecy| unicode:: U+0044D .. CYRILLIC SMALL LETTER E +.. |Fcy| unicode:: U+00424 .. CYRILLIC CAPITAL LETTER EF +.. |fcy| unicode:: U+00444 .. CYRILLIC SMALL LETTER EF +.. |Gcy| unicode:: U+00413 .. CYRILLIC CAPITAL LETTER GHE +.. |gcy| unicode:: U+00433 .. CYRILLIC SMALL LETTER GHE +.. |HARDcy| unicode:: U+0042A .. CYRILLIC CAPITAL LETTER HARD SIGN +.. |hardcy| unicode:: U+0044A .. CYRILLIC SMALL LETTER HARD SIGN +.. |Icy| unicode:: U+00418 .. CYRILLIC CAPITAL LETTER I +.. |icy| unicode:: U+00438 .. CYRILLIC SMALL LETTER I +.. |IEcy| unicode:: U+00415 .. CYRILLIC CAPITAL LETTER IE +.. |iecy| unicode:: U+00435 .. CYRILLIC SMALL LETTER IE +.. |IOcy| unicode:: U+00401 .. CYRILLIC CAPITAL LETTER IO +.. |iocy| unicode:: U+00451 .. CYRILLIC SMALL LETTER IO +.. |Jcy| unicode:: U+00419 .. CYRILLIC CAPITAL LETTER SHORT I +.. |jcy| unicode:: U+00439 .. CYRILLIC SMALL LETTER SHORT I +.. |Kcy| unicode:: U+0041A .. CYRILLIC CAPITAL LETTER KA +.. |kcy| unicode:: U+0043A .. CYRILLIC SMALL LETTER KA +.. |KHcy| unicode:: U+00425 .. CYRILLIC CAPITAL LETTER HA +.. |khcy| unicode:: U+00445 .. CYRILLIC SMALL LETTER HA +.. |Lcy| unicode:: U+0041B .. CYRILLIC CAPITAL LETTER EL +.. |lcy| unicode:: U+0043B .. CYRILLIC SMALL LETTER EL +.. |Mcy| unicode:: U+0041C .. CYRILLIC CAPITAL LETTER EM +.. |mcy| unicode:: U+0043C .. CYRILLIC SMALL LETTER EM +.. |Ncy| unicode:: U+0041D .. CYRILLIC CAPITAL LETTER EN +.. |ncy| unicode:: U+0043D .. CYRILLIC SMALL LETTER EN +.. |numero| unicode:: U+02116 .. NUMERO SIGN +.. |Ocy| unicode:: U+0041E .. CYRILLIC CAPITAL LETTER O +.. |ocy| unicode:: U+0043E .. CYRILLIC SMALL LETTER O +.. |Pcy| unicode:: U+0041F .. CYRILLIC CAPITAL LETTER PE +.. |pcy| unicode:: U+0043F .. CYRILLIC SMALL LETTER PE +.. |Rcy| unicode:: U+00420 .. CYRILLIC CAPITAL LETTER ER +.. |rcy| unicode:: U+00440 .. CYRILLIC SMALL LETTER ER +.. |Scy| unicode:: U+00421 .. CYRILLIC CAPITAL LETTER ES +.. |scy| unicode:: U+00441 .. CYRILLIC SMALL LETTER ES +.. |SHCHcy| unicode:: U+00429 .. CYRILLIC CAPITAL LETTER SHCHA +.. |shchcy| unicode:: U+00449 .. CYRILLIC SMALL LETTER SHCHA +.. |SHcy| unicode:: U+00428 .. CYRILLIC CAPITAL LETTER SHA +.. |shcy| unicode:: U+00448 .. CYRILLIC SMALL LETTER SHA +.. |SOFTcy| unicode:: U+0042C .. CYRILLIC CAPITAL LETTER SOFT SIGN +.. |softcy| unicode:: U+0044C .. CYRILLIC SMALL LETTER SOFT SIGN +.. |Tcy| unicode:: U+00422 .. CYRILLIC CAPITAL LETTER TE +.. |tcy| unicode:: U+00442 .. CYRILLIC SMALL LETTER TE +.. |TScy| unicode:: U+00426 .. CYRILLIC CAPITAL LETTER TSE +.. |tscy| unicode:: U+00446 .. CYRILLIC SMALL LETTER TSE +.. |Ucy| unicode:: U+00423 .. CYRILLIC CAPITAL LETTER U +.. |ucy| unicode:: U+00443 .. CYRILLIC SMALL LETTER U +.. |Vcy| unicode:: U+00412 .. CYRILLIC CAPITAL LETTER VE +.. |vcy| unicode:: U+00432 .. CYRILLIC SMALL LETTER VE +.. |YAcy| unicode:: U+0042F .. CYRILLIC CAPITAL LETTER YA +.. |yacy| unicode:: U+0044F .. CYRILLIC SMALL LETTER YA +.. |Ycy| unicode:: U+0042B .. CYRILLIC CAPITAL LETTER YERU +.. |ycy| unicode:: U+0044B .. CYRILLIC SMALL LETTER YERU +.. |YUcy| unicode:: U+0042E .. CYRILLIC CAPITAL LETTER YU +.. |yucy| unicode:: U+0044E .. CYRILLIC SMALL LETTER YU +.. |Zcy| unicode:: U+00417 .. CYRILLIC CAPITAL LETTER ZE +.. |zcy| unicode:: U+00437 .. CYRILLIC SMALL LETTER ZE +.. |ZHcy| unicode:: U+00416 .. CYRILLIC CAPITAL LETTER ZHE +.. |zhcy| unicode:: U+00436 .. CYRILLIC SMALL LETTER ZHE diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isocyr2.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isocyr2.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isocyr2.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isocyr2.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |DJcy| unicode:: U+00402 .. CYRILLIC CAPITAL LETTER DJE +.. |djcy| unicode:: U+00452 .. CYRILLIC SMALL LETTER DJE +.. |DScy| unicode:: U+00405 .. CYRILLIC CAPITAL LETTER DZE +.. |dscy| unicode:: U+00455 .. CYRILLIC SMALL LETTER DZE +.. |DZcy| unicode:: U+0040F .. CYRILLIC CAPITAL LETTER DZHE +.. |dzcy| unicode:: U+0045F .. CYRILLIC SMALL LETTER DZHE +.. |GJcy| unicode:: U+00403 .. CYRILLIC CAPITAL LETTER GJE +.. |gjcy| unicode:: U+00453 .. CYRILLIC SMALL LETTER GJE +.. |Iukcy| unicode:: U+00406 .. CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I +.. |iukcy| unicode:: U+00456 .. CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I +.. |Jsercy| unicode:: U+00408 .. CYRILLIC CAPITAL LETTER JE +.. |jsercy| unicode:: U+00458 .. CYRILLIC SMALL LETTER JE +.. |Jukcy| unicode:: U+00404 .. CYRILLIC CAPITAL LETTER UKRAINIAN IE +.. |jukcy| unicode:: U+00454 .. CYRILLIC SMALL LETTER UKRAINIAN IE +.. |KJcy| unicode:: U+0040C .. CYRILLIC CAPITAL LETTER KJE +.. |kjcy| unicode:: U+0045C .. CYRILLIC SMALL LETTER KJE +.. |LJcy| unicode:: U+00409 .. CYRILLIC CAPITAL LETTER LJE +.. |ljcy| unicode:: U+00459 .. CYRILLIC SMALL LETTER LJE +.. |NJcy| unicode:: U+0040A .. CYRILLIC CAPITAL LETTER NJE +.. |njcy| unicode:: U+0045A .. CYRILLIC SMALL LETTER NJE +.. |TSHcy| unicode:: U+0040B .. CYRILLIC CAPITAL LETTER TSHE +.. |tshcy| unicode:: U+0045B .. CYRILLIC SMALL LETTER TSHE +.. |Ubrcy| unicode:: U+0040E .. CYRILLIC CAPITAL LETTER SHORT U +.. |ubrcy| unicode:: U+0045E .. CYRILLIC SMALL LETTER SHORT U +.. |YIcy| unicode:: U+00407 .. CYRILLIC CAPITAL LETTER YI +.. |yicy| unicode:: U+00457 .. CYRILLIC SMALL LETTER YI diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isodia.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isodia.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isodia.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isodia.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |acute| unicode:: U+000B4 .. ACUTE ACCENT +.. |breve| unicode:: U+002D8 .. BREVE +.. |caron| unicode:: U+002C7 .. CARON +.. |cedil| unicode:: U+000B8 .. CEDILLA +.. |circ| unicode:: U+002C6 .. MODIFIER LETTER CIRCUMFLEX ACCENT +.. |dblac| unicode:: U+002DD .. DOUBLE ACUTE ACCENT +.. |die| unicode:: U+000A8 .. DIAERESIS +.. |dot| unicode:: U+002D9 .. DOT ABOVE +.. |grave| unicode:: U+00060 .. GRAVE ACCENT +.. |macr| unicode:: U+000AF .. MACRON +.. |ogon| unicode:: U+002DB .. OGONEK +.. |ring| unicode:: U+002DA .. RING ABOVE +.. |tilde| unicode:: U+002DC .. SMALL TILDE +.. |uml| unicode:: U+000A8 .. DIAERESIS diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isogrk1.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk1.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isogrk1.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk1.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,55 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Agr| unicode:: U+00391 .. GREEK CAPITAL LETTER ALPHA +.. |agr| unicode:: U+003B1 .. GREEK SMALL LETTER ALPHA +.. |Bgr| unicode:: U+00392 .. GREEK CAPITAL LETTER BETA +.. |bgr| unicode:: U+003B2 .. GREEK SMALL LETTER BETA +.. |Dgr| unicode:: U+00394 .. GREEK CAPITAL LETTER DELTA +.. |dgr| unicode:: U+003B4 .. GREEK SMALL LETTER DELTA +.. |EEgr| unicode:: U+00397 .. GREEK CAPITAL LETTER ETA +.. |eegr| unicode:: U+003B7 .. GREEK SMALL LETTER ETA +.. |Egr| unicode:: U+00395 .. GREEK CAPITAL LETTER EPSILON +.. |egr| unicode:: U+003B5 .. GREEK SMALL LETTER EPSILON +.. |Ggr| unicode:: U+00393 .. GREEK CAPITAL LETTER GAMMA +.. |ggr| unicode:: U+003B3 .. GREEK SMALL LETTER GAMMA +.. |Igr| unicode:: U+00399 .. GREEK CAPITAL LETTER IOTA +.. |igr| unicode:: U+003B9 .. GREEK SMALL LETTER IOTA +.. |Kgr| unicode:: U+0039A .. GREEK CAPITAL LETTER KAPPA +.. |kgr| unicode:: U+003BA .. GREEK SMALL LETTER KAPPA +.. |KHgr| unicode:: U+003A7 .. GREEK CAPITAL LETTER CHI +.. |khgr| unicode:: U+003C7 .. GREEK SMALL LETTER CHI +.. |Lgr| unicode:: U+0039B .. GREEK CAPITAL LETTER LAMDA +.. |lgr| unicode:: U+003BB .. GREEK SMALL LETTER LAMDA +.. |Mgr| unicode:: U+0039C .. GREEK CAPITAL LETTER MU +.. |mgr| unicode:: U+003BC .. GREEK SMALL LETTER MU +.. |Ngr| unicode:: U+0039D .. GREEK CAPITAL LETTER NU +.. |ngr| unicode:: U+003BD .. GREEK SMALL LETTER NU +.. |Ogr| unicode:: U+0039F .. GREEK CAPITAL LETTER OMICRON +.. |ogr| unicode:: U+003BF .. GREEK SMALL LETTER OMICRON +.. |OHgr| unicode:: U+003A9 .. GREEK CAPITAL LETTER OMEGA +.. |ohgr| unicode:: U+003C9 .. GREEK SMALL LETTER OMEGA +.. |Pgr| unicode:: U+003A0 .. GREEK CAPITAL LETTER PI +.. |pgr| unicode:: U+003C0 .. GREEK SMALL LETTER PI +.. |PHgr| unicode:: U+003A6 .. GREEK CAPITAL LETTER PHI +.. |phgr| unicode:: U+003C6 .. GREEK SMALL LETTER PHI +.. |PSgr| unicode:: U+003A8 .. GREEK CAPITAL LETTER PSI +.. |psgr| unicode:: U+003C8 .. GREEK SMALL LETTER PSI +.. |Rgr| unicode:: U+003A1 .. GREEK CAPITAL LETTER RHO +.. |rgr| unicode:: U+003C1 .. GREEK SMALL LETTER RHO +.. |sfgr| unicode:: U+003C2 .. GREEK SMALL LETTER FINAL SIGMA +.. |Sgr| unicode:: U+003A3 .. GREEK CAPITAL LETTER SIGMA +.. |sgr| unicode:: U+003C3 .. GREEK SMALL LETTER SIGMA +.. |Tgr| unicode:: U+003A4 .. GREEK CAPITAL LETTER TAU +.. |tgr| unicode:: U+003C4 .. GREEK SMALL LETTER TAU +.. |THgr| unicode:: U+00398 .. GREEK CAPITAL LETTER THETA +.. |thgr| unicode:: U+003B8 .. GREEK SMALL LETTER THETA +.. |Ugr| unicode:: U+003A5 .. GREEK CAPITAL LETTER UPSILON +.. |ugr| unicode:: U+003C5 .. GREEK SMALL LETTER UPSILON +.. |Xgr| unicode:: U+0039E .. GREEK CAPITAL LETTER XI +.. |xgr| unicode:: U+003BE .. GREEK SMALL LETTER XI +.. |Zgr| unicode:: U+00396 .. GREEK CAPITAL LETTER ZETA +.. |zgr| unicode:: U+003B6 .. GREEK SMALL LETTER ZETA diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isogrk2.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk2.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isogrk2.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk2.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,26 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Aacgr| unicode:: U+00386 .. GREEK CAPITAL LETTER ALPHA WITH TONOS +.. |aacgr| unicode:: U+003AC .. GREEK SMALL LETTER ALPHA WITH TONOS +.. |Eacgr| unicode:: U+00388 .. GREEK CAPITAL LETTER EPSILON WITH TONOS +.. |eacgr| unicode:: U+003AD .. GREEK SMALL LETTER EPSILON WITH TONOS +.. |EEacgr| unicode:: U+00389 .. GREEK CAPITAL LETTER ETA WITH TONOS +.. |eeacgr| unicode:: U+003AE .. GREEK SMALL LETTER ETA WITH TONOS +.. |Iacgr| unicode:: U+0038A .. GREEK CAPITAL LETTER IOTA WITH TONOS +.. |iacgr| unicode:: U+003AF .. GREEK SMALL LETTER IOTA WITH TONOS +.. |idiagr| unicode:: U+00390 .. GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS +.. |Idigr| unicode:: U+003AA .. GREEK CAPITAL LETTER IOTA WITH DIALYTIKA +.. |idigr| unicode:: U+003CA .. GREEK SMALL LETTER IOTA WITH DIALYTIKA +.. |Oacgr| unicode:: U+0038C .. GREEK CAPITAL LETTER OMICRON WITH TONOS +.. |oacgr| unicode:: U+003CC .. GREEK SMALL LETTER OMICRON WITH TONOS +.. |OHacgr| unicode:: U+0038F .. GREEK CAPITAL LETTER OMEGA WITH TONOS +.. |ohacgr| unicode:: U+003CE .. GREEK SMALL LETTER OMEGA WITH TONOS +.. |Uacgr| unicode:: U+0038E .. GREEK CAPITAL LETTER UPSILON WITH TONOS +.. |uacgr| unicode:: U+003CD .. GREEK SMALL LETTER UPSILON WITH TONOS +.. |udiagr| unicode:: U+003B0 .. GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS +.. |Udigr| unicode:: U+003AB .. GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA +.. |udigr| unicode:: U+003CB .. GREEK SMALL LETTER UPSILON WITH DIALYTIKA diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isogrk3.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk3.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isogrk3.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk3.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,52 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |alpha| unicode:: U+003B1 .. GREEK SMALL LETTER ALPHA +.. |beta| unicode:: U+003B2 .. GREEK SMALL LETTER BETA +.. |chi| unicode:: U+003C7 .. GREEK SMALL LETTER CHI +.. |Delta| unicode:: U+00394 .. GREEK CAPITAL LETTER DELTA +.. |delta| unicode:: U+003B4 .. GREEK SMALL LETTER DELTA +.. |epsi| unicode:: U+003F5 .. GREEK LUNATE EPSILON SYMBOL +.. |epsis| unicode:: U+003F5 .. GREEK LUNATE EPSILON SYMBOL +.. |epsiv| unicode:: U+003B5 .. GREEK SMALL LETTER EPSILON +.. |eta| unicode:: U+003B7 .. GREEK SMALL LETTER ETA +.. |Gamma| unicode:: U+00393 .. GREEK CAPITAL LETTER GAMMA +.. |gamma| unicode:: U+003B3 .. GREEK SMALL LETTER GAMMA +.. |Gammad| unicode:: U+003DC .. GREEK LETTER DIGAMMA +.. |gammad| unicode:: U+003DD .. GREEK SMALL LETTER DIGAMMA +.. |iota| unicode:: U+003B9 .. GREEK SMALL LETTER IOTA +.. |kappa| unicode:: U+003BA .. GREEK SMALL LETTER KAPPA +.. |kappav| unicode:: U+003F0 .. GREEK KAPPA SYMBOL +.. |Lambda| unicode:: U+0039B .. GREEK CAPITAL LETTER LAMDA +.. |lambda| unicode:: U+003BB .. GREEK SMALL LETTER LAMDA +.. |mu| unicode:: U+003BC .. GREEK SMALL LETTER MU +.. |nu| unicode:: U+003BD .. GREEK SMALL LETTER NU +.. |Omega| unicode:: U+003A9 .. GREEK CAPITAL LETTER OMEGA +.. |omega| unicode:: U+003C9 .. GREEK SMALL LETTER OMEGA +.. |Phi| unicode:: U+003A6 .. GREEK CAPITAL LETTER PHI +.. |phi| unicode:: U+003D5 .. GREEK PHI SYMBOL +.. |phis| unicode:: U+003D5 .. GREEK PHI SYMBOL +.. |phiv| unicode:: U+003C6 .. GREEK SMALL LETTER PHI +.. |Pi| unicode:: U+003A0 .. GREEK CAPITAL LETTER PI +.. |pi| unicode:: U+003C0 .. GREEK SMALL LETTER PI +.. |piv| unicode:: U+003D6 .. GREEK PI SYMBOL +.. |Psi| unicode:: U+003A8 .. GREEK CAPITAL LETTER PSI +.. |psi| unicode:: U+003C8 .. GREEK SMALL LETTER PSI +.. |rho| unicode:: U+003C1 .. GREEK SMALL LETTER RHO +.. |rhov| unicode:: U+003F1 .. GREEK RHO SYMBOL +.. |Sigma| unicode:: U+003A3 .. GREEK CAPITAL LETTER SIGMA +.. |sigma| unicode:: U+003C3 .. GREEK SMALL LETTER SIGMA +.. |sigmav| unicode:: U+003C2 .. GREEK SMALL LETTER FINAL SIGMA +.. |tau| unicode:: U+003C4 .. GREEK SMALL LETTER TAU +.. |Theta| unicode:: U+00398 .. GREEK CAPITAL LETTER THETA +.. |theta| unicode:: U+003B8 .. GREEK SMALL LETTER THETA +.. |thetas| unicode:: U+003B8 .. GREEK SMALL LETTER THETA +.. |thetav| unicode:: U+003D1 .. GREEK THETA SYMBOL +.. |Upsi| unicode:: U+003D2 .. GREEK UPSILON WITH HOOK SYMBOL +.. |upsi| unicode:: U+003C5 .. GREEK SMALL LETTER UPSILON +.. |Xi| unicode:: U+0039E .. GREEK CAPITAL LETTER XI +.. |xi| unicode:: U+003BE .. GREEK SMALL LETTER XI +.. |zeta| unicode:: U+003B6 .. GREEK SMALL LETTER ZETA diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isogrk4.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk4.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isogrk4.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk4.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |b.Gammad| unicode:: U+003DC .. GREEK LETTER DIGAMMA +.. |b.gammad| unicode:: U+003DD .. GREEK SMALL LETTER DIGAMMA diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isogrk4-wide.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk4-wide.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isogrk4-wide.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isogrk4-wide.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,49 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |b.alpha| unicode:: U+1D6C2 .. MATHEMATICAL BOLD SMALL ALPHA +.. |b.beta| unicode:: U+1D6C3 .. MATHEMATICAL BOLD SMALL BETA +.. |b.chi| unicode:: U+1D6D8 .. MATHEMATICAL BOLD SMALL CHI +.. |b.Delta| unicode:: U+1D6AB .. MATHEMATICAL BOLD CAPITAL DELTA +.. |b.delta| unicode:: U+1D6C5 .. MATHEMATICAL BOLD SMALL DELTA +.. |b.epsi| unicode:: U+1D6C6 .. MATHEMATICAL BOLD SMALL EPSILON +.. |b.epsiv| unicode:: U+1D6DC .. MATHEMATICAL BOLD EPSILON SYMBOL +.. |b.eta| unicode:: U+1D6C8 .. MATHEMATICAL BOLD SMALL ETA +.. |b.Gamma| unicode:: U+1D6AA .. MATHEMATICAL BOLD CAPITAL GAMMA +.. |b.gamma| unicode:: U+1D6C4 .. MATHEMATICAL BOLD SMALL GAMMA +.. |b.Gammad| unicode:: U+003DC .. GREEK LETTER DIGAMMA +.. |b.gammad| unicode:: U+003DD .. GREEK SMALL LETTER DIGAMMA +.. |b.iota| unicode:: U+1D6CA .. MATHEMATICAL BOLD SMALL IOTA +.. |b.kappa| unicode:: U+1D6CB .. MATHEMATICAL BOLD SMALL KAPPA +.. |b.kappav| unicode:: U+1D6DE .. MATHEMATICAL BOLD KAPPA SYMBOL +.. |b.Lambda| unicode:: U+1D6B2 .. MATHEMATICAL BOLD CAPITAL LAMDA +.. |b.lambda| unicode:: U+1D6CC .. MATHEMATICAL BOLD SMALL LAMDA +.. |b.mu| unicode:: U+1D6CD .. MATHEMATICAL BOLD SMALL MU +.. |b.nu| unicode:: U+1D6CE .. MATHEMATICAL BOLD SMALL NU +.. |b.Omega| unicode:: U+1D6C0 .. MATHEMATICAL BOLD CAPITAL OMEGA +.. |b.omega| unicode:: U+1D6DA .. MATHEMATICAL BOLD SMALL OMEGA +.. |b.Phi| unicode:: U+1D6BD .. MATHEMATICAL BOLD CAPITAL PHI +.. |b.phi| unicode:: U+1D6D7 .. MATHEMATICAL BOLD SMALL PHI +.. |b.phiv| unicode:: U+1D6DF .. MATHEMATICAL BOLD PHI SYMBOL +.. |b.Pi| unicode:: U+1D6B7 .. MATHEMATICAL BOLD CAPITAL PI +.. |b.pi| unicode:: U+1D6D1 .. MATHEMATICAL BOLD SMALL PI +.. |b.piv| unicode:: U+1D6E1 .. MATHEMATICAL BOLD PI SYMBOL +.. |b.Psi| unicode:: U+1D6BF .. MATHEMATICAL BOLD CAPITAL PSI +.. |b.psi| unicode:: U+1D6D9 .. MATHEMATICAL BOLD SMALL PSI +.. |b.rho| unicode:: U+1D6D2 .. MATHEMATICAL BOLD SMALL RHO +.. |b.rhov| unicode:: U+1D6E0 .. MATHEMATICAL BOLD RHO SYMBOL +.. |b.Sigma| unicode:: U+1D6BA .. MATHEMATICAL BOLD CAPITAL SIGMA +.. |b.sigma| unicode:: U+1D6D4 .. MATHEMATICAL BOLD SMALL SIGMA +.. |b.sigmav| unicode:: U+1D6D3 .. MATHEMATICAL BOLD SMALL FINAL SIGMA +.. |b.tau| unicode:: U+1D6D5 .. MATHEMATICAL BOLD SMALL TAU +.. |b.Theta| unicode:: U+1D6AF .. MATHEMATICAL BOLD CAPITAL THETA +.. |b.thetas| unicode:: U+1D6C9 .. MATHEMATICAL BOLD SMALL THETA +.. |b.thetav| unicode:: U+1D6DD .. MATHEMATICAL BOLD THETA SYMBOL +.. |b.Upsi| unicode:: U+1D6BC .. MATHEMATICAL BOLD CAPITAL UPSILON +.. |b.upsi| unicode:: U+1D6D6 .. MATHEMATICAL BOLD SMALL UPSILON +.. |b.Xi| unicode:: U+1D6B5 .. MATHEMATICAL BOLD CAPITAL XI +.. |b.xi| unicode:: U+1D6CF .. MATHEMATICAL BOLD SMALL XI +.. |b.zeta| unicode:: U+1D6C7 .. MATHEMATICAL BOLD SMALL ZETA diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isolat1.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isolat1.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isolat1.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isolat1.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,68 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Aacute| unicode:: U+000C1 .. LATIN CAPITAL LETTER A WITH ACUTE +.. |aacute| unicode:: U+000E1 .. LATIN SMALL LETTER A WITH ACUTE +.. |Acirc| unicode:: U+000C2 .. LATIN CAPITAL LETTER A WITH CIRCUMFLEX +.. |acirc| unicode:: U+000E2 .. LATIN SMALL LETTER A WITH CIRCUMFLEX +.. |AElig| unicode:: U+000C6 .. LATIN CAPITAL LETTER AE +.. |aelig| unicode:: U+000E6 .. LATIN SMALL LETTER AE +.. |Agrave| unicode:: U+000C0 .. LATIN CAPITAL LETTER A WITH GRAVE +.. |agrave| unicode:: U+000E0 .. LATIN SMALL LETTER A WITH GRAVE +.. |Aring| unicode:: U+000C5 .. LATIN CAPITAL LETTER A WITH RING ABOVE +.. |aring| unicode:: U+000E5 .. LATIN SMALL LETTER A WITH RING ABOVE +.. |Atilde| unicode:: U+000C3 .. LATIN CAPITAL LETTER A WITH TILDE +.. |atilde| unicode:: U+000E3 .. LATIN SMALL LETTER A WITH TILDE +.. |Auml| unicode:: U+000C4 .. LATIN CAPITAL LETTER A WITH DIAERESIS +.. |auml| unicode:: U+000E4 .. LATIN SMALL LETTER A WITH DIAERESIS +.. |Ccedil| unicode:: U+000C7 .. LATIN CAPITAL LETTER C WITH CEDILLA +.. |ccedil| unicode:: U+000E7 .. LATIN SMALL LETTER C WITH CEDILLA +.. |Eacute| unicode:: U+000C9 .. LATIN CAPITAL LETTER E WITH ACUTE +.. |eacute| unicode:: U+000E9 .. LATIN SMALL LETTER E WITH ACUTE +.. |Ecirc| unicode:: U+000CA .. LATIN CAPITAL LETTER E WITH CIRCUMFLEX +.. |ecirc| unicode:: U+000EA .. LATIN SMALL LETTER E WITH CIRCUMFLEX +.. |Egrave| unicode:: U+000C8 .. LATIN CAPITAL LETTER E WITH GRAVE +.. |egrave| unicode:: U+000E8 .. LATIN SMALL LETTER E WITH GRAVE +.. |ETH| unicode:: U+000D0 .. LATIN CAPITAL LETTER ETH +.. |eth| unicode:: U+000F0 .. LATIN SMALL LETTER ETH +.. |Euml| unicode:: U+000CB .. LATIN CAPITAL LETTER E WITH DIAERESIS +.. |euml| unicode:: U+000EB .. LATIN SMALL LETTER E WITH DIAERESIS +.. |Iacute| unicode:: U+000CD .. LATIN CAPITAL LETTER I WITH ACUTE +.. |iacute| unicode:: U+000ED .. LATIN SMALL LETTER I WITH ACUTE +.. |Icirc| unicode:: U+000CE .. LATIN CAPITAL LETTER I WITH CIRCUMFLEX +.. |icirc| unicode:: U+000EE .. LATIN SMALL LETTER I WITH CIRCUMFLEX +.. |Igrave| unicode:: U+000CC .. LATIN CAPITAL LETTER I WITH GRAVE +.. |igrave| unicode:: U+000EC .. LATIN SMALL LETTER I WITH GRAVE +.. |Iuml| unicode:: U+000CF .. LATIN CAPITAL LETTER I WITH DIAERESIS +.. |iuml| unicode:: U+000EF .. LATIN SMALL LETTER I WITH DIAERESIS +.. |Ntilde| unicode:: U+000D1 .. LATIN CAPITAL LETTER N WITH TILDE +.. |ntilde| unicode:: U+000F1 .. LATIN SMALL LETTER N WITH TILDE +.. |Oacute| unicode:: U+000D3 .. LATIN CAPITAL LETTER O WITH ACUTE +.. |oacute| unicode:: U+000F3 .. LATIN SMALL LETTER O WITH ACUTE +.. |Ocirc| unicode:: U+000D4 .. LATIN CAPITAL LETTER O WITH CIRCUMFLEX +.. |ocirc| unicode:: U+000F4 .. LATIN SMALL LETTER O WITH CIRCUMFLEX +.. |Ograve| unicode:: U+000D2 .. LATIN CAPITAL LETTER O WITH GRAVE +.. |ograve| unicode:: U+000F2 .. LATIN SMALL LETTER O WITH GRAVE +.. |Oslash| unicode:: U+000D8 .. LATIN CAPITAL LETTER O WITH STROKE +.. |oslash| unicode:: U+000F8 .. LATIN SMALL LETTER O WITH STROKE +.. |Otilde| unicode:: U+000D5 .. LATIN CAPITAL LETTER O WITH TILDE +.. |otilde| unicode:: U+000F5 .. LATIN SMALL LETTER O WITH TILDE +.. |Ouml| unicode:: U+000D6 .. LATIN CAPITAL LETTER O WITH DIAERESIS +.. |ouml| unicode:: U+000F6 .. LATIN SMALL LETTER O WITH DIAERESIS +.. |szlig| unicode:: U+000DF .. LATIN SMALL LETTER SHARP S +.. |THORN| unicode:: U+000DE .. LATIN CAPITAL LETTER THORN +.. |thorn| unicode:: U+000FE .. LATIN SMALL LETTER THORN +.. |Uacute| unicode:: U+000DA .. LATIN CAPITAL LETTER U WITH ACUTE +.. |uacute| unicode:: U+000FA .. LATIN SMALL LETTER U WITH ACUTE +.. |Ucirc| unicode:: U+000DB .. LATIN CAPITAL LETTER U WITH CIRCUMFLEX +.. |ucirc| unicode:: U+000FB .. LATIN SMALL LETTER U WITH CIRCUMFLEX +.. |Ugrave| unicode:: U+000D9 .. LATIN CAPITAL LETTER U WITH GRAVE +.. |ugrave| unicode:: U+000F9 .. LATIN SMALL LETTER U WITH GRAVE +.. |Uuml| unicode:: U+000DC .. LATIN CAPITAL LETTER U WITH DIAERESIS +.. |uuml| unicode:: U+000FC .. LATIN SMALL LETTER U WITH DIAERESIS +.. |Yacute| unicode:: U+000DD .. LATIN CAPITAL LETTER Y WITH ACUTE +.. |yacute| unicode:: U+000FD .. LATIN SMALL LETTER Y WITH ACUTE +.. |yuml| unicode:: U+000FF .. LATIN SMALL LETTER Y WITH DIAERESIS diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isolat2.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isolat2.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isolat2.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isolat2.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,128 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Abreve| unicode:: U+00102 .. LATIN CAPITAL LETTER A WITH BREVE +.. |abreve| unicode:: U+00103 .. LATIN SMALL LETTER A WITH BREVE +.. |Amacr| unicode:: U+00100 .. LATIN CAPITAL LETTER A WITH MACRON +.. |amacr| unicode:: U+00101 .. LATIN SMALL LETTER A WITH MACRON +.. |Aogon| unicode:: U+00104 .. LATIN CAPITAL LETTER A WITH OGONEK +.. |aogon| unicode:: U+00105 .. LATIN SMALL LETTER A WITH OGONEK +.. |Cacute| unicode:: U+00106 .. LATIN CAPITAL LETTER C WITH ACUTE +.. |cacute| unicode:: U+00107 .. LATIN SMALL LETTER C WITH ACUTE +.. |Ccaron| unicode:: U+0010C .. LATIN CAPITAL LETTER C WITH CARON +.. |ccaron| unicode:: U+0010D .. LATIN SMALL LETTER C WITH CARON +.. |Ccirc| unicode:: U+00108 .. LATIN CAPITAL LETTER C WITH CIRCUMFLEX +.. |ccirc| unicode:: U+00109 .. LATIN SMALL LETTER C WITH CIRCUMFLEX +.. |Cdot| unicode:: U+0010A .. LATIN CAPITAL LETTER C WITH DOT ABOVE +.. |cdot| unicode:: U+0010B .. LATIN SMALL LETTER C WITH DOT ABOVE +.. |Dcaron| unicode:: U+0010E .. LATIN CAPITAL LETTER D WITH CARON +.. |dcaron| unicode:: U+0010F .. LATIN SMALL LETTER D WITH CARON +.. |Dstrok| unicode:: U+00110 .. LATIN CAPITAL LETTER D WITH STROKE +.. |dstrok| unicode:: U+00111 .. LATIN SMALL LETTER D WITH STROKE +.. |Ecaron| unicode:: U+0011A .. LATIN CAPITAL LETTER E WITH CARON +.. |ecaron| unicode:: U+0011B .. LATIN SMALL LETTER E WITH CARON +.. |Edot| unicode:: U+00116 .. LATIN CAPITAL LETTER E WITH DOT ABOVE +.. |edot| unicode:: U+00117 .. LATIN SMALL LETTER E WITH DOT ABOVE +.. |Emacr| unicode:: U+00112 .. LATIN CAPITAL LETTER E WITH MACRON +.. |emacr| unicode:: U+00113 .. LATIN SMALL LETTER E WITH MACRON +.. |ENG| unicode:: U+0014A .. LATIN CAPITAL LETTER ENG +.. |eng| unicode:: U+0014B .. LATIN SMALL LETTER ENG +.. |Eogon| unicode:: U+00118 .. LATIN CAPITAL LETTER E WITH OGONEK +.. |eogon| unicode:: U+00119 .. LATIN SMALL LETTER E WITH OGONEK +.. |gacute| unicode:: U+001F5 .. LATIN SMALL LETTER G WITH ACUTE +.. |Gbreve| unicode:: U+0011E .. LATIN CAPITAL LETTER G WITH BREVE +.. |gbreve| unicode:: U+0011F .. LATIN SMALL LETTER G WITH BREVE +.. |Gcedil| unicode:: U+00122 .. LATIN CAPITAL LETTER G WITH CEDILLA +.. |gcedil| unicode:: U+00123 .. LATIN SMALL LETTER G WITH CEDILLA +.. |Gcirc| unicode:: U+0011C .. LATIN CAPITAL LETTER G WITH CIRCUMFLEX +.. |gcirc| unicode:: U+0011D .. LATIN SMALL LETTER G WITH CIRCUMFLEX +.. |Gdot| unicode:: U+00120 .. LATIN CAPITAL LETTER G WITH DOT ABOVE +.. |gdot| unicode:: U+00121 .. LATIN SMALL LETTER G WITH DOT ABOVE +.. |Hcirc| unicode:: U+00124 .. LATIN CAPITAL LETTER H WITH CIRCUMFLEX +.. |hcirc| unicode:: U+00125 .. LATIN SMALL LETTER H WITH CIRCUMFLEX +.. |Hstrok| unicode:: U+00126 .. LATIN CAPITAL LETTER H WITH STROKE +.. |hstrok| unicode:: U+00127 .. LATIN SMALL LETTER H WITH STROKE +.. |Idot| unicode:: U+00130 .. LATIN CAPITAL LETTER I WITH DOT ABOVE +.. |IJlig| unicode:: U+00132 .. LATIN CAPITAL LIGATURE IJ +.. |ijlig| unicode:: U+00133 .. LATIN SMALL LIGATURE IJ +.. |Imacr| unicode:: U+0012A .. LATIN CAPITAL LETTER I WITH MACRON +.. |imacr| unicode:: U+0012B .. LATIN SMALL LETTER I WITH MACRON +.. |inodot| unicode:: U+00131 .. LATIN SMALL LETTER DOTLESS I +.. |Iogon| unicode:: U+0012E .. LATIN CAPITAL LETTER I WITH OGONEK +.. |iogon| unicode:: U+0012F .. LATIN SMALL LETTER I WITH OGONEK +.. |Itilde| unicode:: U+00128 .. LATIN CAPITAL LETTER I WITH TILDE +.. |itilde| unicode:: U+00129 .. LATIN SMALL LETTER I WITH TILDE +.. |Jcirc| unicode:: U+00134 .. LATIN CAPITAL LETTER J WITH CIRCUMFLEX +.. |jcirc| unicode:: U+00135 .. LATIN SMALL LETTER J WITH CIRCUMFLEX +.. |Kcedil| unicode:: U+00136 .. LATIN CAPITAL LETTER K WITH CEDILLA +.. |kcedil| unicode:: U+00137 .. LATIN SMALL LETTER K WITH CEDILLA +.. |kgreen| unicode:: U+00138 .. LATIN SMALL LETTER KRA +.. |Lacute| unicode:: U+00139 .. LATIN CAPITAL LETTER L WITH ACUTE +.. |lacute| unicode:: U+0013A .. LATIN SMALL LETTER L WITH ACUTE +.. |Lcaron| unicode:: U+0013D .. LATIN CAPITAL LETTER L WITH CARON +.. |lcaron| unicode:: U+0013E .. LATIN SMALL LETTER L WITH CARON +.. |Lcedil| unicode:: U+0013B .. LATIN CAPITAL LETTER L WITH CEDILLA +.. |lcedil| unicode:: U+0013C .. LATIN SMALL LETTER L WITH CEDILLA +.. |Lmidot| unicode:: U+0013F .. LATIN CAPITAL LETTER L WITH MIDDLE DOT +.. |lmidot| unicode:: U+00140 .. LATIN SMALL LETTER L WITH MIDDLE DOT +.. |Lstrok| unicode:: U+00141 .. LATIN CAPITAL LETTER L WITH STROKE +.. |lstrok| unicode:: U+00142 .. LATIN SMALL LETTER L WITH STROKE +.. |Nacute| unicode:: U+00143 .. LATIN CAPITAL LETTER N WITH ACUTE +.. |nacute| unicode:: U+00144 .. LATIN SMALL LETTER N WITH ACUTE +.. |napos| unicode:: U+00149 .. LATIN SMALL LETTER N PRECEDED BY APOSTROPHE +.. |Ncaron| unicode:: U+00147 .. LATIN CAPITAL LETTER N WITH CARON +.. |ncaron| unicode:: U+00148 .. LATIN SMALL LETTER N WITH CARON +.. |Ncedil| unicode:: U+00145 .. LATIN CAPITAL LETTER N WITH CEDILLA +.. |ncedil| unicode:: U+00146 .. LATIN SMALL LETTER N WITH CEDILLA +.. |Odblac| unicode:: U+00150 .. LATIN CAPITAL LETTER O WITH DOUBLE ACUTE +.. |odblac| unicode:: U+00151 .. LATIN SMALL LETTER O WITH DOUBLE ACUTE +.. |OElig| unicode:: U+00152 .. LATIN CAPITAL LIGATURE OE +.. |oelig| unicode:: U+00153 .. LATIN SMALL LIGATURE OE +.. |Omacr| unicode:: U+0014C .. LATIN CAPITAL LETTER O WITH MACRON +.. |omacr| unicode:: U+0014D .. LATIN SMALL LETTER O WITH MACRON +.. |Racute| unicode:: U+00154 .. LATIN CAPITAL LETTER R WITH ACUTE +.. |racute| unicode:: U+00155 .. LATIN SMALL LETTER R WITH ACUTE +.. |Rcaron| unicode:: U+00158 .. LATIN CAPITAL LETTER R WITH CARON +.. |rcaron| unicode:: U+00159 .. LATIN SMALL LETTER R WITH CARON +.. |Rcedil| unicode:: U+00156 .. LATIN CAPITAL LETTER R WITH CEDILLA +.. |rcedil| unicode:: U+00157 .. LATIN SMALL LETTER R WITH CEDILLA +.. |Sacute| unicode:: U+0015A .. LATIN CAPITAL LETTER S WITH ACUTE +.. |sacute| unicode:: U+0015B .. LATIN SMALL LETTER S WITH ACUTE +.. |Scaron| unicode:: U+00160 .. LATIN CAPITAL LETTER S WITH CARON +.. |scaron| unicode:: U+00161 .. LATIN SMALL LETTER S WITH CARON +.. |Scedil| unicode:: U+0015E .. LATIN CAPITAL LETTER S WITH CEDILLA +.. |scedil| unicode:: U+0015F .. LATIN SMALL LETTER S WITH CEDILLA +.. |Scirc| unicode:: U+0015C .. LATIN CAPITAL LETTER S WITH CIRCUMFLEX +.. |scirc| unicode:: U+0015D .. LATIN SMALL LETTER S WITH CIRCUMFLEX +.. |Tcaron| unicode:: U+00164 .. LATIN CAPITAL LETTER T WITH CARON +.. |tcaron| unicode:: U+00165 .. LATIN SMALL LETTER T WITH CARON +.. |Tcedil| unicode:: U+00162 .. LATIN CAPITAL LETTER T WITH CEDILLA +.. |tcedil| unicode:: U+00163 .. LATIN SMALL LETTER T WITH CEDILLA +.. |Tstrok| unicode:: U+00166 .. LATIN CAPITAL LETTER T WITH STROKE +.. |tstrok| unicode:: U+00167 .. LATIN SMALL LETTER T WITH STROKE +.. |Ubreve| unicode:: U+0016C .. LATIN CAPITAL LETTER U WITH BREVE +.. |ubreve| unicode:: U+0016D .. LATIN SMALL LETTER U WITH BREVE +.. |Udblac| unicode:: U+00170 .. LATIN CAPITAL LETTER U WITH DOUBLE ACUTE +.. |udblac| unicode:: U+00171 .. LATIN SMALL LETTER U WITH DOUBLE ACUTE +.. |Umacr| unicode:: U+0016A .. LATIN CAPITAL LETTER U WITH MACRON +.. |umacr| unicode:: U+0016B .. LATIN SMALL LETTER U WITH MACRON +.. |Uogon| unicode:: U+00172 .. LATIN CAPITAL LETTER U WITH OGONEK +.. |uogon| unicode:: U+00173 .. LATIN SMALL LETTER U WITH OGONEK +.. |Uring| unicode:: U+0016E .. LATIN CAPITAL LETTER U WITH RING ABOVE +.. |uring| unicode:: U+0016F .. LATIN SMALL LETTER U WITH RING ABOVE +.. |Utilde| unicode:: U+00168 .. LATIN CAPITAL LETTER U WITH TILDE +.. |utilde| unicode:: U+00169 .. LATIN SMALL LETTER U WITH TILDE +.. |Wcirc| unicode:: U+00174 .. LATIN CAPITAL LETTER W WITH CIRCUMFLEX +.. |wcirc| unicode:: U+00175 .. LATIN SMALL LETTER W WITH CIRCUMFLEX +.. |Ycirc| unicode:: U+00176 .. LATIN CAPITAL LETTER Y WITH CIRCUMFLEX +.. |ycirc| unicode:: U+00177 .. LATIN SMALL LETTER Y WITH CIRCUMFLEX +.. |Yuml| unicode:: U+00178 .. LATIN CAPITAL LETTER Y WITH DIAERESIS +.. |Zacute| unicode:: U+00179 .. LATIN CAPITAL LETTER Z WITH ACUTE +.. |zacute| unicode:: U+0017A .. LATIN SMALL LETTER Z WITH ACUTE +.. |Zcaron| unicode:: U+0017D .. LATIN CAPITAL LETTER Z WITH CARON +.. |zcaron| unicode:: U+0017E .. LATIN SMALL LETTER Z WITH CARON +.. |Zdot| unicode:: U+0017B .. LATIN CAPITAL LETTER Z WITH DOT ABOVE +.. |zdot| unicode:: U+0017C .. LATIN SMALL LETTER Z WITH DOT ABOVE diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isomfrk.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomfrk.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isomfrk.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomfrk.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,11 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Cfr| unicode:: U+0212D .. BLACK-LETTER CAPITAL C +.. |Hfr| unicode:: U+0210C .. BLACK-LETTER CAPITAL H +.. |Ifr| unicode:: U+02111 .. BLACK-LETTER CAPITAL I +.. |Rfr| unicode:: U+0211C .. BLACK-LETTER CAPITAL R +.. |Zfr| unicode:: U+02128 .. BLACK-LETTER CAPITAL Z diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isomfrk-wide.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomfrk-wide.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isomfrk-wide.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomfrk-wide.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Afr| unicode:: U+1D504 .. MATHEMATICAL FRAKTUR CAPITAL A +.. |afr| unicode:: U+1D51E .. MATHEMATICAL FRAKTUR SMALL A +.. |Bfr| unicode:: U+1D505 .. MATHEMATICAL FRAKTUR CAPITAL B +.. |bfr| unicode:: U+1D51F .. MATHEMATICAL FRAKTUR SMALL B +.. |Cfr| unicode:: U+0212D .. BLACK-LETTER CAPITAL C +.. |cfr| unicode:: U+1D520 .. MATHEMATICAL FRAKTUR SMALL C +.. |Dfr| unicode:: U+1D507 .. MATHEMATICAL FRAKTUR CAPITAL D +.. |dfr| unicode:: U+1D521 .. MATHEMATICAL FRAKTUR SMALL D +.. |Efr| unicode:: U+1D508 .. MATHEMATICAL FRAKTUR CAPITAL E +.. |efr| unicode:: U+1D522 .. MATHEMATICAL FRAKTUR SMALL E +.. |Ffr| unicode:: U+1D509 .. MATHEMATICAL FRAKTUR CAPITAL F +.. |ffr| unicode:: U+1D523 .. MATHEMATICAL FRAKTUR SMALL F +.. |Gfr| unicode:: U+1D50A .. MATHEMATICAL FRAKTUR CAPITAL G +.. |gfr| unicode:: U+1D524 .. MATHEMATICAL FRAKTUR SMALL G +.. |Hfr| unicode:: U+0210C .. BLACK-LETTER CAPITAL H +.. |hfr| unicode:: U+1D525 .. MATHEMATICAL FRAKTUR SMALL H +.. |Ifr| unicode:: U+02111 .. BLACK-LETTER CAPITAL I +.. |ifr| unicode:: U+1D526 .. MATHEMATICAL FRAKTUR SMALL I +.. |Jfr| unicode:: U+1D50D .. MATHEMATICAL FRAKTUR CAPITAL J +.. |jfr| unicode:: U+1D527 .. MATHEMATICAL FRAKTUR SMALL J +.. |Kfr| unicode:: U+1D50E .. MATHEMATICAL FRAKTUR CAPITAL K +.. |kfr| unicode:: U+1D528 .. MATHEMATICAL FRAKTUR SMALL K +.. |Lfr| unicode:: U+1D50F .. MATHEMATICAL FRAKTUR CAPITAL L +.. |lfr| unicode:: U+1D529 .. MATHEMATICAL FRAKTUR SMALL L +.. |Mfr| unicode:: U+1D510 .. MATHEMATICAL FRAKTUR CAPITAL M +.. |mfr| unicode:: U+1D52A .. MATHEMATICAL FRAKTUR SMALL M +.. |Nfr| unicode:: U+1D511 .. MATHEMATICAL FRAKTUR CAPITAL N +.. |nfr| unicode:: U+1D52B .. MATHEMATICAL FRAKTUR SMALL N +.. |Ofr| unicode:: U+1D512 .. MATHEMATICAL FRAKTUR CAPITAL O +.. |ofr| unicode:: U+1D52C .. MATHEMATICAL FRAKTUR SMALL O +.. |Pfr| unicode:: U+1D513 .. MATHEMATICAL FRAKTUR CAPITAL P +.. |pfr| unicode:: U+1D52D .. MATHEMATICAL FRAKTUR SMALL P +.. |Qfr| unicode:: U+1D514 .. MATHEMATICAL FRAKTUR CAPITAL Q +.. |qfr| unicode:: U+1D52E .. MATHEMATICAL FRAKTUR SMALL Q +.. |Rfr| unicode:: U+0211C .. BLACK-LETTER CAPITAL R +.. |rfr| unicode:: U+1D52F .. MATHEMATICAL FRAKTUR SMALL R +.. |Sfr| unicode:: U+1D516 .. MATHEMATICAL FRAKTUR CAPITAL S +.. |sfr| unicode:: U+1D530 .. MATHEMATICAL FRAKTUR SMALL S +.. |Tfr| unicode:: U+1D517 .. MATHEMATICAL FRAKTUR CAPITAL T +.. |tfr| unicode:: U+1D531 .. MATHEMATICAL FRAKTUR SMALL T +.. |Ufr| unicode:: U+1D518 .. MATHEMATICAL FRAKTUR CAPITAL U +.. |ufr| unicode:: U+1D532 .. MATHEMATICAL FRAKTUR SMALL U +.. |Vfr| unicode:: U+1D519 .. MATHEMATICAL FRAKTUR CAPITAL V +.. |vfr| unicode:: U+1D533 .. MATHEMATICAL FRAKTUR SMALL V +.. |Wfr| unicode:: U+1D51A .. MATHEMATICAL FRAKTUR CAPITAL W +.. |wfr| unicode:: U+1D534 .. MATHEMATICAL FRAKTUR SMALL W +.. |Xfr| unicode:: U+1D51B .. MATHEMATICAL FRAKTUR CAPITAL X +.. |xfr| unicode:: U+1D535 .. MATHEMATICAL FRAKTUR SMALL X +.. |Yfr| unicode:: U+1D51C .. MATHEMATICAL FRAKTUR CAPITAL Y +.. |yfr| unicode:: U+1D536 .. MATHEMATICAL FRAKTUR SMALL Y +.. |Zfr| unicode:: U+02128 .. BLACK-LETTER CAPITAL Z +.. |zfr| unicode:: U+1D537 .. MATHEMATICAL FRAKTUR SMALL Z diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isomopf.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomopf.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isomopf.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomopf.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,13 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Copf| unicode:: U+02102 .. DOUBLE-STRUCK CAPITAL C +.. |Hopf| unicode:: U+0210D .. DOUBLE-STRUCK CAPITAL H +.. |Nopf| unicode:: U+02115 .. DOUBLE-STRUCK CAPITAL N +.. |Popf| unicode:: U+02119 .. DOUBLE-STRUCK CAPITAL P +.. |Qopf| unicode:: U+0211A .. DOUBLE-STRUCK CAPITAL Q +.. |Ropf| unicode:: U+0211D .. DOUBLE-STRUCK CAPITAL R +.. |Zopf| unicode:: U+02124 .. DOUBLE-STRUCK CAPITAL Z diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isomopf-wide.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomopf-wide.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isomopf-wide.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomopf-wide.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Aopf| unicode:: U+1D538 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL A +.. |Bopf| unicode:: U+1D539 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL B +.. |Copf| unicode:: U+02102 .. DOUBLE-STRUCK CAPITAL C +.. |Dopf| unicode:: U+1D53B .. MATHEMATICAL DOUBLE-STRUCK CAPITAL D +.. |Eopf| unicode:: U+1D53C .. MATHEMATICAL DOUBLE-STRUCK CAPITAL E +.. |Fopf| unicode:: U+1D53D .. MATHEMATICAL DOUBLE-STRUCK CAPITAL F +.. |Gopf| unicode:: U+1D53E .. MATHEMATICAL DOUBLE-STRUCK CAPITAL G +.. |Hopf| unicode:: U+0210D .. DOUBLE-STRUCK CAPITAL H +.. |Iopf| unicode:: U+1D540 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL I +.. |Jopf| unicode:: U+1D541 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL J +.. |Kopf| unicode:: U+1D542 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL K +.. |Lopf| unicode:: U+1D543 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL L +.. |Mopf| unicode:: U+1D544 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL M +.. |Nopf| unicode:: U+02115 .. DOUBLE-STRUCK CAPITAL N +.. |Oopf| unicode:: U+1D546 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL O +.. |Popf| unicode:: U+02119 .. DOUBLE-STRUCK CAPITAL P +.. |Qopf| unicode:: U+0211A .. DOUBLE-STRUCK CAPITAL Q +.. |Ropf| unicode:: U+0211D .. DOUBLE-STRUCK CAPITAL R +.. |Sopf| unicode:: U+1D54A .. MATHEMATICAL DOUBLE-STRUCK CAPITAL S +.. |Topf| unicode:: U+1D54B .. MATHEMATICAL DOUBLE-STRUCK CAPITAL T +.. |Uopf| unicode:: U+1D54C .. MATHEMATICAL DOUBLE-STRUCK CAPITAL U +.. |Vopf| unicode:: U+1D54D .. MATHEMATICAL DOUBLE-STRUCK CAPITAL V +.. |Wopf| unicode:: U+1D54E .. MATHEMATICAL DOUBLE-STRUCK CAPITAL W +.. |Xopf| unicode:: U+1D54F .. MATHEMATICAL DOUBLE-STRUCK CAPITAL X +.. |Yopf| unicode:: U+1D550 .. MATHEMATICAL DOUBLE-STRUCK CAPITAL Y +.. |Zopf| unicode:: U+02124 .. DOUBLE-STRUCK CAPITAL Z diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isomscr.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomscr.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isomscr.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomscr.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Bscr| unicode:: U+0212C .. SCRIPT CAPITAL B +.. |Escr| unicode:: U+02130 .. SCRIPT CAPITAL E +.. |escr| unicode:: U+0212F .. SCRIPT SMALL E +.. |Fscr| unicode:: U+02131 .. SCRIPT CAPITAL F +.. |gscr| unicode:: U+0210A .. SCRIPT SMALL G +.. |Hscr| unicode:: U+0210B .. SCRIPT CAPITAL H +.. |Iscr| unicode:: U+02110 .. SCRIPT CAPITAL I +.. |Lscr| unicode:: U+02112 .. SCRIPT CAPITAL L +.. |Mscr| unicode:: U+02133 .. SCRIPT CAPITAL M +.. |oscr| unicode:: U+02134 .. SCRIPT SMALL O +.. |Rscr| unicode:: U+0211B .. SCRIPT CAPITAL R diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isomscr-wide.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomscr-wide.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isomscr-wide.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isomscr-wide.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Ascr| unicode:: U+1D49C .. MATHEMATICAL SCRIPT CAPITAL A +.. |ascr| unicode:: U+1D4B6 .. MATHEMATICAL SCRIPT SMALL A +.. |Bscr| unicode:: U+0212C .. SCRIPT CAPITAL B +.. |bscr| unicode:: U+1D4B7 .. MATHEMATICAL SCRIPT SMALL B +.. |Cscr| unicode:: U+1D49E .. MATHEMATICAL SCRIPT CAPITAL C +.. |cscr| unicode:: U+1D4B8 .. MATHEMATICAL SCRIPT SMALL C +.. |Dscr| unicode:: U+1D49F .. MATHEMATICAL SCRIPT CAPITAL D +.. |dscr| unicode:: U+1D4B9 .. MATHEMATICAL SCRIPT SMALL D +.. |Escr| unicode:: U+02130 .. SCRIPT CAPITAL E +.. |escr| unicode:: U+0212F .. SCRIPT SMALL E +.. |Fscr| unicode:: U+02131 .. SCRIPT CAPITAL F +.. |fscr| unicode:: U+1D4BB .. MATHEMATICAL SCRIPT SMALL F +.. |Gscr| unicode:: U+1D4A2 .. MATHEMATICAL SCRIPT CAPITAL G +.. |gscr| unicode:: U+0210A .. SCRIPT SMALL G +.. |Hscr| unicode:: U+0210B .. SCRIPT CAPITAL H +.. |hscr| unicode:: U+1D4BD .. MATHEMATICAL SCRIPT SMALL H +.. |Iscr| unicode:: U+02110 .. SCRIPT CAPITAL I +.. |iscr| unicode:: U+1D4BE .. MATHEMATICAL SCRIPT SMALL I +.. |Jscr| unicode:: U+1D4A5 .. MATHEMATICAL SCRIPT CAPITAL J +.. |jscr| unicode:: U+1D4BF .. MATHEMATICAL SCRIPT SMALL J +.. |Kscr| unicode:: U+1D4A6 .. MATHEMATICAL SCRIPT CAPITAL K +.. |kscr| unicode:: U+1D4C0 .. MATHEMATICAL SCRIPT SMALL K +.. |Lscr| unicode:: U+02112 .. SCRIPT CAPITAL L +.. |lscr| unicode:: U+1D4C1 .. MATHEMATICAL SCRIPT SMALL L +.. |Mscr| unicode:: U+02133 .. SCRIPT CAPITAL M +.. |mscr| unicode:: U+1D4C2 .. MATHEMATICAL SCRIPT SMALL M +.. |Nscr| unicode:: U+1D4A9 .. MATHEMATICAL SCRIPT CAPITAL N +.. |nscr| unicode:: U+1D4C3 .. MATHEMATICAL SCRIPT SMALL N +.. |Oscr| unicode:: U+1D4AA .. MATHEMATICAL SCRIPT CAPITAL O +.. |oscr| unicode:: U+02134 .. SCRIPT SMALL O +.. |Pscr| unicode:: U+1D4AB .. MATHEMATICAL SCRIPT CAPITAL P +.. |pscr| unicode:: U+1D4C5 .. MATHEMATICAL SCRIPT SMALL P +.. |Qscr| unicode:: U+1D4AC .. MATHEMATICAL SCRIPT CAPITAL Q +.. |qscr| unicode:: U+1D4C6 .. MATHEMATICAL SCRIPT SMALL Q +.. |Rscr| unicode:: U+0211B .. SCRIPT CAPITAL R +.. |rscr| unicode:: U+1D4C7 .. MATHEMATICAL SCRIPT SMALL R +.. |Sscr| unicode:: U+1D4AE .. MATHEMATICAL SCRIPT CAPITAL S +.. |sscr| unicode:: U+1D4C8 .. MATHEMATICAL SCRIPT SMALL S +.. |Tscr| unicode:: U+1D4AF .. MATHEMATICAL SCRIPT CAPITAL T +.. |tscr| unicode:: U+1D4C9 .. MATHEMATICAL SCRIPT SMALL T +.. |Uscr| unicode:: U+1D4B0 .. MATHEMATICAL SCRIPT CAPITAL U +.. |uscr| unicode:: U+1D4CA .. MATHEMATICAL SCRIPT SMALL U +.. |Vscr| unicode:: U+1D4B1 .. MATHEMATICAL SCRIPT CAPITAL V +.. |vscr| unicode:: U+1D4CB .. MATHEMATICAL SCRIPT SMALL V +.. |Wscr| unicode:: U+1D4B2 .. MATHEMATICAL SCRIPT CAPITAL W +.. |wscr| unicode:: U+1D4CC .. MATHEMATICAL SCRIPT SMALL W +.. |Xscr| unicode:: U+1D4B3 .. MATHEMATICAL SCRIPT CAPITAL X +.. |xscr| unicode:: U+1D4CD .. MATHEMATICAL SCRIPT SMALL X +.. |Yscr| unicode:: U+1D4B4 .. MATHEMATICAL SCRIPT CAPITAL Y +.. |yscr| unicode:: U+1D4CE .. MATHEMATICAL SCRIPT SMALL Y +.. |Zscr| unicode:: U+1D4B5 .. MATHEMATICAL SCRIPT CAPITAL Z +.. |zscr| unicode:: U+1D4CF .. MATHEMATICAL SCRIPT SMALL Z diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isonum.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isonum.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isonum.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isonum.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,82 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |amp| unicode:: U+00026 .. AMPERSAND +.. |apos| unicode:: U+00027 .. APOSTROPHE +.. |ast| unicode:: U+0002A .. ASTERISK +.. |brvbar| unicode:: U+000A6 .. BROKEN BAR +.. |bsol| unicode:: U+0005C .. REVERSE SOLIDUS +.. |cent| unicode:: U+000A2 .. CENT SIGN +.. |colon| unicode:: U+0003A .. COLON +.. |comma| unicode:: U+0002C .. COMMA +.. |commat| unicode:: U+00040 .. COMMERCIAL AT +.. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN +.. |curren| unicode:: U+000A4 .. CURRENCY SIGN +.. |darr| unicode:: U+02193 .. DOWNWARDS ARROW +.. |deg| unicode:: U+000B0 .. DEGREE SIGN +.. |divide| unicode:: U+000F7 .. DIVISION SIGN +.. |dollar| unicode:: U+00024 .. DOLLAR SIGN +.. |equals| unicode:: U+0003D .. EQUALS SIGN +.. |excl| unicode:: U+00021 .. EXCLAMATION MARK +.. |frac12| unicode:: U+000BD .. VULGAR FRACTION ONE HALF +.. |frac14| unicode:: U+000BC .. VULGAR FRACTION ONE QUARTER +.. |frac18| unicode:: U+0215B .. VULGAR FRACTION ONE EIGHTH +.. |frac34| unicode:: U+000BE .. VULGAR FRACTION THREE QUARTERS +.. |frac38| unicode:: U+0215C .. VULGAR FRACTION THREE EIGHTHS +.. |frac58| unicode:: U+0215D .. VULGAR FRACTION FIVE EIGHTHS +.. |frac78| unicode:: U+0215E .. VULGAR FRACTION SEVEN EIGHTHS +.. |gt| unicode:: U+0003E .. GREATER-THAN SIGN +.. |half| unicode:: U+000BD .. VULGAR FRACTION ONE HALF +.. |horbar| unicode:: U+02015 .. HORIZONTAL BAR +.. |hyphen| unicode:: U+02010 .. HYPHEN +.. |iexcl| unicode:: U+000A1 .. INVERTED EXCLAMATION MARK +.. |iquest| unicode:: U+000BF .. INVERTED QUESTION MARK +.. |laquo| unicode:: U+000AB .. LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +.. |larr| unicode:: U+02190 .. LEFTWARDS ARROW +.. |lcub| unicode:: U+0007B .. LEFT CURLY BRACKET +.. |ldquo| unicode:: U+0201C .. LEFT DOUBLE QUOTATION MARK +.. |lowbar| unicode:: U+0005F .. LOW LINE +.. |lpar| unicode:: U+00028 .. LEFT PARENTHESIS +.. |lsqb| unicode:: U+0005B .. LEFT SQUARE BRACKET +.. |lsquo| unicode:: U+02018 .. LEFT SINGLE QUOTATION MARK +.. |lt| unicode:: U+0003C .. LESS-THAN SIGN +.. |micro| unicode:: U+000B5 .. MICRO SIGN +.. |middot| unicode:: U+000B7 .. MIDDLE DOT +.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE +.. |not| unicode:: U+000AC .. NOT SIGN +.. |num| unicode:: U+00023 .. NUMBER SIGN +.. |ohm| unicode:: U+02126 .. OHM SIGN +.. |ordf| unicode:: U+000AA .. FEMININE ORDINAL INDICATOR +.. |ordm| unicode:: U+000BA .. MASCULINE ORDINAL INDICATOR +.. |para| unicode:: U+000B6 .. PILCROW SIGN +.. |percnt| unicode:: U+00025 .. PERCENT SIGN +.. |period| unicode:: U+0002E .. FULL STOP +.. |plus| unicode:: U+0002B .. PLUS SIGN +.. |plusmn| unicode:: U+000B1 .. PLUS-MINUS SIGN +.. |pound| unicode:: U+000A3 .. POUND SIGN +.. |quest| unicode:: U+0003F .. QUESTION MARK +.. |quot| unicode:: U+00022 .. QUOTATION MARK +.. |raquo| unicode:: U+000BB .. RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +.. |rarr| unicode:: U+02192 .. RIGHTWARDS ARROW +.. |rcub| unicode:: U+0007D .. RIGHT CURLY BRACKET +.. |rdquo| unicode:: U+0201D .. RIGHT DOUBLE QUOTATION MARK +.. |reg| unicode:: U+000AE .. REGISTERED SIGN +.. |rpar| unicode:: U+00029 .. RIGHT PARENTHESIS +.. |rsqb| unicode:: U+0005D .. RIGHT SQUARE BRACKET +.. |rsquo| unicode:: U+02019 .. RIGHT SINGLE QUOTATION MARK +.. |sect| unicode:: U+000A7 .. SECTION SIGN +.. |semi| unicode:: U+0003B .. SEMICOLON +.. |shy| unicode:: U+000AD .. SOFT HYPHEN +.. |sol| unicode:: U+0002F .. SOLIDUS +.. |sung| unicode:: U+0266A .. EIGHTH NOTE +.. |sup1| unicode:: U+000B9 .. SUPERSCRIPT ONE +.. |sup2| unicode:: U+000B2 .. SUPERSCRIPT TWO +.. |sup3| unicode:: U+000B3 .. SUPERSCRIPT THREE +.. |times| unicode:: U+000D7 .. MULTIPLICATION SIGN +.. |trade| unicode:: U+02122 .. TRADE MARK SIGN +.. |uarr| unicode:: U+02191 .. UPWARDS ARROW +.. |verbar| unicode:: U+0007C .. VERTICAL LINE +.. |yen| unicode:: U+000A5 .. YEN SIGN diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isopub.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isopub.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isopub.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isopub.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,90 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |blank| unicode:: U+02423 .. OPEN BOX +.. |blk12| unicode:: U+02592 .. MEDIUM SHADE +.. |blk14| unicode:: U+02591 .. LIGHT SHADE +.. |blk34| unicode:: U+02593 .. DARK SHADE +.. |block| unicode:: U+02588 .. FULL BLOCK +.. |bull| unicode:: U+02022 .. BULLET +.. |caret| unicode:: U+02041 .. CARET INSERTION POINT +.. |check| unicode:: U+02713 .. CHECK MARK +.. |cir| unicode:: U+025CB .. WHITE CIRCLE +.. |clubs| unicode:: U+02663 .. BLACK CLUB SUIT +.. |copysr| unicode:: U+02117 .. SOUND RECORDING COPYRIGHT +.. |cross| unicode:: U+02717 .. BALLOT X +.. |Dagger| unicode:: U+02021 .. DOUBLE DAGGER +.. |dagger| unicode:: U+02020 .. DAGGER +.. |dash| unicode:: U+02010 .. HYPHEN +.. |diams| unicode:: U+02666 .. BLACK DIAMOND SUIT +.. |dlcrop| unicode:: U+0230D .. BOTTOM LEFT CROP +.. |drcrop| unicode:: U+0230C .. BOTTOM RIGHT CROP +.. |dtri| unicode:: U+025BF .. WHITE DOWN-POINTING SMALL TRIANGLE +.. |dtrif| unicode:: U+025BE .. BLACK DOWN-POINTING SMALL TRIANGLE +.. |emsp| unicode:: U+02003 .. EM SPACE +.. |emsp13| unicode:: U+02004 .. THREE-PER-EM SPACE +.. |emsp14| unicode:: U+02005 .. FOUR-PER-EM SPACE +.. |ensp| unicode:: U+02002 .. EN SPACE +.. |female| unicode:: U+02640 .. FEMALE SIGN +.. |ffilig| unicode:: U+0FB03 .. LATIN SMALL LIGATURE FFI +.. |fflig| unicode:: U+0FB00 .. LATIN SMALL LIGATURE FF +.. |ffllig| unicode:: U+0FB04 .. LATIN SMALL LIGATURE FFL +.. |filig| unicode:: U+0FB01 .. LATIN SMALL LIGATURE FI +.. |flat| unicode:: U+0266D .. MUSIC FLAT SIGN +.. |fllig| unicode:: U+0FB02 .. LATIN SMALL LIGATURE FL +.. |frac13| unicode:: U+02153 .. VULGAR FRACTION ONE THIRD +.. |frac15| unicode:: U+02155 .. VULGAR FRACTION ONE FIFTH +.. |frac16| unicode:: U+02159 .. VULGAR FRACTION ONE SIXTH +.. |frac23| unicode:: U+02154 .. VULGAR FRACTION TWO THIRDS +.. |frac25| unicode:: U+02156 .. VULGAR FRACTION TWO FIFTHS +.. |frac35| unicode:: U+02157 .. VULGAR FRACTION THREE FIFTHS +.. |frac45| unicode:: U+02158 .. VULGAR FRACTION FOUR FIFTHS +.. |frac56| unicode:: U+0215A .. VULGAR FRACTION FIVE SIXTHS +.. |hairsp| unicode:: U+0200A .. HAIR SPACE +.. |hearts| unicode:: U+02665 .. BLACK HEART SUIT +.. |hellip| unicode:: U+02026 .. HORIZONTAL ELLIPSIS +.. |hybull| unicode:: U+02043 .. HYPHEN BULLET +.. |incare| unicode:: U+02105 .. CARE OF +.. |ldquor| unicode:: U+0201E .. DOUBLE LOW-9 QUOTATION MARK +.. |lhblk| unicode:: U+02584 .. LOWER HALF BLOCK +.. |loz| unicode:: U+025CA .. LOZENGE +.. |lozf| unicode:: U+029EB .. BLACK LOZENGE +.. |lsquor| unicode:: U+0201A .. SINGLE LOW-9 QUOTATION MARK +.. |ltri| unicode:: U+025C3 .. WHITE LEFT-POINTING SMALL TRIANGLE +.. |ltrif| unicode:: U+025C2 .. BLACK LEFT-POINTING SMALL TRIANGLE +.. |male| unicode:: U+02642 .. MALE SIGN +.. |malt| unicode:: U+02720 .. MALTESE CROSS +.. |marker| unicode:: U+025AE .. BLACK VERTICAL RECTANGLE +.. |mdash| unicode:: U+02014 .. EM DASH +.. |mldr| unicode:: U+02026 .. HORIZONTAL ELLIPSIS +.. |natur| unicode:: U+0266E .. MUSIC NATURAL SIGN +.. |ndash| unicode:: U+02013 .. EN DASH +.. |nldr| unicode:: U+02025 .. TWO DOT LEADER +.. |numsp| unicode:: U+02007 .. FIGURE SPACE +.. |phone| unicode:: U+0260E .. BLACK TELEPHONE +.. |puncsp| unicode:: U+02008 .. PUNCTUATION SPACE +.. |rdquor| unicode:: U+0201D .. RIGHT DOUBLE QUOTATION MARK +.. |rect| unicode:: U+025AD .. WHITE RECTANGLE +.. |rsquor| unicode:: U+02019 .. RIGHT SINGLE QUOTATION MARK +.. |rtri| unicode:: U+025B9 .. WHITE RIGHT-POINTING SMALL TRIANGLE +.. |rtrif| unicode:: U+025B8 .. BLACK RIGHT-POINTING SMALL TRIANGLE +.. |rx| unicode:: U+0211E .. PRESCRIPTION TAKE +.. |sext| unicode:: U+02736 .. SIX POINTED BLACK STAR +.. |sharp| unicode:: U+0266F .. MUSIC SHARP SIGN +.. |spades| unicode:: U+02660 .. BLACK SPADE SUIT +.. |squ| unicode:: U+025A1 .. WHITE SQUARE +.. |squf| unicode:: U+025AA .. BLACK SMALL SQUARE +.. |star| unicode:: U+02606 .. WHITE STAR +.. |starf| unicode:: U+02605 .. BLACK STAR +.. |target| unicode:: U+02316 .. POSITION INDICATOR +.. |telrec| unicode:: U+02315 .. TELEPHONE RECORDER +.. |thinsp| unicode:: U+02009 .. THIN SPACE +.. |uhblk| unicode:: U+02580 .. UPPER HALF BLOCK +.. |ulcrop| unicode:: U+0230F .. TOP LEFT CROP +.. |urcrop| unicode:: U+0230E .. TOP RIGHT CROP +.. |utri| unicode:: U+025B5 .. WHITE UP-POINTING SMALL TRIANGLE +.. |utrif| unicode:: U+025B4 .. BLACK UP-POINTING SMALL TRIANGLE +.. |vellip| unicode:: U+022EE .. VERTICAL ELLIPSIS diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/isotech.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/isotech.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/isotech.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/isotech.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,168 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |acd| unicode:: U+0223F .. SINE WAVE +.. |aleph| unicode:: U+02135 .. ALEF SYMBOL +.. |And| unicode:: U+02A53 .. DOUBLE LOGICAL AND +.. |and| unicode:: U+02227 .. LOGICAL AND +.. |andand| unicode:: U+02A55 .. TWO INTERSECTING LOGICAL AND +.. |andd| unicode:: U+02A5C .. LOGICAL AND WITH HORIZONTAL DASH +.. |andslope| unicode:: U+02A58 .. SLOPING LARGE AND +.. |andv| unicode:: U+02A5A .. LOGICAL AND WITH MIDDLE STEM +.. |ang90| unicode:: U+0221F .. RIGHT ANGLE +.. |angrt| unicode:: U+0221F .. RIGHT ANGLE +.. |angsph| unicode:: U+02222 .. SPHERICAL ANGLE +.. |angst| unicode:: U+0212B .. ANGSTROM SIGN +.. |ap| unicode:: U+02248 .. ALMOST EQUAL TO +.. |apacir| unicode:: U+02A6F .. ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT +.. |awconint| unicode:: U+02233 .. ANTICLOCKWISE CONTOUR INTEGRAL +.. |awint| unicode:: U+02A11 .. ANTICLOCKWISE INTEGRATION +.. |becaus| unicode:: U+02235 .. BECAUSE +.. |bernou| unicode:: U+0212C .. SCRIPT CAPITAL B +.. |bne| unicode:: U+0003D U+020E5 .. EQUALS SIGN with reverse slash +.. |bnequiv| unicode:: U+02261 U+020E5 .. IDENTICAL TO with reverse slash +.. |bNot| unicode:: U+02AED .. REVERSED DOUBLE STROKE NOT SIGN +.. |bnot| unicode:: U+02310 .. REVERSED NOT SIGN +.. |bottom| unicode:: U+022A5 .. UP TACK +.. |cap| unicode:: U+02229 .. INTERSECTION +.. |Cconint| unicode:: U+02230 .. VOLUME INTEGRAL +.. |cirfnint| unicode:: U+02A10 .. CIRCULATION FUNCTION +.. |compfn| unicode:: U+02218 .. RING OPERATOR +.. |cong| unicode:: U+02245 .. APPROXIMATELY EQUAL TO +.. |Conint| unicode:: U+0222F .. SURFACE INTEGRAL +.. |conint| unicode:: U+0222E .. CONTOUR INTEGRAL +.. |ctdot| unicode:: U+022EF .. MIDLINE HORIZONTAL ELLIPSIS +.. |cup| unicode:: U+0222A .. UNION +.. |cwconint| unicode:: U+02232 .. CLOCKWISE CONTOUR INTEGRAL +.. |cwint| unicode:: U+02231 .. CLOCKWISE INTEGRAL +.. |cylcty| unicode:: U+0232D .. CYLINDRICITY +.. |disin| unicode:: U+022F2 .. ELEMENT OF WITH LONG HORIZONTAL STROKE +.. |Dot| unicode:: U+000A8 .. DIAERESIS +.. |DotDot| unicode:: U+020DC .. COMBINING FOUR DOTS ABOVE +.. |dsol| unicode:: U+029F6 .. SOLIDUS WITH OVERBAR +.. |dtdot| unicode:: U+022F1 .. DOWN RIGHT DIAGONAL ELLIPSIS +.. |dwangle| unicode:: U+029A6 .. OBLIQUE ANGLE OPENING UP +.. |elinters| unicode:: U+0FFFD .. REPLACEMENT CHARACTER +.. |epar| unicode:: U+022D5 .. EQUAL AND PARALLEL TO +.. |eparsl| unicode:: U+029E3 .. EQUALS SIGN AND SLANTED PARALLEL +.. |equiv| unicode:: U+02261 .. IDENTICAL TO +.. |eqvparsl| unicode:: U+029E5 .. IDENTICAL TO AND SLANTED PARALLEL +.. |exist| unicode:: U+02203 .. THERE EXISTS +.. |fltns| unicode:: U+025B1 .. WHITE PARALLELOGRAM +.. |fnof| unicode:: U+00192 .. LATIN SMALL LETTER F WITH HOOK +.. |forall| unicode:: U+02200 .. FOR ALL +.. |fpartint| unicode:: U+02A0D .. FINITE PART INTEGRAL +.. |ge| unicode:: U+02265 .. GREATER-THAN OR EQUAL TO +.. |hamilt| unicode:: U+0210B .. SCRIPT CAPITAL H +.. |iff| unicode:: U+021D4 .. LEFT RIGHT DOUBLE ARROW +.. |iinfin| unicode:: U+029DC .. INCOMPLETE INFINITY +.. |imped| unicode:: U+001B5 .. LATIN CAPITAL LETTER Z WITH STROKE +.. |infin| unicode:: U+0221E .. INFINITY +.. |infintie| unicode:: U+029DD .. TIE OVER INFINITY +.. |Int| unicode:: U+0222C .. DOUBLE INTEGRAL +.. |int| unicode:: U+0222B .. INTEGRAL +.. |intlarhk| unicode:: U+02A17 .. INTEGRAL WITH LEFTWARDS ARROW WITH HOOK +.. |isin| unicode:: U+02208 .. ELEMENT OF +.. |isindot| unicode:: U+022F5 .. ELEMENT OF WITH DOT ABOVE +.. |isinE| unicode:: U+022F9 .. ELEMENT OF WITH TWO HORIZONTAL STROKES +.. |isins| unicode:: U+022F4 .. SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE +.. |isinsv| unicode:: U+022F3 .. ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE +.. |isinv| unicode:: U+02208 .. ELEMENT OF +.. |lagran| unicode:: U+02112 .. SCRIPT CAPITAL L +.. |Lang| unicode:: U+0300A .. LEFT DOUBLE ANGLE BRACKET +.. |lang| unicode:: U+02329 .. LEFT-POINTING ANGLE BRACKET +.. |lArr| unicode:: U+021D0 .. LEFTWARDS DOUBLE ARROW +.. |lbbrk| unicode:: U+03014 .. LEFT TORTOISE SHELL BRACKET +.. |le| unicode:: U+02264 .. LESS-THAN OR EQUAL TO +.. |loang| unicode:: U+03018 .. LEFT WHITE TORTOISE SHELL BRACKET +.. |lobrk| unicode:: U+0301A .. LEFT WHITE SQUARE BRACKET +.. |lopar| unicode:: U+02985 .. LEFT WHITE PARENTHESIS +.. |lowast| unicode:: U+02217 .. ASTERISK OPERATOR +.. |minus| unicode:: U+02212 .. MINUS SIGN +.. |mnplus| unicode:: U+02213 .. MINUS-OR-PLUS SIGN +.. |nabla| unicode:: U+02207 .. NABLA +.. |ne| unicode:: U+02260 .. NOT EQUAL TO +.. |nedot| unicode:: U+02250 U+00338 .. APPROACHES THE LIMIT with slash +.. |nhpar| unicode:: U+02AF2 .. PARALLEL WITH HORIZONTAL STROKE +.. |ni| unicode:: U+0220B .. CONTAINS AS MEMBER +.. |nis| unicode:: U+022FC .. SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE +.. |nisd| unicode:: U+022FA .. CONTAINS WITH LONG HORIZONTAL STROKE +.. |niv| unicode:: U+0220B .. CONTAINS AS MEMBER +.. |Not| unicode:: U+02AEC .. DOUBLE STROKE NOT SIGN +.. |notin| unicode:: U+02209 .. NOT AN ELEMENT OF +.. |notindot| unicode:: U+022F5 U+00338 .. ELEMENT OF WITH DOT ABOVE with slash +.. |notinE| unicode:: U+022F9 U+00338 .. ELEMENT OF WITH TWO HORIZONTAL STROKES with slash +.. |notinva| unicode:: U+02209 .. NOT AN ELEMENT OF +.. |notinvb| unicode:: U+022F7 .. SMALL ELEMENT OF WITH OVERBAR +.. |notinvc| unicode:: U+022F6 .. ELEMENT OF WITH OVERBAR +.. |notni| unicode:: U+0220C .. DOES NOT CONTAIN AS MEMBER +.. |notniva| unicode:: U+0220C .. DOES NOT CONTAIN AS MEMBER +.. |notnivb| unicode:: U+022FE .. SMALL CONTAINS WITH OVERBAR +.. |notnivc| unicode:: U+022FD .. CONTAINS WITH OVERBAR +.. |nparsl| unicode:: U+02AFD U+020E5 .. DOUBLE SOLIDUS OPERATOR with reverse slash +.. |npart| unicode:: U+02202 U+00338 .. PARTIAL DIFFERENTIAL with slash +.. |npolint| unicode:: U+02A14 .. LINE INTEGRATION NOT INCLUDING THE POLE +.. |nvinfin| unicode:: U+029DE .. INFINITY NEGATED WITH VERTICAL BAR +.. |olcross| unicode:: U+029BB .. CIRCLE WITH SUPERIMPOSED X +.. |Or| unicode:: U+02A54 .. DOUBLE LOGICAL OR +.. |or| unicode:: U+02228 .. LOGICAL OR +.. |ord| unicode:: U+02A5D .. LOGICAL OR WITH HORIZONTAL DASH +.. |order| unicode:: U+02134 .. SCRIPT SMALL O +.. |oror| unicode:: U+02A56 .. TWO INTERSECTING LOGICAL OR +.. |orslope| unicode:: U+02A57 .. SLOPING LARGE OR +.. |orv| unicode:: U+02A5B .. LOGICAL OR WITH MIDDLE STEM +.. |par| unicode:: U+02225 .. PARALLEL TO +.. |parsl| unicode:: U+02AFD .. DOUBLE SOLIDUS OPERATOR +.. |part| unicode:: U+02202 .. PARTIAL DIFFERENTIAL +.. |permil| unicode:: U+02030 .. PER MILLE SIGN +.. |perp| unicode:: U+022A5 .. UP TACK +.. |pertenk| unicode:: U+02031 .. PER TEN THOUSAND SIGN +.. |phmmat| unicode:: U+02133 .. SCRIPT CAPITAL M +.. |pointint| unicode:: U+02A15 .. INTEGRAL AROUND A POINT OPERATOR +.. |Prime| unicode:: U+02033 .. DOUBLE PRIME +.. |prime| unicode:: U+02032 .. PRIME +.. |profalar| unicode:: U+0232E .. ALL AROUND-PROFILE +.. |profline| unicode:: U+02312 .. ARC +.. |profsurf| unicode:: U+02313 .. SEGMENT +.. |prop| unicode:: U+0221D .. PROPORTIONAL TO +.. |qint| unicode:: U+02A0C .. QUADRUPLE INTEGRAL OPERATOR +.. |qprime| unicode:: U+02057 .. QUADRUPLE PRIME +.. |quatint| unicode:: U+02A16 .. QUATERNION INTEGRAL OPERATOR +.. |radic| unicode:: U+0221A .. SQUARE ROOT +.. |Rang| unicode:: U+0300B .. RIGHT DOUBLE ANGLE BRACKET +.. |rang| unicode:: U+0232A .. RIGHT-POINTING ANGLE BRACKET +.. |rArr| unicode:: U+021D2 .. RIGHTWARDS DOUBLE ARROW +.. |rbbrk| unicode:: U+03015 .. RIGHT TORTOISE SHELL BRACKET +.. |roang| unicode:: U+03019 .. RIGHT WHITE TORTOISE SHELL BRACKET +.. |robrk| unicode:: U+0301B .. RIGHT WHITE SQUARE BRACKET +.. |ropar| unicode:: U+02986 .. RIGHT WHITE PARENTHESIS +.. |rppolint| unicode:: U+02A12 .. LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE +.. |scpolint| unicode:: U+02A13 .. LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE +.. |sim| unicode:: U+0223C .. TILDE OPERATOR +.. |simdot| unicode:: U+02A6A .. TILDE OPERATOR WITH DOT ABOVE +.. |sime| unicode:: U+02243 .. ASYMPTOTICALLY EQUAL TO +.. |smeparsl| unicode:: U+029E4 .. EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE +.. |square| unicode:: U+025A1 .. WHITE SQUARE +.. |squarf| unicode:: U+025AA .. BLACK SMALL SQUARE +.. |strns| unicode:: U+000AF .. MACRON +.. |sub| unicode:: U+02282 .. SUBSET OF +.. |sube| unicode:: U+02286 .. SUBSET OF OR EQUAL TO +.. |sup| unicode:: U+02283 .. SUPERSET OF +.. |supe| unicode:: U+02287 .. SUPERSET OF OR EQUAL TO +.. |tdot| unicode:: U+020DB .. COMBINING THREE DOTS ABOVE +.. |there4| unicode:: U+02234 .. THEREFORE +.. |tint| unicode:: U+0222D .. TRIPLE INTEGRAL +.. |top| unicode:: U+022A4 .. DOWN TACK +.. |topbot| unicode:: U+02336 .. APL FUNCTIONAL SYMBOL I-BEAM +.. |topcir| unicode:: U+02AF1 .. DOWN TACK WITH CIRCLE BELOW +.. |tprime| unicode:: U+02034 .. TRIPLE PRIME +.. |utdot| unicode:: U+022F0 .. UP RIGHT DIAGONAL ELLIPSIS +.. |uwangle| unicode:: U+029A7 .. OBLIQUE ANGLE OPENING DOWN +.. |vangrt| unicode:: U+0299C .. RIGHT ANGLE VARIANT WITH SQUARE +.. |veeeq| unicode:: U+0225A .. EQUIANGULAR TO +.. |Verbar| unicode:: U+02016 .. DOUBLE VERTICAL LINE +.. |wedgeq| unicode:: U+02259 .. ESTIMATES +.. |xnis| unicode:: U+022FB .. CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/mmlalias.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/mmlalias.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/mmlalias.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/mmlalias.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,554 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |angle| unicode:: U+02220 .. ANGLE +.. |ApplyFunction| unicode:: U+02061 .. FUNCTION APPLICATION +.. |approx| unicode:: U+02248 .. ALMOST EQUAL TO +.. |approxeq| unicode:: U+0224A .. ALMOST EQUAL OR EQUAL TO +.. |Assign| unicode:: U+02254 .. COLON EQUALS +.. |backcong| unicode:: U+0224C .. ALL EQUAL TO +.. |backepsilon| unicode:: U+003F6 .. GREEK REVERSED LUNATE EPSILON SYMBOL +.. |backprime| unicode:: U+02035 .. REVERSED PRIME +.. |backsim| unicode:: U+0223D .. REVERSED TILDE +.. |backsimeq| unicode:: U+022CD .. REVERSED TILDE EQUALS +.. |Backslash| unicode:: U+02216 .. SET MINUS +.. |barwedge| unicode:: U+02305 .. PROJECTIVE +.. |Because| unicode:: U+02235 .. BECAUSE +.. |because| unicode:: U+02235 .. BECAUSE +.. |Bernoullis| unicode:: U+0212C .. SCRIPT CAPITAL B +.. |between| unicode:: U+0226C .. BETWEEN +.. |bigcap| unicode:: U+022C2 .. N-ARY INTERSECTION +.. |bigcirc| unicode:: U+025EF .. LARGE CIRCLE +.. |bigcup| unicode:: U+022C3 .. N-ARY UNION +.. |bigodot| unicode:: U+02A00 .. N-ARY CIRCLED DOT OPERATOR +.. |bigoplus| unicode:: U+02A01 .. N-ARY CIRCLED PLUS OPERATOR +.. |bigotimes| unicode:: U+02A02 .. N-ARY CIRCLED TIMES OPERATOR +.. |bigsqcup| unicode:: U+02A06 .. N-ARY SQUARE UNION OPERATOR +.. |bigstar| unicode:: U+02605 .. BLACK STAR +.. |bigtriangledown| unicode:: U+025BD .. WHITE DOWN-POINTING TRIANGLE +.. |bigtriangleup| unicode:: U+025B3 .. WHITE UP-POINTING TRIANGLE +.. |biguplus| unicode:: U+02A04 .. N-ARY UNION OPERATOR WITH PLUS +.. |bigvee| unicode:: U+022C1 .. N-ARY LOGICAL OR +.. |bigwedge| unicode:: U+022C0 .. N-ARY LOGICAL AND +.. |bkarow| unicode:: U+0290D .. RIGHTWARDS DOUBLE DASH ARROW +.. |blacklozenge| unicode:: U+029EB .. BLACK LOZENGE +.. |blacksquare| unicode:: U+025AA .. BLACK SMALL SQUARE +.. |blacktriangle| unicode:: U+025B4 .. BLACK UP-POINTING SMALL TRIANGLE +.. |blacktriangledown| unicode:: U+025BE .. BLACK DOWN-POINTING SMALL TRIANGLE +.. |blacktriangleleft| unicode:: U+025C2 .. BLACK LEFT-POINTING SMALL TRIANGLE +.. |blacktriangleright| unicode:: U+025B8 .. BLACK RIGHT-POINTING SMALL TRIANGLE +.. |bot| unicode:: U+022A5 .. UP TACK +.. |boxminus| unicode:: U+0229F .. SQUARED MINUS +.. |boxplus| unicode:: U+0229E .. SQUARED PLUS +.. |boxtimes| unicode:: U+022A0 .. SQUARED TIMES +.. |Breve| unicode:: U+002D8 .. BREVE +.. |bullet| unicode:: U+02022 .. BULLET +.. |Bumpeq| unicode:: U+0224E .. GEOMETRICALLY EQUIVALENT TO +.. |bumpeq| unicode:: U+0224F .. DIFFERENCE BETWEEN +.. |CapitalDifferentialD| unicode:: U+02145 .. DOUBLE-STRUCK ITALIC CAPITAL D +.. |Cayleys| unicode:: U+0212D .. BLACK-LETTER CAPITAL C +.. |Cedilla| unicode:: U+000B8 .. CEDILLA +.. |CenterDot| unicode:: U+000B7 .. MIDDLE DOT +.. |centerdot| unicode:: U+000B7 .. MIDDLE DOT +.. |checkmark| unicode:: U+02713 .. CHECK MARK +.. |circeq| unicode:: U+02257 .. RING EQUAL TO +.. |circlearrowleft| unicode:: U+021BA .. ANTICLOCKWISE OPEN CIRCLE ARROW +.. |circlearrowright| unicode:: U+021BB .. CLOCKWISE OPEN CIRCLE ARROW +.. |circledast| unicode:: U+0229B .. CIRCLED ASTERISK OPERATOR +.. |circledcirc| unicode:: U+0229A .. CIRCLED RING OPERATOR +.. |circleddash| unicode:: U+0229D .. CIRCLED DASH +.. |CircleDot| unicode:: U+02299 .. CIRCLED DOT OPERATOR +.. |circledR| unicode:: U+000AE .. REGISTERED SIGN +.. |circledS| unicode:: U+024C8 .. CIRCLED LATIN CAPITAL LETTER S +.. |CircleMinus| unicode:: U+02296 .. CIRCLED MINUS +.. |CirclePlus| unicode:: U+02295 .. CIRCLED PLUS +.. |CircleTimes| unicode:: U+02297 .. CIRCLED TIMES +.. |ClockwiseContourIntegral| unicode:: U+02232 .. CLOCKWISE CONTOUR INTEGRAL +.. |CloseCurlyDoubleQuote| unicode:: U+0201D .. RIGHT DOUBLE QUOTATION MARK +.. |CloseCurlyQuote| unicode:: U+02019 .. RIGHT SINGLE QUOTATION MARK +.. |clubsuit| unicode:: U+02663 .. BLACK CLUB SUIT +.. |coloneq| unicode:: U+02254 .. COLON EQUALS +.. |complement| unicode:: U+02201 .. COMPLEMENT +.. |complexes| unicode:: U+02102 .. DOUBLE-STRUCK CAPITAL C +.. |Congruent| unicode:: U+02261 .. IDENTICAL TO +.. |ContourIntegral| unicode:: U+0222E .. CONTOUR INTEGRAL +.. |Coproduct| unicode:: U+02210 .. N-ARY COPRODUCT +.. |CounterClockwiseContourIntegral| unicode:: U+02233 .. ANTICLOCKWISE CONTOUR INTEGRAL +.. |CupCap| unicode:: U+0224D .. EQUIVALENT TO +.. |curlyeqprec| unicode:: U+022DE .. EQUAL TO OR PRECEDES +.. |curlyeqsucc| unicode:: U+022DF .. EQUAL TO OR SUCCEEDS +.. |curlyvee| unicode:: U+022CE .. CURLY LOGICAL OR +.. |curlywedge| unicode:: U+022CF .. CURLY LOGICAL AND +.. |curvearrowleft| unicode:: U+021B6 .. ANTICLOCKWISE TOP SEMICIRCLE ARROW +.. |curvearrowright| unicode:: U+021B7 .. CLOCKWISE TOP SEMICIRCLE ARROW +.. |dbkarow| unicode:: U+0290F .. RIGHTWARDS TRIPLE DASH ARROW +.. |ddagger| unicode:: U+02021 .. DOUBLE DAGGER +.. |ddotseq| unicode:: U+02A77 .. EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW +.. |Del| unicode:: U+02207 .. NABLA +.. |DiacriticalAcute| unicode:: U+000B4 .. ACUTE ACCENT +.. |DiacriticalDot| unicode:: U+002D9 .. DOT ABOVE +.. |DiacriticalDoubleAcute| unicode:: U+002DD .. DOUBLE ACUTE ACCENT +.. |DiacriticalGrave| unicode:: U+00060 .. GRAVE ACCENT +.. |DiacriticalTilde| unicode:: U+002DC .. SMALL TILDE +.. |Diamond| unicode:: U+022C4 .. DIAMOND OPERATOR +.. |diamond| unicode:: U+022C4 .. DIAMOND OPERATOR +.. |diamondsuit| unicode:: U+02666 .. BLACK DIAMOND SUIT +.. |DifferentialD| unicode:: U+02146 .. DOUBLE-STRUCK ITALIC SMALL D +.. |digamma| unicode:: U+003DD .. GREEK SMALL LETTER DIGAMMA +.. |div| unicode:: U+000F7 .. DIVISION SIGN +.. |divideontimes| unicode:: U+022C7 .. DIVISION TIMES +.. |doteq| unicode:: U+02250 .. APPROACHES THE LIMIT +.. |doteqdot| unicode:: U+02251 .. GEOMETRICALLY EQUAL TO +.. |DotEqual| unicode:: U+02250 .. APPROACHES THE LIMIT +.. |dotminus| unicode:: U+02238 .. DOT MINUS +.. |dotplus| unicode:: U+02214 .. DOT PLUS +.. |dotsquare| unicode:: U+022A1 .. SQUARED DOT OPERATOR +.. |doublebarwedge| unicode:: U+02306 .. PERSPECTIVE +.. |DoubleContourIntegral| unicode:: U+0222F .. SURFACE INTEGRAL +.. |DoubleDot| unicode:: U+000A8 .. DIAERESIS +.. |DoubleDownArrow| unicode:: U+021D3 .. DOWNWARDS DOUBLE ARROW +.. |DoubleLeftArrow| unicode:: U+021D0 .. LEFTWARDS DOUBLE ARROW +.. |DoubleLeftRightArrow| unicode:: U+021D4 .. LEFT RIGHT DOUBLE ARROW +.. |DoubleLeftTee| unicode:: U+02AE4 .. VERTICAL BAR DOUBLE LEFT TURNSTILE +.. |DoubleLongLeftArrow| unicode:: U+027F8 .. LONG LEFTWARDS DOUBLE ARROW +.. |DoubleLongLeftRightArrow| unicode:: U+027FA .. LONG LEFT RIGHT DOUBLE ARROW +.. |DoubleLongRightArrow| unicode:: U+027F9 .. LONG RIGHTWARDS DOUBLE ARROW +.. |DoubleRightArrow| unicode:: U+021D2 .. RIGHTWARDS DOUBLE ARROW +.. |DoubleRightTee| unicode:: U+022A8 .. TRUE +.. |DoubleUpArrow| unicode:: U+021D1 .. UPWARDS DOUBLE ARROW +.. |DoubleUpDownArrow| unicode:: U+021D5 .. UP DOWN DOUBLE ARROW +.. |DoubleVerticalBar| unicode:: U+02225 .. PARALLEL TO +.. |DownArrow| unicode:: U+02193 .. DOWNWARDS ARROW +.. |Downarrow| unicode:: U+021D3 .. DOWNWARDS DOUBLE ARROW +.. |downarrow| unicode:: U+02193 .. DOWNWARDS ARROW +.. |DownArrowUpArrow| unicode:: U+021F5 .. DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW +.. |downdownarrows| unicode:: U+021CA .. DOWNWARDS PAIRED ARROWS +.. |downharpoonleft| unicode:: U+021C3 .. DOWNWARDS HARPOON WITH BARB LEFTWARDS +.. |downharpoonright| unicode:: U+021C2 .. DOWNWARDS HARPOON WITH BARB RIGHTWARDS +.. |DownLeftVector| unicode:: U+021BD .. LEFTWARDS HARPOON WITH BARB DOWNWARDS +.. |DownRightVector| unicode:: U+021C1 .. RIGHTWARDS HARPOON WITH BARB DOWNWARDS +.. |DownTee| unicode:: U+022A4 .. DOWN TACK +.. |DownTeeArrow| unicode:: U+021A7 .. DOWNWARDS ARROW FROM BAR +.. |drbkarow| unicode:: U+02910 .. RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW +.. |Element| unicode:: U+02208 .. ELEMENT OF +.. |emptyset| unicode:: U+02205 .. EMPTY SET +.. |eqcirc| unicode:: U+02256 .. RING IN EQUAL TO +.. |eqcolon| unicode:: U+02255 .. EQUALS COLON +.. |eqsim| unicode:: U+02242 .. MINUS TILDE +.. |eqslantgtr| unicode:: U+02A96 .. SLANTED EQUAL TO OR GREATER-THAN +.. |eqslantless| unicode:: U+02A95 .. SLANTED EQUAL TO OR LESS-THAN +.. |EqualTilde| unicode:: U+02242 .. MINUS TILDE +.. |Equilibrium| unicode:: U+021CC .. RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON +.. |Exists| unicode:: U+02203 .. THERE EXISTS +.. |expectation| unicode:: U+02130 .. SCRIPT CAPITAL E +.. |ExponentialE| unicode:: U+02147 .. DOUBLE-STRUCK ITALIC SMALL E +.. |exponentiale| unicode:: U+02147 .. DOUBLE-STRUCK ITALIC SMALL E +.. |fallingdotseq| unicode:: U+02252 .. APPROXIMATELY EQUAL TO OR THE IMAGE OF +.. |ForAll| unicode:: U+02200 .. FOR ALL +.. |Fouriertrf| unicode:: U+02131 .. SCRIPT CAPITAL F +.. |geq| unicode:: U+02265 .. GREATER-THAN OR EQUAL TO +.. |geqq| unicode:: U+02267 .. GREATER-THAN OVER EQUAL TO +.. |geqslant| unicode:: U+02A7E .. GREATER-THAN OR SLANTED EQUAL TO +.. |gg| unicode:: U+0226B .. MUCH GREATER-THAN +.. |ggg| unicode:: U+022D9 .. VERY MUCH GREATER-THAN +.. |gnapprox| unicode:: U+02A8A .. GREATER-THAN AND NOT APPROXIMATE +.. |gneq| unicode:: U+02A88 .. GREATER-THAN AND SINGLE-LINE NOT EQUAL TO +.. |gneqq| unicode:: U+02269 .. GREATER-THAN BUT NOT EQUAL TO +.. |GreaterEqual| unicode:: U+02265 .. GREATER-THAN OR EQUAL TO +.. |GreaterEqualLess| unicode:: U+022DB .. GREATER-THAN EQUAL TO OR LESS-THAN +.. |GreaterFullEqual| unicode:: U+02267 .. GREATER-THAN OVER EQUAL TO +.. |GreaterLess| unicode:: U+02277 .. GREATER-THAN OR LESS-THAN +.. |GreaterSlantEqual| unicode:: U+02A7E .. GREATER-THAN OR SLANTED EQUAL TO +.. |GreaterTilde| unicode:: U+02273 .. GREATER-THAN OR EQUIVALENT TO +.. |gtrapprox| unicode:: U+02A86 .. GREATER-THAN OR APPROXIMATE +.. |gtrdot| unicode:: U+022D7 .. GREATER-THAN WITH DOT +.. |gtreqless| unicode:: U+022DB .. GREATER-THAN EQUAL TO OR LESS-THAN +.. |gtreqqless| unicode:: U+02A8C .. GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN +.. |gtrless| unicode:: U+02277 .. GREATER-THAN OR LESS-THAN +.. |gtrsim| unicode:: U+02273 .. GREATER-THAN OR EQUIVALENT TO +.. |gvertneqq| unicode:: U+02269 U+0FE00 .. GREATER-THAN BUT NOT EQUAL TO - with vertical stroke +.. |Hacek| unicode:: U+002C7 .. CARON +.. |hbar| unicode:: U+0210F .. PLANCK CONSTANT OVER TWO PI +.. |heartsuit| unicode:: U+02665 .. BLACK HEART SUIT +.. |HilbertSpace| unicode:: U+0210B .. SCRIPT CAPITAL H +.. |hksearow| unicode:: U+02925 .. SOUTH EAST ARROW WITH HOOK +.. |hkswarow| unicode:: U+02926 .. SOUTH WEST ARROW WITH HOOK +.. |hookleftarrow| unicode:: U+021A9 .. LEFTWARDS ARROW WITH HOOK +.. |hookrightarrow| unicode:: U+021AA .. RIGHTWARDS ARROW WITH HOOK +.. |hslash| unicode:: U+0210F .. PLANCK CONSTANT OVER TWO PI +.. |HumpDownHump| unicode:: U+0224E .. GEOMETRICALLY EQUIVALENT TO +.. |HumpEqual| unicode:: U+0224F .. DIFFERENCE BETWEEN +.. |iiiint| unicode:: U+02A0C .. QUADRUPLE INTEGRAL OPERATOR +.. |iiint| unicode:: U+0222D .. TRIPLE INTEGRAL +.. |Im| unicode:: U+02111 .. BLACK-LETTER CAPITAL I +.. |ImaginaryI| unicode:: U+02148 .. DOUBLE-STRUCK ITALIC SMALL I +.. |imagline| unicode:: U+02110 .. SCRIPT CAPITAL I +.. |imagpart| unicode:: U+02111 .. BLACK-LETTER CAPITAL I +.. |Implies| unicode:: U+021D2 .. RIGHTWARDS DOUBLE ARROW +.. |in| unicode:: U+02208 .. ELEMENT OF +.. |integers| unicode:: U+02124 .. DOUBLE-STRUCK CAPITAL Z +.. |Integral| unicode:: U+0222B .. INTEGRAL +.. |intercal| unicode:: U+022BA .. INTERCALATE +.. |Intersection| unicode:: U+022C2 .. N-ARY INTERSECTION +.. |intprod| unicode:: U+02A3C .. INTERIOR PRODUCT +.. |InvisibleComma| unicode:: U+02063 .. INVISIBLE SEPARATOR +.. |InvisibleTimes| unicode:: U+02062 .. INVISIBLE TIMES +.. |langle| unicode:: U+02329 .. LEFT-POINTING ANGLE BRACKET +.. |Laplacetrf| unicode:: U+02112 .. SCRIPT CAPITAL L +.. |lbrace| unicode:: U+0007B .. LEFT CURLY BRACKET +.. |lbrack| unicode:: U+0005B .. LEFT SQUARE BRACKET +.. |LeftAngleBracket| unicode:: U+02329 .. LEFT-POINTING ANGLE BRACKET +.. |LeftArrow| unicode:: U+02190 .. LEFTWARDS ARROW +.. |Leftarrow| unicode:: U+021D0 .. LEFTWARDS DOUBLE ARROW +.. |leftarrow| unicode:: U+02190 .. LEFTWARDS ARROW +.. |LeftArrowBar| unicode:: U+021E4 .. LEFTWARDS ARROW TO BAR +.. |LeftArrowRightArrow| unicode:: U+021C6 .. LEFTWARDS ARROW OVER RIGHTWARDS ARROW +.. |leftarrowtail| unicode:: U+021A2 .. LEFTWARDS ARROW WITH TAIL +.. |LeftCeiling| unicode:: U+02308 .. LEFT CEILING +.. |LeftDoubleBracket| unicode:: U+0301A .. LEFT WHITE SQUARE BRACKET +.. |LeftDownVector| unicode:: U+021C3 .. DOWNWARDS HARPOON WITH BARB LEFTWARDS +.. |LeftFloor| unicode:: U+0230A .. LEFT FLOOR +.. |leftharpoondown| unicode:: U+021BD .. LEFTWARDS HARPOON WITH BARB DOWNWARDS +.. |leftharpoonup| unicode:: U+021BC .. LEFTWARDS HARPOON WITH BARB UPWARDS +.. |leftleftarrows| unicode:: U+021C7 .. LEFTWARDS PAIRED ARROWS +.. |LeftRightArrow| unicode:: U+02194 .. LEFT RIGHT ARROW +.. |Leftrightarrow| unicode:: U+021D4 .. LEFT RIGHT DOUBLE ARROW +.. |leftrightarrow| unicode:: U+02194 .. LEFT RIGHT ARROW +.. |leftrightarrows| unicode:: U+021C6 .. LEFTWARDS ARROW OVER RIGHTWARDS ARROW +.. |leftrightharpoons| unicode:: U+021CB .. LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON +.. |leftrightsquigarrow| unicode:: U+021AD .. LEFT RIGHT WAVE ARROW +.. |LeftTee| unicode:: U+022A3 .. LEFT TACK +.. |LeftTeeArrow| unicode:: U+021A4 .. LEFTWARDS ARROW FROM BAR +.. |leftthreetimes| unicode:: U+022CB .. LEFT SEMIDIRECT PRODUCT +.. |LeftTriangle| unicode:: U+022B2 .. NORMAL SUBGROUP OF +.. |LeftTriangleEqual| unicode:: U+022B4 .. NORMAL SUBGROUP OF OR EQUAL TO +.. |LeftUpVector| unicode:: U+021BF .. UPWARDS HARPOON WITH BARB LEFTWARDS +.. |LeftVector| unicode:: U+021BC .. LEFTWARDS HARPOON WITH BARB UPWARDS +.. |leq| unicode:: U+02264 .. LESS-THAN OR EQUAL TO +.. |leqq| unicode:: U+02266 .. LESS-THAN OVER EQUAL TO +.. |leqslant| unicode:: U+02A7D .. LESS-THAN OR SLANTED EQUAL TO +.. |lessapprox| unicode:: U+02A85 .. LESS-THAN OR APPROXIMATE +.. |lessdot| unicode:: U+022D6 .. LESS-THAN WITH DOT +.. |lesseqgtr| unicode:: U+022DA .. LESS-THAN EQUAL TO OR GREATER-THAN +.. |lesseqqgtr| unicode:: U+02A8B .. LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN +.. |LessEqualGreater| unicode:: U+022DA .. LESS-THAN EQUAL TO OR GREATER-THAN +.. |LessFullEqual| unicode:: U+02266 .. LESS-THAN OVER EQUAL TO +.. |LessGreater| unicode:: U+02276 .. LESS-THAN OR GREATER-THAN +.. |lessgtr| unicode:: U+02276 .. LESS-THAN OR GREATER-THAN +.. |lesssim| unicode:: U+02272 .. LESS-THAN OR EQUIVALENT TO +.. |LessSlantEqual| unicode:: U+02A7D .. LESS-THAN OR SLANTED EQUAL TO +.. |LessTilde| unicode:: U+02272 .. LESS-THAN OR EQUIVALENT TO +.. |ll| unicode:: U+0226A .. MUCH LESS-THAN +.. |llcorner| unicode:: U+0231E .. BOTTOM LEFT CORNER +.. |Lleftarrow| unicode:: U+021DA .. LEFTWARDS TRIPLE ARROW +.. |lmoustache| unicode:: U+023B0 .. UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION +.. |lnapprox| unicode:: U+02A89 .. LESS-THAN AND NOT APPROXIMATE +.. |lneq| unicode:: U+02A87 .. LESS-THAN AND SINGLE-LINE NOT EQUAL TO +.. |lneqq| unicode:: U+02268 .. LESS-THAN BUT NOT EQUAL TO +.. |LongLeftArrow| unicode:: U+027F5 .. LONG LEFTWARDS ARROW +.. |Longleftarrow| unicode:: U+027F8 .. LONG LEFTWARDS DOUBLE ARROW +.. |longleftarrow| unicode:: U+027F5 .. LONG LEFTWARDS ARROW +.. |LongLeftRightArrow| unicode:: U+027F7 .. LONG LEFT RIGHT ARROW +.. |Longleftrightarrow| unicode:: U+027FA .. LONG LEFT RIGHT DOUBLE ARROW +.. |longleftrightarrow| unicode:: U+027F7 .. LONG LEFT RIGHT ARROW +.. |longmapsto| unicode:: U+027FC .. LONG RIGHTWARDS ARROW FROM BAR +.. |LongRightArrow| unicode:: U+027F6 .. LONG RIGHTWARDS ARROW +.. |Longrightarrow| unicode:: U+027F9 .. LONG RIGHTWARDS DOUBLE ARROW +.. |longrightarrow| unicode:: U+027F6 .. LONG RIGHTWARDS ARROW +.. |looparrowleft| unicode:: U+021AB .. LEFTWARDS ARROW WITH LOOP +.. |looparrowright| unicode:: U+021AC .. RIGHTWARDS ARROW WITH LOOP +.. |LowerLeftArrow| unicode:: U+02199 .. SOUTH WEST ARROW +.. |LowerRightArrow| unicode:: U+02198 .. SOUTH EAST ARROW +.. |lozenge| unicode:: U+025CA .. LOZENGE +.. |lrcorner| unicode:: U+0231F .. BOTTOM RIGHT CORNER +.. |Lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS +.. |lvertneqq| unicode:: U+02268 U+0FE00 .. LESS-THAN BUT NOT EQUAL TO - with vertical stroke +.. |maltese| unicode:: U+02720 .. MALTESE CROSS +.. |mapsto| unicode:: U+021A6 .. RIGHTWARDS ARROW FROM BAR +.. |measuredangle| unicode:: U+02221 .. MEASURED ANGLE +.. |Mellintrf| unicode:: U+02133 .. SCRIPT CAPITAL M +.. |MinusPlus| unicode:: U+02213 .. MINUS-OR-PLUS SIGN +.. |mp| unicode:: U+02213 .. MINUS-OR-PLUS SIGN +.. |multimap| unicode:: U+022B8 .. MULTIMAP +.. |napprox| unicode:: U+02249 .. NOT ALMOST EQUAL TO +.. |natural| unicode:: U+0266E .. MUSIC NATURAL SIGN +.. |naturals| unicode:: U+02115 .. DOUBLE-STRUCK CAPITAL N +.. |nearrow| unicode:: U+02197 .. NORTH EAST ARROW +.. |NegativeMediumSpace| unicode:: U+0200B .. ZERO WIDTH SPACE +.. |NegativeThickSpace| unicode:: U+0200B .. ZERO WIDTH SPACE +.. |NegativeThinSpace| unicode:: U+0200B .. ZERO WIDTH SPACE +.. |NegativeVeryThinSpace| unicode:: U+0200B .. ZERO WIDTH SPACE +.. |NestedGreaterGreater| unicode:: U+0226B .. MUCH GREATER-THAN +.. |NestedLessLess| unicode:: U+0226A .. MUCH LESS-THAN +.. |nexists| unicode:: U+02204 .. THERE DOES NOT EXIST +.. |ngeq| unicode:: U+02271 .. NEITHER GREATER-THAN NOR EQUAL TO +.. |ngeqq| unicode:: U+02267 U+00338 .. GREATER-THAN OVER EQUAL TO with slash +.. |ngeqslant| unicode:: U+02A7E U+00338 .. GREATER-THAN OR SLANTED EQUAL TO with slash +.. |ngtr| unicode:: U+0226F .. NOT GREATER-THAN +.. |nLeftarrow| unicode:: U+021CD .. LEFTWARDS DOUBLE ARROW WITH STROKE +.. |nleftarrow| unicode:: U+0219A .. LEFTWARDS ARROW WITH STROKE +.. |nLeftrightarrow| unicode:: U+021CE .. LEFT RIGHT DOUBLE ARROW WITH STROKE +.. |nleftrightarrow| unicode:: U+021AE .. LEFT RIGHT ARROW WITH STROKE +.. |nleq| unicode:: U+02270 .. NEITHER LESS-THAN NOR EQUAL TO +.. |nleqq| unicode:: U+02266 U+00338 .. LESS-THAN OVER EQUAL TO with slash +.. |nleqslant| unicode:: U+02A7D U+00338 .. LESS-THAN OR SLANTED EQUAL TO with slash +.. |nless| unicode:: U+0226E .. NOT LESS-THAN +.. |NonBreakingSpace| unicode:: U+000A0 .. NO-BREAK SPACE +.. |NotCongruent| unicode:: U+02262 .. NOT IDENTICAL TO +.. |NotDoubleVerticalBar| unicode:: U+02226 .. NOT PARALLEL TO +.. |NotElement| unicode:: U+02209 .. NOT AN ELEMENT OF +.. |NotEqual| unicode:: U+02260 .. NOT EQUAL TO +.. |NotEqualTilde| unicode:: U+02242 U+00338 .. MINUS TILDE with slash +.. |NotExists| unicode:: U+02204 .. THERE DOES NOT EXIST +.. |NotGreater| unicode:: U+0226F .. NOT GREATER-THAN +.. |NotGreaterEqual| unicode:: U+02271 .. NEITHER GREATER-THAN NOR EQUAL TO +.. |NotGreaterFullEqual| unicode:: U+02266 U+00338 .. LESS-THAN OVER EQUAL TO with slash +.. |NotGreaterGreater| unicode:: U+0226B U+00338 .. MUCH GREATER THAN with slash +.. |NotGreaterLess| unicode:: U+02279 .. NEITHER GREATER-THAN NOR LESS-THAN +.. |NotGreaterSlantEqual| unicode:: U+02A7E U+00338 .. GREATER-THAN OR SLANTED EQUAL TO with slash +.. |NotGreaterTilde| unicode:: U+02275 .. NEITHER GREATER-THAN NOR EQUIVALENT TO +.. |NotHumpDownHump| unicode:: U+0224E U+00338 .. GEOMETRICALLY EQUIVALENT TO with slash +.. |NotLeftTriangle| unicode:: U+022EA .. NOT NORMAL SUBGROUP OF +.. |NotLeftTriangleEqual| unicode:: U+022EC .. NOT NORMAL SUBGROUP OF OR EQUAL TO +.. |NotLess| unicode:: U+0226E .. NOT LESS-THAN +.. |NotLessEqual| unicode:: U+02270 .. NEITHER LESS-THAN NOR EQUAL TO +.. |NotLessGreater| unicode:: U+02278 .. NEITHER LESS-THAN NOR GREATER-THAN +.. |NotLessLess| unicode:: U+0226A U+00338 .. MUCH LESS THAN with slash +.. |NotLessSlantEqual| unicode:: U+02A7D U+00338 .. LESS-THAN OR SLANTED EQUAL TO with slash +.. |NotLessTilde| unicode:: U+02274 .. NEITHER LESS-THAN NOR EQUIVALENT TO +.. |NotPrecedes| unicode:: U+02280 .. DOES NOT PRECEDE +.. |NotPrecedesEqual| unicode:: U+02AAF U+00338 .. PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash +.. |NotPrecedesSlantEqual| unicode:: U+022E0 .. DOES NOT PRECEDE OR EQUAL +.. |NotReverseElement| unicode:: U+0220C .. DOES NOT CONTAIN AS MEMBER +.. |NotRightTriangle| unicode:: U+022EB .. DOES NOT CONTAIN AS NORMAL SUBGROUP +.. |NotRightTriangleEqual| unicode:: U+022ED .. DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL +.. |NotSquareSubsetEqual| unicode:: U+022E2 .. NOT SQUARE IMAGE OF OR EQUAL TO +.. |NotSquareSupersetEqual| unicode:: U+022E3 .. NOT SQUARE ORIGINAL OF OR EQUAL TO +.. |NotSubset| unicode:: U+02282 U+020D2 .. SUBSET OF with vertical line +.. |NotSubsetEqual| unicode:: U+02288 .. NEITHER A SUBSET OF NOR EQUAL TO +.. |NotSucceeds| unicode:: U+02281 .. DOES NOT SUCCEED +.. |NotSucceedsEqual| unicode:: U+02AB0 U+00338 .. SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash +.. |NotSucceedsSlantEqual| unicode:: U+022E1 .. DOES NOT SUCCEED OR EQUAL +.. |NotSuperset| unicode:: U+02283 U+020D2 .. SUPERSET OF with vertical line +.. |NotSupersetEqual| unicode:: U+02289 .. NEITHER A SUPERSET OF NOR EQUAL TO +.. |NotTilde| unicode:: U+02241 .. NOT TILDE +.. |NotTildeEqual| unicode:: U+02244 .. NOT ASYMPTOTICALLY EQUAL TO +.. |NotTildeFullEqual| unicode:: U+02247 .. NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO +.. |NotTildeTilde| unicode:: U+02249 .. NOT ALMOST EQUAL TO +.. |NotVerticalBar| unicode:: U+02224 .. DOES NOT DIVIDE +.. |nparallel| unicode:: U+02226 .. NOT PARALLEL TO +.. |nprec| unicode:: U+02280 .. DOES NOT PRECEDE +.. |npreceq| unicode:: U+02AAF U+00338 .. PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash +.. |nRightarrow| unicode:: U+021CF .. RIGHTWARDS DOUBLE ARROW WITH STROKE +.. |nrightarrow| unicode:: U+0219B .. RIGHTWARDS ARROW WITH STROKE +.. |nshortmid| unicode:: U+02224 .. DOES NOT DIVIDE +.. |nshortparallel| unicode:: U+02226 .. NOT PARALLEL TO +.. |nsimeq| unicode:: U+02244 .. NOT ASYMPTOTICALLY EQUAL TO +.. |nsubset| unicode:: U+02282 U+020D2 .. SUBSET OF with vertical line +.. |nsubseteq| unicode:: U+02288 .. NEITHER A SUBSET OF NOR EQUAL TO +.. |nsubseteqq| unicode:: U+02AC5 U+00338 .. SUBSET OF ABOVE EQUALS SIGN with slash +.. |nsucc| unicode:: U+02281 .. DOES NOT SUCCEED +.. |nsucceq| unicode:: U+02AB0 U+00338 .. SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash +.. |nsupset| unicode:: U+02283 U+020D2 .. SUPERSET OF with vertical line +.. |nsupseteq| unicode:: U+02289 .. NEITHER A SUPERSET OF NOR EQUAL TO +.. |nsupseteqq| unicode:: U+02AC6 U+00338 .. SUPERSET OF ABOVE EQUALS SIGN with slash +.. |ntriangleleft| unicode:: U+022EA .. NOT NORMAL SUBGROUP OF +.. |ntrianglelefteq| unicode:: U+022EC .. NOT NORMAL SUBGROUP OF OR EQUAL TO +.. |ntriangleright| unicode:: U+022EB .. DOES NOT CONTAIN AS NORMAL SUBGROUP +.. |ntrianglerighteq| unicode:: U+022ED .. DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL +.. |nwarrow| unicode:: U+02196 .. NORTH WEST ARROW +.. |oint| unicode:: U+0222E .. CONTOUR INTEGRAL +.. |OpenCurlyDoubleQuote| unicode:: U+0201C .. LEFT DOUBLE QUOTATION MARK +.. |OpenCurlyQuote| unicode:: U+02018 .. LEFT SINGLE QUOTATION MARK +.. |orderof| unicode:: U+02134 .. SCRIPT SMALL O +.. |parallel| unicode:: U+02225 .. PARALLEL TO +.. |PartialD| unicode:: U+02202 .. PARTIAL DIFFERENTIAL +.. |pitchfork| unicode:: U+022D4 .. PITCHFORK +.. |PlusMinus| unicode:: U+000B1 .. PLUS-MINUS SIGN +.. |pm| unicode:: U+000B1 .. PLUS-MINUS SIGN +.. |Poincareplane| unicode:: U+0210C .. BLACK-LETTER CAPITAL H +.. |prec| unicode:: U+0227A .. PRECEDES +.. |precapprox| unicode:: U+02AB7 .. PRECEDES ABOVE ALMOST EQUAL TO +.. |preccurlyeq| unicode:: U+0227C .. PRECEDES OR EQUAL TO +.. |Precedes| unicode:: U+0227A .. PRECEDES +.. |PrecedesEqual| unicode:: U+02AAF .. PRECEDES ABOVE SINGLE-LINE EQUALS SIGN +.. |PrecedesSlantEqual| unicode:: U+0227C .. PRECEDES OR EQUAL TO +.. |PrecedesTilde| unicode:: U+0227E .. PRECEDES OR EQUIVALENT TO +.. |preceq| unicode:: U+02AAF .. PRECEDES ABOVE SINGLE-LINE EQUALS SIGN +.. |precnapprox| unicode:: U+02AB9 .. PRECEDES ABOVE NOT ALMOST EQUAL TO +.. |precneqq| unicode:: U+02AB5 .. PRECEDES ABOVE NOT EQUAL TO +.. |precnsim| unicode:: U+022E8 .. PRECEDES BUT NOT EQUIVALENT TO +.. |precsim| unicode:: U+0227E .. PRECEDES OR EQUIVALENT TO +.. |primes| unicode:: U+02119 .. DOUBLE-STRUCK CAPITAL P +.. |Proportion| unicode:: U+02237 .. PROPORTION +.. |Proportional| unicode:: U+0221D .. PROPORTIONAL TO +.. |propto| unicode:: U+0221D .. PROPORTIONAL TO +.. |quaternions| unicode:: U+0210D .. DOUBLE-STRUCK CAPITAL H +.. |questeq| unicode:: U+0225F .. QUESTIONED EQUAL TO +.. |rangle| unicode:: U+0232A .. RIGHT-POINTING ANGLE BRACKET +.. |rationals| unicode:: U+0211A .. DOUBLE-STRUCK CAPITAL Q +.. |rbrace| unicode:: U+0007D .. RIGHT CURLY BRACKET +.. |rbrack| unicode:: U+0005D .. RIGHT SQUARE BRACKET +.. |Re| unicode:: U+0211C .. BLACK-LETTER CAPITAL R +.. |realine| unicode:: U+0211B .. SCRIPT CAPITAL R +.. |realpart| unicode:: U+0211C .. BLACK-LETTER CAPITAL R +.. |reals| unicode:: U+0211D .. DOUBLE-STRUCK CAPITAL R +.. |ReverseElement| unicode:: U+0220B .. CONTAINS AS MEMBER +.. |ReverseEquilibrium| unicode:: U+021CB .. LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON +.. |ReverseUpEquilibrium| unicode:: U+0296F .. DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT +.. |RightAngleBracket| unicode:: U+0232A .. RIGHT-POINTING ANGLE BRACKET +.. |RightArrow| unicode:: U+02192 .. RIGHTWARDS ARROW +.. |Rightarrow| unicode:: U+021D2 .. RIGHTWARDS DOUBLE ARROW +.. |rightarrow| unicode:: U+02192 .. RIGHTWARDS ARROW +.. |RightArrowBar| unicode:: U+021E5 .. RIGHTWARDS ARROW TO BAR +.. |RightArrowLeftArrow| unicode:: U+021C4 .. RIGHTWARDS ARROW OVER LEFTWARDS ARROW +.. |rightarrowtail| unicode:: U+021A3 .. RIGHTWARDS ARROW WITH TAIL +.. |RightCeiling| unicode:: U+02309 .. RIGHT CEILING +.. |RightDoubleBracket| unicode:: U+0301B .. RIGHT WHITE SQUARE BRACKET +.. |RightDownVector| unicode:: U+021C2 .. DOWNWARDS HARPOON WITH BARB RIGHTWARDS +.. |RightFloor| unicode:: U+0230B .. RIGHT FLOOR +.. |rightharpoondown| unicode:: U+021C1 .. RIGHTWARDS HARPOON WITH BARB DOWNWARDS +.. |rightharpoonup| unicode:: U+021C0 .. RIGHTWARDS HARPOON WITH BARB UPWARDS +.. |rightleftarrows| unicode:: U+021C4 .. RIGHTWARDS ARROW OVER LEFTWARDS ARROW +.. |rightleftharpoons| unicode:: U+021CC .. RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON +.. |rightrightarrows| unicode:: U+021C9 .. RIGHTWARDS PAIRED ARROWS +.. |rightsquigarrow| unicode:: U+0219D .. RIGHTWARDS WAVE ARROW +.. |RightTee| unicode:: U+022A2 .. RIGHT TACK +.. |RightTeeArrow| unicode:: U+021A6 .. RIGHTWARDS ARROW FROM BAR +.. |rightthreetimes| unicode:: U+022CC .. RIGHT SEMIDIRECT PRODUCT +.. |RightTriangle| unicode:: U+022B3 .. CONTAINS AS NORMAL SUBGROUP +.. |RightTriangleEqual| unicode:: U+022B5 .. CONTAINS AS NORMAL SUBGROUP OR EQUAL TO +.. |RightUpVector| unicode:: U+021BE .. UPWARDS HARPOON WITH BARB RIGHTWARDS +.. |RightVector| unicode:: U+021C0 .. RIGHTWARDS HARPOON WITH BARB UPWARDS +.. |risingdotseq| unicode:: U+02253 .. IMAGE OF OR APPROXIMATELY EQUAL TO +.. |rmoustache| unicode:: U+023B1 .. UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION +.. |Rrightarrow| unicode:: U+021DB .. RIGHTWARDS TRIPLE ARROW +.. |Rsh| unicode:: U+021B1 .. UPWARDS ARROW WITH TIP RIGHTWARDS +.. |searrow| unicode:: U+02198 .. SOUTH EAST ARROW +.. |setminus| unicode:: U+02216 .. SET MINUS +.. |ShortDownArrow| unicode:: U+02193 .. DOWNWARDS ARROW +.. |ShortLeftArrow| unicode:: U+02190 .. LEFTWARDS ARROW +.. |shortmid| unicode:: U+02223 .. DIVIDES +.. |shortparallel| unicode:: U+02225 .. PARALLEL TO +.. |ShortRightArrow| unicode:: U+02192 .. RIGHTWARDS ARROW +.. |ShortUpArrow| unicode:: U+02191 .. UPWARDS ARROW +.. |simeq| unicode:: U+02243 .. ASYMPTOTICALLY EQUAL TO +.. |SmallCircle| unicode:: U+02218 .. RING OPERATOR +.. |smallsetminus| unicode:: U+02216 .. SET MINUS +.. |spadesuit| unicode:: U+02660 .. BLACK SPADE SUIT +.. |Sqrt| unicode:: U+0221A .. SQUARE ROOT +.. |sqsubset| unicode:: U+0228F .. SQUARE IMAGE OF +.. |sqsubseteq| unicode:: U+02291 .. SQUARE IMAGE OF OR EQUAL TO +.. |sqsupset| unicode:: U+02290 .. SQUARE ORIGINAL OF +.. |sqsupseteq| unicode:: U+02292 .. SQUARE ORIGINAL OF OR EQUAL TO +.. |Square| unicode:: U+025A1 .. WHITE SQUARE +.. |SquareIntersection| unicode:: U+02293 .. SQUARE CAP +.. |SquareSubset| unicode:: U+0228F .. SQUARE IMAGE OF +.. |SquareSubsetEqual| unicode:: U+02291 .. SQUARE IMAGE OF OR EQUAL TO +.. |SquareSuperset| unicode:: U+02290 .. SQUARE ORIGINAL OF +.. |SquareSupersetEqual| unicode:: U+02292 .. SQUARE ORIGINAL OF OR EQUAL TO +.. |SquareUnion| unicode:: U+02294 .. SQUARE CUP +.. |Star| unicode:: U+022C6 .. STAR OPERATOR +.. |straightepsilon| unicode:: U+003F5 .. GREEK LUNATE EPSILON SYMBOL +.. |straightphi| unicode:: U+003D5 .. GREEK PHI SYMBOL +.. |Subset| unicode:: U+022D0 .. DOUBLE SUBSET +.. |subset| unicode:: U+02282 .. SUBSET OF +.. |subseteq| unicode:: U+02286 .. SUBSET OF OR EQUAL TO +.. |subseteqq| unicode:: U+02AC5 .. SUBSET OF ABOVE EQUALS SIGN +.. |SubsetEqual| unicode:: U+02286 .. SUBSET OF OR EQUAL TO +.. |subsetneq| unicode:: U+0228A .. SUBSET OF WITH NOT EQUAL TO +.. |subsetneqq| unicode:: U+02ACB .. SUBSET OF ABOVE NOT EQUAL TO +.. |succ| unicode:: U+0227B .. SUCCEEDS +.. |succapprox| unicode:: U+02AB8 .. SUCCEEDS ABOVE ALMOST EQUAL TO +.. |succcurlyeq| unicode:: U+0227D .. SUCCEEDS OR EQUAL TO +.. |Succeeds| unicode:: U+0227B .. SUCCEEDS +.. |SucceedsEqual| unicode:: U+02AB0 .. SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN +.. |SucceedsSlantEqual| unicode:: U+0227D .. SUCCEEDS OR EQUAL TO +.. |SucceedsTilde| unicode:: U+0227F .. SUCCEEDS OR EQUIVALENT TO +.. |succeq| unicode:: U+02AB0 .. SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN +.. |succnapprox| unicode:: U+02ABA .. SUCCEEDS ABOVE NOT ALMOST EQUAL TO +.. |succneqq| unicode:: U+02AB6 .. SUCCEEDS ABOVE NOT EQUAL TO +.. |succnsim| unicode:: U+022E9 .. SUCCEEDS BUT NOT EQUIVALENT TO +.. |succsim| unicode:: U+0227F .. SUCCEEDS OR EQUIVALENT TO +.. |SuchThat| unicode:: U+0220B .. CONTAINS AS MEMBER +.. |Sum| unicode:: U+02211 .. N-ARY SUMMATION +.. |Superset| unicode:: U+02283 .. SUPERSET OF +.. |SupersetEqual| unicode:: U+02287 .. SUPERSET OF OR EQUAL TO +.. |Supset| unicode:: U+022D1 .. DOUBLE SUPERSET +.. |supset| unicode:: U+02283 .. SUPERSET OF +.. |supseteq| unicode:: U+02287 .. SUPERSET OF OR EQUAL TO +.. |supseteqq| unicode:: U+02AC6 .. SUPERSET OF ABOVE EQUALS SIGN +.. |supsetneq| unicode:: U+0228B .. SUPERSET OF WITH NOT EQUAL TO +.. |supsetneqq| unicode:: U+02ACC .. SUPERSET OF ABOVE NOT EQUAL TO +.. |swarrow| unicode:: U+02199 .. SOUTH WEST ARROW +.. |Therefore| unicode:: U+02234 .. THEREFORE +.. |therefore| unicode:: U+02234 .. THEREFORE +.. |thickapprox| unicode:: U+02248 .. ALMOST EQUAL TO +.. |thicksim| unicode:: U+0223C .. TILDE OPERATOR +.. |ThinSpace| unicode:: U+02009 .. THIN SPACE +.. |Tilde| unicode:: U+0223C .. TILDE OPERATOR +.. |TildeEqual| unicode:: U+02243 .. ASYMPTOTICALLY EQUAL TO +.. |TildeFullEqual| unicode:: U+02245 .. APPROXIMATELY EQUAL TO +.. |TildeTilde| unicode:: U+02248 .. ALMOST EQUAL TO +.. |toea| unicode:: U+02928 .. NORTH EAST ARROW AND SOUTH EAST ARROW +.. |tosa| unicode:: U+02929 .. SOUTH EAST ARROW AND SOUTH WEST ARROW +.. |triangle| unicode:: U+025B5 .. WHITE UP-POINTING SMALL TRIANGLE +.. |triangledown| unicode:: U+025BF .. WHITE DOWN-POINTING SMALL TRIANGLE +.. |triangleleft| unicode:: U+025C3 .. WHITE LEFT-POINTING SMALL TRIANGLE +.. |trianglelefteq| unicode:: U+022B4 .. NORMAL SUBGROUP OF OR EQUAL TO +.. |triangleq| unicode:: U+0225C .. DELTA EQUAL TO +.. |triangleright| unicode:: U+025B9 .. WHITE RIGHT-POINTING SMALL TRIANGLE +.. |trianglerighteq| unicode:: U+022B5 .. CONTAINS AS NORMAL SUBGROUP OR EQUAL TO +.. |TripleDot| unicode:: U+020DB .. COMBINING THREE DOTS ABOVE +.. |twoheadleftarrow| unicode:: U+0219E .. LEFTWARDS TWO HEADED ARROW +.. |twoheadrightarrow| unicode:: U+021A0 .. RIGHTWARDS TWO HEADED ARROW +.. |ulcorner| unicode:: U+0231C .. TOP LEFT CORNER +.. |Union| unicode:: U+022C3 .. N-ARY UNION +.. |UnionPlus| unicode:: U+0228E .. MULTISET UNION +.. |UpArrow| unicode:: U+02191 .. UPWARDS ARROW +.. |Uparrow| unicode:: U+021D1 .. UPWARDS DOUBLE ARROW +.. |uparrow| unicode:: U+02191 .. UPWARDS ARROW +.. |UpArrowDownArrow| unicode:: U+021C5 .. UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW +.. |UpDownArrow| unicode:: U+02195 .. UP DOWN ARROW +.. |Updownarrow| unicode:: U+021D5 .. UP DOWN DOUBLE ARROW +.. |updownarrow| unicode:: U+02195 .. UP DOWN ARROW +.. |UpEquilibrium| unicode:: U+0296E .. UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT +.. |upharpoonleft| unicode:: U+021BF .. UPWARDS HARPOON WITH BARB LEFTWARDS +.. |upharpoonright| unicode:: U+021BE .. UPWARDS HARPOON WITH BARB RIGHTWARDS +.. |UpperLeftArrow| unicode:: U+02196 .. NORTH WEST ARROW +.. |UpperRightArrow| unicode:: U+02197 .. NORTH EAST ARROW +.. |upsilon| unicode:: U+003C5 .. GREEK SMALL LETTER UPSILON +.. |UpTee| unicode:: U+022A5 .. UP TACK +.. |UpTeeArrow| unicode:: U+021A5 .. UPWARDS ARROW FROM BAR +.. |upuparrows| unicode:: U+021C8 .. UPWARDS PAIRED ARROWS +.. |urcorner| unicode:: U+0231D .. TOP RIGHT CORNER +.. |varepsilon| unicode:: U+003B5 .. GREEK SMALL LETTER EPSILON +.. |varkappa| unicode:: U+003F0 .. GREEK KAPPA SYMBOL +.. |varnothing| unicode:: U+02205 .. EMPTY SET +.. |varphi| unicode:: U+003C6 .. GREEK SMALL LETTER PHI +.. |varpi| unicode:: U+003D6 .. GREEK PI SYMBOL +.. |varpropto| unicode:: U+0221D .. PROPORTIONAL TO +.. |varrho| unicode:: U+003F1 .. GREEK RHO SYMBOL +.. |varsigma| unicode:: U+003C2 .. GREEK SMALL LETTER FINAL SIGMA +.. |varsubsetneq| unicode:: U+0228A U+0FE00 .. SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members +.. |varsubsetneqq| unicode:: U+02ACB U+0FE00 .. SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members +.. |varsupsetneq| unicode:: U+0228B U+0FE00 .. SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members +.. |varsupsetneqq| unicode:: U+02ACC U+0FE00 .. SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members +.. |vartheta| unicode:: U+003D1 .. GREEK THETA SYMBOL +.. |vartriangleleft| unicode:: U+022B2 .. NORMAL SUBGROUP OF +.. |vartriangleright| unicode:: U+022B3 .. CONTAINS AS NORMAL SUBGROUP +.. |Vee| unicode:: U+022C1 .. N-ARY LOGICAL OR +.. |vee| unicode:: U+02228 .. LOGICAL OR +.. |Vert| unicode:: U+02016 .. DOUBLE VERTICAL LINE +.. |vert| unicode:: U+0007C .. VERTICAL LINE +.. |VerticalBar| unicode:: U+02223 .. DIVIDES +.. |VerticalTilde| unicode:: U+02240 .. WREATH PRODUCT +.. |VeryThinSpace| unicode:: U+0200A .. HAIR SPACE +.. |Wedge| unicode:: U+022C0 .. N-ARY LOGICAL AND +.. |wedge| unicode:: U+02227 .. LOGICAL AND +.. |wp| unicode:: U+02118 .. SCRIPT CAPITAL P +.. |wr| unicode:: U+02240 .. WREATH PRODUCT +.. |zeetrf| unicode:: U+02128 .. BLACK-LETTER CAPITAL Z diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/mmlextra.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/mmlextra.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/mmlextra.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/mmlextra.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,87 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |af| unicode:: U+02061 .. FUNCTION APPLICATION +.. |asympeq| unicode:: U+0224D .. EQUIVALENT TO +.. |Cross| unicode:: U+02A2F .. VECTOR OR CROSS PRODUCT +.. |DD| unicode:: U+02145 .. DOUBLE-STRUCK ITALIC CAPITAL D +.. |dd| unicode:: U+02146 .. DOUBLE-STRUCK ITALIC SMALL D +.. |DownArrowBar| unicode:: U+02913 .. DOWNWARDS ARROW TO BAR +.. |DownBreve| unicode:: U+00311 .. COMBINING INVERTED BREVE +.. |DownLeftRightVector| unicode:: U+02950 .. LEFT BARB DOWN RIGHT BARB DOWN HARPOON +.. |DownLeftTeeVector| unicode:: U+0295E .. LEFTWARDS HARPOON WITH BARB DOWN FROM BAR +.. |DownLeftVectorBar| unicode:: U+02956 .. LEFTWARDS HARPOON WITH BARB DOWN TO BAR +.. |DownRightTeeVector| unicode:: U+0295F .. RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR +.. |DownRightVectorBar| unicode:: U+02957 .. RIGHTWARDS HARPOON WITH BARB DOWN TO BAR +.. |ee| unicode:: U+02147 .. DOUBLE-STRUCK ITALIC SMALL E +.. |EmptySmallSquare| unicode:: U+025FB .. WHITE MEDIUM SQUARE +.. |EmptyVerySmallSquare| unicode:: U+025AB .. WHITE SMALL SQUARE +.. |Equal| unicode:: U+02A75 .. TWO CONSECUTIVE EQUALS SIGNS +.. |FilledSmallSquare| unicode:: U+025FC .. BLACK MEDIUM SQUARE +.. |FilledVerySmallSquare| unicode:: U+025AA .. BLACK SMALL SQUARE +.. |GreaterGreater| unicode:: U+02AA2 .. DOUBLE NESTED GREATER-THAN +.. |Hat| unicode:: U+0005E .. CIRCUMFLEX ACCENT +.. |HorizontalLine| unicode:: U+02500 .. BOX DRAWINGS LIGHT HORIZONTAL +.. |ic| unicode:: U+02063 .. INVISIBLE SEPARATOR +.. |ii| unicode:: U+02148 .. DOUBLE-STRUCK ITALIC SMALL I +.. |it| unicode:: U+02062 .. INVISIBLE TIMES +.. |larrb| unicode:: U+021E4 .. LEFTWARDS ARROW TO BAR +.. |LeftDownTeeVector| unicode:: U+02961 .. DOWNWARDS HARPOON WITH BARB LEFT FROM BAR +.. |LeftDownVectorBar| unicode:: U+02959 .. DOWNWARDS HARPOON WITH BARB LEFT TO BAR +.. |LeftRightVector| unicode:: U+0294E .. LEFT BARB UP RIGHT BARB UP HARPOON +.. |LeftTeeVector| unicode:: U+0295A .. LEFTWARDS HARPOON WITH BARB UP FROM BAR +.. |LeftTriangleBar| unicode:: U+029CF .. LEFT TRIANGLE BESIDE VERTICAL BAR +.. |LeftUpDownVector| unicode:: U+02951 .. UP BARB LEFT DOWN BARB LEFT HARPOON +.. |LeftUpTeeVector| unicode:: U+02960 .. UPWARDS HARPOON WITH BARB LEFT FROM BAR +.. |LeftUpVectorBar| unicode:: U+02958 .. UPWARDS HARPOON WITH BARB LEFT TO BAR +.. |LeftVectorBar| unicode:: U+02952 .. LEFTWARDS HARPOON WITH BARB UP TO BAR +.. |LessLess| unicode:: U+02AA1 .. DOUBLE NESTED LESS-THAN +.. |mapstodown| unicode:: U+021A7 .. DOWNWARDS ARROW FROM BAR +.. |mapstoleft| unicode:: U+021A4 .. LEFTWARDS ARROW FROM BAR +.. |mapstoup| unicode:: U+021A5 .. UPWARDS ARROW FROM BAR +.. |MediumSpace| unicode:: U+0205F .. MEDIUM MATHEMATICAL SPACE +.. |nbump| unicode:: U+0224E U+00338 .. GEOMETRICALLY EQUIVALENT TO with slash +.. |nbumpe| unicode:: U+0224F U+00338 .. DIFFERENCE BETWEEN with slash +.. |nesim| unicode:: U+02242 U+00338 .. MINUS TILDE with slash +.. |NewLine| unicode:: U+0000A .. LINE FEED (LF) +.. |NoBreak| unicode:: U+02060 .. WORD JOINER +.. |NotCupCap| unicode:: U+0226D .. NOT EQUIVALENT TO +.. |NotHumpEqual| unicode:: U+0224F U+00338 .. DIFFERENCE BETWEEN with slash +.. |NotLeftTriangleBar| unicode:: U+029CF U+00338 .. LEFT TRIANGLE BESIDE VERTICAL BAR with slash +.. |NotNestedGreaterGreater| unicode:: U+02AA2 U+00338 .. DOUBLE NESTED GREATER-THAN with slash +.. |NotNestedLessLess| unicode:: U+02AA1 U+00338 .. DOUBLE NESTED LESS-THAN with slash +.. |NotRightTriangleBar| unicode:: U+029D0 U+00338 .. VERTICAL BAR BESIDE RIGHT TRIANGLE with slash +.. |NotSquareSubset| unicode:: U+0228F U+00338 .. SQUARE IMAGE OF with slash +.. |NotSquareSuperset| unicode:: U+02290 U+00338 .. SQUARE ORIGINAL OF with slash +.. |NotSucceedsTilde| unicode:: U+0227F U+00338 .. SUCCEEDS OR EQUIVALENT TO with slash +.. |OverBar| unicode:: U+000AF .. MACRON +.. |OverBrace| unicode:: U+0FE37 .. PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET +.. |OverBracket| unicode:: U+023B4 .. TOP SQUARE BRACKET +.. |OverParenthesis| unicode:: U+0FE35 .. PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS +.. |planckh| unicode:: U+0210E .. PLANCK CONSTANT +.. |Product| unicode:: U+0220F .. N-ARY PRODUCT +.. |rarrb| unicode:: U+021E5 .. RIGHTWARDS ARROW TO BAR +.. |RightDownTeeVector| unicode:: U+0295D .. DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR +.. |RightDownVectorBar| unicode:: U+02955 .. DOWNWARDS HARPOON WITH BARB RIGHT TO BAR +.. |RightTeeVector| unicode:: U+0295B .. RIGHTWARDS HARPOON WITH BARB UP FROM BAR +.. |RightTriangleBar| unicode:: U+029D0 .. VERTICAL BAR BESIDE RIGHT TRIANGLE +.. |RightUpDownVector| unicode:: U+0294F .. UP BARB RIGHT DOWN BARB RIGHT HARPOON +.. |RightUpTeeVector| unicode:: U+0295C .. UPWARDS HARPOON WITH BARB RIGHT FROM BAR +.. |RightUpVectorBar| unicode:: U+02954 .. UPWARDS HARPOON WITH BARB RIGHT TO BAR +.. |RightVectorBar| unicode:: U+02953 .. RIGHTWARDS HARPOON WITH BARB UP TO BAR +.. |RoundImplies| unicode:: U+02970 .. RIGHT DOUBLE ARROW WITH ROUNDED HEAD +.. |RuleDelayed| unicode:: U+029F4 .. RULE-DELAYED +.. |Tab| unicode:: U+00009 .. CHARACTER TABULATION +.. |ThickSpace| unicode:: U+02009 U+0200A U+0200A .. space of width 5/18 em +.. |UnderBar| unicode:: U+00332 .. COMBINING LOW LINE +.. |UnderBrace| unicode:: U+0FE38 .. PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET +.. |UnderBracket| unicode:: U+023B5 .. BOTTOM SQUARE BRACKET +.. |UnderParenthesis| unicode:: U+0FE36 .. PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS +.. |UpArrowBar| unicode:: U+02912 .. UPWARDS ARROW TO BAR +.. |Upsilon| unicode:: U+003A5 .. GREEK CAPITAL LETTER UPSILON +.. |VerticalLine| unicode:: U+0007C .. VERTICAL LINE +.. |VerticalSeparator| unicode:: U+02758 .. LIGHT VERTICAL BAR +.. |ZeroWidthSpace| unicode:: U+0200B .. ZERO WIDTH SPACE diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/mmlextra-wide.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/mmlextra-wide.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/mmlextra-wide.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/mmlextra-wide.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,113 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |af| unicode:: U+02061 .. FUNCTION APPLICATION +.. |aopf| unicode:: U+1D552 .. MATHEMATICAL DOUBLE-STRUCK SMALL A +.. |asympeq| unicode:: U+0224D .. EQUIVALENT TO +.. |bopf| unicode:: U+1D553 .. MATHEMATICAL DOUBLE-STRUCK SMALL B +.. |copf| unicode:: U+1D554 .. MATHEMATICAL DOUBLE-STRUCK SMALL C +.. |Cross| unicode:: U+02A2F .. VECTOR OR CROSS PRODUCT +.. |DD| unicode:: U+02145 .. DOUBLE-STRUCK ITALIC CAPITAL D +.. |dd| unicode:: U+02146 .. DOUBLE-STRUCK ITALIC SMALL D +.. |dopf| unicode:: U+1D555 .. MATHEMATICAL DOUBLE-STRUCK SMALL D +.. |DownArrowBar| unicode:: U+02913 .. DOWNWARDS ARROW TO BAR +.. |DownBreve| unicode:: U+00311 .. COMBINING INVERTED BREVE +.. |DownLeftRightVector| unicode:: U+02950 .. LEFT BARB DOWN RIGHT BARB DOWN HARPOON +.. |DownLeftTeeVector| unicode:: U+0295E .. LEFTWARDS HARPOON WITH BARB DOWN FROM BAR +.. |DownLeftVectorBar| unicode:: U+02956 .. LEFTWARDS HARPOON WITH BARB DOWN TO BAR +.. |DownRightTeeVector| unicode:: U+0295F .. RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR +.. |DownRightVectorBar| unicode:: U+02957 .. RIGHTWARDS HARPOON WITH BARB DOWN TO BAR +.. |ee| unicode:: U+02147 .. DOUBLE-STRUCK ITALIC SMALL E +.. |EmptySmallSquare| unicode:: U+025FB .. WHITE MEDIUM SQUARE +.. |EmptyVerySmallSquare| unicode:: U+025AB .. WHITE SMALL SQUARE +.. |eopf| unicode:: U+1D556 .. MATHEMATICAL DOUBLE-STRUCK SMALL E +.. |Equal| unicode:: U+02A75 .. TWO CONSECUTIVE EQUALS SIGNS +.. |FilledSmallSquare| unicode:: U+025FC .. BLACK MEDIUM SQUARE +.. |FilledVerySmallSquare| unicode:: U+025AA .. BLACK SMALL SQUARE +.. |fopf| unicode:: U+1D557 .. MATHEMATICAL DOUBLE-STRUCK SMALL F +.. |gopf| unicode:: U+1D558 .. MATHEMATICAL DOUBLE-STRUCK SMALL G +.. |GreaterGreater| unicode:: U+02AA2 .. DOUBLE NESTED GREATER-THAN +.. |Hat| unicode:: U+0005E .. CIRCUMFLEX ACCENT +.. |hopf| unicode:: U+1D559 .. MATHEMATICAL DOUBLE-STRUCK SMALL H +.. |HorizontalLine| unicode:: U+02500 .. BOX DRAWINGS LIGHT HORIZONTAL +.. |ic| unicode:: U+02063 .. INVISIBLE SEPARATOR +.. |ii| unicode:: U+02148 .. DOUBLE-STRUCK ITALIC SMALL I +.. |iopf| unicode:: U+1D55A .. MATHEMATICAL DOUBLE-STRUCK SMALL I +.. |it| unicode:: U+02062 .. INVISIBLE TIMES +.. |jopf| unicode:: U+1D55B .. MATHEMATICAL DOUBLE-STRUCK SMALL J +.. |kopf| unicode:: U+1D55C .. MATHEMATICAL DOUBLE-STRUCK SMALL K +.. |larrb| unicode:: U+021E4 .. LEFTWARDS ARROW TO BAR +.. |LeftDownTeeVector| unicode:: U+02961 .. DOWNWARDS HARPOON WITH BARB LEFT FROM BAR +.. |LeftDownVectorBar| unicode:: U+02959 .. DOWNWARDS HARPOON WITH BARB LEFT TO BAR +.. |LeftRightVector| unicode:: U+0294E .. LEFT BARB UP RIGHT BARB UP HARPOON +.. |LeftTeeVector| unicode:: U+0295A .. LEFTWARDS HARPOON WITH BARB UP FROM BAR +.. |LeftTriangleBar| unicode:: U+029CF .. LEFT TRIANGLE BESIDE VERTICAL BAR +.. |LeftUpDownVector| unicode:: U+02951 .. UP BARB LEFT DOWN BARB LEFT HARPOON +.. |LeftUpTeeVector| unicode:: U+02960 .. UPWARDS HARPOON WITH BARB LEFT FROM BAR +.. |LeftUpVectorBar| unicode:: U+02958 .. UPWARDS HARPOON WITH BARB LEFT TO BAR +.. |LeftVectorBar| unicode:: U+02952 .. LEFTWARDS HARPOON WITH BARB UP TO BAR +.. |LessLess| unicode:: U+02AA1 .. DOUBLE NESTED LESS-THAN +.. |lopf| unicode:: U+1D55D .. MATHEMATICAL DOUBLE-STRUCK SMALL L +.. |mapstodown| unicode:: U+021A7 .. DOWNWARDS ARROW FROM BAR +.. |mapstoleft| unicode:: U+021A4 .. LEFTWARDS ARROW FROM BAR +.. |mapstoup| unicode:: U+021A5 .. UPWARDS ARROW FROM BAR +.. |MediumSpace| unicode:: U+0205F .. MEDIUM MATHEMATICAL SPACE +.. |mopf| unicode:: U+1D55E .. MATHEMATICAL DOUBLE-STRUCK SMALL M +.. |nbump| unicode:: U+0224E U+00338 .. GEOMETRICALLY EQUIVALENT TO with slash +.. |nbumpe| unicode:: U+0224F U+00338 .. DIFFERENCE BETWEEN with slash +.. |nesim| unicode:: U+02242 U+00338 .. MINUS TILDE with slash +.. |NewLine| unicode:: U+0000A .. LINE FEED (LF) +.. |NoBreak| unicode:: U+02060 .. WORD JOINER +.. |nopf| unicode:: U+1D55F .. MATHEMATICAL DOUBLE-STRUCK SMALL N +.. |NotCupCap| unicode:: U+0226D .. NOT EQUIVALENT TO +.. |NotHumpEqual| unicode:: U+0224F U+00338 .. DIFFERENCE BETWEEN with slash +.. |NotLeftTriangleBar| unicode:: U+029CF U+00338 .. LEFT TRIANGLE BESIDE VERTICAL BAR with slash +.. |NotNestedGreaterGreater| unicode:: U+02AA2 U+00338 .. DOUBLE NESTED GREATER-THAN with slash +.. |NotNestedLessLess| unicode:: U+02AA1 U+00338 .. DOUBLE NESTED LESS-THAN with slash +.. |NotRightTriangleBar| unicode:: U+029D0 U+00338 .. VERTICAL BAR BESIDE RIGHT TRIANGLE with slash +.. |NotSquareSubset| unicode:: U+0228F U+00338 .. SQUARE IMAGE OF with slash +.. |NotSquareSuperset| unicode:: U+02290 U+00338 .. SQUARE ORIGINAL OF with slash +.. |NotSucceedsTilde| unicode:: U+0227F U+00338 .. SUCCEEDS OR EQUIVALENT TO with slash +.. |oopf| unicode:: U+1D560 .. MATHEMATICAL DOUBLE-STRUCK SMALL O +.. |OverBar| unicode:: U+000AF .. MACRON +.. |OverBrace| unicode:: U+0FE37 .. PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET +.. |OverBracket| unicode:: U+023B4 .. TOP SQUARE BRACKET +.. |OverParenthesis| unicode:: U+0FE35 .. PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS +.. |planckh| unicode:: U+0210E .. PLANCK CONSTANT +.. |popf| unicode:: U+1D561 .. MATHEMATICAL DOUBLE-STRUCK SMALL P +.. |Product| unicode:: U+0220F .. N-ARY PRODUCT +.. |qopf| unicode:: U+1D562 .. MATHEMATICAL DOUBLE-STRUCK SMALL Q +.. |rarrb| unicode:: U+021E5 .. RIGHTWARDS ARROW TO BAR +.. |RightDownTeeVector| unicode:: U+0295D .. DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR +.. |RightDownVectorBar| unicode:: U+02955 .. DOWNWARDS HARPOON WITH BARB RIGHT TO BAR +.. |RightTeeVector| unicode:: U+0295B .. RIGHTWARDS HARPOON WITH BARB UP FROM BAR +.. |RightTriangleBar| unicode:: U+029D0 .. VERTICAL BAR BESIDE RIGHT TRIANGLE +.. |RightUpDownVector| unicode:: U+0294F .. UP BARB RIGHT DOWN BARB RIGHT HARPOON +.. |RightUpTeeVector| unicode:: U+0295C .. UPWARDS HARPOON WITH BARB RIGHT FROM BAR +.. |RightUpVectorBar| unicode:: U+02954 .. UPWARDS HARPOON WITH BARB RIGHT TO BAR +.. |RightVectorBar| unicode:: U+02953 .. RIGHTWARDS HARPOON WITH BARB UP TO BAR +.. |ropf| unicode:: U+1D563 .. MATHEMATICAL DOUBLE-STRUCK SMALL R +.. |RoundImplies| unicode:: U+02970 .. RIGHT DOUBLE ARROW WITH ROUNDED HEAD +.. |RuleDelayed| unicode:: U+029F4 .. RULE-DELAYED +.. |sopf| unicode:: U+1D564 .. MATHEMATICAL DOUBLE-STRUCK SMALL S +.. |Tab| unicode:: U+00009 .. CHARACTER TABULATION +.. |ThickSpace| unicode:: U+02009 U+0200A U+0200A .. space of width 5/18 em +.. |topf| unicode:: U+1D565 .. MATHEMATICAL DOUBLE-STRUCK SMALL T +.. |UnderBar| unicode:: U+00332 .. COMBINING LOW LINE +.. |UnderBrace| unicode:: U+0FE38 .. PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET +.. |UnderBracket| unicode:: U+023B5 .. BOTTOM SQUARE BRACKET +.. |UnderParenthesis| unicode:: U+0FE36 .. PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS +.. |uopf| unicode:: U+1D566 .. MATHEMATICAL DOUBLE-STRUCK SMALL U +.. |UpArrowBar| unicode:: U+02912 .. UPWARDS ARROW TO BAR +.. |Upsilon| unicode:: U+003A5 .. GREEK CAPITAL LETTER UPSILON +.. |VerticalLine| unicode:: U+0007C .. VERTICAL LINE +.. |VerticalSeparator| unicode:: U+02758 .. LIGHT VERTICAL BAR +.. |vopf| unicode:: U+1D567 .. MATHEMATICAL DOUBLE-STRUCK SMALL V +.. |wopf| unicode:: U+1D568 .. MATHEMATICAL DOUBLE-STRUCK SMALL W +.. |xopf| unicode:: U+1D569 .. MATHEMATICAL DOUBLE-STRUCK SMALL X +.. |yopf| unicode:: U+1D56A .. MATHEMATICAL DOUBLE-STRUCK SMALL Y +.. |ZeroWidthSpace| unicode:: U+0200B .. ZERO WIDTH SPACE +.. |zopf| unicode:: U+1D56B .. MATHEMATICAL DOUBLE-STRUCK SMALL Z diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/README.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/README.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,17 @@ +============================================ + ``docutils/parsers/rst/include`` Directory +============================================ + +This directory contains standard data files intended for inclusion in +reStructuredText documents. To access these files, use the "include" +directive with the special syntax for standard "include" data files, +angle brackets around the file name:: + + .. include:: + +See the documentation for the `"include" directive`__ and +`reStructuredText Standard Substitution Definition Sets`__ for +details. + +__ http://docutils.sf.net/docs/ref/rst/directives.html#include +__ http://docutils.sf.net/docs/ref/rst/substitutions.html diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/s5defs.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/s5defs.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/s5defs.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/s5defs.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,68 @@ +.. Definitions of interpreted text roles (classes) for S5/HTML data. +.. This data file has been placed in the public domain. + +.. Colours + ======= + +.. role:: black +.. role:: gray +.. role:: silver +.. role:: white + +.. role:: maroon +.. role:: red +.. role:: magenta +.. role:: fuchsia +.. role:: pink +.. role:: orange +.. role:: yellow +.. role:: lime +.. role:: green +.. role:: olive +.. role:: teal +.. role:: cyan +.. role:: aqua +.. role:: blue +.. role:: navy +.. role:: purple + + +.. Text Sizes + ========== + +.. role:: huge +.. role:: big +.. role:: small +.. role:: tiny + + +.. Display in Slides (Presentation Mode) Only + ========================================== + +.. role:: slide + :class: slide-display + + +.. Display in Outline Mode Only + ============================ + +.. role:: outline + + +.. Display in Print Only + ===================== + +.. role:: print + + +.. Display in Handout Mode Only + ============================ + +.. role:: handout + + +.. Incremental Display + =================== + +.. role:: incremental +.. default-role:: incremental diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/xhtml1-lat1.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/xhtml1-lat1.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/xhtml1-lat1.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/xhtml1-lat1.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,102 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |Aacute| unicode:: U+000C1 .. LATIN CAPITAL LETTER A WITH ACUTE +.. |aacute| unicode:: U+000E1 .. LATIN SMALL LETTER A WITH ACUTE +.. |Acirc| unicode:: U+000C2 .. LATIN CAPITAL LETTER A WITH CIRCUMFLEX +.. |acirc| unicode:: U+000E2 .. LATIN SMALL LETTER A WITH CIRCUMFLEX +.. |acute| unicode:: U+000B4 .. ACUTE ACCENT +.. |AElig| unicode:: U+000C6 .. LATIN CAPITAL LETTER AE +.. |aelig| unicode:: U+000E6 .. LATIN SMALL LETTER AE +.. |Agrave| unicode:: U+000C0 .. LATIN CAPITAL LETTER A WITH GRAVE +.. |agrave| unicode:: U+000E0 .. LATIN SMALL LETTER A WITH GRAVE +.. |Aring| unicode:: U+000C5 .. LATIN CAPITAL LETTER A WITH RING ABOVE +.. |aring| unicode:: U+000E5 .. LATIN SMALL LETTER A WITH RING ABOVE +.. |Atilde| unicode:: U+000C3 .. LATIN CAPITAL LETTER A WITH TILDE +.. |atilde| unicode:: U+000E3 .. LATIN SMALL LETTER A WITH TILDE +.. |Auml| unicode:: U+000C4 .. LATIN CAPITAL LETTER A WITH DIAERESIS +.. |auml| unicode:: U+000E4 .. LATIN SMALL LETTER A WITH DIAERESIS +.. |brvbar| unicode:: U+000A6 .. BROKEN BAR +.. |Ccedil| unicode:: U+000C7 .. LATIN CAPITAL LETTER C WITH CEDILLA +.. |ccedil| unicode:: U+000E7 .. LATIN SMALL LETTER C WITH CEDILLA +.. |cedil| unicode:: U+000B8 .. CEDILLA +.. |cent| unicode:: U+000A2 .. CENT SIGN +.. |copy| unicode:: U+000A9 .. COPYRIGHT SIGN +.. |curren| unicode:: U+000A4 .. CURRENCY SIGN +.. |deg| unicode:: U+000B0 .. DEGREE SIGN +.. |divide| unicode:: U+000F7 .. DIVISION SIGN +.. |Eacute| unicode:: U+000C9 .. LATIN CAPITAL LETTER E WITH ACUTE +.. |eacute| unicode:: U+000E9 .. LATIN SMALL LETTER E WITH ACUTE +.. |Ecirc| unicode:: U+000CA .. LATIN CAPITAL LETTER E WITH CIRCUMFLEX +.. |ecirc| unicode:: U+000EA .. LATIN SMALL LETTER E WITH CIRCUMFLEX +.. |Egrave| unicode:: U+000C8 .. LATIN CAPITAL LETTER E WITH GRAVE +.. |egrave| unicode:: U+000E8 .. LATIN SMALL LETTER E WITH GRAVE +.. |ETH| unicode:: U+000D0 .. LATIN CAPITAL LETTER ETH +.. |eth| unicode:: U+000F0 .. LATIN SMALL LETTER ETH +.. |Euml| unicode:: U+000CB .. LATIN CAPITAL LETTER E WITH DIAERESIS +.. |euml| unicode:: U+000EB .. LATIN SMALL LETTER E WITH DIAERESIS +.. |frac12| unicode:: U+000BD .. VULGAR FRACTION ONE HALF +.. |frac14| unicode:: U+000BC .. VULGAR FRACTION ONE QUARTER +.. |frac34| unicode:: U+000BE .. VULGAR FRACTION THREE QUARTERS +.. |Iacute| unicode:: U+000CD .. LATIN CAPITAL LETTER I WITH ACUTE +.. |iacute| unicode:: U+000ED .. LATIN SMALL LETTER I WITH ACUTE +.. |Icirc| unicode:: U+000CE .. LATIN CAPITAL LETTER I WITH CIRCUMFLEX +.. |icirc| unicode:: U+000EE .. LATIN SMALL LETTER I WITH CIRCUMFLEX +.. |iexcl| unicode:: U+000A1 .. INVERTED EXCLAMATION MARK +.. |Igrave| unicode:: U+000CC .. LATIN CAPITAL LETTER I WITH GRAVE +.. |igrave| unicode:: U+000EC .. LATIN SMALL LETTER I WITH GRAVE +.. |iquest| unicode:: U+000BF .. INVERTED QUESTION MARK +.. |Iuml| unicode:: U+000CF .. LATIN CAPITAL LETTER I WITH DIAERESIS +.. |iuml| unicode:: U+000EF .. LATIN SMALL LETTER I WITH DIAERESIS +.. |laquo| unicode:: U+000AB .. LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +.. |macr| unicode:: U+000AF .. MACRON +.. |micro| unicode:: U+000B5 .. MICRO SIGN +.. |middot| unicode:: U+000B7 .. MIDDLE DOT +.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE +.. |not| unicode:: U+000AC .. NOT SIGN +.. |Ntilde| unicode:: U+000D1 .. LATIN CAPITAL LETTER N WITH TILDE +.. |ntilde| unicode:: U+000F1 .. LATIN SMALL LETTER N WITH TILDE +.. |Oacute| unicode:: U+000D3 .. LATIN CAPITAL LETTER O WITH ACUTE +.. |oacute| unicode:: U+000F3 .. LATIN SMALL LETTER O WITH ACUTE +.. |Ocirc| unicode:: U+000D4 .. LATIN CAPITAL LETTER O WITH CIRCUMFLEX +.. |ocirc| unicode:: U+000F4 .. LATIN SMALL LETTER O WITH CIRCUMFLEX +.. |Ograve| unicode:: U+000D2 .. LATIN CAPITAL LETTER O WITH GRAVE +.. |ograve| unicode:: U+000F2 .. LATIN SMALL LETTER O WITH GRAVE +.. |ordf| unicode:: U+000AA .. FEMININE ORDINAL INDICATOR +.. |ordm| unicode:: U+000BA .. MASCULINE ORDINAL INDICATOR +.. |Oslash| unicode:: U+000D8 .. LATIN CAPITAL LETTER O WITH STROKE +.. |oslash| unicode:: U+000F8 .. LATIN SMALL LETTER O WITH STROKE +.. |Otilde| unicode:: U+000D5 .. LATIN CAPITAL LETTER O WITH TILDE +.. |otilde| unicode:: U+000F5 .. LATIN SMALL LETTER O WITH TILDE +.. |Ouml| unicode:: U+000D6 .. LATIN CAPITAL LETTER O WITH DIAERESIS +.. |ouml| unicode:: U+000F6 .. LATIN SMALL LETTER O WITH DIAERESIS +.. |para| unicode:: U+000B6 .. PILCROW SIGN +.. |plusmn| unicode:: U+000B1 .. PLUS-MINUS SIGN +.. |pound| unicode:: U+000A3 .. POUND SIGN +.. |raquo| unicode:: U+000BB .. RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +.. |reg| unicode:: U+000AE .. REGISTERED SIGN +.. |sect| unicode:: U+000A7 .. SECTION SIGN +.. |shy| unicode:: U+000AD .. SOFT HYPHEN +.. |sup1| unicode:: U+000B9 .. SUPERSCRIPT ONE +.. |sup2| unicode:: U+000B2 .. SUPERSCRIPT TWO +.. |sup3| unicode:: U+000B3 .. SUPERSCRIPT THREE +.. |szlig| unicode:: U+000DF .. LATIN SMALL LETTER SHARP S +.. |THORN| unicode:: U+000DE .. LATIN CAPITAL LETTER THORN +.. |thorn| unicode:: U+000FE .. LATIN SMALL LETTER THORN +.. |times| unicode:: U+000D7 .. MULTIPLICATION SIGN +.. |Uacute| unicode:: U+000DA .. LATIN CAPITAL LETTER U WITH ACUTE +.. |uacute| unicode:: U+000FA .. LATIN SMALL LETTER U WITH ACUTE +.. |Ucirc| unicode:: U+000DB .. LATIN CAPITAL LETTER U WITH CIRCUMFLEX +.. |ucirc| unicode:: U+000FB .. LATIN SMALL LETTER U WITH CIRCUMFLEX +.. |Ugrave| unicode:: U+000D9 .. LATIN CAPITAL LETTER U WITH GRAVE +.. |ugrave| unicode:: U+000F9 .. LATIN SMALL LETTER U WITH GRAVE +.. |uml| unicode:: U+000A8 .. DIAERESIS +.. |Uuml| unicode:: U+000DC .. LATIN CAPITAL LETTER U WITH DIAERESIS +.. |uuml| unicode:: U+000FC .. LATIN SMALL LETTER U WITH DIAERESIS +.. |Yacute| unicode:: U+000DD .. LATIN CAPITAL LETTER Y WITH ACUTE +.. |yacute| unicode:: U+000FD .. LATIN SMALL LETTER Y WITH ACUTE +.. |yen| unicode:: U+000A5 .. YEN SIGN +.. |yuml| unicode:: U+000FF .. LATIN SMALL LETTER Y WITH DIAERESIS diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/xhtml1-special.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/xhtml1-special.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/xhtml1-special.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/xhtml1-special.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,37 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |bdquo| unicode:: U+0201E .. DOUBLE LOW-9 QUOTATION MARK +.. |circ| unicode:: U+002C6 .. MODIFIER LETTER CIRCUMFLEX ACCENT +.. |Dagger| unicode:: U+02021 .. DOUBLE DAGGER +.. |dagger| unicode:: U+02020 .. DAGGER +.. |emsp| unicode:: U+02003 .. EM SPACE +.. |ensp| unicode:: U+02002 .. EN SPACE +.. |euro| unicode:: U+020AC .. EURO SIGN +.. |gt| unicode:: U+0003E .. GREATER-THAN SIGN +.. |ldquo| unicode:: U+0201C .. LEFT DOUBLE QUOTATION MARK +.. |lrm| unicode:: U+0200E .. LEFT-TO-RIGHT MARK +.. |lsaquo| unicode:: U+02039 .. SINGLE LEFT-POINTING ANGLE QUOTATION MARK +.. |lsquo| unicode:: U+02018 .. LEFT SINGLE QUOTATION MARK +.. |lt| unicode:: U+0003C .. LESS-THAN SIGN +.. |mdash| unicode:: U+02014 .. EM DASH +.. |ndash| unicode:: U+02013 .. EN DASH +.. |OElig| unicode:: U+00152 .. LATIN CAPITAL LIGATURE OE +.. |oelig| unicode:: U+00153 .. LATIN SMALL LIGATURE OE +.. |permil| unicode:: U+02030 .. PER MILLE SIGN +.. |quot| unicode:: U+00022 .. QUOTATION MARK +.. |rdquo| unicode:: U+0201D .. RIGHT DOUBLE QUOTATION MARK +.. |rlm| unicode:: U+0200F .. RIGHT-TO-LEFT MARK +.. |rsaquo| unicode:: U+0203A .. SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +.. |rsquo| unicode:: U+02019 .. RIGHT SINGLE QUOTATION MARK +.. |sbquo| unicode:: U+0201A .. SINGLE LOW-9 QUOTATION MARK +.. |Scaron| unicode:: U+00160 .. LATIN CAPITAL LETTER S WITH CARON +.. |scaron| unicode:: U+00161 .. LATIN SMALL LETTER S WITH CARON +.. |thinsp| unicode:: U+02009 .. THIN SPACE +.. |tilde| unicode:: U+002DC .. SMALL TILDE +.. |Yuml| unicode:: U+00178 .. LATIN CAPITAL LETTER Y WITH DIAERESIS +.. |zwj| unicode:: U+0200D .. ZERO WIDTH JOINER +.. |zwnj| unicode:: U+0200C .. ZERO WIDTH NON-JOINER diff -Nru zope3-3.4.0/src/docutils/parsers/rst/include/xhtml1-symbol.txt zope3-3.5~bzr18/src/docutils/parsers/rst/include/xhtml1-symbol.txt --- zope3-3.4.0/src/docutils/parsers/rst/include/xhtml1-symbol.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/include/xhtml1-symbol.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,130 @@ +.. This data file has been placed in the public domain. +.. Derived from the Unicode character mappings available from + . + Processed by unicode2rstsubs.py, part of Docutils: + . + +.. |alefsym| unicode:: U+02135 .. ALEF SYMBOL +.. |Alpha| unicode:: U+00391 .. GREEK CAPITAL LETTER ALPHA +.. |alpha| unicode:: U+003B1 .. GREEK SMALL LETTER ALPHA +.. |and| unicode:: U+02227 .. LOGICAL AND +.. |ang| unicode:: U+02220 .. ANGLE +.. |asymp| unicode:: U+02248 .. ALMOST EQUAL TO +.. |Beta| unicode:: U+00392 .. GREEK CAPITAL LETTER BETA +.. |beta| unicode:: U+003B2 .. GREEK SMALL LETTER BETA +.. |bull| unicode:: U+02022 .. BULLET +.. |cap| unicode:: U+02229 .. INTERSECTION +.. |Chi| unicode:: U+003A7 .. GREEK CAPITAL LETTER CHI +.. |chi| unicode:: U+003C7 .. GREEK SMALL LETTER CHI +.. |clubs| unicode:: U+02663 .. BLACK CLUB SUIT +.. |cong| unicode:: U+02245 .. APPROXIMATELY EQUAL TO +.. |crarr| unicode:: U+021B5 .. DOWNWARDS ARROW WITH CORNER LEFTWARDS +.. |cup| unicode:: U+0222A .. UNION +.. |dArr| unicode:: U+021D3 .. DOWNWARDS DOUBLE ARROW +.. |darr| unicode:: U+02193 .. DOWNWARDS ARROW +.. |Delta| unicode:: U+00394 .. GREEK CAPITAL LETTER DELTA +.. |delta| unicode:: U+003B4 .. GREEK SMALL LETTER DELTA +.. |diams| unicode:: U+02666 .. BLACK DIAMOND SUIT +.. |empty| unicode:: U+02205 .. EMPTY SET +.. |Epsilon| unicode:: U+00395 .. GREEK CAPITAL LETTER EPSILON +.. |epsilon| unicode:: U+003B5 .. GREEK SMALL LETTER EPSILON +.. |equiv| unicode:: U+02261 .. IDENTICAL TO +.. |Eta| unicode:: U+00397 .. GREEK CAPITAL LETTER ETA +.. |eta| unicode:: U+003B7 .. GREEK SMALL LETTER ETA +.. |exist| unicode:: U+02203 .. THERE EXISTS +.. |fnof| unicode:: U+00192 .. LATIN SMALL LETTER F WITH HOOK +.. |forall| unicode:: U+02200 .. FOR ALL +.. |frasl| unicode:: U+02044 .. FRACTION SLASH +.. |Gamma| unicode:: U+00393 .. GREEK CAPITAL LETTER GAMMA +.. |gamma| unicode:: U+003B3 .. GREEK SMALL LETTER GAMMA +.. |ge| unicode:: U+02265 .. GREATER-THAN OR EQUAL TO +.. |hArr| unicode:: U+021D4 .. LEFT RIGHT DOUBLE ARROW +.. |harr| unicode:: U+02194 .. LEFT RIGHT ARROW +.. |hearts| unicode:: U+02665 .. BLACK HEART SUIT +.. |hellip| unicode:: U+02026 .. HORIZONTAL ELLIPSIS +.. |image| unicode:: U+02111 .. BLACK-LETTER CAPITAL I +.. |infin| unicode:: U+0221E .. INFINITY +.. |int| unicode:: U+0222B .. INTEGRAL +.. |Iota| unicode:: U+00399 .. GREEK CAPITAL LETTER IOTA +.. |iota| unicode:: U+003B9 .. GREEK SMALL LETTER IOTA +.. |isin| unicode:: U+02208 .. ELEMENT OF +.. |Kappa| unicode:: U+0039A .. GREEK CAPITAL LETTER KAPPA +.. |kappa| unicode:: U+003BA .. GREEK SMALL LETTER KAPPA +.. |Lambda| unicode:: U+0039B .. GREEK CAPITAL LETTER LAMDA +.. |lambda| unicode:: U+003BB .. GREEK SMALL LETTER LAMDA +.. |lang| unicode:: U+02329 .. LEFT-POINTING ANGLE BRACKET +.. |lArr| unicode:: U+021D0 .. LEFTWARDS DOUBLE ARROW +.. |larr| unicode:: U+02190 .. LEFTWARDS ARROW +.. |lceil| unicode:: U+02308 .. LEFT CEILING +.. |le| unicode:: U+02264 .. LESS-THAN OR EQUAL TO +.. |lfloor| unicode:: U+0230A .. LEFT FLOOR +.. |lowast| unicode:: U+02217 .. ASTERISK OPERATOR +.. |loz| unicode:: U+025CA .. LOZENGE +.. |minus| unicode:: U+02212 .. MINUS SIGN +.. |Mu| unicode:: U+0039C .. GREEK CAPITAL LETTER MU +.. |mu| unicode:: U+003BC .. GREEK SMALL LETTER MU +.. |nabla| unicode:: U+02207 .. NABLA +.. |ne| unicode:: U+02260 .. NOT EQUAL TO +.. |ni| unicode:: U+0220B .. CONTAINS AS MEMBER +.. |notin| unicode:: U+02209 .. NOT AN ELEMENT OF +.. |nsub| unicode:: U+02284 .. NOT A SUBSET OF +.. |Nu| unicode:: U+0039D .. GREEK CAPITAL LETTER NU +.. |nu| unicode:: U+003BD .. GREEK SMALL LETTER NU +.. |oline| unicode:: U+0203E .. OVERLINE +.. |Omega| unicode:: U+003A9 .. GREEK CAPITAL LETTER OMEGA +.. |omega| unicode:: U+003C9 .. GREEK SMALL LETTER OMEGA +.. |Omicron| unicode:: U+0039F .. GREEK CAPITAL LETTER OMICRON +.. |omicron| unicode:: U+003BF .. GREEK SMALL LETTER OMICRON +.. |oplus| unicode:: U+02295 .. CIRCLED PLUS +.. |or| unicode:: U+02228 .. LOGICAL OR +.. |otimes| unicode:: U+02297 .. CIRCLED TIMES +.. |part| unicode:: U+02202 .. PARTIAL DIFFERENTIAL +.. |perp| unicode:: U+022A5 .. UP TACK +.. |Phi| unicode:: U+003A6 .. GREEK CAPITAL LETTER PHI +.. |phi| unicode:: U+003D5 .. GREEK PHI SYMBOL +.. |Pi| unicode:: U+003A0 .. GREEK CAPITAL LETTER PI +.. |pi| unicode:: U+003C0 .. GREEK SMALL LETTER PI +.. |piv| unicode:: U+003D6 .. GREEK PI SYMBOL +.. |Prime| unicode:: U+02033 .. DOUBLE PRIME +.. |prime| unicode:: U+02032 .. PRIME +.. |prod| unicode:: U+0220F .. N-ARY PRODUCT +.. |prop| unicode:: U+0221D .. PROPORTIONAL TO +.. |Psi| unicode:: U+003A8 .. GREEK CAPITAL LETTER PSI +.. |psi| unicode:: U+003C8 .. GREEK SMALL LETTER PSI +.. |radic| unicode:: U+0221A .. SQUARE ROOT +.. |rang| unicode:: U+0232A .. RIGHT-POINTING ANGLE BRACKET +.. |rArr| unicode:: U+021D2 .. RIGHTWARDS DOUBLE ARROW +.. |rarr| unicode:: U+02192 .. RIGHTWARDS ARROW +.. |rceil| unicode:: U+02309 .. RIGHT CEILING +.. |real| unicode:: U+0211C .. BLACK-LETTER CAPITAL R +.. |rfloor| unicode:: U+0230B .. RIGHT FLOOR +.. |Rho| unicode:: U+003A1 .. GREEK CAPITAL LETTER RHO +.. |rho| unicode:: U+003C1 .. GREEK SMALL LETTER RHO +.. |sdot| unicode:: U+022C5 .. DOT OPERATOR +.. |Sigma| unicode:: U+003A3 .. GREEK CAPITAL LETTER SIGMA +.. |sigma| unicode:: U+003C3 .. GREEK SMALL LETTER SIGMA +.. |sigmaf| unicode:: U+003C2 .. GREEK SMALL LETTER FINAL SIGMA +.. |sim| unicode:: U+0223C .. TILDE OPERATOR +.. |spades| unicode:: U+02660 .. BLACK SPADE SUIT +.. |sub| unicode:: U+02282 .. SUBSET OF +.. |sube| unicode:: U+02286 .. SUBSET OF OR EQUAL TO +.. |sum| unicode:: U+02211 .. N-ARY SUMMATION +.. |sup| unicode:: U+02283 .. SUPERSET OF +.. |supe| unicode:: U+02287 .. SUPERSET OF OR EQUAL TO +.. |Tau| unicode:: U+003A4 .. GREEK CAPITAL LETTER TAU +.. |tau| unicode:: U+003C4 .. GREEK SMALL LETTER TAU +.. |there4| unicode:: U+02234 .. THEREFORE +.. |Theta| unicode:: U+00398 .. GREEK CAPITAL LETTER THETA +.. |theta| unicode:: U+003B8 .. GREEK SMALL LETTER THETA +.. |thetasym| unicode:: U+003D1 .. GREEK THETA SYMBOL +.. |trade| unicode:: U+02122 .. TRADE MARK SIGN +.. |uArr| unicode:: U+021D1 .. UPWARDS DOUBLE ARROW +.. |uarr| unicode:: U+02191 .. UPWARDS ARROW +.. |upsih| unicode:: U+003D2 .. GREEK UPSILON WITH HOOK SYMBOL +.. |Upsilon| unicode:: U+003A5 .. GREEK CAPITAL LETTER UPSILON +.. |upsilon| unicode:: U+003C5 .. GREEK SMALL LETTER UPSILON +.. |weierp| unicode:: U+02118 .. SCRIPT CAPITAL P +.. |Xi| unicode:: U+0039E .. GREEK CAPITAL LETTER XI +.. |xi| unicode:: U+003BE .. GREEK SMALL LETTER XI +.. |Zeta| unicode:: U+00396 .. GREEK CAPITAL LETTER ZETA +.. |zeta| unicode:: U+003B6 .. GREEK SMALL LETTER ZETA diff -Nru zope3-3.4.0/src/docutils/parsers/rst/__init__.py zope3-3.5~bzr18/src/docutils/parsers/rst/__init__.py --- zope3-3.4.0/src/docutils/parsers/rst/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,375 @@ +# $Id: __init__.py 6141 2009-09-25 18:50:30Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This is ``docutils.parsers.rst`` package. It exports a single class, `Parser`, +the reStructuredText parser. + + +Usage +===== + +1. Create a parser:: + + parser = docutils.parsers.rst.Parser() + + Several optional arguments may be passed to modify the parser's behavior. + Please see `Customizing the Parser`_ below for details. + +2. Gather input (a multi-line string), by reading a file or the standard + input:: + + input = sys.stdin.read() + +3. Create a new empty `docutils.nodes.document` tree:: + + document = docutils.utils.new_document(source, settings) + + See `docutils.utils.new_document()` for parameter details. + +4. Run the parser, populating the document tree:: + + parser.parse(input, document) + + +Parser Overview +=============== + +The reStructuredText parser is implemented as a state machine, examining its +input one line at a time. To understand how the parser works, please first +become familiar with the `docutils.statemachine` module, then see the +`states` module. + + +Customizing the Parser +---------------------- + +Anything that isn't already customizable is that way simply because that type +of customizability hasn't been implemented yet. Patches welcome! + +When instantiating an object of the `Parser` class, two parameters may be +passed: ``rfc2822`` and ``inliner``. Pass ``rfc2822=1`` to enable an initial +RFC-2822 style header block, parsed as a "field_list" element (with "class" +attribute set to "rfc2822"). Currently this is the only body-level element +which is customizable without subclassing. (Tip: subclass `Parser` and change +its "state_classes" and "initial_state" attributes to refer to new classes. +Contact the author if you need more details.) + +The ``inliner`` parameter takes an instance of `states.Inliner` or a subclass. +It handles inline markup recognition. A common extension is the addition of +further implicit hyperlinks, like "RFC 2822". This can be done by subclassing +`states.Inliner`, adding a new method for the implicit markup, and adding a +``(pattern, method)`` pair to the "implicit_dispatch" attribute of the +subclass. See `states.Inliner.implicit_inline()` for details. Explicit +inline markup can be customized in a `states.Inliner` subclass via the +``patterns.initial`` and ``dispatch`` attributes (and new methods as +appropriate). +""" + +__docformat__ = 'reStructuredText' + + +import docutils.parsers +import docutils.statemachine +from docutils.parsers.rst import states +from docutils import frontend, nodes + + +class Parser(docutils.parsers.Parser): + + """The reStructuredText parser.""" + + supported = ('restructuredtext', 'rst', 'rest', 'restx', 'rtxt', 'rstx') + """Aliases this parser supports.""" + + settings_spec = ( + 'reStructuredText Parser Options', + None, + (('Recognize and link to standalone PEP references (like "PEP 258").', + ['--pep-references'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Base URL for PEP references ' + '(default "http://www.python.org/dev/peps/").', + ['--pep-base-url'], + {'metavar': '', 'default': 'http://www.python.org/dev/peps/', + 'validator': frontend.validate_url_trailing_slash}), + ('Template for PEP file part of URL. (default "pep-%04d")', + ['--pep-file-url-template'], + {'metavar': '', 'default': 'pep-%04d'}), + ('Recognize and link to standalone RFC references (like "RFC 822").', + ['--rfc-references'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Base URL for RFC references (default "http://www.faqs.org/rfcs/").', + ['--rfc-base-url'], + {'metavar': '', 'default': 'http://www.faqs.org/rfcs/', + 'validator': frontend.validate_url_trailing_slash}), + ('Set number of spaces for tab expansion (default 8).', + ['--tab-width'], + {'metavar': '', 'type': 'int', 'default': 8, + 'validator': frontend.validate_nonnegative_int}), + ('Remove spaces before footnote references.', + ['--trim-footnote-reference-space'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Leave spaces before footnote references.', + ['--leave-footnote-reference-space'], + {'action': 'store_false', 'dest': 'trim_footnote_reference_space'}), + ('Disable directives that insert the contents of external file ' + '("include" & "raw"); replaced with a "warning" system message.', + ['--no-file-insertion'], + {'action': 'store_false', 'default': 1, + 'dest': 'file_insertion_enabled', + 'validator': frontend.validate_boolean}), + ('Enable directives that insert the contents of external file ' + '("include" & "raw"). Enabled by default.', + ['--file-insertion-enabled'], + {'action': 'store_true'}), + ('Disable the "raw" directives; replaced with a "warning" ' + 'system message.', + ['--no-raw'], + {'action': 'store_false', 'default': 1, 'dest': 'raw_enabled', + 'validator': frontend.validate_boolean}), + ('Enable the "raw" directive. Enabled by default.', + ['--raw-enabled'], + {'action': 'store_true'}),)) + + config_section = 'restructuredtext parser' + config_section_dependencies = ('parsers',) + + def __init__(self, rfc2822=None, inliner=None): + if rfc2822: + self.initial_state = 'RFC2822Body' + else: + self.initial_state = 'Body' + self.state_classes = states.state_classes + self.inliner = inliner + + def parse(self, inputstring, document): + """Parse `inputstring` and populate `document`, a document tree.""" + self.setup_parse(inputstring, document) + self.statemachine = states.RSTStateMachine( + state_classes=self.state_classes, + initial_state=self.initial_state, + debug=document.reporter.debug_flag) + inputlines = docutils.statemachine.string2lines( + inputstring, tab_width=document.settings.tab_width, + convert_whitespace=1) + self.statemachine.run(inputlines, document, inliner=self.inliner) + self.finish_parse() + + +class DirectiveError(Exception): + + """ + Store a message and a system message level. + + To be thrown from inside directive code. + + Do not instantiate directly -- use `Directive.directive_error()` + instead! + """ + + def __init__(self, level, message, source, line): + """ + Initialize with message `message`. `level` is a system message level. + """ + Exception.__init__(self) + self.level = level + self.msg = message + self.source = source + self.line = line + + +class Directive(object): + + """ + Base class for reStructuredText directives. + + The following attributes may be set by subclasses. They are + interpreted by the directive parser (which runs the directive + class): + + - `required_arguments`: The number of required arguments (default: + 0). + + - `optional_arguments`: The number of optional arguments (default: + 0). + + - `final_argument_whitespace`: A boolean, indicating if the final + argument may contain whitespace (default: False). + + - `option_spec`: A dictionary, mapping known option names to + conversion functions such as `int` or `float` (default: {}, no + options). Several conversion functions are defined in the + directives/__init__.py module. + + Option conversion functions take a single parameter, the option + argument (a string or ``None``), validate it and/or convert it + to the appropriate form. Conversion functions may raise + `ValueError` and `TypeError` exceptions. + + - `has_content`: A boolean; True if content is allowed. Client + code must handle the case where content is required but not + supplied (an empty content list will be supplied). + + Arguments are normally single whitespace-separated words. The + final argument may contain whitespace and/or newlines if + `final_argument_whitespace` is True. + + If the form of the arguments is more complex, specify only one + argument (either required or optional) and set + `final_argument_whitespace` to True; the client code must do any + context-sensitive parsing. + + When a directive implementation is being run, the directive class + is instantiated, and the `run()` method is executed. During + instantiation, the following instance variables are set: + + - ``name`` is the directive type or name (string). + + - ``arguments`` is the list of positional arguments (strings). + + - ``options`` is a dictionary mapping option names (strings) to + values (type depends on option conversion functions; see + `option_spec` above). + + - ``content`` is a list of strings, the directive content line by line. + + - ``lineno`` is the line number of the first line of the directive. + + - ``content_offset`` is the line offset of the first line of the content from + the beginning of the current input. Used when initiating a nested parse. + + - ``block_text`` is a string containing the entire directive. + + - ``state`` is the state which called the directive function. + + - ``state_machine`` is the state machine which controls the state which called + the directive function. + + Directive functions return a list of nodes which will be inserted + into the document tree at the point where the directive was + encountered. This can be an empty list if there is nothing to + insert. + + For ordinary directives, the list must contain body elements or + structural elements. Some directives are intended specifically + for substitution definitions, and must return a list of `Text` + nodes and/or inline elements (suitable for inline insertion, in + place of the substitution reference). Such directives must verify + substitution definition context, typically using code like this:: + + if not isinstance(state, states.SubstitutionDef): + error = state_machine.reporter.error( + 'Invalid context: the "%s" directive can only be used ' + 'within a substitution definition.' % (name), + nodes.literal_block(block_text, block_text), line=lineno) + return [error] + """ + + # There is a "Creating reStructuredText Directives" how-to at + # . If you + # update this docstring, please update the how-to as well. + + required_arguments = 0 + """Number of required directive arguments.""" + + optional_arguments = 0 + """Number of optional arguments after the required arguments.""" + + final_argument_whitespace = False + """May the final argument contain whitespace?""" + + option_spec = None + """Mapping of option names to validator functions.""" + + has_content = False + """May the directive have content?""" + + def __init__(self, name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + self.name = name + self.arguments = arguments + self.options = options + self.content = content + self.lineno = lineno + self.content_offset = content_offset + self.block_text = block_text + self.state = state + self.state_machine = state_machine + + def run(self): + raise NotImplementedError('Must override run() is subclass.') + + # Directive errors: + + def directive_error(self, level, message): + """ + Return a DirectiveError suitable for being thrown as an exception. + + Call "raise self.directive_error(level, message)" from within + a directive implementation to return one single system message + at level `level`, which automatically gets the directive block + and the line number added. + + You'd often use self.error(message) instead, which will + generate an ERROR-level directive error. + """ + # source = self.state_machine.get_source(self.lineno - 1) + try: + (source, line) = self.state_machine.input_lines.info(self.lineno) + except IndexError: + source = self.state_machine.get_source(self.lineno - 1) + line = self.lineno + return DirectiveError(level, message, source, line) + + def debug(self, message): + return self.directive_error(0, message) + + def info(self, message): + return self.directive_error(1, message) + + def warning(self, message): + return self.directive_error(2, message) + + def error(self, message): + return self.directive_error(3, message) + + def severe(self, message): + return self.directive_error(4, message) + + # Convenience methods: + + def assert_has_content(self): + """ + Throw an ERROR-level DirectiveError if the directive doesn't + have contents. + """ + if not self.content: + raise self.error('Content block expected for the "%s" directive; ' + 'none found.' % self.name) + + +def convert_directive_function(directive_fn): + """ + Define & return a directive class generated from `directive_fn`. + + `directive_fn` uses the old-style, functional interface. + """ + + class FunctionalDirective(Directive): + + option_spec = getattr(directive_fn, 'options', None) + has_content = getattr(directive_fn, 'content', False) + _argument_spec = getattr(directive_fn, 'arguments', (0, 0, False)) + required_arguments, optional_arguments, final_argument_whitespace \ + = _argument_spec + + def run(self): + return directive_fn( + self.name, self.arguments, self.options, self.content, + self.lineno, self.content_offset, self.block_text, + self.state, self.state_machine) + + # Return new-style directive. + return FunctionalDirective diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/af.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/af.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/af.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/af.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,102 @@ +# $Id: af.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Jannie Hofmeyr +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Afrikaans-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + 'aandag': 'attention', + 'versigtig': 'caution', + 'gevaar': 'danger', + 'fout': 'error', + 'wenk': 'hint', + 'belangrik': 'important', + 'nota': 'note', + 'tip': 'tip', # hint and tip both have the same translation: wenk + 'waarskuwing': 'warning', + 'vermaning': 'admonition', + 'kantstreep': 'sidebar', + 'onderwerp': 'topic', + 'lynblok': 'line-block', + 'parsed-literal (translation required)': 'parsed-literal', + 'rubriek': 'rubric', + 'epigraaf': 'epigraph', + 'hoogtepunte': 'highlights', + 'pull-quote (translation required)': 'pull-quote', + u'compound (translation required)': 'compound', + u'container (translation required)': 'container', + #'vrae': 'questions', + #'qa': 'questions', + #'faq': 'questions', + 'table (translation required)': 'table', + 'csv-table (translation required)': 'csv-table', + 'list-table (translation required)': 'list-table', + 'meta': 'meta', + #'beeldkaart': 'imagemap', + 'beeld': 'image', + 'figuur': 'figure', + 'insluiting': 'include', + 'rou': 'raw', + 'vervang': 'replace', + 'unicode': 'unicode', # should this be translated? unikode + 'datum': 'date', + 'klas': 'class', + 'role (translation required)': 'role', + 'default-role (translation required)': 'default-role', + 'title (translation required)': 'title', + 'inhoud': 'contents', + 'sectnum': 'sectnum', + 'section-numbering': 'sectnum', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #'voetnote': 'footnotes', + #'aanhalings': 'citations', + 'teikennotas': 'target-notes', + 'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Afrikaans name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + 'afkorting': 'abbreviation', + 'ab': 'abbreviation', + 'akroniem': 'acronym', + 'ac': 'acronym', + 'indeks': 'index', + 'i': 'index', + 'voetskrif': 'subscript', + 'sub': 'subscript', + 'boskrif': 'superscript', + 'sup': 'superscript', + 'titelverwysing': 'title-reference', + 'titel': 'title-reference', + 't': 'title-reference', + 'pep-verwysing': 'pep-reference', + 'pep': 'pep-reference', + 'rfc-verwysing': 'rfc-reference', + 'rfc': 'rfc-reference', + 'nadruk': 'emphasis', + 'sterk': 'strong', + 'literal (translation required)': 'literal', + 'benoemde verwysing': 'named-reference', + 'anonieme verwysing': 'anonymous-reference', + 'voetnootverwysing': 'footnote-reference', + 'aanhalingverwysing': 'citation-reference', + 'vervangingsverwysing': 'substitution-reference', + 'teiken': 'target', + 'uri-verwysing': 'uri-reference', + 'uri': 'uri-reference', + 'url': 'uri-reference', + 'rou': 'raw',} +"""Mapping of Afrikaans role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/ca.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/ca.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/ca.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/ca.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,121 @@ +# $Id: ca.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Ivan Vilata i Balaguer +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Catalan-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'atenci\u00F3': 'attention', + u'compte': 'caution', + u'perill': 'danger', + u'error': 'error', + u'suggeriment': 'hint', + u'important': 'important', + u'nota': 'note', + u'consell': 'tip', + u'av\u00EDs': 'warning', + u'advertiment': 'admonition', + u'nota-al-marge': 'sidebar', + u'nota-marge': 'sidebar', + u'tema': 'topic', + u'bloc-de-l\u00EDnies': 'line-block', + u'bloc-l\u00EDnies': 'line-block', + u'literal-analitzat': 'parsed-literal', + u'r\u00FAbrica': 'rubric', + u'ep\u00EDgraf': 'epigraph', + u'sumari': 'highlights', + u'cita-destacada': 'pull-quote', + u'compost': 'compound', + u'container (translation required)': 'container', + #'questions': 'questions', + u'taula': 'table', + u'taula-csv': 'csv-table', + u'taula-llista': 'list-table', + #'qa': 'questions', + #'faq': 'questions', + u'meta': 'meta', + #'imagemap': 'imagemap', + u'imatge': 'image', + u'figura': 'figure', + u'inclou': 'include', + u'incloure': 'include', + u'cru': 'raw', + u'reempla\u00E7a': 'replace', + u'reempla\u00E7ar': 'replace', + u'unicode': 'unicode', + u'data': 'date', + u'classe': 'class', + u'rol': 'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + u'contingut': 'contents', + u'numsec': 'sectnum', + u'numeraci\u00F3-de-seccions': 'sectnum', + u'numeraci\u00F3-seccions': 'sectnum', + u'cap\u00E7alera': 'header', + u'peu-de-p\u00E0gina': 'footer', + u'peu-p\u00E0gina': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + u'notes-amb-destinacions': 'target-notes', + u'notes-destinacions': 'target-notes', + u'directiva-de-prova-de-restructuredtext': 'restructuredtext-test-directive'} +"""Catalan name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + u'abreviatura': 'abbreviation', + u'abreviaci\u00F3': 'abbreviation', + u'abrev': 'abbreviation', + u'ab': 'abbreviation', + u'acr\u00F2nim': 'acronym', + u'ac': 'acronym', + u'\u00EDndex': 'index', + u'i': 'index', + u'sub\u00EDndex': 'subscript', + u'sub': 'subscript', + u'super\u00EDndex': 'superscript', + u'sup': 'superscript', + u'refer\u00E8ncia-a-t\u00EDtol': 'title-reference', + u'refer\u00E8ncia-t\u00EDtol': 'title-reference', + u't\u00EDtol': 'title-reference', + u't': 'title-reference', + u'refer\u00E8ncia-a-pep': 'pep-reference', + u'refer\u00E8ncia-pep': 'pep-reference', + u'pep': 'pep-reference', + u'refer\u00E8ncia-a-rfc': 'rfc-reference', + u'refer\u00E8ncia-rfc': 'rfc-reference', + u'rfc': 'rfc-reference', + u'\u00E8mfasi': 'emphasis', + u'destacat': 'strong', + u'literal': 'literal', + u'refer\u00E8ncia-amb-nom': 'named-reference', + u'refer\u00E8ncia-nom': 'named-reference', + u'refer\u00E8ncia-an\u00F2nima': 'anonymous-reference', + u'refer\u00E8ncia-a-nota-al-peu': 'footnote-reference', + u'refer\u00E8ncia-nota-al-peu': 'footnote-reference', + u'refer\u00E8ncia-a-cita': 'citation-reference', + u'refer\u00E8ncia-cita': 'citation-reference', + u'refer\u00E8ncia-a-substituci\u00F3': 'substitution-reference', + u'refer\u00E8ncia-substituci\u00F3': 'substitution-reference', + u'destinaci\u00F3': 'target', + u'refer\u00E8ncia-a-uri': 'uri-reference', + u'refer\u00E8ncia-uri': 'uri-reference', + u'uri': 'uri-reference', + u'url': 'uri-reference', + u'cru': 'raw',} +"""Mapping of Catalan role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/cs.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/cs.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/cs.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/cs.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,104 @@ +# $Id: cs.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Marek Blaha +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Czech-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'pozor': 'attention', + u'caution (translation required)': 'caution', # jak rozlisit caution a warning? + u'nebezpe\u010D\u00ED': 'danger', + u'chyba': 'error', + u'rada': 'hint', + u'd\u016Fle\u017Eit\u00E9': 'important', + u'pozn\u00E1mka': 'note', + u'tip (translation required)': 'tip', + u'varov\u00E1n\u00ED': 'warning', + u'admonition (translation required)': 'admonition', + u'sidebar (translation required)': 'sidebar', + u't\u00E9ma': 'topic', + u'line-block (translation required)': 'line-block', + u'parsed-literal (translation required)': 'parsed-literal', + u'odd\u00EDl': 'rubric', + u'moto': 'epigraph', + u'highlights (translation required)': 'highlights', + u'pull-quote (translation required)': 'pull-quote', + u'compound (translation required)': 'compound', + u'container (translation required)': 'container', + #'questions': 'questions', + #'qa': 'questions', + #'faq': 'questions', + u'table (translation required)': 'table', + u'csv-table (translation required)': 'csv-table', + u'list-table (translation required)': 'list-table', + u'meta (translation required)': 'meta', + #'imagemap': 'imagemap', + u'image (translation required)': 'image', # obrazek + u'figure (translation required)': 'figure', # a tady? + u'include (translation required)': 'include', + u'raw (translation required)': 'raw', + u'replace (translation required)': 'replace', + u'unicode (translation required)': 'unicode', + u'datum': 'date', + u't\u0159\u00EDda': 'class', + u'role (translation required)': 'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + u'obsah': 'contents', + u'sectnum (translation required)': 'sectnum', + u'section-numbering (translation required)': 'sectnum', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + u'target-notes (translation required)': 'target-notes', + u'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Czech name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + u'abbreviation (translation required)': 'abbreviation', + u'ab (translation required)': 'abbreviation', + u'acronym (translation required)': 'acronym', + u'ac (translation required)': 'acronym', + u'index (translation required)': 'index', + u'i (translation required)': 'index', + u'subscript (translation required)': 'subscript', + u'sub (translation required)': 'subscript', + u'superscript (translation required)': 'superscript', + u'sup (translation required)': 'superscript', + u'title-reference (translation required)': 'title-reference', + u'title (translation required)': 'title-reference', + u't (translation required)': 'title-reference', + u'pep-reference (translation required)': 'pep-reference', + u'pep (translation required)': 'pep-reference', + u'rfc-reference (translation required)': 'rfc-reference', + u'rfc (translation required)': 'rfc-reference', + u'emphasis (translation required)': 'emphasis', + u'strong (translation required)': 'strong', + u'literal (translation required)': 'literal', + u'named-reference (translation required)': 'named-reference', + u'anonymous-reference (translation required)': 'anonymous-reference', + u'footnote-reference (translation required)': 'footnote-reference', + u'citation-reference (translation required)': 'citation-reference', + u'substitution-reference (translation required)': 'substitution-reference', + u'target (translation required)': 'target', + u'uri-reference (translation required)': 'uri-reference', + u'uri (translation required)': 'uri-reference', + u'url (translation required)': 'uri-reference', + u'raw (translation required)': 'raw',} +"""Mapping of Czech role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/de.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/de.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/de.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/de.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,96 @@ +# $Id: de.py 5174 2007-05-31 00:01:52Z wiemann $ +# Authors: Engelbert Gruber ; +# Lea Wiemann +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +German-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + 'achtung': 'attention', + 'vorsicht': 'caution', + 'gefahr': 'danger', + 'fehler': 'error', + 'hinweis': 'hint', + 'wichtig': 'important', + 'notiz': 'note', + 'tipp': 'tip', + 'warnung': 'warning', + 'ermahnung': 'admonition', + 'kasten': 'sidebar', + 'seitenkasten': 'sidebar', + 'thema': 'topic', + 'zeilen-block': 'line-block', + 'parsed-literal (translation required)': 'parsed-literal', + 'rubrik': 'rubric', + 'epigraph': 'epigraph', + 'highlights (translation required)': 'highlights', + 'pull-quote (translation required)': 'pull-quote', # kasten too ? + 'zusammengesetzt': 'compound', + 'verbund': 'compound', + u'container (translation required)': 'container', + #'fragen': 'questions', + 'tabelle': 'table', + 'csv-tabelle': 'csv-table', + 'list-table (translation required)': 'list-table', + 'meta': 'meta', + #'imagemap': 'imagemap', + 'bild': 'image', + 'abbildung': 'figure', + u'unver\xe4ndert': 'raw', + u'roh': 'raw', + u'einf\xfcgen': 'include', + 'ersetzung': 'replace', + 'ersetzen': 'replace', + 'ersetze': 'replace', + 'unicode': 'unicode', + 'datum': 'date', + 'klasse': 'class', + 'rolle': 'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + 'inhalt': 'contents', + 'kapitel-nummerierung': 'sectnum', + 'abschnitts-nummerierung': 'sectnum', + u'linkziel-fu\xdfnoten': 'target-notes', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #u'fu\xdfnoten': 'footnotes', + #'zitate': 'citations', + } +"""German name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + u'abk\xfcrzung': 'abbreviation', + 'akronym': 'acronym', + 'index': 'index', + 'tiefgestellt': 'subscript', + 'hochgestellt': 'superscript', + 'titel-referenz': 'title-reference', + 'pep-referenz': 'pep-reference', + 'rfc-referenz': 'rfc-reference', + 'betonung': 'emphasis', + 'fett': 'strong', + u'w\xf6rtlich': 'literal', + 'benannte-referenz': 'named-reference', + 'unbenannte-referenz': 'anonymous-reference', + u'fu\xdfnoten-referenz': 'footnote-reference', + 'zitat-referenz': 'citation-reference', + 'ersetzungs-referenz': 'substitution-reference', + 'ziel': 'target', + 'uri-referenz': 'uri-reference', + u'unver\xe4ndert': 'raw', + u'roh': 'raw',} +"""Mapping of German role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/en.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/en.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/en.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/en.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,104 @@ +# $Id: en.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +English-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + 'attention': 'attention', + 'caution': 'caution', + 'danger': 'danger', + 'error': 'error', + 'hint': 'hint', + 'important': 'important', + 'note': 'note', + 'tip': 'tip', + 'warning': 'warning', + 'admonition': 'admonition', + 'sidebar': 'sidebar', + 'topic': 'topic', + 'line-block': 'line-block', + 'parsed-literal': 'parsed-literal', + 'rubric': 'rubric', + 'epigraph': 'epigraph', + 'highlights': 'highlights', + 'pull-quote': 'pull-quote', + 'compound': 'compound', + 'container': 'container', + #'questions': 'questions', + 'table': 'table', + 'csv-table': 'csv-table', + 'list-table': 'list-table', + #'qa': 'questions', + #'faq': 'questions', + 'meta': 'meta', + #'imagemap': 'imagemap', + 'image': 'image', + 'figure': 'figure', + 'include': 'include', + 'raw': 'raw', + 'replace': 'replace', + 'unicode': 'unicode', + 'date': 'date', + 'class': 'class', + 'role': 'role', + 'default-role': 'default-role', + 'title': 'title', + 'contents': 'contents', + 'sectnum': 'sectnum', + 'section-numbering': 'sectnum', + 'header': 'header', + 'footer': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + 'target-notes': 'target-notes', + 'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""English name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + 'abbreviation': 'abbreviation', + 'ab': 'abbreviation', + 'acronym': 'acronym', + 'ac': 'acronym', + 'index': 'index', + 'i': 'index', + 'subscript': 'subscript', + 'sub': 'subscript', + 'superscript': 'superscript', + 'sup': 'superscript', + 'title-reference': 'title-reference', + 'title': 'title-reference', + 't': 'title-reference', + 'pep-reference': 'pep-reference', + 'pep': 'pep-reference', + 'rfc-reference': 'rfc-reference', + 'rfc': 'rfc-reference', + 'emphasis': 'emphasis', + 'strong': 'strong', + 'literal': 'literal', + 'named-reference': 'named-reference', + 'anonymous-reference': 'anonymous-reference', + 'footnote-reference': 'footnote-reference', + 'citation-reference': 'citation-reference', + 'substitution-reference': 'substitution-reference', + 'target': 'target', + 'uri-reference': 'uri-reference', + 'uri': 'uri-reference', + 'url': 'uri-reference', + 'raw': 'raw',} +"""Mapping of English role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/eo.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/eo.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/eo.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/eo.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,114 @@ +# $Id: eo.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Marcelo Huerta San Martin +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Esperanto-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'atentu': 'attention', + u'zorgu': 'caution', + u'dangxero': 'danger', + u'dan\u011dero': 'danger', + u'eraro': 'error', + u'spuro': 'hint', + u'grava': 'important', + u'noto': 'note', + u'helpeto': 'tip', + u'averto': 'warning', + u'admono': 'admonition', + u'flankteksto': 'sidebar', + u'temo': 'topic', + u'linea-bloko': 'line-block', + u'analizota-literalo': 'parsed-literal', + u'rubriko': 'rubric', + u'epigrafo': 'epigraph', + u'elstarajxoj': 'highlights', + u'elstara\u0135oj': 'highlights', + u'ekstera-citajxo': 'pull-quote', + u'ekstera-cita\u0135o': 'pull-quote', + u'kombinajxo': 'compound', + u'kombina\u0135o': 'compound', + u'tekstingo': 'container', + u'enhavilo': 'container', + #'questions': 'questions', + #'qa': 'questions', + #'faq': 'questions', + u'tabelo': 'table', + u'tabelo-vdk': 'csv-table', # "valoroj disigitaj per komoj" + u'tabelo-csv': 'csv-table', + u'tabelo-lista': 'list-table', + u'meta': 'meta', + #'imagemap': 'imagemap', + u'bildo': 'image', + u'figuro': 'figure', + u'inkludi': 'include', + u'senanaliza': 'raw', + u'anstatauxi': 'replace', + u'anstata\u016di': 'replace', + u'unicode': 'unicode', + u'dato': 'date', + u'klaso': 'class', + u'rolo': 'role', + u'preterlasita-rolo': 'default-role', + u'titolo': 'title', + u'enhavo': 'contents', + u'seknum': 'sectnum', + u'sekcia-numerado': 'sectnum', + u'kapsekcio': 'header', + u'piedsekcio': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + u'celaj-notoj': 'target-notes', + u'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Esperanto name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + u'mallongigo': 'abbreviation', + u'mall': 'abbreviation', + u'komenclitero': 'acronym', + u'kl': 'acronym', + u'indekso': 'index', + u'i': 'index', + u'subskribo': 'subscript', + u'sub': 'subscript', + u'supraskribo': 'superscript', + u'sup': 'superscript', + u'titola-referenco': 'title-reference', + u'titolo': 'title-reference', + u't': 'title-reference', + u'pep-referenco': 'pep-reference', + u'pep': 'pep-reference', + u'rfc-referenco': 'rfc-reference', + u'rfc': 'rfc-reference', + u'emfazo': 'emphasis', + u'forta': 'strong', + u'litera': 'literal', + u'nomita-referenco': 'named-reference', + u'nenomita-referenco': 'anonymous-reference', + u'piednota-referenco': 'footnote-reference', + u'citajxo-referenco': 'citation-reference', + u'cita\u0135o-referenco': 'citation-reference', + u'anstatauxa-referenco': 'substitution-reference', + u'anstata\u016da-referenco': 'substitution-reference', + u'celo': 'target', + u'uri-referenco': 'uri-reference', + u'uri': 'uri-reference', + u'url': 'uri-reference', + u'senanaliza': 'raw', +} +"""Mapping of Esperanto role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/es.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/es.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/es.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/es.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# $Id: es.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Marcelo Huerta San Martín +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Spanish-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + u'atenci\u00f3n': 'attention', + u'atencion': 'attention', + u'precauci\u00f3n': 'caution', + u'precaucion': 'caution', + u'peligro': 'danger', + u'error': 'error', + u'sugerencia': 'hint', + u'importante': 'important', + u'nota': 'note', + u'consejo': 'tip', + u'advertencia': 'warning', + u'exhortacion': 'admonition', + u'exhortaci\u00f3n': 'admonition', + u'nota-al-margen': 'sidebar', + u'tema': 'topic', + u'bloque-de-lineas': 'line-block', + u'bloque-de-l\u00edneas': 'line-block', + u'literal-evaluado': 'parsed-literal', + u'firma': 'rubric', + u'ep\u00edgrafe': 'epigraph', + u'epigrafe': 'epigraph', + u'destacado': 'highlights', + u'cita-destacada': 'pull-quote', + u'combinacion': 'compound', + u'combinaci\u00f3n': 'compound', + u'contenedor': 'container', + #'questions': 'questions', + #'qa': 'questions', + #'faq': 'questions', + u'tabla': 'table', + u'tabla-vsc': 'csv-table', + u'tabla-csv': 'csv-table', + u'tabla-lista': 'list-table', + u'meta': 'meta', + #'imagemap': 'imagemap', + u'imagen': 'image', + u'figura': 'figure', + u'incluir': 'include', + u'sin-analisis': 'raw', + u'sin-an\u00e1lisis': 'raw', + u'reemplazar': 'replace', + u'unicode': 'unicode', + u'fecha': 'date', + u'clase': 'class', + u'rol': 'role', + u'rol-por-omision': 'default-role', + u'rol-por-omisi\u00f3n': 'default-role', + u'titulo': 'title', + u't\u00edtulo': 'title', + u'contenido': 'contents', + u'numseccion': 'sectnum', + u'numsecci\u00f3n': 'sectnum', + u'numeracion-seccion': 'sectnum', + u'numeraci\u00f3n-secci\u00f3n': 'sectnum', + u'notas-destino': 'target-notes', + u'cabecera': 'header', + u'pie': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + u'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Spanish name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + u'abreviatura': 'abbreviation', + u'ab': 'abbreviation', + u'acronimo': 'acronym', + u'acronimo': 'acronym', + u'ac': 'acronym', + u'indice': 'index', + u'i': 'index', + u'subindice': 'subscript', + u'sub\u00edndice': 'subscript', + u'superindice': 'superscript', + u'super\u00edndice': 'superscript', + u'referencia-titulo': 'title-reference', + u'titulo': 'title-reference', + u't': 'title-reference', + u'referencia-pep': 'pep-reference', + u'pep': 'pep-reference', + u'referencia-rfc': 'rfc-reference', + u'rfc': 'rfc-reference', + u'enfasis': 'emphasis', + u'\u00e9nfasis': 'emphasis', + u'destacado': 'strong', + u'literal': 'literal', # "literal" is also a word in Spanish :-) + u'referencia-con-nombre': 'named-reference', + u'referencia-anonima': 'anonymous-reference', + u'referencia-an\u00f3nima': 'anonymous-reference', + u'referencia-nota-al-pie': 'footnote-reference', + u'referencia-cita': 'citation-reference', + u'referencia-sustitucion': 'substitution-reference', + u'referencia-sustituci\u00f3n': 'substitution-reference', + u'destino': 'target', + u'referencia-uri': 'uri-reference', + u'uri': 'uri-reference', + u'url': 'uri-reference', + u'sin-analisis': 'raw', + u'sin-an\u00e1lisis': 'raw', +} +"""Mapping of Spanish role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/fi.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/fi.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/fi.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/fi.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,93 @@ +# $Id: fi.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Asko Soukka +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Finnish-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'huomio': u'attention', + u'varo': u'caution', + u'vaara': u'danger', + u'virhe': u'error', + u'vihje': u'hint', + u't\u00e4rke\u00e4\u00e4': u'important', + u'huomautus': u'note', + u'neuvo': u'tip', + u'varoitus': u'warning', + u'kehotus': u'admonition', + u'sivupalkki': u'sidebar', + u'aihe': u'topic', + u'rivi': u'line-block', + u'tasalevyinen': u'parsed-literal', + u'ohje': u'rubric', + u'epigraafi': u'epigraph', + u'kohokohdat': u'highlights', + u'lainaus': u'pull-quote', + u'taulukko': u'table', + u'csv-taulukko': u'csv-table', + u'list-table (translation required)': 'list-table', + u'compound (translation required)': 'compound', + u'container (translation required)': 'container', + #u'kysymykset': u'questions', + u'meta': u'meta', + #u'kuvakartta': u'imagemap', + u'kuva': u'image', + u'kaavio': u'figure', + u'sis\u00e4llyt\u00e4': u'include', + u'raaka': u'raw', + u'korvaa': u'replace', + u'unicode': u'unicode', + u'p\u00e4iv\u00e4ys': u'date', + u'luokka': u'class', + u'rooli': u'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + u'sis\u00e4llys': u'contents', + u'kappale': u'sectnum', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #u'alaviitteet': u'footnotes', + #u'viitaukset': u'citations', + u'target-notes (translation required)': u'target-notes'} +"""Finnish name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + u'lyhennys': u'abbreviation', + u'akronyymi': u'acronym', + u'kirjainsana': u'acronym', + u'hakemisto': u'index', + u'luettelo': u'index', + u'alaindeksi': u'subscript', + u'indeksi': u'subscript', + u'yl\u00e4indeksi': u'superscript', + u'title-reference (translation required)': u'title-reference', + u'title (translation required)': u'title-reference', + u'pep-reference (translation required)': u'pep-reference', + u'rfc-reference (translation required)': u'rfc-reference', + u'korostus': u'emphasis', + u'vahvistus': u'strong', + u'tasalevyinen': u'literal', + u'named-reference (translation required)': u'named-reference', + u'anonymous-reference (translation required)': u'anonymous-reference', + u'footnote-reference (translation required)': u'footnote-reference', + u'citation-reference (translation required)': u'citation-reference', + u'substitution-reference (translation required)': u'substitution-reference', + u'kohde': u'target', + u'uri-reference (translation required)': u'uri-reference', + u'raw (translation required)': 'raw',} +"""Mapping of Finnish role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/fr.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/fr.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/fr.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/fr.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ +# $Id: fr.py 4564 2006-05-21 20:44:42Z wiemann $ +# Authors: David Goodger ; William Dode +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +French-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + u'attention': 'attention', + u'pr\u00E9caution': 'caution', + u'danger': 'danger', + u'erreur': 'error', + u'conseil': 'hint', + u'important': 'important', + u'note': 'note', + u'astuce': 'tip', + u'avertissement': 'warning', + u'admonition': 'admonition', + u'encadr\u00E9': 'sidebar', + u'sujet': 'topic', + u'bloc-textuel': 'line-block', + u'bloc-interpr\u00E9t\u00E9': 'parsed-literal', + u'code-interpr\u00E9t\u00E9': 'parsed-literal', + u'intertitre': 'rubric', + u'exergue': 'epigraph', + u'\u00E9pigraphe': 'epigraph', + u'chapeau': 'highlights', + u'accroche': 'pull-quote', + u'compound (translation required)': 'compound', + u'container (translation required)': 'container', + #u'questions': 'questions', + #u'qr': 'questions', + #u'faq': 'questions', + u'tableau': 'table', + u'csv-table (translation required)': 'csv-table', + u'list-table (translation required)': 'list-table', + u'm\u00E9ta': 'meta', + #u'imagemap (translation required)': 'imagemap', + u'image': 'image', + u'figure': 'figure', + u'inclure': 'include', + u'brut': 'raw', + u'remplacer': 'replace', + u'remplace': 'replace', + u'unicode': 'unicode', + u'date': 'date', + u'classe': 'class', + u'role (translation required)': 'role', + u'default-role (translation required)': 'default-role', + u'titre (translation required)': 'title', + u'sommaire': 'contents', + u'table-des-mati\u00E8res': 'contents', + u'sectnum': 'sectnum', + u'section-num\u00E9rot\u00E9e': 'sectnum', + u'liens': 'target-notes', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #u'footnotes (translation required)': 'footnotes', + #u'citations (translation required)': 'citations', + } +"""French name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + u'abr\u00E9viation': 'abbreviation', + u'acronyme': 'acronym', + u'sigle': 'acronym', + u'index': 'index', + u'indice': 'subscript', + u'ind': 'subscript', + u'exposant': 'superscript', + u'exp': 'superscript', + u'titre-r\u00E9f\u00E9rence': 'title-reference', + u'titre': 'title-reference', + u'pep-r\u00E9f\u00E9rence': 'pep-reference', + u'rfc-r\u00E9f\u00E9rence': 'rfc-reference', + u'emphase': 'emphasis', + u'fort': 'strong', + u'litt\u00E9ral': 'literal', + u'nomm\u00E9e-r\u00E9f\u00E9rence': 'named-reference', + u'anonyme-r\u00E9f\u00E9rence': 'anonymous-reference', + u'note-r\u00E9f\u00E9rence': 'footnote-reference', + u'citation-r\u00E9f\u00E9rence': 'citation-reference', + u'substitution-r\u00E9f\u00E9rence': 'substitution-reference', + u'lien': 'target', + u'uri-r\u00E9f\u00E9rence': 'uri-reference', + u'brut': 'raw',} +"""Mapping of French role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/gl.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/gl.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/gl.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/gl.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# Author: David Goodger +# Contact: goodger@users.sourceforge.net +# Revision: $Revision: 4229 $ +# Date: $Date: 2005-12-23 00:46:16 +0100 (Fri, 23 Dec 2005) $ +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Galician-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'atenci\u00f3n': 'attention', + u'advertencia': 'caution', + u'perigo': 'danger', + u'erro': 'error', + u'pista': 'hint', + u'importante': 'important', + u'nota': 'note', + u'consello': 'tip', + u'aviso': 'warning', + u'admonici\u00f3n': 'admonition', + u'barra lateral': 'sidebar', + u't\u00f3pico': 'topic', + u'bloque-li\u00f1a': 'line-block', + u'literal-analizado': 'parsed-literal', + u'r\u00fabrica': 'rubric', + u'ep\u00edgrafe': 'epigraph', + u'realzados': 'highlights', + u'coller-citaci\u00f3n': 'pull-quote', + u'compor': 'compound', + u'recipiente': 'container', + #'questions': 'questions', + u't\u00e1boa': 'table', + u't\u00e1boa-csv': 'csv-table', + u't\u00e1boa-listaxe': 'list-table', + #'qa': 'questions', + #'faq': 'questions', + u'meta': 'meta', + #'imagemap': 'imagemap', + u'imaxe': 'image', + u'figura': 'figure', + u'inclu\u00edr': 'include', + u'cru': 'raw', + u'substitu\u00edr': 'replace', + u'unicode': 'unicode', + u'data': 'date', + u'clase': 'class', + u'regra': 'role', + u'regra-predeterminada': 'default-role', + u't\u00edtulo': 'title', + u'contido': 'contents', + u'seccnum': 'sectnum', + u'secci\u00f3n-numerar': 'sectnum', + u'cabeceira': 'header', + u'p\u00e9 de p\u00e1xina': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + u'notas-destino': 'target-notes', + u'texto restruturado-proba-directiva': 'restructuredtext-test-directive'} +"""Galician name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + u'abreviatura': 'abbreviation', + u'ab': 'abbreviation', + u'acr\u00f3nimo': 'acronym', + u'ac': 'acronym', + u'\u00edndice': 'index', + u'i': 'index', + u'sub\u00edndice': 'subscript', + u'sub': 'subscript', + u'super\u00edndice': 'superscript', + u'sup': 'superscript', + u'referencia t\u00edtulo': 'title-reference', + u't\u00edtulo': 'title-reference', + u't': 'title-reference', + u'referencia-pep': 'pep-reference', + u'pep': 'pep-reference', + u'referencia-rfc': 'rfc-reference', + u'rfc': 'rfc-reference', + u'\u00e9nfase': 'emphasis', + u'forte': 'strong', + u'literal': 'literal', + u'referencia-nome': 'named-reference', + u'referencia-an\u00f3nimo': 'anonymous-reference', + u'referencia-nota ao p\u00e9': 'footnote-reference', + u'referencia-citaci\u00f3n': 'citation-reference', + u'referencia-substituci\u00f3n': 'substitution-reference', + u'destino': 'target', + u'referencia-uri': 'uri-reference', + u'uri': 'uri-reference', + u'url': 'uri-reference', + u'cru': 'raw',} +"""Mapping of Galician role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/he.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/he.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/he.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/he.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,104 @@ +# Author: Meir Kriheli +# Id: $Id: he.py 4837 2006-12-26 09:59:41Z sfcben $ +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +English-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'\u05ea\u05e9\u05d5\u05de\u05ea \u05dc\u05d1': 'attention', + u'\u05d6\u05d4\u05d9\u05e8\u05d5\u05ea': 'caution', + u'\u05e1\u05db\u05e0\u05d4': 'danger', + u'\u05e9\u05d2\u05d9\u05d0\u05d4' : 'error', + u'\u05e8\u05de\u05d6': 'hint', + u'\u05d7\u05e9\u05d5\u05d1': 'important', + u'\u05d4\u05e2\u05e8\u05d4': 'note', + u'\u05d8\u05d9\u05e4': 'tip', + u'\u05d0\u05d6\u05d4\u05e8\u05d4': 'warning', + 'admonition': 'admonition', + 'sidebar': 'sidebar', + 'topic': 'topic', + 'line-block': 'line-block', + 'parsed-literal': 'parsed-literal', + 'rubric': 'rubric', + 'epigraph': 'epigraph', + 'highlights': 'highlights', + 'pull-quote': 'pull-quote', + 'compound': 'compound', + 'container': 'container', + #'questions': 'questions', + 'table': 'table', + 'csv-table': 'csv-table', + 'list-table': 'list-table', + #'qa': 'questions', + #'faq': 'questions', + 'meta': 'meta', + #'imagemap': 'imagemap', + u'\u05ea\u05de\u05d5\u05e0\u05d4': 'image', + 'figure': 'figure', + 'include': 'include', + 'raw': 'raw', + 'replace': 'replace', + 'unicode': 'unicode', + 'date': 'date', + u'\u05e1\u05d2\u05e0\u05d5\u05df': 'class', + 'role': 'role', + 'default-role': 'default-role', + 'title': 'title', + u'\u05ea\u05d5\u05db\u05df': 'contents', + 'sectnum': 'sectnum', + 'section-numbering': 'sectnum', + 'header': 'header', + 'footer': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + 'target-notes': 'target-notes', + 'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""English name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + 'abbreviation': 'abbreviation', + 'ab': 'abbreviation', + 'acronym': 'acronym', + 'ac': 'acronym', + 'index': 'index', + 'i': 'index', + u'\u05ea\u05d7\u05ea\u05d9': 'subscript', + 'sub': 'subscript', + u'\u05e2\u05d9\u05dc\u05d9': 'superscript', + 'sup': 'superscript', + 'title-reference': 'title-reference', + 'title': 'title-reference', + 't': 'title-reference', + 'pep-reference': 'pep-reference', + 'pep': 'pep-reference', + 'rfc-reference': 'rfc-reference', + 'rfc': 'rfc-reference', + 'emphasis': 'emphasis', + 'strong': 'strong', + 'literal': 'literal', + 'named-reference': 'named-reference', + 'anonymous-reference': 'anonymous-reference', + 'footnote-reference': 'footnote-reference', + 'citation-reference': 'citation-reference', + 'substitution-reference': 'substitution-reference', + 'target': 'target', + 'uri-reference': 'uri-reference', + 'uri': 'uri-reference', + 'url': 'uri-reference', + 'raw': 'raw',} +"""Mapping of English role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/__init__.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/__init__.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ +# $Id: __init__.py 5618 2008-07-28 08:37:32Z strank $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +# Internationalization details are documented in +# . + +""" +This package contains modules for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + +_languages = {} + +def get_language(language_code): + if language_code in _languages: + return _languages[language_code] + try: + module = __import__(language_code, globals(), locals()) + except ImportError: + return None + _languages[language_code] = module + return module diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/it.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/it.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/it.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/it.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,93 @@ +# $Id: it.py 4564 2006-05-21 20:44:42Z wiemann $ +# Authors: Nicola Larosa ; +# Lele Gaifax +# Copyright: This module has been placed in the public domain. + +# Beware: the italian translation of the reStructuredText documentation +# at http://docit.bice.dyndns.org/static/ReST, in particular +# http://docit.bice.dyndns.org/static/ReST/ref/rst/directives.html, needs +# to be synced with the content of this file. + +""" +Italian-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + 'attenzione': 'attention', + 'cautela': 'caution', + 'pericolo': 'danger', + 'errore': 'error', + 'suggerimento': 'hint', + 'importante': 'important', + 'nota': 'note', + 'consiglio': 'tip', + 'avvertenza': 'warning', + 'ammonizione': 'admonition', + 'riquadro': 'sidebar', + 'argomento': 'topic', + 'blocco-di-righe': 'line-block', + 'blocco-interpretato': 'parsed-literal', + 'rubrica': 'rubric', + 'epigrafe': 'epigraph', + 'punti-salienti': 'highlights', + 'estratto-evidenziato': 'pull-quote', + 'composito': 'compound', + u'container (translation required)': 'container', + #'questions': 'questions', + #'qa': 'questions', + #'faq': 'questions', + 'tabella': 'table', + 'tabella-csv': 'csv-table', + 'tabella-elenco': 'list-table', + 'meta': 'meta', + #'imagemap': 'imagemap', + 'immagine': 'image', + 'figura': 'figure', + 'includi': 'include', + 'grezzo': 'raw', + 'sostituisci': 'replace', + 'unicode': 'unicode', + 'data': 'date', + 'classe': 'class', + 'ruolo': 'role', + 'ruolo-predefinito': 'default-role', + 'titolo': 'title', + 'indice': 'contents', + 'contenuti': 'contents', + 'seznum': 'sectnum', + 'sezioni-autonumerate': 'sectnum', + 'annota-riferimenti-esterni': 'target-notes', + 'intestazione': 'header', + 'piede-pagina': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + 'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Italian name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + 'abbreviazione': 'abbreviation', + 'acronimo': 'acronym', + 'indice': 'index', + 'deponente': 'subscript', + 'esponente': 'superscript', + 'riferimento-titolo': 'title-reference', + 'riferimento-pep': 'pep-reference', + 'riferimento-rfc': 'rfc-reference', + 'enfasi': 'emphasis', + 'forte': 'strong', + 'letterale': 'literal', + 'riferimento-con-nome': 'named-reference', + 'riferimento-anonimo': 'anonymous-reference', + 'riferimento-nota': 'footnote-reference', + 'riferimento-citazione': 'citation-reference', + 'riferimento-sostituzione': 'substitution-reference', + 'destinazione': 'target', + 'riferimento-uri': 'uri-reference', + 'grezzo': 'raw',} +"""Mapping of Italian role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/ja.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/ja.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/ja.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/ja.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +# $Id: ja.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Japanese-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + +# Corrections to these translations are welcome! +# 間違いがあれば、どうぞ正しい翻訳を教えて下さい。 + +directives = { + # language-dependent: fixed + u'注目': 'attention', + u'注意': 'caution', + u'危険': 'danger', + u'エラー': 'error', + u'ヒント': 'hint', + u'重要': 'important', + u'備考': 'note', + u'通報': 'tip', + u'警告': 'warning', + u'戒告': 'admonition', + u'サイドバー': 'sidebar', + u'トピック': 'topic', + u'ラインブロック': 'line-block', + u'パーズドリテラル': 'parsed-literal', + u'ルブリック': 'rubric', + u'エピグラフ': 'epigraph', + u'題言': 'epigraph', + u'ハイライト': 'highlights', + u'見所': 'highlights', + u'プルクオート': 'pull-quote', + u'合成': 'compound', + u'コンテナー': 'container', + u'容器': 'container', + u'表': 'table', + u'csv表': 'csv-table', + u'リスト表': 'list-table', + #u'質問': 'questions', + #u'問答': 'questions', + #u'faq': 'questions', + u'メタ': 'meta', + #u'イメージマプ': 'imagemap', + u'イメージ': 'image', + u'画像': 'image', + u'フィグア': 'figure', + u'図版': 'figure', + u'インクルード': 'include', + u'含む': 'include', + u'組み込み': 'include', + u'生': 'raw', + u'原': 'raw', + u'換える': 'replace', + u'取り換える': 'replace', + u'掛け替える': 'replace', + u'ユニコード': 'unicode', + u'日付': 'date', + u'クラス': 'class', + u'ロール': 'role', + u'役': 'role', + u'ディフォルトロール': 'default-role', + u'既定役': 'default-role', + u'タイトル': 'title', + u'題': 'title', # 題名 件名 + u'目次': 'contents', + u'節数': 'sectnum', + u'ヘッダ': 'header', + u'フッタ': 'footer', + #u'脚注': 'footnotes', # 脚註? + #u'サイテーション': 'citations',   # 出典 引証 引用 + u'ターゲットノート': 'target-notes', # 的注 的脚注 + } +"""Japanese name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + u'略': 'abbreviation', + u'頭字語': 'acronym', + u'インデックス': 'index', + u'索引': 'index', + u'添字': 'subscript', + u'下付': 'subscript', + u'下': 'subscript', + u'上付': 'superscript', + u'上': 'superscript', + u'題参照': 'title-reference', + u'pep参照': 'pep-reference', + u'rfc参照': 'rfc-reference', + u'強調': 'emphasis', + u'強い': 'strong', + u'リテラル': 'literal', + u'整形済み': 'literal', + u'名付参照': 'named-reference', + u'無名参照': 'anonymous-reference', + u'脚注参照': 'footnote-reference', + u'出典参照': 'citation-reference', + u'代入参照': 'substitution-reference', + u'的': 'target', + u'uri参照': 'uri-reference', + u'uri': 'uri-reference', + u'url': 'uri-reference', + u'生': 'raw',} +"""Mapping of Japanese role names to canonical role names for interpreted +text.""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/nl.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/nl.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/nl.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/nl.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,108 @@ +# $Id: nl.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Martijn Pieters +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Dutch-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + 'attentie': 'attention', + 'let-op': 'caution', + 'gevaar': 'danger', + 'fout': 'error', + 'hint': 'hint', + 'belangrijk': 'important', + 'opmerking': 'note', + 'tip': 'tip', + 'waarschuwing': 'warning', + 'aanmaning': 'admonition', + 'katern': 'sidebar', + 'onderwerp': 'topic', + 'lijn-blok': 'line-block', + 'letterlijk-ontleed': 'parsed-literal', + 'rubriek': 'rubric', + 'opschrift': 'epigraph', + 'hoogtepunten': 'highlights', + 'pull-quote': 'pull-quote', # Dutch printers use the english term + 'samenstelling': 'compound', + 'verbinding': 'compound', + u'container (translation required)': 'container', + #'vragen': 'questions', + 'tabel': 'table', + 'csv-tabel': 'csv-table', + 'lijst-tabel': 'list-table', + #'veelgestelde-vragen': 'questions', + 'meta': 'meta', + #'imagemap': 'imagemap', + 'beeld': 'image', + 'figuur': 'figure', + 'opnemen': 'include', + 'onbewerkt': 'raw', + 'vervang': 'replace', + 'vervanging': 'replace', + 'unicode': 'unicode', + 'datum': 'date', + 'klasse': 'class', + 'rol': 'role', + u'default-role (translation required)': 'default-role', + 'title (translation required)': 'title', + 'inhoud': 'contents', + 'sectnum': 'sectnum', + 'sectie-nummering': 'sectnum', + 'hoofdstuk-nummering': 'sectnum', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #'voetnoten': 'footnotes', + #'citaten': 'citations', + 'verwijzing-voetnoten': 'target-notes', + 'restructuredtext-test-instructie': 'restructuredtext-test-directive'} +"""Dutch name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + 'afkorting': 'abbreviation', + # 'ab': 'abbreviation', + 'acroniem': 'acronym', + 'ac': 'acronym', + 'index': 'index', + 'i': 'index', + 'inferieur': 'subscript', + 'inf': 'subscript', + 'superieur': 'superscript', + 'sup': 'superscript', + 'titel-referentie': 'title-reference', + 'titel': 'title-reference', + 't': 'title-reference', + 'pep-referentie': 'pep-reference', + 'pep': 'pep-reference', + 'rfc-referentie': 'rfc-reference', + 'rfc': 'rfc-reference', + 'nadruk': 'emphasis', + 'extra': 'strong', + 'extra-nadruk': 'strong', + 'vet': 'strong', + 'letterlijk': 'literal', + 'benoemde-referentie': 'named-reference', + 'anonieme-referentie': 'anonymous-reference', + 'voetnoot-referentie': 'footnote-reference', + 'citaat-referentie': 'citation-reference', + 'substitie-reference': 'substitution-reference', + 'verwijzing': 'target', + 'uri-referentie': 'uri-reference', + 'uri': 'uri-reference', + 'url': 'uri-reference', + 'onbewerkt': 'raw',} +"""Mapping of Dutch role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/pl.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/pl.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/pl.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/pl.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,98 @@ +# $Id$ +# Author: Robert Wojciechowicz +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Polish-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'uwaga': 'attention', + u'ostro\u017cnie': 'caution', + u'niebezpiecze\u0144stwo': 'danger', + u'b\u0142\u0105d': 'error', + u'wskaz\u00f3wka': 'hint', + u'wa\u017cne': 'important', + u'przypis': 'note', + u'rada': 'tip', + u'ostrze\u017cenie': 'warning', + u'upomnienie': 'admonition', + u'ramka': 'sidebar', + u'temat': 'topic', + u'blok-linii': 'line-block', + u'sparsowany-litera\u0142': 'parsed-literal', + u'rubryka': 'rubric', + u'epigraf': 'epigraph', + u'highlights': 'highlights', # FIXME no polish equivalent? + u'pull-quote': 'pull-quote', # FIXME no polish equivalent? + u'z\u0142o\u017cony': 'compound', + u'kontener': 'container', + #'questions': 'questions', + u'tabela': 'table', + u'tabela-csv': 'csv-table', + u'tabela-listowa': 'list-table', + #'qa': 'questions', + #'faq': 'questions', + u'meta': 'meta', + #'imagemap': 'imagemap', + u'obraz': 'image', + u'rycina': 'figure', + u'do\u0142\u0105cz': 'include', + u'surowe': 'raw', + u'zast\u0105p': 'replace', + u'unikod': 'unicode', + u'data': 'date', + u'klasa': 'class', + u'rola': 'role', + u'rola-domy\u015blna': 'default-role', + u'tytu\u0142': 'title', + u'tre\u015b\u0107': 'contents', + u'sectnum': 'sectnum', + u'numeracja-sekcji': 'sectnum', + u'nag\u0142\u00f3wek': 'header', + u'stopka': 'footer', + #'footnotes': 'footnotes', + #'citations': 'citations', + u'target-notes': 'target-notes', # FIXME no polish equivalent? + u'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Polish name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + # language-dependent: fixed + u'skr\u00f3t': 'abbreviation', + u'akronim': 'acronym', + u'indeks': 'index', + u'indeks-dolny': 'subscript', + u'indeks-g\u00f3rny': 'superscript', + u'referencja-tytu\u0142': 'title-reference', + u'referencja-pep': 'pep-reference', + u'referencja-rfc': 'rfc-reference', + u'podkre\u015blenie': 'emphasis', + u'wyt\u0142uszczenie': 'strong', + u'dos\u0142ownie': 'literal', + u'referencja-nazwana': 'named-reference', + u'referencja-anonimowa': 'anonymous-reference', + u'referencja-przypis': 'footnote-reference', + u'referencja-cytat': 'citation-reference', + u'referencja-podstawienie': 'substitution-reference', + u'cel': 'target', + u'referencja-uri': 'uri-reference', + u'uri': 'uri-reference', + u'url': 'uri-reference', + u'surowe': 'raw',} +"""Mapping of Polish role names to canonical role names for interpreted text. +""" + + + diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/pt_br.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/pt_br.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/pt_br.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/pt_br.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,104 @@ +# $Id: pt_br.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Brazilian Portuguese-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'aten\u00E7\u00E3o': 'attention', + 'cuidado': 'caution', + 'perigo': 'danger', + 'erro': 'error', + u'sugest\u00E3o': 'hint', + 'importante': 'important', + 'nota': 'note', + 'dica': 'tip', + 'aviso': 'warning', + u'exorta\u00E7\u00E3o': 'admonition', + 'barra-lateral': 'sidebar', + u't\u00F3pico': 'topic', + 'bloco-de-linhas': 'line-block', + 'literal-interpretado': 'parsed-literal', + 'rubrica': 'rubric', + u'ep\u00EDgrafo': 'epigraph', + 'destaques': 'highlights', + u'cita\u00E7\u00E3o-destacada': 'pull-quote', + u'compound (translation required)': 'compound', + u'container (translation required)': 'container', + #'perguntas': 'questions', + #'qa': 'questions', + #'faq': 'questions', + u'table (translation required)': 'table', + u'csv-table (translation required)': 'csv-table', + u'list-table (translation required)': 'list-table', + 'meta': 'meta', + #'imagemap': 'imagemap', + 'imagem': 'image', + 'figura': 'figure', + u'inclus\u00E3o': 'include', + 'cru': 'raw', + u'substitui\u00E7\u00E3o': 'replace', + 'unicode': 'unicode', + 'data': 'date', + 'classe': 'class', + 'role (translation required)': 'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + u'\u00EDndice': 'contents', + 'numsec': 'sectnum', + u'numera\u00E7\u00E3o-de-se\u00E7\u00F5es': 'sectnum', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #u'notas-de-rorap\u00E9': 'footnotes', + #u'cita\u00E7\u00F5es': 'citations', + u'links-no-rodap\u00E9': 'target-notes', + 'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Brazilian Portuguese name to registered (in directives/__init__.py) +directive name mapping.""" + +roles = { + # language-dependent: fixed + u'abbrevia\u00E7\u00E3o': 'abbreviation', + 'ab': 'abbreviation', + u'acr\u00F4nimo': 'acronym', + 'ac': 'acronym', + u'\u00EDndice-remissivo': 'index', + 'i': 'index', + 'subscrito': 'subscript', + 'sub': 'subscript', + 'sobrescrito': 'superscript', + 'sob': 'superscript', + u'refer\u00EAncia-a-t\u00EDtulo': 'title-reference', + u't\u00EDtulo': 'title-reference', + 't': 'title-reference', + u'refer\u00EAncia-a-pep': 'pep-reference', + 'pep': 'pep-reference', + u'refer\u00EAncia-a-rfc': 'rfc-reference', + 'rfc': 'rfc-reference', + u'\u00EAnfase': 'emphasis', + 'forte': 'strong', + 'literal': 'literal', # translation required? + u'refer\u00EAncia-por-nome': 'named-reference', + u'refer\u00EAncia-an\u00F4nima': 'anonymous-reference', + u'refer\u00EAncia-a-nota-de-rodap\u00E9': 'footnote-reference', + u'refer\u00EAncia-a-cita\u00E7\u00E3o': 'citation-reference', + u'refer\u00EAncia-a-substitui\u00E7\u00E3o': 'substitution-reference', + 'alvo': 'target', + u'refer\u00EAncia-a-uri': 'uri-reference', + 'uri': 'uri-reference', + 'url': 'uri-reference', + 'cru': 'raw',} +"""Mapping of Brazilian Portuguese role names to canonical role names +for interpreted text.""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/ru.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/ru.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/ru.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/ru.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,103 @@ +# $Id: ru.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Roman Suzi +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Russian-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + +directives = { + u'\u0431\u043b\u043e\u043a-\u0441\u0442\u0440\u043e\u043a': u'line-block', + u'meta': u'meta', + u'\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0439-\u043b\u0438\u0442\u0435\u0440\u0430\u043b': + u'parsed-literal', + u'\u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u0430\u044f-\u0446\u0438\u0442\u0430\u0442\u0430': + u'pull-quote', + u'compound (translation required)': 'compound', + u'container (translation required)': 'container', + u'table (translation required)': 'table', + u'csv-table (translation required)': 'csv-table', + u'list-table (translation required)': 'list-table', + u'\u0441\u044b\u0440\u043e\u0439': u'raw', + u'\u0437\u0430\u043c\u0435\u043d\u0430': u'replace', + u'\u0442\u0435\u0441\u0442\u043e\u0432\u0430\u044f-\u0434\u0438\u0440\u0435\u043a\u0442\u0438\u0432\u0430-restructuredtext': + u'restructuredtext-test-directive', + u'\u0446\u0435\u043b\u0435\u0432\u044b\u0435-\u0441\u043d\u043e\u0441\u043a\u0438': + u'target-notes', + u'unicode': u'unicode', + u'\u0434\u0430\u0442\u0430': u'date', + u'\u0431\u043e\u043a\u043e\u0432\u0430\u044f-\u043f\u043e\u043b\u043e\u0441\u0430': + u'sidebar', + u'\u0432\u0430\u0436\u043d\u043e': u'important', + u'\u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c': u'include', + u'\u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435': u'attention', + u'\u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435': u'highlights', + u'\u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u0435': u'admonition', + u'\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435': + u'image', + u'\u043a\u043b\u0430\u0441\u0441': u'class', + u'role (translation required)': 'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + u'\u043d\u043e\u043c\u0435\u0440-\u0440\u0430\u0437\u0434\u0435\u043b\u0430': + u'sectnum', + u'\u043d\u0443\u043c\u0435\u0440\u0430\u0446\u0438\u044f-\u0440\u0430\u0437' + u'\u0434\u0435\u043b\u043e\u0432': u'sectnum', + u'\u043e\u043f\u0430\u0441\u043d\u043e': u'danger', + u'\u043e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u043e': u'caution', + u'\u043e\u0448\u0438\u0431\u043a\u0430': u'error', + u'\u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0430': u'tip', + u'\u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d' + u'\u0438\u0435': u'warning', + u'\u043f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435': u'note', + u'\u0440\u0438\u0441\u0443\u043d\u043e\u043a': u'figure', + u'\u0440\u0443\u0431\u0440\u0438\u043a\u0430': u'rubric', + u'\u0441\u043e\u0432\u0435\u0442': u'hint', + u'\u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435': u'contents', + u'\u0442\u0435\u043c\u0430': u'topic', + u'\u044d\u043f\u0438\u0433\u0440\u0430\u0444': u'epigraph', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer',} +"""Russian name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + u'\u0430\u043a\u0440\u043e\u043d\u0438\u043c': 'acronym', + u'\u0430\u043d\u043e\u043d\u0438\u043c\u043d\u0430\u044f-\u0441\u0441\u044b\u043b\u043a\u0430': + 'anonymous-reference', + u'\u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e': 'literal', + u'\u0432\u0435\u0440\u0445\u043d\u0438\u0439-\u0438\u043d\u0434\u0435\u043a\u0441': + 'superscript', + u'\u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435': 'emphasis', + u'\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u043d\u0430\u044f-\u0441\u0441\u044b\u043b\u043a\u0430': + 'named-reference', + u'\u0438\u043d\u0434\u0435\u043a\u0441': 'index', + u'\u043d\u0438\u0436\u043d\u0438\u0439-\u0438\u043d\u0434\u0435\u043a\u0441': + 'subscript', + u'\u0441\u0438\u043b\u044c\u043d\u043e\u0435-\u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435': + 'strong', + u'\u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435': + 'abbreviation', + u'\u0441\u0441\u044b\u043b\u043a\u0430-\u0437\u0430\u043c\u0435\u043d\u0430': + 'substitution-reference', + u'\u0441\u0441\u044b\u043b\u043a\u0430-\u043d\u0430-pep': 'pep-reference', + u'\u0441\u0441\u044b\u043b\u043a\u0430-\u043d\u0430-rfc': 'rfc-reference', + u'\u0441\u0441\u044b\u043b\u043a\u0430-\u043d\u0430-uri': 'uri-reference', + u'\u0441\u0441\u044b\u043b\u043a\u0430-\u043d\u0430-\u0437\u0430\u0433\u043b\u0430\u0432\u0438\u0435': + 'title-reference', + u'\u0441\u0441\u044b\u043b\u043a\u0430-\u043d\u0430-\u0441\u043d\u043e\u0441\u043a\u0443': + 'footnote-reference', + u'\u0446\u0438\u0442\u0430\u0442\u043d\u0430\u044f-\u0441\u0441\u044b\u043b\u043a\u0430': + 'citation-reference', + u'\u0446\u0435\u043b\u044c': 'target', + u'raw (translation required)': 'raw',} +"""Mapping of Russian role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/sk.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/sk.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/sk.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/sk.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,91 @@ +# $Id: sk.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Miroslav Vasko +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Slovak-language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + u'pozor': 'attention', + u'opatrne': 'caution', + u'nebezpe\xe8enstvo': 'danger', + u'chyba': 'error', + u'rada': 'hint', + u'd\xf4le\x9eit\xe9': 'important', + u'pozn\xe1mka': 'note', + u'tip (translation required)': 'tip', + u'varovanie': 'warning', + u'admonition (translation required)': 'admonition', + u'sidebar (translation required)': 'sidebar', + u't\xe9ma': 'topic', + u'blok-riadkov': 'line-block', + u'parsed-literal': 'parsed-literal', + u'rubric (translation required)': 'rubric', + u'epigraph (translation required)': 'epigraph', + u'highlights (translation required)': 'highlights', + u'pull-quote (translation required)': 'pull-quote', + u'compound (translation required)': 'compound', + u'container (translation required)': 'container', + #u'questions': 'questions', + #u'qa': 'questions', + #u'faq': 'questions', + u'table (translation required)': 'table', + u'csv-table (translation required)': 'csv-table', + u'list-table (translation required)': 'list-table', + u'meta': 'meta', + #u'imagemap': 'imagemap', + u'obr\xe1zok': 'image', + u'tvar': 'figure', + u'vlo\x9ei\x9d': 'include', + u'raw (translation required)': 'raw', + u'nahradi\x9d': 'replace', + u'unicode': 'unicode', + u'd\u00E1tum': 'date', + u'class (translation required)': 'class', + u'role (translation required)': 'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + u'obsah': 'contents', + u'\xe8as\x9d': 'sectnum', + u'\xe8as\x9d-\xe8\xedslovanie': 'sectnum', + u'cie\xbeov\xe9-pozn\xe1mky': 'target-notes', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #u'footnotes': 'footnotes', + #u'citations': 'citations', + } +"""Slovak name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + u'abbreviation (translation required)': 'abbreviation', + u'acronym (translation required)': 'acronym', + u'index (translation required)': 'index', + u'subscript (translation required)': 'subscript', + u'superscript (translation required)': 'superscript', + u'title-reference (translation required)': 'title-reference', + u'pep-reference (translation required)': 'pep-reference', + u'rfc-reference (translation required)': 'rfc-reference', + u'emphasis (translation required)': 'emphasis', + u'strong (translation required)': 'strong', + u'literal (translation required)': 'literal', + u'named-reference (translation required)': 'named-reference', + u'anonymous-reference (translation required)': 'anonymous-reference', + u'footnote-reference (translation required)': 'footnote-reference', + u'citation-reference (translation required)': 'citation-reference', + u'substitution-reference (translation required)': 'substitution-reference', + u'target (translation required)': 'target', + u'uri-reference (translation required)': 'uri-reference', + u'raw (translation required)': 'raw',} +"""Mapping of Slovak role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/sv.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/sv.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/sv.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/sv.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,90 @@ +# $Id: sv.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Adam Chodorowski +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Swedish language mappings for language-dependent features of reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + u'observera': 'attention', + u'caution (translation required)': 'caution', + u'fara': 'danger', + u'fel': 'error', + u'v\u00e4gledning': 'hint', + u'viktigt': 'important', + u'notera': 'note', + u'tips': 'tip', + u'varning': 'warning', + u'admonition (translation required)': 'admonition', + u'sidebar (translation required)': 'sidebar', + u'\u00e4mne': 'topic', + u'line-block (translation required)': 'line-block', + u'parsed-literal (translation required)': 'parsed-literal', + u'mellanrubrik': 'rubric', + u'epigraph (translation required)': 'epigraph', + u'highlights (translation required)': 'highlights', + u'pull-quote (translation required)': 'pull-quote', + u'compound (translation required)': 'compound', + u'container (translation required)': 'container', + # u'fr\u00e5gor': 'questions', + # NOTE: A bit long, but recommended by http://www.nada.kth.se/dataterm/: + # u'fr\u00e5gor-och-svar': 'questions', + # u'vanliga-fr\u00e5gor': 'questions', + u'table (translation required)': 'table', + u'csv-table (translation required)': 'csv-table', + u'list-table (translation required)': 'list-table', + u'meta': 'meta', + # u'bildkarta': 'imagemap', # FIXME: Translation might be too literal. + u'bild': 'image', + u'figur': 'figure', + u'inkludera': 'include', + u'r\u00e5': 'raw', # FIXME: Translation might be too literal. + u'ers\u00e4tt': 'replace', + u'unicode': 'unicode', + u'datum': 'date', + u'class (translation required)': 'class', + u'role (translation required)': 'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + u'inneh\u00e5ll': 'contents', + u'sektionsnumrering': 'sectnum', + u'target-notes (translation required)': 'target-notes', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + # u'fotnoter': 'footnotes', + # u'citeringar': 'citations', + } +"""Swedish name to registered (in directives/__init__.py) directive name +mapping.""" + +roles = { + u'abbreviation (translation required)': 'abbreviation', + u'acronym (translation required)': 'acronym', + u'index (translation required)': 'index', + u'subscript (translation required)': 'subscript', + u'superscript (translation required)': 'superscript', + u'title-reference (translation required)': 'title-reference', + u'pep-reference (translation required)': 'pep-reference', + u'rfc-reference (translation required)': 'rfc-reference', + u'emphasis (translation required)': 'emphasis', + u'strong (translation required)': 'strong', + u'literal (translation required)': 'literal', + u'named-reference (translation required)': 'named-reference', + u'anonymous-reference (translation required)': 'anonymous-reference', + u'footnote-reference (translation required)': 'footnote-reference', + u'citation-reference (translation required)': 'citation-reference', + u'substitution-reference (translation required)': 'substitution-reference', + u'target (translation required)': 'target', + u'uri-reference (translation required)': 'uri-reference', + u'r\u00e5': 'raw',} +"""Mapping of Swedish role names to canonical role names for interpreted text. +""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/zh_cn.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/zh_cn.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/zh_cn.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/zh_cn.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# $Id: zh_cn.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Panjunyong +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Simplified Chinese language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + u'注意': 'attention', + u'小心': 'caution', + u'危险': 'danger', + u'错误': 'error', + u'提示': 'hint', + u'重要': 'important', + u'注解': 'note', + u'技巧': 'tip', + u'警告': 'warning', + u'忠告': 'admonition', + u'侧框': 'sidebar', + u'主题': 'topic', + u'line-block (translation required)': 'line-block', + u'parsed-literal (translation required)': 'parsed-literal', + u'醒目': 'rubric', + u'铭文': 'epigraph', + u'要点': 'highlights', + u'pull-quote (translation required)': 'pull-quote', + u'复合': 'compound', + u'容器': 'container', + #u'questions (translation required)': 'questions', + u'表格': 'table', + u'csv表格': 'csv-table', + u'列表表格': 'list-table', + #u'qa (translation required)': 'questions', + #u'faq (translation required)': 'questions', + u'元数据': 'meta', + #u'imagemap (translation required)': 'imagemap', + u'图片': 'image', + u'图例': 'figure', + u'包含': 'include', + u'原文': 'raw', + u'代替': 'replace', + u'统一码': 'unicode', + u'日期': 'date', + u'类型': 'class', + u'角色': 'role', + u'默认角色': 'default-role', + u'标题': 'title', + u'目录': 'contents', + u'章节序号': 'sectnum', + u'题头': 'header', + u'页脚': 'footer', + #u'footnotes (translation required)': 'footnotes', + #u'citations (translation required)': 'citations', + u'target-notes (translation required)': 'target-notes', + u'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Simplified Chinese name to registered (in directives/__init__.py) +directive name mapping.""" + +roles = { + # language-dependent: fixed + u'缩写': 'abbreviation', + u'简称': 'acronym', + u'index (translation required)': 'index', + u'i (translation required)': 'index', + u'下标': 'subscript', + u'上标': 'superscript', + u'title-reference (translation required)': 'title-reference', + u'title (translation required)': 'title-reference', + u't (translation required)': 'title-reference', + u'pep-reference (translation required)': 'pep-reference', + u'pep (translation required)': 'pep-reference', + u'rfc-reference (translation required)': 'rfc-reference', + u'rfc (translation required)': 'rfc-reference', + u'强调': 'emphasis', + u'加粗': 'strong', + u'字面': 'literal', + u'named-reference (translation required)': 'named-reference', + u'anonymous-reference (translation required)': 'anonymous-reference', + u'footnote-reference (translation required)': 'footnote-reference', + u'citation-reference (translation required)': 'citation-reference', + u'substitution-reference (translation required)': 'substitution-reference', + u'target (translation required)': 'target', + u'uri-reference (translation required)': 'uri-reference', + u'uri (translation required)': 'uri-reference', + u'url (translation required)': 'uri-reference', + u'raw (translation required)': 'raw',} +"""Mapping of Simplified Chinese role names to canonical role names +for interpreted text.""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/languages/zh_tw.py zope3-3.5~bzr18/src/docutils/parsers/rst/languages/zh_tw.py --- zope3-3.4.0/src/docutils/parsers/rst/languages/zh_tw.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/languages/zh_tw.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +# $Id: zh_tw.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +# New language mappings are welcome. Before doing a new translation, please +# read . Two files must be +# translated for each language: one in docutils/languages, the other in +# docutils/parsers/rst/languages. + +""" +Traditional Chinese language mappings for language-dependent features of +reStructuredText. +""" + +__docformat__ = 'reStructuredText' + + +directives = { + # language-dependent: fixed + 'attention (translation required)': 'attention', + 'caution (translation required)': 'caution', + 'danger (translation required)': 'danger', + 'error (translation required)': 'error', + 'hint (translation required)': 'hint', + 'important (translation required)': 'important', + 'note (translation required)': 'note', + 'tip (translation required)': 'tip', + 'warning (translation required)': 'warning', + 'admonition (translation required)': 'admonition', + 'sidebar (translation required)': 'sidebar', + 'topic (translation required)': 'topic', + 'line-block (translation required)': 'line-block', + 'parsed-literal (translation required)': 'parsed-literal', + 'rubric (translation required)': 'rubric', + 'epigraph (translation required)': 'epigraph', + 'highlights (translation required)': 'highlights', + 'pull-quote (translation required)': 'pull-quote', + 'compound (translation required)': 'compound', + u'container (translation required)': 'container', + #'questions (translation required)': 'questions', + 'table (translation required)': 'table', + 'csv-table (translation required)': 'csv-table', + 'list-table (translation required)': 'list-table', + #'qa (translation required)': 'questions', + #'faq (translation required)': 'questions', + 'meta (translation required)': 'meta', + #'imagemap (translation required)': 'imagemap', + 'image (translation required)': 'image', + 'figure (translation required)': 'figure', + 'include (translation required)': 'include', + 'raw (translation required)': 'raw', + 'replace (translation required)': 'replace', + 'unicode (translation required)': 'unicode', + u'日期': 'date', + 'class (translation required)': 'class', + 'role (translation required)': 'role', + u'default-role (translation required)': 'default-role', + u'title (translation required)': 'title', + 'contents (translation required)': 'contents', + 'sectnum (translation required)': 'sectnum', + 'section-numbering (translation required)': 'sectnum', + u'header (translation required)': 'header', + u'footer (translation required)': 'footer', + #'footnotes (translation required)': 'footnotes', + #'citations (translation required)': 'citations', + 'target-notes (translation required)': 'target-notes', + 'restructuredtext-test-directive': 'restructuredtext-test-directive'} +"""Traditional Chinese name to registered (in directives/__init__.py) +directive name mapping.""" + +roles = { + # language-dependent: fixed + 'abbreviation (translation required)': 'abbreviation', + 'ab (translation required)': 'abbreviation', + 'acronym (translation required)': 'acronym', + 'ac (translation required)': 'acronym', + 'index (translation required)': 'index', + 'i (translation required)': 'index', + 'subscript (translation required)': 'subscript', + 'sub (translation required)': 'subscript', + 'superscript (translation required)': 'superscript', + 'sup (translation required)': 'superscript', + 'title-reference (translation required)': 'title-reference', + 'title (translation required)': 'title-reference', + 't (translation required)': 'title-reference', + 'pep-reference (translation required)': 'pep-reference', + 'pep (translation required)': 'pep-reference', + 'rfc-reference (translation required)': 'rfc-reference', + 'rfc (translation required)': 'rfc-reference', + 'emphasis (translation required)': 'emphasis', + 'strong (translation required)': 'strong', + 'literal (translation required)': 'literal', + 'named-reference (translation required)': 'named-reference', + 'anonymous-reference (translation required)': 'anonymous-reference', + 'footnote-reference (translation required)': 'footnote-reference', + 'citation-reference (translation required)': 'citation-reference', + 'substitution-reference (translation required)': 'substitution-reference', + 'target (translation required)': 'target', + 'uri-reference (translation required)': 'uri-reference', + 'uri (translation required)': 'uri-reference', + 'url (translation required)': 'uri-reference', + 'raw (translation required)': 'raw',} +"""Mapping of Traditional Chinese role names to canonical role names for +interpreted text.""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/roles.py zope3-3.5~bzr18/src/docutils/parsers/rst/roles.py --- zope3-3.4.0/src/docutils/parsers/rst/roles.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/roles.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,350 @@ +# $Id: roles.py 6121 2009-09-10 12:05:04Z milde $ +# Author: Edward Loper +# Copyright: This module has been placed in the public domain. + +""" +This module defines standard interpreted text role functions, a registry for +interpreted text roles, and an API for adding to and retrieving from the +registry. + +The interface for interpreted role functions is as follows:: + + def role_fn(name, rawtext, text, lineno, inliner, + options={}, content=[]): + code... + + # Set function attributes for customization: + role_fn.options = ... + role_fn.content = ... + +Parameters: + +- ``name`` is the local name of the interpreted text role, the role name + actually used in the document. + +- ``rawtext`` is a string containing the entire interpreted text construct. + Return it as a ``problematic`` node linked to a system message if there is a + problem. + +- ``text`` is the interpreted text content, with backslash escapes converted + to nulls (``\x00``). + +- ``lineno`` is the line number where the interpreted text beings. + +- ``inliner`` is the Inliner object that called the role function. + It defines the following useful attributes: ``reporter``, + ``problematic``, ``memo``, ``parent``, ``document``. + +- ``options``: A dictionary of directive options for customization, to be + interpreted by the role function. Used for additional attributes for the + generated elements and other functionality. + +- ``content``: A list of strings, the directive content for customization + ("role" directive). To be interpreted by the role function. + +Function attributes for customization, interpreted by the "role" directive: + +- ``options``: A dictionary, mapping known option names to conversion + functions such as `int` or `float`. ``None`` or an empty dict implies no + options to parse. Several directive option conversion functions are defined + in the `directives` module. + + All role functions implicitly support the "class" option, unless disabled + with an explicit ``{'class': None}``. + +- ``content``: A boolean; true if content is allowed. Client code must handle + the case where content is required but not supplied (an empty content list + will be supplied). + +Note that unlike directives, the "arguments" function attribute is not +supported for role customization. Directive arguments are handled by the +"role" directive itself. + +Interpreted role functions return a tuple of two values: + +- A list of nodes which will be inserted into the document tree at the + point where the interpreted role was encountered (can be an empty + list). + +- A list of system messages, which will be inserted into the document tree + immediately after the end of the current inline block (can also be empty). +""" + +__docformat__ = 'reStructuredText' + +from docutils import nodes, utils +from docutils.parsers.rst import directives +from docutils.parsers.rst.languages import en as _fallback_language_module + +DEFAULT_INTERPRETED_ROLE = 'title-reference' +""" +The canonical name of the default interpreted role. This role is used +when no role is specified for a piece of interpreted text. +""" + +_role_registry = {} +"""Mapping of canonical role names to role functions. Language-dependent role +names are defined in the ``language`` subpackage.""" + +_roles = {} +"""Mapping of local or language-dependent interpreted text role names to role +functions.""" + +def role(role_name, language_module, lineno, reporter): + """ + Locate and return a role function from its language-dependent name, along + with a list of system messages. If the role is not found in the current + language, check English. Return a 2-tuple: role function (``None`` if the + named role cannot be found) and a list of system messages. + """ + normname = role_name.lower() + messages = [] + msg_text = [] + + if normname in _roles: + return _roles[normname], messages + + if role_name: + canonicalname = None + try: + canonicalname = language_module.roles[normname] + except AttributeError, error: + msg_text.append('Problem retrieving role entry from language ' + 'module %r: %s.' % (language_module, error)) + except KeyError: + msg_text.append('No role entry for "%s" in module "%s".' + % (role_name, language_module.__name__)) + else: + canonicalname = DEFAULT_INTERPRETED_ROLE + + # If we didn't find it, try English as a fallback. + if not canonicalname: + try: + canonicalname = _fallback_language_module.roles[normname] + msg_text.append('Using English fallback for role "%s".' + % role_name) + except KeyError: + msg_text.append('Trying "%s" as canonical role name.' + % role_name) + # The canonical name should be an English name, but just in case: + canonicalname = normname + + # Collect any messages that we generated. + if msg_text: + message = reporter.info('\n'.join(msg_text), line=lineno) + messages.append(message) + + # Look the role up in the registry, and return it. + if canonicalname in _role_registry: + role_fn = _role_registry[canonicalname] + register_local_role(normname, role_fn) + return role_fn, messages + else: + return None, messages # Error message will be generated by caller. + +def register_canonical_role(name, role_fn): + """ + Register an interpreted text role by its canonical name. + + :Parameters: + - `name`: The canonical name of the interpreted role. + - `role_fn`: The role function. See the module docstring. + """ + set_implicit_options(role_fn) + _role_registry[name] = role_fn + +def register_local_role(name, role_fn): + """ + Register an interpreted text role by its local or language-dependent name. + + :Parameters: + - `name`: The local or language-dependent name of the interpreted role. + - `role_fn`: The role function. See the module docstring. + """ + set_implicit_options(role_fn) + _roles[name] = role_fn + +def set_implicit_options(role_fn): + """ + Add customization options to role functions, unless explicitly set or + disabled. + """ + if not hasattr(role_fn, 'options') or role_fn.options is None: + role_fn.options = {'class': directives.class_option} + elif 'class' not in role_fn.options: + role_fn.options['class'] = directives.class_option + +def register_generic_role(canonical_name, node_class): + """For roles which simply wrap a given `node_class` around the text.""" + role = GenericRole(canonical_name, node_class) + register_canonical_role(canonical_name, role) + + +class GenericRole: + + """ + Generic interpreted text role, where the interpreted text is simply + wrapped with the provided node class. + """ + + def __init__(self, role_name, node_class): + self.name = role_name + self.node_class = node_class + + def __call__(self, role, rawtext, text, lineno, inliner, + options={}, content=[]): + set_classes(options) + return [self.node_class(rawtext, utils.unescape(text), **options)], [] + + +class CustomRole: + + """ + Wrapper for custom interpreted text roles. + """ + + def __init__(self, role_name, base_role, options={}, content=[]): + self.name = role_name + self.base_role = base_role + self.options = None + if hasattr(base_role, 'options'): + self.options = base_role.options + self.content = None + if hasattr(base_role, 'content'): + self.content = base_role.content + self.supplied_options = options + self.supplied_content = content + + def __call__(self, role, rawtext, text, lineno, inliner, + options={}, content=[]): + opts = self.supplied_options.copy() + opts.update(options) + cont = list(self.supplied_content) + if cont and content: + cont += '\n' + cont.extend(content) + return self.base_role(role, rawtext, text, lineno, inliner, + options=opts, content=cont) + + +def generic_custom_role(role, rawtext, text, lineno, inliner, + options={}, content=[]): + """""" + # Once nested inline markup is implemented, this and other methods should + # recursively call inliner.nested_parse(). + set_classes(options) + return [nodes.inline(rawtext, utils.unescape(text), **options)], [] + +generic_custom_role.options = {'class': directives.class_option} + + +###################################################################### +# Define and register the standard roles: +###################################################################### + +register_generic_role('abbreviation', nodes.abbreviation) +register_generic_role('acronym', nodes.acronym) +register_generic_role('emphasis', nodes.emphasis) +register_generic_role('literal', nodes.literal) +register_generic_role('strong', nodes.strong) +register_generic_role('subscript', nodes.subscript) +register_generic_role('superscript', nodes.superscript) +register_generic_role('title-reference', nodes.title_reference) + +def pep_reference_role(role, rawtext, text, lineno, inliner, + options={}, content=[]): + try: + pepnum = int(text) + if pepnum < 0 or pepnum > 9999: + raise ValueError + except ValueError: + msg = inliner.reporter.error( + 'PEP number must be a number from 0 to 9999; "%s" is invalid.' + % text, line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + # Base URL mainly used by inliner.pep_reference; so this is correct: + ref = (inliner.document.settings.pep_base_url + + inliner.document.settings.pep_file_url_template % pepnum) + set_classes(options) + return [nodes.reference(rawtext, 'PEP ' + utils.unescape(text), refuri=ref, + **options)], [] + +register_canonical_role('pep-reference', pep_reference_role) + +def rfc_reference_role(role, rawtext, text, lineno, inliner, + options={}, content=[]): + try: + rfcnum = int(text) + if rfcnum <= 0: + raise ValueError + except ValueError: + msg = inliner.reporter.error( + 'RFC number must be a number greater than or equal to 1; ' + '"%s" is invalid.' % text, line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + # Base URL mainly used by inliner.rfc_reference, so this is correct: + ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum + set_classes(options) + node = nodes.reference(rawtext, 'RFC ' + utils.unescape(text), refuri=ref, + **options) + return [node], [] + +register_canonical_role('rfc-reference', rfc_reference_role) + +def raw_role(role, rawtext, text, lineno, inliner, options={}, content=[]): + if not inliner.document.settings.raw_enabled: + msg = inliner.reporter.warning('raw (and derived) roles disabled') + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + if 'format' not in options: + msg = inliner.reporter.error( + 'No format (Writer name) is associated with this role: "%s".\n' + 'The "raw" role cannot be used directly.\n' + 'Instead, use the "role" directive to create a new role with ' + 'an associated format.' % role, line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + set_classes(options) + node = nodes.raw(rawtext, utils.unescape(text, 1), **options) + return [node], [] + +raw_role.options = {'format': directives.unchanged} + +register_canonical_role('raw', raw_role) + + +###################################################################### +# Register roles that are currently unimplemented. +###################################################################### + +def unimplemented_role(role, rawtext, text, lineno, inliner, attributes={}): + msg = inliner.reporter.error( + 'Interpreted text role "%s" not implemented.' % role, line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + +register_canonical_role('index', unimplemented_role) +register_canonical_role('named-reference', unimplemented_role) +register_canonical_role('anonymous-reference', unimplemented_role) +register_canonical_role('uri-reference', unimplemented_role) +register_canonical_role('footnote-reference', unimplemented_role) +register_canonical_role('citation-reference', unimplemented_role) +register_canonical_role('substitution-reference', unimplemented_role) +register_canonical_role('target', unimplemented_role) + +# This should remain unimplemented, for testing purposes: +register_canonical_role('restructuredtext-unimplemented-role', + unimplemented_role) + + +def set_classes(options): + """ + Auxiliary function to set options['classes'] and delete + options['class']. + """ + if 'class' in options: + assert 'classes' not in options + options['classes'] = options['class'] + del options['class'] diff -Nru zope3-3.4.0/src/docutils/parsers/rst/states.py zope3-3.5~bzr18/src/docutils/parsers/rst/states.py --- zope3-3.4.0/src/docutils/parsers/rst/states.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/states.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,3008 @@ +# $Id: states.py 6141 2009-09-25 18:50:30Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This is the ``docutils.parsers.restructuredtext.states`` module, the core of +the reStructuredText parser. It defines the following: + +:Classes: + - `RSTStateMachine`: reStructuredText parser's entry point. + - `NestedStateMachine`: recursive StateMachine. + - `RSTState`: reStructuredText State superclass. + - `Inliner`: For parsing inline markup. + - `Body`: Generic classifier of the first line of a block. + - `SpecializedBody`: Superclass for compound element members. + - `BulletList`: Second and subsequent bullet_list list_items + - `DefinitionList`: Second+ definition_list_items. + - `EnumeratedList`: Second+ enumerated_list list_items. + - `FieldList`: Second+ fields. + - `OptionList`: Second+ option_list_items. + - `RFC2822List`: Second+ RFC2822-style fields. + - `ExtensionOptions`: Parses directive option fields. + - `Explicit`: Second+ explicit markup constructs. + - `SubstitutionDef`: For embedded directives in substitution definitions. + - `Text`: Classifier of second line of a text block. + - `SpecializedText`: Superclass for continuation lines of Text-variants. + - `Definition`: Second line of potential definition_list_item. + - `Line`: Second line of overlined section title or transition marker. + - `Struct`: An auxiliary collection class. + +:Exception classes: + - `MarkupError` + - `ParserError` + - `MarkupMismatch` + +:Functions: + - `escape2null()`: Return a string, escape-backslashes converted to nulls. + - `unescape()`: Return a string, nulls removed or restored to backslashes. + +:Attributes: + - `state_classes`: set of State classes used with `RSTStateMachine`. + +Parser Overview +=============== + +The reStructuredText parser is implemented as a recursive state machine, +examining its input one line at a time. To understand how the parser works, +please first become familiar with the `docutils.statemachine` module. In the +description below, references are made to classes defined in this module; +please see the individual classes for details. + +Parsing proceeds as follows: + +1. The state machine examines each line of input, checking each of the + transition patterns of the state `Body`, in order, looking for a match. + The implicit transitions (blank lines and indentation) are checked before + any others. The 'text' transition is a catch-all (matches anything). + +2. The method associated with the matched transition pattern is called. + + A. Some transition methods are self-contained, appending elements to the + document tree (`Body.doctest` parses a doctest block). The parser's + current line index is advanced to the end of the element, and parsing + continues with step 1. + + B. Other transition methods trigger the creation of a nested state machine, + whose job is to parse a compound construct ('indent' does a block quote, + 'bullet' does a bullet list, 'overline' does a section [first checking + for a valid section header], etc.). + + - In the case of lists and explicit markup, a one-off state machine is + created and run to parse contents of the first item. + + - A new state machine is created and its initial state is set to the + appropriate specialized state (`BulletList` in the case of the + 'bullet' transition; see `SpecializedBody` for more detail). This + state machine is run to parse the compound element (or series of + explicit markup elements), and returns as soon as a non-member element + is encountered. For example, the `BulletList` state machine ends as + soon as it encounters an element which is not a list item of that + bullet list. The optional omission of inter-element blank lines is + enabled by this nested state machine. + + - The current line index is advanced to the end of the elements parsed, + and parsing continues with step 1. + + C. The result of the 'text' transition depends on the next line of text. + The current state is changed to `Text`, under which the second line is + examined. If the second line is: + + - Indented: The element is a definition list item, and parsing proceeds + similarly to step 2.B, using the `DefinitionList` state. + + - A line of uniform punctuation characters: The element is a section + header; again, parsing proceeds as in step 2.B, and `Body` is still + used. + + - Anything else: The element is a paragraph, which is examined for + inline markup and appended to the parent element. Processing + continues with step 1. +""" + +__docformat__ = 'reStructuredText' + + +import sys +import re +import roman +from types import FunctionType, MethodType +from docutils import nodes, statemachine, utils, urischemes +from docutils import ApplicationError, DataError +from docutils.statemachine import StateMachineWS, StateWS +from docutils.nodes import fully_normalize_name as normalize_name +from docutils.nodes import whitespace_normalize_name +from docutils.utils import escape2null, unescape, column_width +import docutils.parsers.rst +from docutils.parsers.rst import directives, languages, tableparser, roles +from docutils.parsers.rst.languages import en as _fallback_language_module + + +class MarkupError(DataError): pass +class UnknownInterpretedRoleError(DataError): pass +class InterpretedRoleNotImplementedError(DataError): pass +class ParserError(ApplicationError): pass +class MarkupMismatch(Exception): pass + + +class Struct: + + """Stores data attributes for dotted-attribute access.""" + + def __init__(self, **keywordargs): + self.__dict__.update(keywordargs) + + +class RSTStateMachine(StateMachineWS): + + """ + reStructuredText's master StateMachine. + + The entry point to reStructuredText parsing is the `run()` method. + """ + + def run(self, input_lines, document, input_offset=0, match_titles=1, + inliner=None): + """ + Parse `input_lines` and modify the `document` node in place. + + Extend `StateMachineWS.run()`: set up parse-global data and + run the StateMachine. + """ + self.language = languages.get_language( + document.settings.language_code) + self.match_titles = match_titles + if inliner is None: + inliner = Inliner() + inliner.init_customizations(document.settings) + self.memo = Struct(document=document, + reporter=document.reporter, + language=self.language, + title_styles=[], + section_level=0, + section_bubble_up_kludge=0, + inliner=inliner) + self.document = document + self.attach_observer(document.note_source) + self.reporter = self.memo.reporter + self.node = document + results = StateMachineWS.run(self, input_lines, input_offset, + input_source=document['source']) + assert results == [], 'RSTStateMachine.run() results should be empty!' + self.node = self.memo = None # remove unneeded references + + +class NestedStateMachine(StateMachineWS): + + """ + StateMachine run from within other StateMachine runs, to parse nested + document structures. + """ + + def run(self, input_lines, input_offset, memo, node, match_titles=1): + """ + Parse `input_lines` and populate a `docutils.nodes.document` instance. + + Extend `StateMachineWS.run()`: set up document-wide data. + """ + self.match_titles = match_titles + self.memo = memo + self.document = memo.document + self.attach_observer(self.document.note_source) + self.reporter = memo.reporter + self.language = memo.language + self.node = node + results = StateMachineWS.run(self, input_lines, input_offset) + assert results == [], ('NestedStateMachine.run() results should be ' + 'empty!') + return results + + +class RSTState(StateWS): + + """ + reStructuredText State superclass. + + Contains methods used by all State subclasses. + """ + + nested_sm = NestedStateMachine + nested_sm_cache = [] + + def __init__(self, state_machine, debug=0): + self.nested_sm_kwargs = {'state_classes': state_classes, + 'initial_state': 'Body'} + StateWS.__init__(self, state_machine, debug) + + def runtime_init(self): + StateWS.runtime_init(self) + memo = self.state_machine.memo + self.memo = memo + self.reporter = memo.reporter + self.inliner = memo.inliner + self.document = memo.document + self.parent = self.state_machine.node + + def goto_line(self, abs_line_offset): + """ + Jump to input line `abs_line_offset`, ignoring jumps past the end. + """ + try: + self.state_machine.goto_line(abs_line_offset) + except EOFError: + pass + + def no_match(self, context, transitions): + """ + Override `StateWS.no_match` to generate a system message. + + This code should never be run. + """ + self.reporter.severe( + 'Internal error: no transition pattern match. State: "%s"; ' + 'transitions: %s; context: %s; current line: %r.' + % (self.__class__.__name__, transitions, context, + self.state_machine.line), + line=self.state_machine.abs_line_number()) + return context, None, [] + + def bof(self, context): + """Called at beginning of file.""" + return [], [] + + def nested_parse(self, block, input_offset, node, match_titles=0, + state_machine_class=None, state_machine_kwargs=None): + """ + Create a new StateMachine rooted at `node` and run it over the input + `block`. + """ + use_default = 0 + if state_machine_class is None: + state_machine_class = self.nested_sm + use_default += 1 + if state_machine_kwargs is None: + state_machine_kwargs = self.nested_sm_kwargs + use_default += 1 + block_length = len(block) + + state_machine = None + if use_default == 2: + try: + state_machine = self.nested_sm_cache.pop() + except IndexError: + pass + if not state_machine: + state_machine = state_machine_class(debug=self.debug, + **state_machine_kwargs) + state_machine.run(block, input_offset, memo=self.memo, + node=node, match_titles=match_titles) + if use_default == 2: + self.nested_sm_cache.append(state_machine) + else: + state_machine.unlink() + new_offset = state_machine.abs_line_offset() + # No `block.parent` implies disconnected -- lines aren't in sync: + if block.parent and (len(block) - block_length) != 0: + # Adjustment for block if modified in nested parse: + self.state_machine.next_line(len(block) - block_length) + return new_offset + + def nested_list_parse(self, block, input_offset, node, initial_state, + blank_finish, + blank_finish_state=None, + extra_settings={}, + match_titles=0, + state_machine_class=None, + state_machine_kwargs=None): + """ + Create a new StateMachine rooted at `node` and run it over the input + `block`. Also keep track of optional intermediate blank lines and the + required final one. + """ + if state_machine_class is None: + state_machine_class = self.nested_sm + if state_machine_kwargs is None: + state_machine_kwargs = self.nested_sm_kwargs.copy() + state_machine_kwargs['initial_state'] = initial_state + state_machine = state_machine_class(debug=self.debug, + **state_machine_kwargs) + if blank_finish_state is None: + blank_finish_state = initial_state + state_machine.states[blank_finish_state].blank_finish = blank_finish + for key, value in extra_settings.items(): + setattr(state_machine.states[initial_state], key, value) + state_machine.run(block, input_offset, memo=self.memo, + node=node, match_titles=match_titles) + blank_finish = state_machine.states[blank_finish_state].blank_finish + state_machine.unlink() + return state_machine.abs_line_offset(), blank_finish + + def section(self, title, source, style, lineno, messages): + """Check for a valid subsection and create one if it checks out.""" + if self.check_subsection(source, style, lineno): + self.new_subsection(title, lineno, messages) + + def check_subsection(self, source, style, lineno): + """ + Check for a valid subsection header. Return 1 (true) or None (false). + + When a new section is reached that isn't a subsection of the current + section, back up the line count (use ``previous_line(-x)``), then + ``raise EOFError``. The current StateMachine will finish, then the + calling StateMachine can re-examine the title. This will work its way + back up the calling chain until the correct section level isreached. + + @@@ Alternative: Evaluate the title, store the title info & level, and + back up the chain until that level is reached. Store in memo? Or + return in results? + + :Exception: `EOFError` when a sibling or supersection encountered. + """ + memo = self.memo + title_styles = memo.title_styles + mylevel = memo.section_level + try: # check for existing title style + level = title_styles.index(style) + 1 + except ValueError: # new title style + if len(title_styles) == memo.section_level: # new subsection + title_styles.append(style) + return 1 + else: # not at lowest level + self.parent += self.title_inconsistent(source, lineno) + return None + if level <= mylevel: # sibling or supersection + memo.section_level = level # bubble up to parent section + if len(style) == 2: + memo.section_bubble_up_kludge = 1 + # back up 2 lines for underline title, 3 for overline title + self.state_machine.previous_line(len(style) + 1) + raise EOFError # let parent section re-evaluate + if level == mylevel + 1: # immediate subsection + return 1 + else: # invalid subsection + self.parent += self.title_inconsistent(source, lineno) + return None + + def title_inconsistent(self, sourcetext, lineno): + error = self.reporter.severe( + 'Title level inconsistent:', nodes.literal_block('', sourcetext), + line=lineno) + return error + + def new_subsection(self, title, lineno, messages): + """Append new subsection to document tree. On return, check level.""" + memo = self.memo + mylevel = memo.section_level + memo.section_level += 1 + section_node = nodes.section() + self.parent += section_node + textnodes, title_messages = self.inline_text(title, lineno) + titlenode = nodes.title(title, '', *textnodes) + name = normalize_name(titlenode.astext()) + section_node['names'].append(name) + section_node += titlenode + section_node += messages + section_node += title_messages + self.document.note_implicit_target(section_node, section_node) + offset = self.state_machine.line_offset + 1 + absoffset = self.state_machine.abs_line_offset() + 1 + newabsoffset = self.nested_parse( + self.state_machine.input_lines[offset:], input_offset=absoffset, + node=section_node, match_titles=1) + self.goto_line(newabsoffset) + if memo.section_level <= mylevel: # can't handle next section? + raise EOFError # bubble up to supersection + # reset section_level; next pass will detect it properly + memo.section_level = mylevel + + def paragraph(self, lines, lineno): + """ + Return a list (paragraph & messages) & a boolean: literal_block next? + """ + data = '\n'.join(lines).rstrip() + if re.search(r'(?%(or_group)s)%(suffix)s' % locals() + if compile: + return re.compile(regexp, re.UNICODE) + else: + return regexp + + +class Inliner: + + """ + Parse inline markup; call the `parse()` method. + """ + + def __init__(self): + self.implicit_dispatch = [(self.patterns.uri, self.standalone_uri),] + """List of (pattern, bound method) tuples, used by + `self.implicit_inline`.""" + + def init_customizations(self, settings): + """Setting-based customizations; run when parsing begins.""" + if settings.pep_references: + self.implicit_dispatch.append((self.patterns.pep, + self.pep_reference)) + if settings.rfc_references: + self.implicit_dispatch.append((self.patterns.rfc, + self.rfc_reference)) + + def parse(self, text, lineno, memo, parent): + # Needs to be refactored for nested inline markup. + # Add nested_parse() method? + """ + Return 2 lists: nodes (text and inline elements), and system_messages. + + Using `self.patterns.initial`, a pattern which matches start-strings + (emphasis, strong, interpreted, phrase reference, literal, + substitution reference, and inline target) and complete constructs + (simple reference, footnote reference), search for a candidate. When + one is found, check for validity (e.g., not a quoted '*' character). + If valid, search for the corresponding end string if applicable, and + check it for validity. If not found or invalid, generate a warning + and ignore the start-string. Implicit inline markup (e.g. standalone + URIs) is found last. + """ + self.reporter = memo.reporter + self.document = memo.document + self.language = memo.language + self.parent = parent + pattern_search = self.patterns.initial.search + dispatch = self.dispatch + remaining = escape2null(text) + processed = [] + unprocessed = [] + messages = [] + while remaining: + match = pattern_search(remaining) + if match: + groups = match.groupdict() + method = dispatch[groups['start'] or groups['backquote'] + or groups['refend'] or groups['fnend']] + before, inlines, remaining, sysmessages = method(self, match, + lineno) + unprocessed.append(before) + messages += sysmessages + if inlines: + processed += self.implicit_inline(''.join(unprocessed), + lineno) + processed += inlines + unprocessed = [] + else: + break + remaining = ''.join(unprocessed) + remaining + if remaining: + processed += self.implicit_inline(remaining, lineno) + return processed, messages + + openers = u'\'"([{<\u2018\u201c\xab\u00a1\u00bf' # see quoted_start below + closers = u'\'")]}>\u2019\u201d\xbb!?' + unicode_delimiters = u'\u2010\u2011\u2012\u2013\u2014\u00a0' + start_string_prefix = (u'((?<=^)|(?<=[-/: \\n\u2019%s%s]))' + % (re.escape(unicode_delimiters), + re.escape(openers))) + end_string_suffix = (r'((?=$)|(?=[-/:.,; \n\x00%s%s]))' + % (re.escape(unicode_delimiters), + re.escape(closers))) + non_whitespace_before = r'(?]""" + # Last URI character; same as uric but no punctuation: + urilast = r"""[_~*/=+a-zA-Z0-9]""" + # End of a URI (either 'urilast' or 'uric followed by a + # uri_end_delim'): + uri_end = r"""(?:%(urilast)s|%(uric)s(?=%(uri_end_delim)s))""" % locals() + emailc = r"""[-_!~*'{|}/#?^`&=+$%a-zA-Z0-9\x00]""" + email_pattern = r""" + %(emailc)s+(?:\.%(emailc)s+)* # name + (?%s)(?P__?)' % simplename, + ('footnotelabel', r'\[', r'(?P\]_)', + [r'[0-9]+', # manually numbered + r'\#(%s)?' % simplename, # auto-numbered (w/ label?) + r'\*', # auto-symbol + r'(?P%s)' % simplename] # citation reference + ) + ] + ), + ('backquote', # interpreted text or phrase reference + '(?P(:%s:)?)' % simplename, # optional role + non_whitespace_after, + ['`(?!`)'] # but not literal + ) + ] + ) + patterns = Struct( + initial=build_regexp(parts), + emphasis=re.compile(non_whitespace_escape_before + + r'(\*)' + end_string_suffix), + strong=re.compile(non_whitespace_escape_before + + r'(\*\*)' + end_string_suffix), + interpreted_or_phrase_ref=re.compile( + r""" + %(non_whitespace_escape_before)s + ( + ` + (?P + (?P:%(simplename)s:)? + (?P__?)? + ) + ) + %(end_string_suffix)s + """ % locals(), re.VERBOSE | re.UNICODE), + embedded_uri=re.compile( + r""" + ( + (?:[ \n]+|^) # spaces or beginning of line/string + < # open bracket + %(non_whitespace_after)s + ([^<>\x00]+) # anything but angle brackets & nulls + %(non_whitespace_before)s + > # close bracket w/o whitespace before + ) + $ # end of string + """ % locals(), re.VERBOSE), + literal=re.compile(non_whitespace_before + '(``)' + + end_string_suffix), + target=re.compile(non_whitespace_escape_before + + r'(`)' + end_string_suffix), + substitution_ref=re.compile(non_whitespace_escape_before + + r'(\|_{0,2})' + + end_string_suffix), + email=re.compile(email_pattern % locals() + '$', re.VERBOSE), + uri=re.compile( + (r""" + %(start_string_prefix)s + (?P + (?P # absolute URI + (?P # scheme (http, ftp, mailto) + [a-zA-Z][a-zA-Z0-9.+-]* + ) + : + ( + ( # either: + (//?)? # hierarchical URI + %(uric)s* # URI characters + %(uri_end)s # final URI char + ) + ( # optional query + \?%(uric)s* + %(uri_end)s + )? + ( # optional fragment + \#%(uric)s* + %(uri_end)s + )? + ) + ) + | # *OR* + (?P # email address + """ + email_pattern + r""" + ) + ) + %(end_string_suffix)s + """) % locals(), re.VERBOSE), + pep=re.compile( + r""" + %(start_string_prefix)s + ( + (pep-(?P\d+)(.txt)?) # reference to source file + | + (PEP\s+(?P\d+)) # reference by name + ) + %(end_string_suffix)s""" % locals(), re.VERBOSE), + rfc=re.compile( + r""" + %(start_string_prefix)s + (RFC(-|\s+)?(?P\d+)) + %(end_string_suffix)s""" % locals(), re.VERBOSE)) + + def quoted_start(self, match): + """Return 1 if inline markup start-string is 'quoted', 0 if not.""" + string = match.string + start = match.start() + end = match.end() + if start == 0: # start-string at beginning of text + return 0 + prestart = string[start - 1] + try: + poststart = string[end] + if self.openers.index(prestart) \ + == self.closers.index(poststart): # quoted + return 1 + except IndexError: # start-string at end of text + return 1 + except ValueError: # not quoted + pass + return 0 + + def inline_obj(self, match, lineno, end_pattern, nodeclass, + restore_backslashes=0): + string = match.string + matchstart = match.start('start') + matchend = match.end('start') + if self.quoted_start(match): + return (string[:matchend], [], string[matchend:], [], '') + endmatch = end_pattern.search(string[matchend:]) + if endmatch and endmatch.start(1): # 1 or more chars + text = unescape(endmatch.string[:endmatch.start(1)], + restore_backslashes) + textend = matchend + endmatch.end(1) + rawsource = unescape(string[matchstart:textend], 1) + return (string[:matchstart], [nodeclass(rawsource, text)], + string[textend:], [], endmatch.group(1)) + msg = self.reporter.warning( + 'Inline %s start-string without end-string.' + % nodeclass.__name__, line=lineno) + text = unescape(string[matchstart:matchend], 1) + rawsource = unescape(string[matchstart:matchend], 1) + prb = self.problematic(text, rawsource, msg) + return string[:matchstart], [prb], string[matchend:], [msg], '' + + def problematic(self, text, rawsource, message): + msgid = self.document.set_id(message, self.parent) + problematic = nodes.problematic(rawsource, text, refid=msgid) + prbid = self.document.set_id(problematic) + message.add_backref(prbid) + return problematic + + def emphasis(self, match, lineno): + before, inlines, remaining, sysmessages, endstring = self.inline_obj( + match, lineno, self.patterns.emphasis, nodes.emphasis) + return before, inlines, remaining, sysmessages + + def strong(self, match, lineno): + before, inlines, remaining, sysmessages, endstring = self.inline_obj( + match, lineno, self.patterns.strong, nodes.strong) + return before, inlines, remaining, sysmessages + + def interpreted_or_phrase_ref(self, match, lineno): + end_pattern = self.patterns.interpreted_or_phrase_ref + string = match.string + matchstart = match.start('backquote') + matchend = match.end('backquote') + rolestart = match.start('role') + role = match.group('role') + position = '' + if role: + role = role[1:-1] + position = 'prefix' + elif self.quoted_start(match): + return (string[:matchend], [], string[matchend:], []) + endmatch = end_pattern.search(string[matchend:]) + if endmatch and endmatch.start(1): # 1 or more chars + textend = matchend + endmatch.end() + if endmatch.group('role'): + if role: + msg = self.reporter.warning( + 'Multiple roles in interpreted text (both ' + 'prefix and suffix present; only one allowed).', + line=lineno) + text = unescape(string[rolestart:textend], 1) + prb = self.problematic(text, text, msg) + return string[:rolestart], [prb], string[textend:], [msg] + role = endmatch.group('suffix')[1:-1] + position = 'suffix' + escaped = endmatch.string[:endmatch.start(1)] + rawsource = unescape(string[matchstart:textend], 1) + if rawsource[-1:] == '_': + if role: + msg = self.reporter.warning( + 'Mismatch: both interpreted text role %s and ' + 'reference suffix.' % position, line=lineno) + text = unescape(string[rolestart:textend], 1) + prb = self.problematic(text, text, msg) + return string[:rolestart], [prb], string[textend:], [msg] + return self.phrase_ref(string[:matchstart], string[textend:], + rawsource, escaped, unescape(escaped)) + else: + rawsource = unescape(string[rolestart:textend], 1) + nodelist, messages = self.interpreted(rawsource, escaped, role, + lineno) + return (string[:rolestart], nodelist, + string[textend:], messages) + msg = self.reporter.warning( + 'Inline interpreted text or phrase reference start-string ' + 'without end-string.', line=lineno) + text = unescape(string[matchstart:matchend], 1) + prb = self.problematic(text, text, msg) + return string[:matchstart], [prb], string[matchend:], [msg] + + def phrase_ref(self, before, after, rawsource, escaped, text): + match = self.patterns.embedded_uri.search(escaped) + if match: + text = unescape(escaped[:match.start(0)]) + uri_text = match.group(2) + uri = ''.join(uri_text.split()) + uri = self.adjust_uri(uri) + if uri: + target = nodes.target(match.group(1), refuri=uri) + else: + raise ApplicationError('problem with URI: %r' % uri_text) + if not text: + text = uri + else: + target = None + refname = normalize_name(text) + reference = nodes.reference(rawsource, text, + name=whitespace_normalize_name(text)) + node_list = [reference] + if rawsource[-2:] == '__': + if target: + reference['refuri'] = uri + else: + reference['anonymous'] = 1 + else: + if target: + reference['refuri'] = uri + target['names'].append(refname) + self.document.note_explicit_target(target, self.parent) + node_list.append(target) + else: + reference['refname'] = refname + self.document.note_refname(reference) + return before, node_list, after, [] + + def adjust_uri(self, uri): + match = self.patterns.email.match(uri) + if match: + return 'mailto:' + uri + else: + return uri + + def interpreted(self, rawsource, text, role, lineno): + role_fn, messages = roles.role(role, self.language, lineno, + self.reporter) + if role_fn: + nodes, messages2 = role_fn(role, rawsource, text, lineno, self) + return nodes, messages + messages2 + else: + msg = self.reporter.error( + 'Unknown interpreted text role "%s".' % role, + line=lineno) + return ([self.problematic(rawsource, rawsource, msg)], + messages + [msg]) + + def literal(self, match, lineno): + before, inlines, remaining, sysmessages, endstring = self.inline_obj( + match, lineno, self.patterns.literal, nodes.literal, + restore_backslashes=1) + return before, inlines, remaining, sysmessages + + def inline_internal_target(self, match, lineno): + before, inlines, remaining, sysmessages, endstring = self.inline_obj( + match, lineno, self.patterns.target, nodes.target) + if inlines and isinstance(inlines[0], nodes.target): + assert len(inlines) == 1 + target = inlines[0] + name = normalize_name(target.astext()) + target['names'].append(name) + self.document.note_explicit_target(target, self.parent) + return before, inlines, remaining, sysmessages + + def substitution_reference(self, match, lineno): + before, inlines, remaining, sysmessages, endstring = self.inline_obj( + match, lineno, self.patterns.substitution_ref, + nodes.substitution_reference) + if len(inlines) == 1: + subref_node = inlines[0] + if isinstance(subref_node, nodes.substitution_reference): + subref_text = subref_node.astext() + self.document.note_substitution_ref(subref_node, subref_text) + if endstring[-1:] == '_': + reference_node = nodes.reference( + '|%s%s' % (subref_text, endstring), '') + if endstring[-2:] == '__': + reference_node['anonymous'] = 1 + else: + reference_node['refname'] = normalize_name(subref_text) + self.document.note_refname(reference_node) + reference_node += subref_node + inlines = [reference_node] + return before, inlines, remaining, sysmessages + + def footnote_reference(self, match, lineno): + """ + Handles `nodes.footnote_reference` and `nodes.citation_reference` + elements. + """ + label = match.group('footnotelabel') + refname = normalize_name(label) + string = match.string + before = string[:match.start('whole')] + remaining = string[match.end('whole'):] + if match.group('citationlabel'): + refnode = nodes.citation_reference('[%s]_' % label, + refname=refname) + refnode += nodes.Text(label) + self.document.note_citation_ref(refnode) + else: + refnode = nodes.footnote_reference('[%s]_' % label) + if refname[0] == '#': + refname = refname[1:] + refnode['auto'] = 1 + self.document.note_autofootnote_ref(refnode) + elif refname == '*': + refname = '' + refnode['auto'] = '*' + self.document.note_symbol_footnote_ref( + refnode) + else: + refnode += nodes.Text(label) + if refname: + refnode['refname'] = refname + self.document.note_footnote_ref(refnode) + if utils.get_trim_footnote_ref_space(self.document.settings): + before = before.rstrip() + return (before, [refnode], remaining, []) + + def reference(self, match, lineno, anonymous=None): + referencename = match.group('refname') + refname = normalize_name(referencename) + referencenode = nodes.reference( + referencename + match.group('refend'), referencename, + name=whitespace_normalize_name(referencename)) + if anonymous: + referencenode['anonymous'] = 1 + else: + referencenode['refname'] = refname + self.document.note_refname(referencenode) + string = match.string + matchstart = match.start('whole') + matchend = match.end('whole') + return (string[:matchstart], [referencenode], string[matchend:], []) + + def anonymous_reference(self, match, lineno): + return self.reference(match, lineno, anonymous=1) + + def standalone_uri(self, match, lineno): + if (not match.group('scheme') + or match.group('scheme').lower() in urischemes.schemes): + if match.group('email'): + addscheme = 'mailto:' + else: + addscheme = '' + text = match.group('whole') + unescaped = unescape(text, 0) + return [nodes.reference(unescape(text, 1), unescaped, + refuri=addscheme + unescaped)] + else: # not a valid scheme + raise MarkupMismatch + + def pep_reference(self, match, lineno): + text = match.group(0) + if text.startswith('pep-'): + pepnum = int(match.group('pepnum1')) + elif text.startswith('PEP'): + pepnum = int(match.group('pepnum2')) + else: + raise MarkupMismatch + ref = (self.document.settings.pep_base_url + + self.document.settings.pep_file_url_template % pepnum) + unescaped = unescape(text, 0) + return [nodes.reference(unescape(text, 1), unescaped, refuri=ref)] + + rfc_url = 'rfc%d.html' + + def rfc_reference(self, match, lineno): + text = match.group(0) + if text.startswith('RFC'): + rfcnum = int(match.group('rfcnum')) + ref = self.document.settings.rfc_base_url + self.rfc_url % rfcnum + else: + raise MarkupMismatch + unescaped = unescape(text, 0) + return [nodes.reference(unescape(text, 1), unescaped, refuri=ref)] + + def implicit_inline(self, text, lineno): + """ + Check each of the patterns in `self.implicit_dispatch` for a match, + and dispatch to the stored method for the pattern. Recursively check + the text before and after the match. Return a list of `nodes.Text` + and inline element nodes. + """ + if not text: + return [] + for pattern, method in self.implicit_dispatch: + match = pattern.search(text) + if match: + try: + # Must recurse on strings before *and* after the match; + # there may be multiple patterns. + return (self.implicit_inline(text[:match.start()], lineno) + + method(match, lineno) + + self.implicit_inline(text[match.end():], lineno)) + except MarkupMismatch: + pass + return [nodes.Text(unescape(text), rawsource=unescape(text, 1))] + + dispatch = {'*': emphasis, + '**': strong, + '`': interpreted_or_phrase_ref, + '``': literal, + '_`': inline_internal_target, + ']_': footnote_reference, + '|': substitution_reference, + '_': reference, + '__': anonymous_reference} + + +def _loweralpha_to_int(s, _zero=(ord('a')-1)): + return ord(s) - _zero + +def _upperalpha_to_int(s, _zero=(ord('A')-1)): + return ord(s) - _zero + +def _lowerroman_to_int(s): + return roman.fromRoman(s.upper()) + + +class Body(RSTState): + + """ + Generic classifier of the first line of a block. + """ + + double_width_pad_char = tableparser.TableParser.double_width_pad_char + """Padding character for East Asian double-width text.""" + + enum = Struct() + """Enumerated list parsing information.""" + + enum.formatinfo = { + 'parens': Struct(prefix='(', suffix=')', start=1, end=-1), + 'rparen': Struct(prefix='', suffix=')', start=0, end=-1), + 'period': Struct(prefix='', suffix='.', start=0, end=-1)} + enum.formats = enum.formatinfo.keys() + enum.sequences = ['arabic', 'loweralpha', 'upperalpha', + 'lowerroman', 'upperroman'] # ORDERED! + enum.sequencepats = {'arabic': '[0-9]+', + 'loweralpha': '[a-z]', + 'upperalpha': '[A-Z]', + 'lowerroman': '[ivxlcdm]+', + 'upperroman': '[IVXLCDM]+',} + enum.converters = {'arabic': int, + 'loweralpha': _loweralpha_to_int, + 'upperalpha': _upperalpha_to_int, + 'lowerroman': _lowerroman_to_int, + 'upperroman': roman.fromRoman} + + enum.sequenceregexps = {} + for sequence in enum.sequences: + enum.sequenceregexps[sequence] = re.compile( + enum.sequencepats[sequence] + '$') + + grid_table_top_pat = re.compile(r'\+-[-+]+-\+ *$') + """Matches the top (& bottom) of a full table).""" + + simple_table_top_pat = re.compile('=+( +=+)+ *$') + """Matches the top of a simple table.""" + + simple_table_border_pat = re.compile('=+[ =]*$') + """Matches the bottom & header bottom of a simple table.""" + + pats = {} + """Fragments of patterns used by transitions.""" + + pats['nonalphanum7bit'] = '[!-/:-@[-`{-~]' + pats['alpha'] = '[a-zA-Z]' + pats['alphanum'] = '[a-zA-Z0-9]' + pats['alphanumplus'] = '[a-zA-Z0-9_-]' + pats['enum'] = ('(%(arabic)s|%(loweralpha)s|%(upperalpha)s|%(lowerroman)s' + '|%(upperroman)s|#)' % enum.sequencepats) + pats['optname'] = '%(alphanum)s%(alphanumplus)s*' % pats + # @@@ Loosen up the pattern? Allow Unicode? + pats['optarg'] = '(%(alpha)s%(alphanumplus)s*|<[^<>]+>)' % pats + pats['shortopt'] = r'(-|\+)%(alphanum)s( ?%(optarg)s)?' % pats + pats['longopt'] = r'(--|/)%(optname)s([ =]%(optarg)s)?' % pats + pats['option'] = r'(%(shortopt)s|%(longopt)s)' % pats + + for format in enum.formats: + pats[format] = '(?P<%s>%s%s%s)' % ( + format, re.escape(enum.formatinfo[format].prefix), + pats['enum'], re.escape(enum.formatinfo[format].suffix)) + + patterns = { + 'bullet': u'[-+*\u2022\u2023\u2043]( +|$)', + 'enumerator': r'(%(parens)s|%(rparen)s|%(period)s)( +|$)' % pats, + 'field_marker': r':(?![: ])([^:\\]|\\.)*(?>>( +|$)', + 'line_block': r'\|( +|$)', + 'grid_table_top': grid_table_top_pat, + 'simple_table_top': simple_table_top_pat, + 'explicit_markup': r'\.\.( +|$)', + 'anonymous': r'__( +|$)', + 'line': r'(%(nonalphanum7bit)s)\1* *$' % pats, + 'text': r''} + initial_transitions = ( + 'bullet', + 'enumerator', + 'field_marker', + 'option_marker', + 'doctest', + 'line_block', + 'grid_table_top', + 'simple_table_top', + 'explicit_markup', + 'anonymous', + 'line', + 'text') + + def indent(self, match, context, next_state): + """Block quote.""" + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_indented() + elements = self.block_quote(indented, line_offset) + self.parent += elements + if not blank_finish: + self.parent += self.unindent_warning('Block quote') + return context, next_state, [] + + def block_quote(self, indented, line_offset): + elements = [] + while indented: + (blockquote_lines, + attribution_lines, + attribution_offset, + indented, + new_line_offset) = self.split_attribution(indented, line_offset) + blockquote = nodes.block_quote() + self.nested_parse(blockquote_lines, line_offset, blockquote) + elements.append(blockquote) + if attribution_lines: + attribution, messages = self.parse_attribution( + attribution_lines, attribution_offset) + blockquote += attribution + elements += messages + line_offset = new_line_offset + while indented and not indented[0]: + indented = indented[1:] + line_offset += 1 + return elements + + # U+2014 is an em-dash: + attribution_pattern = re.compile(u'(---?(?!-)|\u2014) *(?=[^ \\n])') + + def split_attribution(self, indented, line_offset): + """ + Check for a block quote attribution and split it off: + + * First line after a blank line must begin with a dash ("--", "---", + em-dash; matches `self.attribution_pattern`). + * Every line after that must have consistent indentation. + * Attributions must be preceded by block quote content. + + Return a tuple of: (block quote content lines, content offset, + attribution lines, attribution offset, remaining indented lines). + """ + blank = None + nonblank_seen = False + for i in range(len(indented)): + line = indented[i].rstrip() + if line: + if nonblank_seen and blank == i - 1: # last line blank + match = self.attribution_pattern.match(line) + if match: + attribution_end, indent = self.check_attribution( + indented, i) + if attribution_end: + a_lines = indented[i:attribution_end] + a_lines.trim_left(match.end(), end=1) + a_lines.trim_left(indent, start=1) + return (indented[:i], a_lines, + i, indented[attribution_end:], + line_offset + attribution_end) + nonblank_seen = True + else: + blank = i + else: + return (indented, None, None, None, None) + + def check_attribution(self, indented, attribution_start): + """ + Check attribution shape. + Return the index past the end of the attribution, and the indent. + """ + indent = None + i = attribution_start + 1 + for i in range(attribution_start + 1, len(indented)): + line = indented[i].rstrip() + if not line: + break + if indent is None: + indent = len(line) - len(line.lstrip()) + elif len(line) - len(line.lstrip()) != indent: + return None, None # bad shape; not an attribution + else: + # return index of line after last attribution line: + i += 1 + return i, (indent or 0) + + def parse_attribution(self, indented, line_offset): + text = '\n'.join(indented).rstrip() + lineno = self.state_machine.abs_line_number() + line_offset + textnodes, messages = self.inline_text(text, lineno) + node = nodes.attribution(text, '', *textnodes) + node.line = lineno + return node, messages + + def bullet(self, match, context, next_state): + """Bullet list item.""" + bulletlist = nodes.bullet_list() + self.parent += bulletlist + bulletlist['bullet'] = match.string[0] + i, blank_finish = self.list_item(match.end()) + bulletlist += i + offset = self.state_machine.line_offset + 1 # next line + new_line_offset, blank_finish = self.nested_list_parse( + self.state_machine.input_lines[offset:], + input_offset=self.state_machine.abs_line_offset() + 1, + node=bulletlist, initial_state='BulletList', + blank_finish=blank_finish) + self.goto_line(new_line_offset) + if not blank_finish: + self.parent += self.unindent_warning('Bullet list') + return [], next_state, [] + + def list_item(self, indent): + if self.state_machine.line[indent:]: + indented, line_offset, blank_finish = ( + self.state_machine.get_known_indented(indent)) + else: + indented, indent, line_offset, blank_finish = ( + self.state_machine.get_first_known_indented(indent)) + listitem = nodes.list_item('\n'.join(indented)) + if indented: + self.nested_parse(indented, input_offset=line_offset, + node=listitem) + return listitem, blank_finish + + def enumerator(self, match, context, next_state): + """Enumerated List Item""" + format, sequence, text, ordinal = self.parse_enumerator(match) + if not self.is_enumerated_list_item(ordinal, sequence, format): + raise statemachine.TransitionCorrection('text') + enumlist = nodes.enumerated_list() + self.parent += enumlist + if sequence == '#': + enumlist['enumtype'] = 'arabic' + else: + enumlist['enumtype'] = sequence + enumlist['prefix'] = self.enum.formatinfo[format].prefix + enumlist['suffix'] = self.enum.formatinfo[format].suffix + if ordinal != 1: + enumlist['start'] = ordinal + msg = self.reporter.info( + 'Enumerated list start value not ordinal-1: "%s" (ordinal %s)' + % (text, ordinal), line=self.state_machine.abs_line_number()) + self.parent += msg + listitem, blank_finish = self.list_item(match.end()) + enumlist += listitem + offset = self.state_machine.line_offset + 1 # next line + newline_offset, blank_finish = self.nested_list_parse( + self.state_machine.input_lines[offset:], + input_offset=self.state_machine.abs_line_offset() + 1, + node=enumlist, initial_state='EnumeratedList', + blank_finish=blank_finish, + extra_settings={'lastordinal': ordinal, + 'format': format, + 'auto': sequence == '#'}) + self.goto_line(newline_offset) + if not blank_finish: + self.parent += self.unindent_warning('Enumerated list') + return [], next_state, [] + + def parse_enumerator(self, match, expected_sequence=None): + """ + Analyze an enumerator and return the results. + + :Return: + - the enumerator format ('period', 'parens', or 'rparen'), + - the sequence used ('arabic', 'loweralpha', 'upperroman', etc.), + - the text of the enumerator, stripped of formatting, and + - the ordinal value of the enumerator ('a' -> 1, 'ii' -> 2, etc.; + ``None`` is returned for invalid enumerator text). + + The enumerator format has already been determined by the regular + expression match. If `expected_sequence` is given, that sequence is + tried first. If not, we check for Roman numeral 1. This way, + single-character Roman numerals (which are also alphabetical) can be + matched. If no sequence has been matched, all sequences are checked in + order. + """ + groupdict = match.groupdict() + sequence = '' + for format in self.enum.formats: + if groupdict[format]: # was this the format matched? + break # yes; keep `format` + else: # shouldn't happen + raise ParserError('enumerator format not matched') + text = groupdict[format][self.enum.formatinfo[format].start + :self.enum.formatinfo[format].end] + if text == '#': + sequence = '#' + elif expected_sequence: + try: + if self.enum.sequenceregexps[expected_sequence].match(text): + sequence = expected_sequence + except KeyError: # shouldn't happen + raise ParserError('unknown enumerator sequence: %s' + % sequence) + elif text == 'i': + sequence = 'lowerroman' + elif text == 'I': + sequence = 'upperroman' + if not sequence: + for sequence in self.enum.sequences: + if self.enum.sequenceregexps[sequence].match(text): + break + else: # shouldn't happen + raise ParserError('enumerator sequence not matched') + if sequence == '#': + ordinal = 1 + else: + try: + ordinal = self.enum.converters[sequence](text) + except roman.InvalidRomanNumeralError: + ordinal = None + return format, sequence, text, ordinal + + def is_enumerated_list_item(self, ordinal, sequence, format): + """ + Check validity based on the ordinal value and the second line. + + Return true if the ordinal is valid and the second line is blank, + indented, or starts with the next enumerator or an auto-enumerator. + """ + if ordinal is None: + return None + try: + next_line = self.state_machine.next_line() + except EOFError: # end of input lines + self.state_machine.previous_line() + return 1 + else: + self.state_machine.previous_line() + if not next_line[:1].strip(): # blank or indented + return 1 + result = self.make_enumerator(ordinal + 1, sequence, format) + if result: + next_enumerator, auto_enumerator = result + try: + if ( next_line.startswith(next_enumerator) or + next_line.startswith(auto_enumerator) ): + return 1 + except TypeError: + pass + return None + + def make_enumerator(self, ordinal, sequence, format): + """ + Construct and return the next enumerated list item marker, and an + auto-enumerator ("#" instead of the regular enumerator). + + Return ``None`` for invalid (out of range) ordinals. + """ #" + if sequence == '#': + enumerator = '#' + elif sequence == 'arabic': + enumerator = str(ordinal) + else: + if sequence.endswith('alpha'): + if ordinal > 26: + return None + enumerator = chr(ordinal + ord('a') - 1) + elif sequence.endswith('roman'): + try: + enumerator = roman.toRoman(ordinal) + except roman.RomanError: + return None + else: # shouldn't happen + raise ParserError('unknown enumerator sequence: "%s"' + % sequence) + if sequence.startswith('lower'): + enumerator = enumerator.lower() + elif sequence.startswith('upper'): + enumerator = enumerator.upper() + else: # shouldn't happen + raise ParserError('unknown enumerator sequence: "%s"' + % sequence) + formatinfo = self.enum.formatinfo[format] + next_enumerator = (formatinfo.prefix + enumerator + formatinfo.suffix + + ' ') + auto_enumerator = formatinfo.prefix + '#' + formatinfo.suffix + ' ' + return next_enumerator, auto_enumerator + + def field_marker(self, match, context, next_state): + """Field list item.""" + field_list = nodes.field_list() + self.parent += field_list + field, blank_finish = self.field(match) + field_list += field + offset = self.state_machine.line_offset + 1 # next line + newline_offset, blank_finish = self.nested_list_parse( + self.state_machine.input_lines[offset:], + input_offset=self.state_machine.abs_line_offset() + 1, + node=field_list, initial_state='FieldList', + blank_finish=blank_finish) + self.goto_line(newline_offset) + if not blank_finish: + self.parent += self.unindent_warning('Field list') + return [], next_state, [] + + def field(self, match): + name = self.parse_field_marker(match) + lineno = self.state_machine.abs_line_number() + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end()) + field_node = nodes.field() + field_node.line = lineno + name_nodes, name_messages = self.inline_text(name, lineno) + field_node += nodes.field_name(name, '', *name_nodes) + field_body = nodes.field_body('\n'.join(indented), *name_messages) + field_node += field_body + if indented: + self.parse_field_body(indented, line_offset, field_body) + return field_node, blank_finish + + def parse_field_marker(self, match): + """Extract & return field name from a field marker match.""" + field = match.group()[1:] # strip off leading ':' + field = field[:field.rfind(':')] # strip off trailing ':' etc. + return field + + def parse_field_body(self, indented, offset, node): + self.nested_parse(indented, input_offset=offset, node=node) + + def option_marker(self, match, context, next_state): + """Option list item.""" + optionlist = nodes.option_list() + try: + listitem, blank_finish = self.option_list_item(match) + except MarkupError, (message, lineno): + # This shouldn't happen; pattern won't match. + msg = self.reporter.error( + 'Invalid option list marker: %s' % message, line=lineno) + self.parent += msg + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end()) + elements = self.block_quote(indented, line_offset) + self.parent += elements + if not blank_finish: + self.parent += self.unindent_warning('Option list') + return [], next_state, [] + self.parent += optionlist + optionlist += listitem + offset = self.state_machine.line_offset + 1 # next line + newline_offset, blank_finish = self.nested_list_parse( + self.state_machine.input_lines[offset:], + input_offset=self.state_machine.abs_line_offset() + 1, + node=optionlist, initial_state='OptionList', + blank_finish=blank_finish) + self.goto_line(newline_offset) + if not blank_finish: + self.parent += self.unindent_warning('Option list') + return [], next_state, [] + + def option_list_item(self, match): + offset = self.state_machine.abs_line_offset() + options = self.parse_option_marker(match) + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end()) + if not indented: # not an option list item + self.goto_line(offset) + raise statemachine.TransitionCorrection('text') + option_group = nodes.option_group('', *options) + description = nodes.description('\n'.join(indented)) + option_list_item = nodes.option_list_item('', option_group, + description) + if indented: + self.nested_parse(indented, input_offset=line_offset, + node=description) + return option_list_item, blank_finish + + def parse_option_marker(self, match): + """ + Return a list of `node.option` and `node.option_argument` objects, + parsed from an option marker match. + + :Exception: `MarkupError` for invalid option markers. + """ + optlist = [] + optionstrings = match.group().rstrip().split(', ') + for optionstring in optionstrings: + tokens = optionstring.split() + delimiter = ' ' + firstopt = tokens[0].split('=') + if len(firstopt) > 1: + # "--opt=value" form + tokens[:1] = firstopt + delimiter = '=' + elif (len(tokens[0]) > 2 + and ((tokens[0].startswith('-') + and not tokens[0].startswith('--')) + or tokens[0].startswith('+'))): + # "-ovalue" form + tokens[:1] = [tokens[0][:2], tokens[0][2:]] + delimiter = '' + if len(tokens) > 1 and (tokens[1].startswith('<') + and tokens[-1].endswith('>')): + # "-o " form; join all values into one token + tokens[1:] = [' '.join(tokens[1:])] + if 0 < len(tokens) <= 2: + option = nodes.option(optionstring) + option += nodes.option_string(tokens[0], tokens[0]) + if len(tokens) > 1: + option += nodes.option_argument(tokens[1], tokens[1], + delimiter=delimiter) + optlist.append(option) + else: + raise MarkupError( + 'wrong number of option tokens (=%s), should be 1 or 2: ' + '"%s"' % (len(tokens), optionstring), + self.state_machine.abs_line_number() + 1) + return optlist + + def doctest(self, match, context, next_state): + data = '\n'.join(self.state_machine.get_text_block()) + self.parent += nodes.doctest_block(data, data) + return [], next_state, [] + + def line_block(self, match, context, next_state): + """First line of a line block.""" + block = nodes.line_block() + self.parent += block + lineno = self.state_machine.abs_line_number() + line, messages, blank_finish = self.line_block_line(match, lineno) + block += line + self.parent += messages + if not blank_finish: + offset = self.state_machine.line_offset + 1 # next line + new_line_offset, blank_finish = self.nested_list_parse( + self.state_machine.input_lines[offset:], + input_offset=self.state_machine.abs_line_offset() + 1, + node=block, initial_state='LineBlock', + blank_finish=0) + self.goto_line(new_line_offset) + if not blank_finish: + self.parent += self.reporter.warning( + 'Line block ends without a blank line.', + line=(self.state_machine.abs_line_number() + 1)) + if len(block): + if block[0].indent is None: + block[0].indent = 0 + self.nest_line_block_lines(block) + return [], next_state, [] + + def line_block_line(self, match, lineno): + """Return one line element of a line_block.""" + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end(), + until_blank=1) + text = u'\n'.join(indented) + text_nodes, messages = self.inline_text(text, lineno) + line = nodes.line(text, '', *text_nodes) + if match.string.rstrip() != '|': # not empty + line.indent = len(match.group(1)) - 1 + return line, messages, blank_finish + + def nest_line_block_lines(self, block): + for index in range(1, len(block)): + if block[index].indent is None: + block[index].indent = block[index - 1].indent + self.nest_line_block_segment(block) + + def nest_line_block_segment(self, block): + indents = [item.indent for item in block] + least = min(indents) + new_items = [] + new_block = nodes.line_block() + for item in block: + if item.indent > least: + new_block.append(item) + else: + if len(new_block): + self.nest_line_block_segment(new_block) + new_items.append(new_block) + new_block = nodes.line_block() + new_items.append(item) + if len(new_block): + self.nest_line_block_segment(new_block) + new_items.append(new_block) + block[:] = new_items + + def grid_table_top(self, match, context, next_state): + """Top border of a full table.""" + return self.table_top(match, context, next_state, + self.isolate_grid_table, + tableparser.GridTableParser) + + def simple_table_top(self, match, context, next_state): + """Top border of a simple table.""" + return self.table_top(match, context, next_state, + self.isolate_simple_table, + tableparser.SimpleTableParser) + + def table_top(self, match, context, next_state, + isolate_function, parser_class): + """Top border of a generic table.""" + nodelist, blank_finish = self.table(isolate_function, parser_class) + self.parent += nodelist + if not blank_finish: + msg = self.reporter.warning( + 'Blank line required after table.', + line=self.state_machine.abs_line_number() + 1) + self.parent += msg + return [], next_state, [] + + def table(self, isolate_function, parser_class): + """Parse a table.""" + block, messages, blank_finish = isolate_function() + if block: + try: + parser = parser_class() + tabledata = parser.parse(block) + tableline = (self.state_machine.abs_line_number() - len(block) + + 1) + table = self.build_table(tabledata, tableline) + nodelist = [table] + messages + except tableparser.TableMarkupError, detail: + nodelist = self.malformed_table( + block, ' '.join(detail.args)) + messages + else: + nodelist = messages + return nodelist, blank_finish + + def isolate_grid_table(self): + messages = [] + blank_finish = 1 + try: + block = self.state_machine.get_text_block(flush_left=1) + except statemachine.UnexpectedIndentationError, instance: + block, source, lineno = instance.args + messages.append(self.reporter.error('Unexpected indentation.', + source=source, line=lineno)) + blank_finish = 0 + block.disconnect() + # for East Asian chars: + block.pad_double_width(self.double_width_pad_char) + width = len(block[0].strip()) + for i in range(len(block)): + block[i] = block[i].strip() + if block[i][0] not in '+|': # check left edge + blank_finish = 0 + self.state_machine.previous_line(len(block) - i) + del block[i:] + break + if not self.grid_table_top_pat.match(block[-1]): # find bottom + blank_finish = 0 + # from second-last to third line of table: + for i in range(len(block) - 2, 1, -1): + if self.grid_table_top_pat.match(block[i]): + self.state_machine.previous_line(len(block) - i + 1) + del block[i+1:] + break + else: + messages.extend(self.malformed_table(block)) + return [], messages, blank_finish + for i in range(len(block)): # check right edge + if len(block[i]) != width or block[i][-1] not in '+|': + messages.extend(self.malformed_table(block)) + return [], messages, blank_finish + return block, messages, blank_finish + + def isolate_simple_table(self): + start = self.state_machine.line_offset + lines = self.state_machine.input_lines + limit = len(lines) - 1 + toplen = len(lines[start].strip()) + pattern_match = self.simple_table_border_pat.match + found = 0 + found_at = None + i = start + 1 + while i <= limit: + line = lines[i] + match = pattern_match(line) + if match: + if len(line.strip()) != toplen: + self.state_machine.next_line(i - start) + messages = self.malformed_table( + lines[start:i+1], 'Bottom/header table border does ' + 'not match top border.') + return [], messages, i == limit or not lines[i+1].strip() + found += 1 + found_at = i + if found == 2 or i == limit or not lines[i+1].strip(): + end = i + break + i += 1 + else: # reached end of input_lines + if found: + extra = ' or no blank line after table bottom' + self.state_machine.next_line(found_at - start) + block = lines[start:found_at+1] + else: + extra = '' + self.state_machine.next_line(i - start - 1) + block = lines[start:] + messages = self.malformed_table( + block, 'No bottom table border found%s.' % extra) + return [], messages, not extra + self.state_machine.next_line(end - start) + block = lines[start:end+1] + # for East Asian chars: + block.pad_double_width(self.double_width_pad_char) + return block, [], end == limit or not lines[end+1].strip() + + def malformed_table(self, block, detail=''): + block.replace(self.double_width_pad_char, '') + data = '\n'.join(block) + message = 'Malformed table.' + lineno = self.state_machine.abs_line_number() - len(block) + 1 + if detail: + message += '\n' + detail + error = self.reporter.error(message, nodes.literal_block(data, data), + line=lineno) + return [error] + + def build_table(self, tabledata, tableline, stub_columns=0): + colwidths, headrows, bodyrows = tabledata + table = nodes.table() + tgroup = nodes.tgroup(cols=len(colwidths)) + table += tgroup + for colwidth in colwidths: + colspec = nodes.colspec(colwidth=colwidth) + if stub_columns: + colspec.attributes['stub'] = 1 + stub_columns -= 1 + tgroup += colspec + if headrows: + thead = nodes.thead() + tgroup += thead + for row in headrows: + thead += self.build_table_row(row, tableline) + tbody = nodes.tbody() + tgroup += tbody + for row in bodyrows: + tbody += self.build_table_row(row, tableline) + return table + + def build_table_row(self, rowdata, tableline): + row = nodes.row() + for cell in rowdata: + if cell is None: + continue + morerows, morecols, offset, cellblock = cell + attributes = {} + if morerows: + attributes['morerows'] = morerows + if morecols: + attributes['morecols'] = morecols + entry = nodes.entry(**attributes) + row += entry + if ''.join(cellblock): + self.nested_parse(cellblock, input_offset=tableline+offset, + node=entry) + return row + + + explicit = Struct() + """Patterns and constants used for explicit markup recognition.""" + + explicit.patterns = Struct( + target=re.compile(r""" + ( + _ # anonymous target + | # *OR* + (?!_) # no underscore at the beginning + (?P`?) # optional open quote + (?![ `]) # first char. not space or + # backquote + (?P # reference name + .+? + ) + %(non_whitespace_escape_before)s + (?P=quote) # close quote if open quote used + ) + (?%(simplename)s)_ + | # *OR* + ` # open backquote + (?![ ]) # not space + (?P.+?) # hyperlink phrase + %(non_whitespace_escape_before)s + `_ # close backquote, + # reference mark + ) + $ # end of string + """ % vars(Inliner), re.VERBOSE | re.UNICODE), + substitution=re.compile(r""" + ( + (?![ ]) # first char. not space + (?P.+?) # substitution text + %(non_whitespace_escape_before)s + \| # close delimiter + ) + ([ ]+|$) # followed by whitespace + """ % vars(Inliner), re.VERBOSE),) + + def footnote(self, match): + lineno = self.state_machine.abs_line_number() + indented, indent, offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end()) + label = match.group(1) + name = normalize_name(label) + footnote = nodes.footnote('\n'.join(indented)) + footnote.line = lineno + if name[0] == '#': # auto-numbered + name = name[1:] # autonumber label + footnote['auto'] = 1 + if name: + footnote['names'].append(name) + self.document.note_autofootnote(footnote) + elif name == '*': # auto-symbol + name = '' + footnote['auto'] = '*' + self.document.note_symbol_footnote(footnote) + else: # manually numbered + footnote += nodes.label('', label) + footnote['names'].append(name) + self.document.note_footnote(footnote) + if name: + self.document.note_explicit_target(footnote, footnote) + else: + self.document.set_id(footnote, footnote) + if indented: + self.nested_parse(indented, input_offset=offset, node=footnote) + return [footnote], blank_finish + + def citation(self, match): + lineno = self.state_machine.abs_line_number() + indented, indent, offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end()) + label = match.group(1) + name = normalize_name(label) + citation = nodes.citation('\n'.join(indented)) + citation.line = lineno + citation += nodes.label('', label) + citation['names'].append(name) + self.document.note_citation(citation) + self.document.note_explicit_target(citation, citation) + if indented: + self.nested_parse(indented, input_offset=offset, node=citation) + return [citation], blank_finish + + def hyperlink_target(self, match): + pattern = self.explicit.patterns.target + lineno = self.state_machine.abs_line_number() + block, indent, offset, blank_finish = \ + self.state_machine.get_first_known_indented( + match.end(), until_blank=1, strip_indent=0) + blocktext = match.string[:match.end()] + '\n'.join(block) + block = [escape2null(line) for line in block] + escaped = block[0] + blockindex = 0 + while 1: + targetmatch = pattern.match(escaped) + if targetmatch: + break + blockindex += 1 + try: + escaped += block[blockindex] + except IndexError: + raise MarkupError('malformed hyperlink target.', lineno) + del block[:blockindex] + block[0] = (block[0] + ' ')[targetmatch.end()-len(escaped)-1:].strip() + target = self.make_target(block, blocktext, lineno, + targetmatch.group('name')) + return [target], blank_finish + + def make_target(self, block, block_text, lineno, target_name): + target_type, data = self.parse_target(block, block_text, lineno) + if target_type == 'refname': + target = nodes.target(block_text, '', refname=normalize_name(data)) + target.indirect_reference_name = data + self.add_target(target_name, '', target, lineno) + self.document.note_indirect_target(target) + return target + elif target_type == 'refuri': + target = nodes.target(block_text, '') + self.add_target(target_name, data, target, lineno) + return target + else: + return data + + def parse_target(self, block, block_text, lineno): + """ + Determine the type of reference of a target. + + :Return: A 2-tuple, one of: + + - 'refname' and the indirect reference name + - 'refuri' and the URI + - 'malformed' and a system_message node + """ + if block and block[-1].strip()[-1:] == '_': # possible indirect target + reference = ' '.join([line.strip() for line in block]) + refname = self.is_reference(reference) + if refname: + return 'refname', refname + reference = ''.join([''.join(line.split()) for line in block]) + return 'refuri', unescape(reference) + + def is_reference(self, reference): + match = self.explicit.patterns.reference.match( + whitespace_normalize_name(reference)) + if not match: + return None + return unescape(match.group('simple') or match.group('phrase')) + + def add_target(self, targetname, refuri, target, lineno): + target.line = lineno + if targetname: + name = normalize_name(unescape(targetname)) + target['names'].append(name) + if refuri: + uri = self.inliner.adjust_uri(refuri) + if uri: + target['refuri'] = uri + else: + raise ApplicationError('problem with URI: %r' % refuri) + self.document.note_explicit_target(target, self.parent) + else: # anonymous target + if refuri: + target['refuri'] = refuri + target['anonymous'] = 1 + self.document.note_anonymous_target(target) + + def substitution_def(self, match): + pattern = self.explicit.patterns.substitution + lineno = self.state_machine.abs_line_number() + block, indent, offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end(), + strip_indent=0) + blocktext = (match.string[:match.end()] + '\n'.join(block)) + block.disconnect() + escaped = escape2null(block[0].rstrip()) + blockindex = 0 + while 1: + subdefmatch = pattern.match(escaped) + if subdefmatch: + break + blockindex += 1 + try: + escaped = escaped + ' ' + escape2null(block[blockindex].strip()) + except IndexError: + raise MarkupError('malformed substitution definition.', + lineno) + del block[:blockindex] # strip out the substitution marker + block[0] = (block[0].strip() + ' ')[subdefmatch.end()-len(escaped)-1:-1] + if not block[0]: + del block[0] + offset += 1 + while block and not block[-1].strip(): + block.pop() + subname = subdefmatch.group('name') + substitution_node = nodes.substitution_definition(blocktext) + substitution_node.line = lineno + if not block: + msg = self.reporter.warning( + 'Substitution definition "%s" missing contents.' % subname, + nodes.literal_block(blocktext, blocktext), line=lineno) + return [msg], blank_finish + block[0] = block[0].strip() + substitution_node['names'].append( + nodes.whitespace_normalize_name(subname)) + new_abs_offset, blank_finish = self.nested_list_parse( + block, input_offset=offset, node=substitution_node, + initial_state='SubstitutionDef', blank_finish=blank_finish) + i = 0 + for node in substitution_node[:]: + if not (isinstance(node, nodes.Inline) or + isinstance(node, nodes.Text)): + self.parent += substitution_node[i] + del substitution_node[i] + else: + i += 1 + for node in substitution_node.traverse(nodes.Element): + if self.disallowed_inside_substitution_definitions(node): + pformat = nodes.literal_block('', node.pformat().rstrip()) + msg = self.reporter.error( + 'Substitution definition contains illegal element:', + pformat, nodes.literal_block(blocktext, blocktext), + line=lineno) + return [msg], blank_finish + if len(substitution_node) == 0: + msg = self.reporter.warning( + 'Substitution definition "%s" empty or invalid.' + % subname, + nodes.literal_block(blocktext, blocktext), line=lineno) + return [msg], blank_finish + self.document.note_substitution_def( + substitution_node, subname, self.parent) + return [substitution_node], blank_finish + + def disallowed_inside_substitution_definitions(self, node): + if (node['ids'] or + isinstance(node, nodes.reference) and node.get('anonymous') or + isinstance(node, nodes.footnote_reference) and node.get('auto')): + return 1 + else: + return 0 + + def directive(self, match, **option_presets): + """Returns a 2-tuple: list of nodes, and a "blank finish" boolean.""" + type_name = match.group(1) + directive_class, messages = directives.directive( + type_name, self.memo.language, self.document) + self.parent += messages + if directive_class: + return self.run_directive( + directive_class, match, type_name, option_presets) + else: + return self.unknown_directive(type_name) + + def run_directive(self, directive, match, type_name, option_presets): + """ + Parse a directive then run its directive function. + + Parameters: + + - `directive`: The class implementing the directive. Must be + a subclass of `rst.Directive`. + + - `match`: A regular expression match object which matched the first + line of the directive. + + - `type_name`: The directive name, as used in the source text. + + - `option_presets`: A dictionary of preset options, defaults for the + directive options. Currently, only an "alt" option is passed by + substitution definitions (value: the substitution name), which may + be used by an embedded image directive. + + Returns a 2-tuple: list of nodes, and a "blank finish" boolean. + """ + if isinstance(directive, (FunctionType, MethodType)): + from docutils.parsers.rst import convert_directive_function + directive = convert_directive_function(directive) + lineno = self.state_machine.abs_line_number() + initial_line_offset = self.state_machine.line_offset + indented, indent, line_offset, blank_finish \ + = self.state_machine.get_first_known_indented(match.end(), + strip_top=0) + block_text = '\n'.join(self.state_machine.input_lines[ + initial_line_offset : self.state_machine.line_offset + 1]) + try: + arguments, options, content, content_offset = ( + self.parse_directive_block(indented, line_offset, + directive, option_presets)) + except MarkupError, detail: + error = self.reporter.error( + 'Error in "%s" directive:\n%s.' % (type_name, + ' '.join(detail.args)), + nodes.literal_block(block_text, block_text), line=lineno) + return [error], blank_finish + directive_instance = directive( + type_name, arguments, options, content, lineno, + content_offset, block_text, self, self.state_machine) + try: + result = directive_instance.run() + except docutils.parsers.rst.DirectiveError, error: + msg_node = self.reporter.system_message(error.level, error.msg, + source=error.source, line=error.line) + msg_node += nodes.literal_block(block_text, block_text) + msg_node['line'] = lineno + result = [msg_node] + assert isinstance(result, list), \ + 'Directive "%s" must return a list of nodes.' % type_name + for i in range(len(result)): + assert isinstance(result[i], nodes.Node), \ + ('Directive "%s" returned non-Node object (index %s): %r' + % (type_name, i, result[i])) + return (result, + blank_finish or self.state_machine.is_next_line_blank()) + + def parse_directive_block(self, indented, line_offset, directive, + option_presets): + option_spec = directive.option_spec + has_content = directive.has_content + if indented and not indented[0].strip(): + indented.trim_start() + line_offset += 1 + while indented and not indented[-1].strip(): + indented.trim_end() + if indented and (directive.required_arguments + or directive.optional_arguments + or option_spec): + for i in range(len(indented)): + if not indented[i].strip(): + break + else: + i += 1 + arg_block = indented[:i] + content = indented[i+1:] + content_offset = line_offset + i + 1 + else: + content = indented + content_offset = line_offset + arg_block = [] + while content and not content[0].strip(): + content.trim_start() + content_offset += 1 + if option_spec: + options, arg_block = self.parse_directive_options( + option_presets, option_spec, arg_block) + if arg_block and not (directive.required_arguments + or directive.optional_arguments): + raise MarkupError('no arguments permitted; blank line ' + 'required before content block') + else: + options = {} + if directive.required_arguments or directive.optional_arguments: + arguments = self.parse_directive_arguments( + directive, arg_block) + else: + arguments = [] + if content and not has_content: + raise MarkupError('no content permitted') + return (arguments, options, content, content_offset) + + def parse_directive_options(self, option_presets, option_spec, arg_block): + options = option_presets.copy() + for i in range(len(arg_block)): + if arg_block[i][:1] == ':': + opt_block = arg_block[i:] + arg_block = arg_block[:i] + break + else: + opt_block = [] + if opt_block: + success, data = self.parse_extension_options(option_spec, + opt_block) + if success: # data is a dict of options + options.update(data) + else: # data is an error string + raise MarkupError(data) + return options, arg_block + + def parse_directive_arguments(self, directive, arg_block): + required = directive.required_arguments + optional = directive.optional_arguments + arg_text = '\n'.join(arg_block) + arguments = arg_text.split() + if len(arguments) < required: + raise MarkupError('%s argument(s) required, %s supplied' + % (required, len(arguments))) + elif len(arguments) > required + optional: + if directive.final_argument_whitespace: + arguments = arg_text.split(None, required + optional - 1) + else: + raise MarkupError( + 'maximum %s argument(s) allowed, %s supplied' + % (required + optional, len(arguments))) + return arguments + + def parse_extension_options(self, option_spec, datalines): + """ + Parse `datalines` for a field list containing extension options + matching `option_spec`. + + :Parameters: + - `option_spec`: a mapping of option name to conversion + function, which should raise an exception on bad input. + - `datalines`: a list of input strings. + + :Return: + - Success value, 1 or 0. + - An option dictionary on success, an error string on failure. + """ + node = nodes.field_list() + newline_offset, blank_finish = self.nested_list_parse( + datalines, 0, node, initial_state='ExtensionOptions', + blank_finish=1) + if newline_offset != len(datalines): # incomplete parse of block + return 0, 'invalid option block' + try: + options = utils.extract_extension_options(node, option_spec) + except KeyError, detail: + return 0, ('unknown option: "%s"' % detail.args[0]) + except (ValueError, TypeError), detail: + return 0, ('invalid option value: %s' % ' '.join(detail.args)) + except utils.ExtensionOptionError, detail: + return 0, ('invalid option data: %s' % ' '.join(detail.args)) + if blank_finish: + return 1, options + else: + return 0, 'option data incompletely parsed' + + def unknown_directive(self, type_name): + lineno = self.state_machine.abs_line_number() + indented, indent, offset, blank_finish = \ + self.state_machine.get_first_known_indented(0, strip_indent=0) + text = '\n'.join(indented) + error = self.reporter.error( + 'Unknown directive type "%s".' % type_name, + nodes.literal_block(text, text), line=lineno) + return [error], blank_finish + + def comment(self, match): + if not match.string[match.end():].strip() \ + and self.state_machine.is_next_line_blank(): # an empty comment? + return [nodes.comment()], 1 # "A tiny but practical wart." + indented, indent, offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end()) + while indented and not indented[-1].strip(): + indented.trim_end() + text = '\n'.join(indented) + return [nodes.comment(text, text)], blank_finish + + explicit.constructs = [ + (footnote, + re.compile(r""" + \.\.[ ]+ # explicit markup start + \[ + ( # footnote label: + [0-9]+ # manually numbered footnote + | # *OR* + \# # anonymous auto-numbered footnote + | # *OR* + \#%s # auto-number ed?) footnote label + | # *OR* + \* # auto-symbol footnote + ) + \] + ([ ]+|$) # whitespace or end of line + """ % Inliner.simplename, re.VERBOSE | re.UNICODE)), + (citation, + re.compile(r""" + \.\.[ ]+ # explicit markup start + \[(%s)\] # citation label + ([ ]+|$) # whitespace or end of line + """ % Inliner.simplename, re.VERBOSE | re.UNICODE)), + (hyperlink_target, + re.compile(r""" + \.\.[ ]+ # explicit markup start + _ # target indicator + (?![ ]|$) # first char. not space or EOL + """, re.VERBOSE)), + (substitution_def, + re.compile(r""" + \.\.[ ]+ # explicit markup start + \| # substitution indicator + (?![ ]|$) # first char. not space or EOL + """, re.VERBOSE)), + (directive, + re.compile(r""" + \.\.[ ]+ # explicit markup start + (%s) # directive name + [ ]? # optional space + :: # directive delimiter + ([ ]+|$) # whitespace or end of line + """ % Inliner.simplename, re.VERBOSE | re.UNICODE))] + + def explicit_markup(self, match, context, next_state): + """Footnotes, hyperlink targets, directives, comments.""" + nodelist, blank_finish = self.explicit_construct(match) + self.parent += nodelist + self.explicit_list(blank_finish) + return [], next_state, [] + + def explicit_construct(self, match): + """Determine which explicit construct this is, parse & return it.""" + errors = [] + for method, pattern in self.explicit.constructs: + expmatch = pattern.match(match.string) + if expmatch: + try: + return method(self, expmatch) + except MarkupError, error: # never reached? + message, lineno = error.args + errors.append(self.reporter.warning(message, line=lineno)) + break + nodelist, blank_finish = self.comment(match) + return nodelist + errors, blank_finish + + def explicit_list(self, blank_finish): + """ + Create a nested state machine for a series of explicit markup + constructs (including anonymous hyperlink targets). + """ + offset = self.state_machine.line_offset + 1 # next line + newline_offset, blank_finish = self.nested_list_parse( + self.state_machine.input_lines[offset:], + input_offset=self.state_machine.abs_line_offset() + 1, + node=self.parent, initial_state='Explicit', + blank_finish=blank_finish, + match_titles=self.state_machine.match_titles) + self.goto_line(newline_offset) + if not blank_finish: + self.parent += self.unindent_warning('Explicit markup') + + def anonymous(self, match, context, next_state): + """Anonymous hyperlink targets.""" + nodelist, blank_finish = self.anonymous_target(match) + self.parent += nodelist + self.explicit_list(blank_finish) + return [], next_state, [] + + def anonymous_target(self, match): + lineno = self.state_machine.abs_line_number() + block, indent, offset, blank_finish \ + = self.state_machine.get_first_known_indented(match.end(), + until_blank=1) + blocktext = match.string[:match.end()] + '\n'.join(block) + block = [escape2null(line) for line in block] + target = self.make_target(block, blocktext, lineno, '') + return [target], blank_finish + + def line(self, match, context, next_state): + """Section title overline or transition marker.""" + if self.state_machine.match_titles: + return [match.string], 'Line', [] + elif match.string.strip() == '::': + raise statemachine.TransitionCorrection('text') + elif len(match.string.strip()) < 4: + msg = self.reporter.info( + 'Unexpected possible title overline or transition.\n' + "Treating it as ordinary text because it's so short.", + line=self.state_machine.abs_line_number()) + self.parent += msg + raise statemachine.TransitionCorrection('text') + else: + blocktext = self.state_machine.line + msg = self.reporter.severe( + 'Unexpected section title or transition.', + nodes.literal_block(blocktext, blocktext), + line=self.state_machine.abs_line_number()) + self.parent += msg + return [], next_state, [] + + def text(self, match, context, next_state): + """Titles, definition lists, paragraphs.""" + return [match.string], 'Text', [] + + +class RFC2822Body(Body): + + """ + RFC2822 headers are only valid as the first constructs in documents. As + soon as anything else appears, the `Body` state should take over. + """ + + patterns = Body.patterns.copy() # can't modify the original + patterns['rfc2822'] = r'[!-9;-~]+:( +|$)' + initial_transitions = [(name, 'Body') + for name in Body.initial_transitions] + initial_transitions.insert(-1, ('rfc2822', 'Body')) # just before 'text' + + def rfc2822(self, match, context, next_state): + """RFC2822-style field list item.""" + fieldlist = nodes.field_list(classes=['rfc2822']) + self.parent += fieldlist + field, blank_finish = self.rfc2822_field(match) + fieldlist += field + offset = self.state_machine.line_offset + 1 # next line + newline_offset, blank_finish = self.nested_list_parse( + self.state_machine.input_lines[offset:], + input_offset=self.state_machine.abs_line_offset() + 1, + node=fieldlist, initial_state='RFC2822List', + blank_finish=blank_finish) + self.goto_line(newline_offset) + if not blank_finish: + self.parent += self.unindent_warning( + 'RFC2822-style field list') + return [], next_state, [] + + def rfc2822_field(self, match): + name = match.string[:match.string.find(':')] + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end(), + until_blank=1) + fieldnode = nodes.field() + fieldnode += nodes.field_name(name, name) + fieldbody = nodes.field_body('\n'.join(indented)) + fieldnode += fieldbody + if indented: + self.nested_parse(indented, input_offset=line_offset, + node=fieldbody) + return fieldnode, blank_finish + + +class SpecializedBody(Body): + + """ + Superclass for second and subsequent compound element members. Compound + elements are lists and list-like constructs. + + All transition methods are disabled (redefined as `invalid_input`). + Override individual methods in subclasses to re-enable. + + For example, once an initial bullet list item, say, is recognized, the + `BulletList` subclass takes over, with a "bullet_list" node as its + container. Upon encountering the initial bullet list item, `Body.bullet` + calls its ``self.nested_list_parse`` (`RSTState.nested_list_parse`), which + starts up a nested parsing session with `BulletList` as the initial state. + Only the ``bullet`` transition method is enabled in `BulletList`; as long + as only bullet list items are encountered, they are parsed and inserted + into the container. The first construct which is *not* a bullet list item + triggers the `invalid_input` method, which ends the nested parse and + closes the container. `BulletList` needs to recognize input that is + invalid in the context of a bullet list, which means everything *other + than* bullet list items, so it inherits the transition list created in + `Body`. + """ + + def invalid_input(self, match=None, context=None, next_state=None): + """Not a compound element member. Abort this state machine.""" + self.state_machine.previous_line() # back up so parent SM can reassess + raise EOFError + + indent = invalid_input + bullet = invalid_input + enumerator = invalid_input + field_marker = invalid_input + option_marker = invalid_input + doctest = invalid_input + line_block = invalid_input + grid_table_top = invalid_input + simple_table_top = invalid_input + explicit_markup = invalid_input + anonymous = invalid_input + line = invalid_input + text = invalid_input + + +class BulletList(SpecializedBody): + + """Second and subsequent bullet_list list_items.""" + + def bullet(self, match, context, next_state): + """Bullet list item.""" + if match.string[0] != self.parent['bullet']: + # different bullet: new list + self.invalid_input() + listitem, blank_finish = self.list_item(match.end()) + self.parent += listitem + self.blank_finish = blank_finish + return [], next_state, [] + + +class DefinitionList(SpecializedBody): + + """Second and subsequent definition_list_items.""" + + def text(self, match, context, next_state): + """Definition lists.""" + return [match.string], 'Definition', [] + + +class EnumeratedList(SpecializedBody): + + """Second and subsequent enumerated_list list_items.""" + + def enumerator(self, match, context, next_state): + """Enumerated list item.""" + format, sequence, text, ordinal = self.parse_enumerator( + match, self.parent['enumtype']) + if ( format != self.format + or (sequence != '#' and (sequence != self.parent['enumtype'] + or self.auto + or ordinal != (self.lastordinal + 1))) + or not self.is_enumerated_list_item(ordinal, sequence, format)): + # different enumeration: new list + self.invalid_input() + if sequence == '#': + self.auto = 1 + listitem, blank_finish = self.list_item(match.end()) + self.parent += listitem + self.blank_finish = blank_finish + self.lastordinal = ordinal + return [], next_state, [] + + +class FieldList(SpecializedBody): + + """Second and subsequent field_list fields.""" + + def field_marker(self, match, context, next_state): + """Field list field.""" + field, blank_finish = self.field(match) + self.parent += field + self.blank_finish = blank_finish + return [], next_state, [] + + +class OptionList(SpecializedBody): + + """Second and subsequent option_list option_list_items.""" + + def option_marker(self, match, context, next_state): + """Option list item.""" + try: + option_list_item, blank_finish = self.option_list_item(match) + except MarkupError, (message, lineno): + self.invalid_input() + self.parent += option_list_item + self.blank_finish = blank_finish + return [], next_state, [] + + +class RFC2822List(SpecializedBody, RFC2822Body): + + """Second and subsequent RFC2822-style field_list fields.""" + + patterns = RFC2822Body.patterns + initial_transitions = RFC2822Body.initial_transitions + + def rfc2822(self, match, context, next_state): + """RFC2822-style field list item.""" + field, blank_finish = self.rfc2822_field(match) + self.parent += field + self.blank_finish = blank_finish + return [], 'RFC2822List', [] + + blank = SpecializedBody.invalid_input + + +class ExtensionOptions(FieldList): + + """ + Parse field_list fields for extension options. + + No nested parsing is done (including inline markup parsing). + """ + + def parse_field_body(self, indented, offset, node): + """Override `Body.parse_field_body` for simpler parsing.""" + lines = [] + for line in list(indented) + ['']: + if line.strip(): + lines.append(line) + elif lines: + text = '\n'.join(lines) + node += nodes.paragraph(text, text) + lines = [] + + +class LineBlock(SpecializedBody): + + """Second and subsequent lines of a line_block.""" + + blank = SpecializedBody.invalid_input + + def line_block(self, match, context, next_state): + """New line of line block.""" + lineno = self.state_machine.abs_line_number() + line, messages, blank_finish = self.line_block_line(match, lineno) + self.parent += line + self.parent.parent += messages + self.blank_finish = blank_finish + return [], next_state, [] + + +class Explicit(SpecializedBody): + + """Second and subsequent explicit markup construct.""" + + def explicit_markup(self, match, context, next_state): + """Footnotes, hyperlink targets, directives, comments.""" + nodelist, blank_finish = self.explicit_construct(match) + self.parent += nodelist + self.blank_finish = blank_finish + return [], next_state, [] + + def anonymous(self, match, context, next_state): + """Anonymous hyperlink targets.""" + nodelist, blank_finish = self.anonymous_target(match) + self.parent += nodelist + self.blank_finish = blank_finish + return [], next_state, [] + + blank = SpecializedBody.invalid_input + + +class SubstitutionDef(Body): + + """ + Parser for the contents of a substitution_definition element. + """ + + patterns = { + 'embedded_directive': re.compile(r'(%s)::( +|$)' + % Inliner.simplename, re.UNICODE), + 'text': r''} + initial_transitions = ['embedded_directive', 'text'] + + def embedded_directive(self, match, context, next_state): + nodelist, blank_finish = self.directive(match, + alt=self.parent['names'][0]) + self.parent += nodelist + if not self.state_machine.at_eof(): + self.blank_finish = blank_finish + raise EOFError + + def text(self, match, context, next_state): + if not self.state_machine.at_eof(): + self.blank_finish = self.state_machine.is_next_line_blank() + raise EOFError + + +class Text(RSTState): + + """ + Classifier of second line of a text block. + + Could be a paragraph, a definition list item, or a title. + """ + + patterns = {'underline': Body.patterns['line'], + 'text': r''} + initial_transitions = [('underline', 'Body'), ('text', 'Body')] + + def blank(self, match, context, next_state): + """End of paragraph.""" + paragraph, literalnext = self.paragraph( + context, self.state_machine.abs_line_number() - 1) + self.parent += paragraph + if literalnext: + self.parent += self.literal_block() + return [], 'Body', [] + + def eof(self, context): + if context: + self.blank(None, context, None) + return [] + + def indent(self, match, context, next_state): + """Definition list item.""" + definitionlist = nodes.definition_list() + definitionlistitem, blank_finish = self.definition_list_item(context) + definitionlist += definitionlistitem + self.parent += definitionlist + offset = self.state_machine.line_offset + 1 # next line + newline_offset, blank_finish = self.nested_list_parse( + self.state_machine.input_lines[offset:], + input_offset=self.state_machine.abs_line_offset() + 1, + node=definitionlist, initial_state='DefinitionList', + blank_finish=blank_finish, blank_finish_state='Definition') + self.goto_line(newline_offset) + if not blank_finish: + self.parent += self.unindent_warning('Definition list') + return [], 'Body', [] + + def underline(self, match, context, next_state): + """Section title.""" + lineno = self.state_machine.abs_line_number() + title = context[0].rstrip() + underline = match.string.rstrip() + source = title + '\n' + underline + messages = [] + if column_width(title) > len(underline): + if len(underline) < 4: + if self.state_machine.match_titles: + msg = self.reporter.info( + 'Possible title underline, too short for the title.\n' + "Treating it as ordinary text because it's so short.", + line=lineno) + self.parent += msg + raise statemachine.TransitionCorrection('text') + else: + blocktext = context[0] + '\n' + self.state_machine.line + msg = self.reporter.warning( + 'Title underline too short.', + nodes.literal_block(blocktext, blocktext), line=lineno) + messages.append(msg) + if not self.state_machine.match_titles: + blocktext = context[0] + '\n' + self.state_machine.line + msg = self.reporter.severe( + 'Unexpected section title.', + nodes.literal_block(blocktext, blocktext), line=lineno) + self.parent += messages + self.parent += msg + return [], next_state, [] + style = underline[0] + context[:] = [] + self.section(title, source, style, lineno - 1, messages) + return [], next_state, [] + + def text(self, match, context, next_state): + """Paragraph.""" + startline = self.state_machine.abs_line_number() - 1 + msg = None + try: + block = self.state_machine.get_text_block(flush_left=1) + except statemachine.UnexpectedIndentationError, instance: + block, source, lineno = instance.args + msg = self.reporter.error('Unexpected indentation.', + source=source, line=lineno) + lines = context + list(block) + paragraph, literalnext = self.paragraph(lines, startline) + self.parent += paragraph + self.parent += msg + if literalnext: + try: + self.state_machine.next_line() + except EOFError: + pass + self.parent += self.literal_block() + return [], next_state, [] + + def literal_block(self): + """Return a list of nodes.""" + indented, indent, offset, blank_finish = \ + self.state_machine.get_indented() + while indented and not indented[-1].strip(): + indented.trim_end() + if not indented: + return self.quoted_literal_block() + data = '\n'.join(indented) + literal_block = nodes.literal_block(data, data) + literal_block.line = offset + 1 + nodelist = [literal_block] + if not blank_finish: + nodelist.append(self.unindent_warning('Literal block')) + return nodelist + + def quoted_literal_block(self): + abs_line_offset = self.state_machine.abs_line_offset() + offset = self.state_machine.line_offset + parent_node = nodes.Element() + new_abs_offset = self.nested_parse( + self.state_machine.input_lines[offset:], + input_offset=abs_line_offset, node=parent_node, match_titles=0, + state_machine_kwargs={'state_classes': (QuotedLiteralBlock,), + 'initial_state': 'QuotedLiteralBlock'}) + self.goto_line(new_abs_offset) + return parent_node.children + + def definition_list_item(self, termline): + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_indented() + definitionlistitem = nodes.definition_list_item( + '\n'.join(termline + list(indented))) + lineno = self.state_machine.abs_line_number() - 1 + definitionlistitem.line = lineno + termlist, messages = self.term(termline, lineno) + definitionlistitem += termlist + definition = nodes.definition('', *messages) + definitionlistitem += definition + if termline[0][-2:] == '::': + definition += self.reporter.info( + 'Blank line missing before literal block (after the "::")? ' + 'Interpreted as a definition list item.', line=line_offset+1) + self.nested_parse(indented, input_offset=line_offset, node=definition) + return definitionlistitem, blank_finish + + classifier_delimiter = re.compile(' +: +') + + def term(self, lines, lineno): + """Return a definition_list's term and optional classifiers.""" + assert len(lines) == 1 + text_nodes, messages = self.inline_text(lines[0], lineno) + term_node = nodes.term() + node_list = [term_node] + for i in range(len(text_nodes)): + node = text_nodes[i] + if isinstance(node, nodes.Text): + parts = self.classifier_delimiter.split(node.rawsource) + if len(parts) == 1: + node_list[-1] += node + else: + + node_list[-1] += nodes.Text(parts[0].rstrip()) + for part in parts[1:]: + classifier_node = nodes.classifier('', part) + node_list.append(classifier_node) + else: + node_list[-1] += node + return node_list, messages + + +class SpecializedText(Text): + + """ + Superclass for second and subsequent lines of Text-variants. + + All transition methods are disabled. Override individual methods in + subclasses to re-enable. + """ + + def eof(self, context): + """Incomplete construct.""" + return [] + + def invalid_input(self, match=None, context=None, next_state=None): + """Not a compound element member. Abort this state machine.""" + raise EOFError + + blank = invalid_input + indent = invalid_input + underline = invalid_input + text = invalid_input + + +class Definition(SpecializedText): + + """Second line of potential definition_list_item.""" + + def eof(self, context): + """Not a definition.""" + self.state_machine.previous_line(2) # so parent SM can reassess + return [] + + def indent(self, match, context, next_state): + """Definition list item.""" + definitionlistitem, blank_finish = self.definition_list_item(context) + self.parent += definitionlistitem + self.blank_finish = blank_finish + return [], 'DefinitionList', [] + + +class Line(SpecializedText): + + """ + Second line of over- & underlined section title or transition marker. + """ + + eofcheck = 1 # @@@ ??? + """Set to 0 while parsing sections, so that we don't catch the EOF.""" + + def eof(self, context): + """Transition marker at end of section or document.""" + marker = context[0].strip() + if self.memo.section_bubble_up_kludge: + self.memo.section_bubble_up_kludge = 0 + elif len(marker) < 4: + self.state_correction(context) + if self.eofcheck: # ignore EOFError with sections + lineno = self.state_machine.abs_line_number() - 1 + transition = nodes.transition(rawsource=context[0]) + transition.line = lineno + self.parent += transition + self.eofcheck = 1 + return [] + + def blank(self, match, context, next_state): + """Transition marker.""" + lineno = self.state_machine.abs_line_number() - 1 + marker = context[0].strip() + if len(marker) < 4: + self.state_correction(context) + transition = nodes.transition(rawsource=marker) + transition.line = lineno + self.parent += transition + return [], 'Body', [] + + def text(self, match, context, next_state): + """Potential over- & underlined title.""" + lineno = self.state_machine.abs_line_number() - 1 + overline = context[0] + title = match.string + underline = '' + try: + underline = self.state_machine.next_line() + except EOFError: + blocktext = overline + '\n' + title + if len(overline.rstrip()) < 4: + self.short_overline(context, blocktext, lineno, 2) + else: + msg = self.reporter.severe( + 'Incomplete section title.', + nodes.literal_block(blocktext, blocktext), line=lineno) + self.parent += msg + return [], 'Body', [] + source = '%s\n%s\n%s' % (overline, title, underline) + overline = overline.rstrip() + underline = underline.rstrip() + if not self.transitions['underline'][0].match(underline): + blocktext = overline + '\n' + title + '\n' + underline + if len(overline.rstrip()) < 4: + self.short_overline(context, blocktext, lineno, 2) + else: + msg = self.reporter.severe( + 'Missing matching underline for section title overline.', + nodes.literal_block(source, source), line=lineno) + self.parent += msg + return [], 'Body', [] + elif overline != underline: + blocktext = overline + '\n' + title + '\n' + underline + if len(overline.rstrip()) < 4: + self.short_overline(context, blocktext, lineno, 2) + else: + msg = self.reporter.severe( + 'Title overline & underline mismatch.', + nodes.literal_block(source, source), line=lineno) + self.parent += msg + return [], 'Body', [] + title = title.rstrip() + messages = [] + if column_width(title) > len(overline): + blocktext = overline + '\n' + title + '\n' + underline + if len(overline.rstrip()) < 4: + self.short_overline(context, blocktext, lineno, 2) + else: + msg = self.reporter.warning( + 'Title overline too short.', + nodes.literal_block(source, source), line=lineno) + messages.append(msg) + style = (overline[0], underline[0]) + self.eofcheck = 0 # @@@ not sure this is correct + self.section(title.lstrip(), source, style, lineno + 1, messages) + self.eofcheck = 1 + return [], 'Body', [] + + indent = text # indented title + + def underline(self, match, context, next_state): + overline = context[0] + blocktext = overline + '\n' + self.state_machine.line + lineno = self.state_machine.abs_line_number() - 1 + if len(overline.rstrip()) < 4: + self.short_overline(context, blocktext, lineno, 1) + msg = self.reporter.error( + 'Invalid section title or transition marker.', + nodes.literal_block(blocktext, blocktext), line=lineno) + self.parent += msg + return [], 'Body', [] + + def short_overline(self, context, blocktext, lineno, lines=1): + msg = self.reporter.info( + 'Possible incomplete section title.\nTreating the overline as ' + "ordinary text because it's so short.", line=lineno) + self.parent += msg + self.state_correction(context, lines) + + def state_correction(self, context, lines=1): + self.state_machine.previous_line(lines) + context[:] = [] + raise statemachine.StateCorrection('Body', 'text') + + +class QuotedLiteralBlock(RSTState): + + """ + Nested parse handler for quoted (unindented) literal blocks. + + Special-purpose. Not for inclusion in `state_classes`. + """ + + patterns = {'initial_quoted': r'(%(nonalphanum7bit)s)' % Body.pats, + 'text': r''} + initial_transitions = ('initial_quoted', 'text') + + def __init__(self, state_machine, debug=0): + RSTState.__init__(self, state_machine, debug) + self.messages = [] + self.initial_lineno = None + + def blank(self, match, context, next_state): + if context: + raise EOFError + else: + return context, next_state, [] + + def eof(self, context): + if context: + text = '\n'.join(context) + literal_block = nodes.literal_block(text, text) + literal_block.line = self.initial_lineno + self.parent += literal_block + else: + self.parent += self.reporter.warning( + 'Literal block expected; none found.', + line=self.state_machine.abs_line_number()) + self.state_machine.previous_line() + self.parent += self.messages + return [] + + def indent(self, match, context, next_state): + assert context, ('QuotedLiteralBlock.indent: context should not ' + 'be empty!') + self.messages.append( + self.reporter.error('Unexpected indentation.', + line=self.state_machine.abs_line_number())) + self.state_machine.previous_line() + raise EOFError + + def initial_quoted(self, match, context, next_state): + """Match arbitrary quote character on the first line only.""" + self.remove_transition('initial_quoted') + quote = match.string[0] + pattern = re.compile(re.escape(quote)) + # New transition matches consistent quotes only: + self.add_transition('quoted', + (pattern, self.quoted, self.__class__.__name__)) + self.initial_lineno = self.state_machine.abs_line_number() + return [match.string], next_state, [] + + def quoted(self, match, context, next_state): + """Match consistent quotes on subsequent lines.""" + context.append(match.string) + return context, next_state, [] + + def text(self, match, context, next_state): + if context: + self.messages.append( + self.reporter.error('Inconsistent literal block quoting.', + line=self.state_machine.abs_line_number())) + self.state_machine.previous_line() + raise EOFError + + +state_classes = (Body, BulletList, DefinitionList, EnumeratedList, FieldList, + OptionList, LineBlock, ExtensionOptions, Explicit, Text, + Definition, Line, SubstitutionDef, RFC2822Body, RFC2822List) +"""Standard set of State classes used to start `RSTStateMachine`.""" diff -Nru zope3-3.4.0/src/docutils/parsers/rst/tableparser.py zope3-3.5~bzr18/src/docutils/parsers/rst/tableparser.py --- zope3-3.4.0/src/docutils/parsers/rst/tableparser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/parsers/rst/tableparser.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,525 @@ +# $Id: tableparser.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This module defines table parser classes,which parse plaintext-graphic tables +and produce a well-formed data structure suitable for building a CALS table. + +:Classes: + - `GridTableParser`: Parse fully-formed tables represented with a grid. + - `SimpleTableParser`: Parse simple tables, delimited by top & bottom + borders. + +:Exception class: `TableMarkupError` + +:Function: + `update_dict_of_lists()`: Merge two dictionaries containing list values. +""" + +__docformat__ = 'reStructuredText' + + +import re +import sys +from docutils import DataError + + +class TableMarkupError(DataError): pass + + +class TableParser: + + """ + Abstract superclass for the common parts of the syntax-specific parsers. + """ + + head_body_separator_pat = None + """Matches the row separator between head rows and body rows.""" + + double_width_pad_char = '\x00' + """Padding character for East Asian double-width text.""" + + def parse(self, block): + """ + Analyze the text `block` and return a table data structure. + + Given a plaintext-graphic table in `block` (list of lines of text; no + whitespace padding), parse the table, construct and return the data + necessary to construct a CALS table or equivalent. + + Raise `TableMarkupError` if there is any problem with the markup. + """ + self.setup(block) + self.find_head_body_sep() + self.parse_table() + structure = self.structure_from_cells() + return structure + + def find_head_body_sep(self): + """Look for a head/body row separator line; store the line index.""" + for i in range(len(self.block)): + line = self.block[i] + if self.head_body_separator_pat.match(line): + if self.head_body_sep: + raise TableMarkupError( + 'Multiple head/body row separators in table (at line ' + 'offset %s and %s); only one allowed.' + % (self.head_body_sep, i)) + else: + self.head_body_sep = i + self.block[i] = line.replace('=', '-') + if self.head_body_sep == 0 or self.head_body_sep == (len(self.block) + - 1): + raise TableMarkupError('The head/body row separator may not be ' + 'the first or last line of the table.') + + +class GridTableParser(TableParser): + + """ + Parse a grid table using `parse()`. + + Here's an example of a grid table:: + + +------------------------+------------+----------+----------+ + | Header row, column 1 | Header 2 | Header 3 | Header 4 | + +========================+============+==========+==========+ + | body row 1, column 1 | column 2 | column 3 | column 4 | + +------------------------+------------+----------+----------+ + | body row 2 | Cells may span columns. | + +------------------------+------------+---------------------+ + | body row 3 | Cells may | - Table cells | + +------------------------+ span rows. | - contain | + | body row 4 | | - body elements. | + +------------------------+------------+---------------------+ + + Intersections use '+', row separators use '-' (except for one optional + head/body row separator, which uses '='), and column separators use '|'. + + Passing the above table to the `parse()` method will result in the + following data structure:: + + ([24, 12, 10, 10], + [[(0, 0, 1, ['Header row, column 1']), + (0, 0, 1, ['Header 2']), + (0, 0, 1, ['Header 3']), + (0, 0, 1, ['Header 4'])]], + [[(0, 0, 3, ['body row 1, column 1']), + (0, 0, 3, ['column 2']), + (0, 0, 3, ['column 3']), + (0, 0, 3, ['column 4'])], + [(0, 0, 5, ['body row 2']), + (0, 2, 5, ['Cells may span columns.']), + None, + None], + [(0, 0, 7, ['body row 3']), + (1, 0, 7, ['Cells may', 'span rows.', '']), + (1, 1, 7, ['- Table cells', '- contain', '- body elements.']), + None], + [(0, 0, 9, ['body row 4']), None, None, None]]) + + The first item is a list containing column widths (colspecs). The second + item is a list of head rows, and the third is a list of body rows. Each + row contains a list of cells. Each cell is either None (for a cell unused + because of another cell's span), or a tuple. A cell tuple contains four + items: the number of extra rows used by the cell in a vertical span + (morerows); the number of extra columns used by the cell in a horizontal + span (morecols); the line offset of the first line of the cell contents; + and the cell contents, a list of lines of text. + """ + + head_body_separator_pat = re.compile(r'\+=[=+]+=\+ *$') + + def setup(self, block): + self.block = block[:] # make a copy; it may be modified + self.block.disconnect() # don't propagate changes to parent + self.bottom = len(block) - 1 + self.right = len(block[0]) - 1 + self.head_body_sep = None + self.done = [-1] * len(block[0]) + self.cells = [] + self.rowseps = {0: [0]} + self.colseps = {0: [0]} + + def parse_table(self): + """ + Start with a queue of upper-left corners, containing the upper-left + corner of the table itself. Trace out one rectangular cell, remember + it, and add its upper-right and lower-left corners to the queue of + potential upper-left corners of further cells. Process the queue in + top-to-bottom order, keeping track of how much of each text column has + been seen. + + We'll end up knowing all the row and column boundaries, cell positions + and their dimensions. + """ + corners = [(0, 0)] + while corners: + top, left = corners.pop(0) + if top == self.bottom or left == self.right \ + or top <= self.done[left]: + continue + result = self.scan_cell(top, left) + if not result: + continue + bottom, right, rowseps, colseps = result + update_dict_of_lists(self.rowseps, rowseps) + update_dict_of_lists(self.colseps, colseps) + self.mark_done(top, left, bottom, right) + cellblock = self.block.get_2D_block(top + 1, left + 1, + bottom, right) + cellblock.disconnect() # lines in cell can't sync with parent + cellblock.replace(self.double_width_pad_char, '') + self.cells.append((top, left, bottom, right, cellblock)) + corners.extend([(top, right), (bottom, left)]) + corners.sort() + if not self.check_parse_complete(): + raise TableMarkupError('Malformed table; parse incomplete.') + + def mark_done(self, top, left, bottom, right): + """For keeping track of how much of each text column has been seen.""" + before = top - 1 + after = bottom - 1 + for col in range(left, right): + assert self.done[col] == before + self.done[col] = after + + def check_parse_complete(self): + """Each text column should have been completely seen.""" + last = self.bottom - 1 + for col in range(self.right): + if self.done[col] != last: + return None + return 1 + + def scan_cell(self, top, left): + """Starting at the top-left corner, start tracing out a cell.""" + assert self.block[top][left] == '+' + result = self.scan_right(top, left) + return result + + def scan_right(self, top, left): + """ + Look for the top-right corner of the cell, and make note of all column + boundaries ('+'). + """ + colseps = {} + line = self.block[top] + for i in range(left + 1, self.right + 1): + if line[i] == '+': + colseps[i] = [top] + result = self.scan_down(top, left, i) + if result: + bottom, rowseps, newcolseps = result + update_dict_of_lists(colseps, newcolseps) + return bottom, i, rowseps, colseps + elif line[i] != '-': + return None + return None + + def scan_down(self, top, left, right): + """ + Look for the bottom-right corner of the cell, making note of all row + boundaries. + """ + rowseps = {} + for i in range(top + 1, self.bottom + 1): + if self.block[i][right] == '+': + rowseps[i] = [right] + result = self.scan_left(top, left, i, right) + if result: + newrowseps, colseps = result + update_dict_of_lists(rowseps, newrowseps) + return i, rowseps, colseps + elif self.block[i][right] != '|': + return None + return None + + def scan_left(self, top, left, bottom, right): + """ + Noting column boundaries, look for the bottom-left corner of the cell. + It must line up with the starting point. + """ + colseps = {} + line = self.block[bottom] + for i in range(right - 1, left, -1): + if line[i] == '+': + colseps[i] = [bottom] + elif line[i] != '-': + return None + if line[left] != '+': + return None + result = self.scan_up(top, left, bottom, right) + if result is not None: + rowseps = result + return rowseps, colseps + return None + + def scan_up(self, top, left, bottom, right): + """ + Noting row boundaries, see if we can return to the starting point. + """ + rowseps = {} + for i in range(bottom - 1, top, -1): + if self.block[i][left] == '+': + rowseps[i] = [left] + elif self.block[i][left] != '|': + return None + return rowseps + + def structure_from_cells(self): + """ + From the data collected by `scan_cell()`, convert to the final data + structure. + """ + rowseps = self.rowseps.keys() # list of row boundaries + rowseps.sort() + rowindex = {} + for i in range(len(rowseps)): + rowindex[rowseps[i]] = i # row boundary -> row number mapping + colseps = self.colseps.keys() # list of column boundaries + colseps.sort() + colindex = {} + for i in range(len(colseps)): + colindex[colseps[i]] = i # column boundary -> col number map + colspecs = [(colseps[i] - colseps[i - 1] - 1) + for i in range(1, len(colseps))] # list of column widths + # prepare an empty table with the correct number of rows & columns + onerow = [None for i in range(len(colseps) - 1)] + rows = [onerow[:] for i in range(len(rowseps) - 1)] + # keep track of # of cells remaining; should reduce to zero + remaining = (len(rowseps) - 1) * (len(colseps) - 1) + for top, left, bottom, right, block in self.cells: + rownum = rowindex[top] + colnum = colindex[left] + assert rows[rownum][colnum] is None, ( + 'Cell (row %s, column %s) already used.' + % (rownum + 1, colnum + 1)) + morerows = rowindex[bottom] - rownum - 1 + morecols = colindex[right] - colnum - 1 + remaining -= (morerows + 1) * (morecols + 1) + # write the cell into the table + rows[rownum][colnum] = (morerows, morecols, top + 1, block) + assert remaining == 0, 'Unused cells remaining.' + if self.head_body_sep: # separate head rows from body rows + numheadrows = rowindex[self.head_body_sep] + headrows = rows[:numheadrows] + bodyrows = rows[numheadrows:] + else: + headrows = [] + bodyrows = rows + return (colspecs, headrows, bodyrows) + + +class SimpleTableParser(TableParser): + + """ + Parse a simple table using `parse()`. + + Here's an example of a simple table:: + + ===== ===== + col 1 col 2 + ===== ===== + 1 Second column of row 1. + 2 Second column of row 2. + Second line of paragraph. + 3 - Second column of row 3. + + - Second item in bullet + list (row 3, column 2). + 4 is a span + ------------ + 5 + ===== ===== + + Top and bottom borders use '=', column span underlines use '-', column + separation is indicated with spaces. + + Passing the above table to the `parse()` method will result in the + following data structure, whose interpretation is the same as for + `GridTableParser`:: + + ([5, 25], + [[(0, 0, 1, ['col 1']), + (0, 0, 1, ['col 2'])]], + [[(0, 0, 3, ['1']), + (0, 0, 3, ['Second column of row 1.'])], + [(0, 0, 4, ['2']), + (0, 0, 4, ['Second column of row 2.', + 'Second line of paragraph.'])], + [(0, 0, 6, ['3']), + (0, 0, 6, ['- Second column of row 3.', + '', + '- Second item in bullet', + ' list (row 3, column 2).'])], + [(0, 1, 10, ['4 is a span'])], + [(0, 0, 12, ['5']), + (0, 0, 12, [''])]]) + """ + + head_body_separator_pat = re.compile('=[ =]*$') + span_pat = re.compile('-[ -]*$') + + def setup(self, block): + self.block = block[:] # make a copy; it will be modified + self.block.disconnect() # don't propagate changes to parent + # Convert top & bottom borders to column span underlines: + self.block[0] = self.block[0].replace('=', '-') + self.block[-1] = self.block[-1].replace('=', '-') + self.head_body_sep = None + self.columns = [] + self.border_end = None + self.table = [] + self.done = [-1] * len(block[0]) + self.rowseps = {0: [0]} + self.colseps = {0: [0]} + + def parse_table(self): + """ + First determine the column boundaries from the top border, then + process rows. Each row may consist of multiple lines; accumulate + lines until a row is complete. Call `self.parse_row` to finish the + job. + """ + # Top border must fully describe all table columns. + self.columns = self.parse_columns(self.block[0], 0) + self.border_end = self.columns[-1][1] + firststart, firstend = self.columns[0] + offset = 1 # skip top border + start = 1 + text_found = None + while offset < len(self.block): + line = self.block[offset] + if self.span_pat.match(line): + # Column span underline or border; row is complete. + self.parse_row(self.block[start:offset], start, + (line.rstrip(), offset)) + start = offset + 1 + text_found = None + elif line[firststart:firstend].strip(): + # First column not blank, therefore it's a new row. + if text_found and offset != start: + self.parse_row(self.block[start:offset], start) + start = offset + text_found = 1 + elif not text_found: + start = offset + 1 + offset += 1 + + def parse_columns(self, line, offset): + """ + Given a column span underline, return a list of (begin, end) pairs. + """ + cols = [] + end = 0 + while 1: + begin = line.find('-', end) + end = line.find(' ', begin) + if begin < 0: + break + if end < 0: + end = len(line) + cols.append((begin, end)) + if self.columns: + if cols[-1][1] != self.border_end: + raise TableMarkupError('Column span incomplete at line ' + 'offset %s.' % offset) + # Allow for an unbounded rightmost column: + cols[-1] = (cols[-1][0], self.columns[-1][1]) + return cols + + def init_row(self, colspec, offset): + i = 0 + cells = [] + for start, end in colspec: + morecols = 0 + try: + assert start == self.columns[i][0] + while end != self.columns[i][1]: + i += 1 + morecols += 1 + except (AssertionError, IndexError): + raise TableMarkupError('Column span alignment problem at ' + 'line offset %s.' % (offset + 1)) + cells.append([0, morecols, offset, []]) + i += 1 + return cells + + def parse_row(self, lines, start, spanline=None): + """ + Given the text `lines` of a row, parse it and append to `self.table`. + + The row is parsed according to the current column spec (either + `spanline` if provided or `self.columns`). For each column, extract + text from each line, and check for text in column margins. Finally, + adjust for insigificant whitespace. + """ + if not (lines or spanline): + # No new row, just blank lines. + return + if spanline: + columns = self.parse_columns(*spanline) + span_offset = spanline[1] + else: + columns = self.columns[:] + span_offset = start + self.check_columns(lines, start, columns) + row = self.init_row(columns, start) + for i in range(len(columns)): + start, end = columns[i] + cellblock = lines.get_2D_block(0, start, len(lines), end) + cellblock.disconnect() # lines in cell can't sync with parent + cellblock.replace(self.double_width_pad_char, '') + row[i][3] = cellblock + self.table.append(row) + + def check_columns(self, lines, first_line, columns): + """ + Check for text in column margins and text overflow in the last column. + Raise TableMarkupError if anything but whitespace is in column margins. + Adjust the end value for the last column if there is text overflow. + """ + # "Infinite" value for a dummy last column's beginning, used to + # check for text overflow: + columns.append((sys.maxint, None)) + lastcol = len(columns) - 2 + for i in range(len(columns) - 1): + start, end = columns[i] + nextstart = columns[i+1][0] + offset = 0 + for line in lines: + if i == lastcol and line[end:].strip(): + text = line[start:].rstrip() + new_end = start + len(text) + columns[i] = (start, new_end) + main_start, main_end = self.columns[-1] + if new_end > main_end: + self.columns[-1] = (main_start, new_end) + elif line[end:nextstart].strip(): + raise TableMarkupError('Text in column margin at line ' + 'offset %s.' % (first_line + offset)) + offset += 1 + columns.pop() + + def structure_from_cells(self): + colspecs = [end - start for start, end in self.columns] + first_body_row = 0 + if self.head_body_sep: + for i in range(len(self.table)): + if self.table[i][0][2] > self.head_body_sep: + first_body_row = i + break + return (colspecs, self.table[:first_body_row], + self.table[first_body_row:]) + + +def update_dict_of_lists(master, newdata): + """ + Extend the list values of `master` with those from `newdata`. + + Both parameters must be dictionaries containing list values. + """ + for key, values in newdata.items(): + master.setdefault(key, []).extend(values) diff -Nru zope3-3.4.0/src/docutils/readers/doctree.py zope3-3.5~bzr18/src/docutils/readers/doctree.py --- zope3-3.4.0/src/docutils/readers/doctree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/readers/doctree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,46 @@ +# $Id: doctree.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Martin Blais +# Copyright: This module has been placed in the public domain. + +"""Reader for existing document trees.""" + +from docutils import readers, utils, transforms + + +class Reader(readers.ReReader): + + """ + Adapt the Reader API for an existing document tree. + + The existing document tree must be passed as the ``source`` parameter to + the `docutils.core.Publisher` initializer, wrapped in a + `docutils.io.DocTreeInput` object:: + + pub = docutils.core.Publisher( + ..., source=docutils.io.DocTreeInput(document), ...) + + The original document settings are overridden; if you want to use the + settings of the original document, pass ``settings=document.settings`` to + the Publisher call above. + """ + + supported = ('doctree',) + + config_section = 'doctree reader' + config_section_dependencies = ('readers',) + + def parse(self): + """ + No parsing to do; refurbish the document tree instead. + Overrides the inherited method. + """ + self.document = self.input + # Create fresh Transformer object, to be populated from Writer + # component. + self.document.transformer = transforms.Transformer(self.document) + # Replace existing settings object with new one. + self.document.settings = self.settings + # Create fresh Reporter object because it is dependent on + # (new) settings. + self.document.reporter = utils.new_reporter( + self.document.get('source', ''), self.document.settings) diff -Nru zope3-3.4.0/src/docutils/readers/__init__.py zope3-3.5~bzr18/src/docutils/readers/__init__.py --- zope3-3.4.0/src/docutils/readers/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/readers/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,107 @@ +# $Id: __init__.py 5618 2008-07-28 08:37:32Z strank $ +# Authors: David Goodger ; Ueli Schlaepfer +# Copyright: This module has been placed in the public domain. + +""" +This package contains Docutils Reader modules. +""" + +__docformat__ = 'reStructuredText' + + +from docutils import utils, parsers, Component +from docutils.transforms import universal + + +class Reader(Component): + + """ + Abstract base class for docutils Readers. + + Each reader module or package must export a subclass also called 'Reader'. + + The two steps of a Reader's responsibility are `scan()` and + `parse()`. Call `read()` to process a document. + """ + + component_type = 'reader' + config_section = 'readers' + + def get_transforms(self): + return Component.get_transforms(self) + [ + universal.Decorations, + universal.ExposeInternals, + universal.StripComments,] + + def __init__(self, parser=None, parser_name=None): + """ + Initialize the Reader instance. + + Several instance attributes are defined with dummy initial values. + Subclasses may use these attributes as they wish. + """ + + self.parser = parser + """A `parsers.Parser` instance shared by all doctrees. May be left + unspecified if the document source determines the parser.""" + + if parser is None and parser_name: + self.set_parser(parser_name) + + self.source = None + """`docutils.io` IO object, source of input data.""" + + self.input = None + """Raw text input; either a single string or, for more complex cases, + a collection of strings.""" + + def set_parser(self, parser_name): + """Set `self.parser` by name.""" + parser_class = parsers.get_parser_class(parser_name) + self.parser = parser_class() + + def read(self, source, parser, settings): + self.source = source + if not self.parser: + self.parser = parser + self.settings = settings + self.input = self.source.read() + self.parse() + return self.document + + def parse(self): + """Parse `self.input` into a document tree.""" + self.document = document = self.new_document() + self.parser.parse(self.input, document) + document.current_source = document.current_line = None + + def new_document(self): + """Create and return a new empty document tree (root node).""" + document = utils.new_document(self.source.source_path, self.settings) + return document + + +class ReReader(Reader): + + """ + A reader which rereads an existing document tree (e.g. a + deserializer). + + Often used in conjunction with `writers.UnfilteredWriter`. + """ + + def get_transforms(self): + # Do not add any transforms. They have already been applied + # by the reader which originally created the document. + return Component.get_transforms(self) + + +_reader_aliases = {} + +def get_reader_class(reader_name): + """Return the Reader class from the `reader_name` module.""" + reader_name = reader_name.lower() + if reader_name in _reader_aliases: + reader_name = _reader_aliases[reader_name] + module = __import__(reader_name, globals(), locals()) + return module.Reader diff -Nru zope3-3.4.0/src/docutils/readers/pep.py zope3-3.5~bzr18/src/docutils/readers/pep.py --- zope3-3.4.0/src/docutils/readers/pep.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/readers/pep.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ +# $Id: pep.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Python Enhancement Proposal (PEP) Reader. +""" + +__docformat__ = 'reStructuredText' + + +from docutils.readers import standalone +from docutils.transforms import peps, references, misc, frontmatter +from docutils.parsers import rst + + +class Reader(standalone.Reader): + + supported = ('pep',) + """Contexts this reader supports.""" + + settings_spec = ( + 'PEP Reader Option Defaults', + 'The --pep-references and --rfc-references options (for the ' + 'reStructuredText parser) are on by default.', + ()) + + config_section = 'pep reader' + config_section_dependencies = ('readers', 'standalone reader') + + def get_transforms(self): + transforms = standalone.Reader.get_transforms(self) + # We have PEP-specific frontmatter handling. + transforms.remove(frontmatter.DocTitle) + transforms.remove(frontmatter.SectionSubTitle) + transforms.remove(frontmatter.DocInfo) + transforms.extend([peps.Headers, peps.Contents, peps.TargetNotes]) + return transforms + + settings_default_overrides = {'pep_references': 1, 'rfc_references': 1} + + inliner_class = rst.states.Inliner + + def __init__(self, parser=None, parser_name=None): + """`parser` should be ``None``.""" + if parser is None: + parser = rst.Parser(rfc2822=1, inliner=self.inliner_class()) + standalone.Reader.__init__(self, parser, '') diff -Nru zope3-3.4.0/src/docutils/readers/python/__init__.py zope3-3.5~bzr18/src/docutils/readers/python/__init__.py --- zope3-3.4.0/src/docutils/readers/python/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/readers/python/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,127 @@ +# $Id: __init__.py 5618 2008-07-28 08:37:32Z strank $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This package contains the Python Source Reader modules. +""" + +__docformat__ = 'reStructuredText' + + +import sys +import docutils.readers +from docutils.readers.python import moduleparser +from docutils import parsers +from docutils import nodes +from docutils.readers.python import pynodes +from docutils import readers + +class Reader(docutils.readers.Reader): + + config_section = 'python reader' + config_section_dependencies = ('readers',) + + default_parser = 'restructuredtext' + + def parse(self): + """Parse `self.input` into a document tree.""" + self.document = document = self.new_document() + module_section = moduleparser.parse_module(self.input, + self.source.source_path) + module_section.walk(DocformatVisitor(self.document)) + visitor = DocstringFormattingVisitor( + document=document, + default_parser=self.default_parser) + module_section.walk(visitor) + self.document.append(module_section) + + +class DocformatVisitor(nodes.SparseNodeVisitor): + + """ + This sets docformat attributes in a module. Wherever an assignment + to __docformat__ is found, we look for the enclosing scope -- a class, + a module, or a function -- and set the docformat attribute there. + + We can't do this during the DocstringFormattingVisitor walking, + because __docformat__ may appear below a docstring in that format + (typically below the module docstring). + """ + + def visit_attribute(self, node): + assert isinstance(node[0], pynodes.object_name) + name = node[0][0].data + if name != '__docformat__': + return + value = None + for child in children: + if isinstance(child, pynodes.expression_value): + value = child[0].data + break + assert value.startswith("'") or value.startswith('"'), "__docformat__ must be assigned a string literal (not %s); line: %s" % (value, node['lineno']) + name = name[1:-1] + looking_in = node.parent + while not isinstance(looking_in, (pynodes.module_section, + pynodes.function_section, + pynodes.class_section)): + looking_in = looking_in.parent + looking_in['docformat'] = name + + +class DocstringFormattingVisitor(nodes.SparseNodeVisitor): + + def __init__(self, document, default_parser): + self.document = document + self.default_parser = default_parser + self.parsers = {} + + def visit_docstring(self, node): + text = node[0].data + docformat = self.find_docformat(node) + del node[0] + node['docformat'] = docformat + parser = self.get_parser(docformat) + parser.parse(text, self.document) + for child in self.document.children: + node.append(child) + self.document.current_source = self.document.current_line = None + del self.document[:] + + def get_parser(self, parser_name): + """ + Get a parser based on its name. We reuse parsers during this + visitation, so parser instances are cached. + """ + parser_name = parsers._parser_aliases.get(parser_name, parser_name) + if parser_name not in self.parsers: + cls = parsers.get_parser_class(parser_name) + self.parsers[parser_name] = cls() + return self.parsers[parser_name] + + def find_docformat(self, node): + """ + Find the __docformat__ closest to this node (i.e., look in the + class or module) + """ + while node: + if node.get('docformat'): + return node['docformat'] + node = node.parent + return self.default_parser + + +if __name__ == '__main__': + try: + import locale + locale.setlocale(locale.LC_ALL, '') + except: + pass + + from docutils.core import publish_cmdline, default_description + + description = ('Generates pseudo-XML from Python modules ' + '(for testing purposes). ' + default_description) + + publish_cmdline(description=description, + reader=Reader()) diff -Nru zope3-3.4.0/src/docutils/readers/python/moduleparser.py zope3-3.5~bzr18/src/docutils/readers/python/moduleparser.py --- zope3-3.4.0/src/docutils/readers/python/moduleparser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/readers/python/moduleparser.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,757 @@ +# $Id: moduleparser.py 5738 2008-11-30 08:59:04Z grubert $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Parser for Python modules. + +The `parse_module()` function takes a module's text and file name, +runs it through the module parser (using compiler.py and tokenize.py) +and produces a parse tree of the source code, using the nodes as found +in pynodes.py. For example, given this module (x.py):: + + # comment + + '''Docstring''' + + '''Additional docstring''' + + __docformat__ = 'reStructuredText' + + a = 1 + '''Attribute docstring''' + + class C(Super): + + '''C's docstring''' + + class_attribute = 1 + '''class_attribute's docstring''' + + def __init__(self, text=None): + '''__init__'s docstring''' + + self.instance_attribute = (text * 7 + + ' whaddyaknow') + '''instance_attribute's docstring''' + + + def f(x, # parameter x + y=a*5, # parameter y + *args): # parameter args + '''f's docstring''' + return [x + item for item in args] + + f.function_attribute = 1 + '''f.function_attribute's docstring''' + +The module parser will produce this module documentation tree:: + + + + Docstring + + Additional docstring + + + __docformat__ + + 'reStructuredText' + + + a + + 1 + + Attribute docstring + + + C + + Super + + C's docstring + + + class_attribute + + 1 + + class_attribute's docstring + + + __init__ + + __init__'s docstring + + + + self + + + text + + None + + + self.instance_attribute + + (text * 7 + ' whaddyaknow') + + instance_attribute's docstring + + + f + + f's docstring + + + + x + + # parameter x + + + y + + a * 5 + + # parameter y + + + args + + # parameter args + + + f.function_attribute + + 1 + + f.function_attribute's docstring + +(Comments are not implemented yet.) + +compiler.parse() provides most of what's needed for this doctree, and +"tokenize" can be used to get the rest. We can determine the line +number from the compiler.parse() AST, and the TokenParser.rhs(lineno) +method provides the rest. + +The Docutils Python reader component will transform this module doctree into a +Python-specific Docutils doctree, and then a "stylist transform" will +further transform it into a generic doctree. Namespaces will have to be +compiled for each of the scopes, but I'm not certain at what stage of +processing. + +It's very important to keep all docstring processing out of this, so that it's +a completely generic and not tool-specific. + +:: + +> Why perform all of those transformations? Why not go from the AST to a +> generic doctree? Or, even from the AST to the final output? + +I want the docutils.readers.python.moduleparser.parse_module() function to +produce a standard documentation-oriented tree that can be used by any tool. +We can develop it together without having to compromise on the rest of our +design (i.e., HappyDoc doesn't have to be made to work like Docutils, and +vice-versa). It would be a higher-level version of what compiler.py provides. + +The Python reader component transforms this generic AST into a Python-specific +doctree (it knows about modules, classes, functions, etc.), but this is +specific to Docutils and cannot be used by HappyDoc or others. The stylist +transform does the final layout, converting Python-specific structures +("class" sections, etc.) into a generic doctree using primitives (tables, +sections, lists, etc.). This generic doctree does *not* know about Python +structures any more. The advantage is that this doctree can be handed off to +any of the output writers to create any output format we like. + +The latter two transforms are separate because I want to be able to have +multiple independent layout styles (multiple runtime-selectable "stylist +transforms"). Each of the existing tools (HappyDoc, pydoc, epydoc, Crystal, +etc.) has its own fixed format. I personally don't like the tables-based +format produced by these tools, and I'd like to be able to customize the +format easily. That's the goal of stylist transforms, which are independent +from the Reader component itself. One stylist transform could produce +HappyDoc-like output, another could produce output similar to module docs in +the Python library reference manual, and so on. + +It's for exactly this reason:: + +>> It's very important to keep all docstring processing out of this, so that +>> it's a completely generic and not tool-specific. + +... but it goes past docstring processing. It's also important to keep style +decisions and tool-specific data transforms out of this module parser. + + +Issues +====== + +* At what point should namespaces be computed? Should they be part of the + basic AST produced by the ASTVisitor walk, or generated by another tree + traversal? + +* At what point should a distinction be made between local variables & + instance attributes in __init__ methods? + +* Docstrings are getting their lineno from their parents. Should the + TokenParser find the real line no's? + +* Comments: include them? How and when? Only full-line comments, or + parameter comments too? (See function "f" above for an example.) + +* Module could use more docstrings & refactoring in places. + +""" + +__docformat__ = 'reStructuredText' + +import sys +import compiler +import compiler.ast +import tokenize +import token +from compiler.consts import OP_ASSIGN +from compiler.visitor import ASTVisitor +from docutils.readers.python import pynodes +from docutils.nodes import Text + + +def parse_module(module_text, filename): + """Return a module documentation tree from `module_text`.""" + ast = compiler.parse(module_text) + token_parser = TokenParser(module_text) + visitor = ModuleVisitor(filename, token_parser) + compiler.walk(ast, visitor, walker=visitor) + return visitor.module + +class BaseVisitor(ASTVisitor): + + def __init__(self, token_parser): + ASTVisitor.__init__(self) + self.token_parser = token_parser + self.context = [] + self.documentable = None + + def default(self, node, *args): + self.documentable = None + #print 'in default (%s)' % node.__class__.__name__ + #ASTVisitor.default(self, node, *args) + + def default_visit(self, node, *args): + #print 'in default_visit (%s)' % node.__class__.__name__ + ASTVisitor.default(self, node, *args) + + +class DocstringVisitor(BaseVisitor): + + def visitDiscard(self, node): + if self.documentable: + self.visit(node.expr) + + def visitConst(self, node): + if self.documentable: + if type(node.value) in (str, unicode): + self.documentable.append(make_docstring(node.value, node.lineno)) + else: + self.documentable = None + + def visitStmt(self, node): + self.default_visit(node) + + +class AssignmentVisitor(DocstringVisitor): + + def visitAssign(self, node): + visitor = AttributeVisitor(self.token_parser) + compiler.walk(node, visitor, walker=visitor) + if visitor.attributes: + self.context[-1].extend(visitor.attributes) + if len(visitor.attributes) == 1: + self.documentable = visitor.attributes[0] + else: + self.documentable = None + + +class ModuleVisitor(AssignmentVisitor): + + def __init__(self, filename, token_parser): + AssignmentVisitor.__init__(self, token_parser) + self.filename = filename + self.module = None + + def visitModule(self, node): + self.module = module = pynodes.module_section() + module['filename'] = self.filename + append_docstring(module, node.doc, node.lineno) + self.context.append(module) + self.documentable = module + self.visit(node.node) + self.context.pop() + + def visitImport(self, node): + self.context[-1] += make_import_group(names=node.names, + lineno=node.lineno) + self.documentable = None + + def visitFrom(self, node): + self.context[-1].append( + make_import_group(names=node.names, from_name=node.modname, + lineno=node.lineno)) + self.documentable = None + + def visitFunction(self, node): + visitor = FunctionVisitor(self.token_parser, + function_class=pynodes.function_section) + compiler.walk(node, visitor, walker=visitor) + self.context[-1].append(visitor.function) + + def visitClass(self, node): + visitor = ClassVisitor(self.token_parser) + compiler.walk(node, visitor, walker=visitor) + self.context[-1].append(visitor.klass) + + +class AttributeVisitor(BaseVisitor): + + def __init__(self, token_parser): + BaseVisitor.__init__(self, token_parser) + self.attributes = pynodes.class_attribute_section() + + def visitAssign(self, node): + # Don't visit the expression itself, just the attribute nodes: + for child in node.nodes: + self.dispatch(child) + expression_text = self.token_parser.rhs(node.lineno) + expression = pynodes.expression_value() + expression.append(Text(expression_text)) + for attribute in self.attributes: + attribute.append(expression) + + def visitAssName(self, node): + self.attributes.append(make_attribute(node.name, + lineno=node.lineno)) + + def visitAssTuple(self, node): + attributes = self.attributes + self.attributes = [] + self.default_visit(node) + n = pynodes.attribute_tuple() + n.extend(self.attributes) + n['lineno'] = self.attributes[0]['lineno'] + attributes.append(n) + self.attributes = attributes + #self.attributes.append(att_tuple) + + def visitAssAttr(self, node): + self.default_visit(node, node.attrname) + + def visitGetattr(self, node, suffix): + self.default_visit(node, node.attrname + '.' + suffix) + + def visitName(self, node, suffix): + self.attributes.append(make_attribute(node.name + '.' + suffix, + lineno=node.lineno)) + + +class FunctionVisitor(DocstringVisitor): + + in_function = 0 + + def __init__(self, token_parser, function_class): + DocstringVisitor.__init__(self, token_parser) + self.function_class = function_class + + def visitFunction(self, node): + if self.in_function: + self.documentable = None + # Don't bother with nested function definitions. + return + self.in_function = 1 + self.function = function = make_function_like_section( + name=node.name, + lineno=node.lineno, + doc=node.doc, + function_class=self.function_class) + self.context.append(function) + self.documentable = function + self.parse_parameter_list(node) + self.visit(node.code) + self.context.pop() + + def parse_parameter_list(self, node): + parameters = [] + special = [] + argnames = list(node.argnames) + if node.kwargs: + special.append(make_parameter(argnames[-1], excess_keyword=1)) + argnames.pop() + if node.varargs: + special.append(make_parameter(argnames[-1], + excess_positional=1)) + argnames.pop() + defaults = list(node.defaults) + defaults = [None] * (len(argnames) - len(defaults)) + defaults + function_parameters = self.token_parser.function_parameters( + node.lineno) + #print >>sys.stderr, function_parameters + for argname, default in zip(argnames, defaults): + if type(argname) is tuple: + parameter = pynodes.parameter_tuple() + for tuplearg in argname: + parameter.append(make_parameter(tuplearg)) + argname = normalize_parameter_name(argname) + else: + parameter = make_parameter(argname) + if default: + n_default = pynodes.parameter_default() + n_default.append(Text(function_parameters[argname])) + parameter.append(n_default) + parameters.append(parameter) + if parameters or special: + special.reverse() + parameters.extend(special) + parameter_list = pynodes.parameter_list() + parameter_list.extend(parameters) + self.function.append(parameter_list) + + +class ClassVisitor(AssignmentVisitor): + + in_class = 0 + + def __init__(self, token_parser): + AssignmentVisitor.__init__(self, token_parser) + self.bases = [] + + def visitClass(self, node): + if self.in_class: + self.documentable = None + # Don't bother with nested class definitions. + return + self.in_class = 1 + #import mypdb as pdb + #pdb.set_trace() + for base in node.bases: + self.visit(base) + self.klass = klass = make_class_section(node.name, self.bases, + doc=node.doc, + lineno=node.lineno) + self.context.append(klass) + self.documentable = klass + self.visit(node.code) + self.context.pop() + + def visitGetattr(self, node, suffix=None): + if suffix: + name = node.attrname + '.' + suffix + else: + name = node.attrname + self.default_visit(node, name) + + def visitName(self, node, suffix=None): + if suffix: + name = node.name + '.' + suffix + else: + name = node.name + self.bases.append(name) + + def visitFunction(self, node): + if node.name == '__init__': + visitor = InitMethodVisitor(self.token_parser, + function_class=pynodes.method_section) + compiler.walk(node, visitor, walker=visitor) + else: + visitor = FunctionVisitor(self.token_parser, + function_class=pynodes.method_section) + compiler.walk(node, visitor, walker=visitor) + self.context[-1].append(visitor.function) + + +class InitMethodVisitor(FunctionVisitor, AssignmentVisitor): pass + + +class TokenParser: + + def __init__(self, text): + self.text = text + '\n\n' + self.lines = self.text.splitlines(1) + self.generator = tokenize.generate_tokens(iter(self.lines).next) + self.next() + + def __iter__(self): + return self + + def next(self): + self.token = self.generator.next() + self.type, self.string, self.start, self.end, self.line = self.token + return self.token + + def goto_line(self, lineno): + while self.start[0] < lineno: + self.next() + return token + + def rhs(self, lineno): + """ + Return a whitespace-normalized expression string from the right-hand + side of an assignment at line `lineno`. + """ + self.goto_line(lineno) + while self.string != '=': + self.next() + self.stack = None + while self.type != token.NEWLINE and self.string != ';': + if self.string == '=' and not self.stack: + self.tokens = [] + self.stack = [] + self._type = None + self._string = None + self._backquote = 0 + else: + self.note_token() + self.next() + self.next() + text = ''.join(self.tokens) + return text.strip() + + closers = {')': '(', ']': '[', '}': '{'} + openers = {'(': 1, '[': 1, '{': 1} + del_ws_prefix = {'.': 1, '=': 1, ')': 1, ']': 1, '}': 1, ':': 1, ',': 1} + no_ws_suffix = {'.': 1, '=': 1, '(': 1, '[': 1, '{': 1} + + def note_token(self): + if self.type == tokenize.NL: + return + del_ws = self.string in self.del_ws_prefix + append_ws = self.string not in self.no_ws_suffix + if self.string in self.openers: + self.stack.append(self.string) + if (self._type == token.NAME + or self._string in self.closers): + del_ws = 1 + elif self.string in self.closers: + assert self.stack[-1] == self.closers[self.string] + self.stack.pop() + elif self.string == '`': + if self._backquote: + del_ws = 1 + assert self.stack[-1] == '`' + self.stack.pop() + else: + append_ws = 0 + self.stack.append('`') + self._backquote = not self._backquote + if del_ws and self.tokens and self.tokens[-1] == ' ': + del self.tokens[-1] + self.tokens.append(self.string) + self._type = self.type + self._string = self.string + if append_ws: + self.tokens.append(' ') + + def function_parameters(self, lineno): + """ + Return a dictionary mapping parameters to defaults + (whitespace-normalized strings). + """ + self.goto_line(lineno) + while self.string != 'def': + self.next() + while self.string != '(': + self.next() + name = None + default = None + parameter_tuple = None + self.tokens = [] + parameters = {} + self.stack = [self.string] + self.next() + while 1: + if len(self.stack) == 1: + if parameter_tuple: + # Just encountered ")". + #print >>sys.stderr, 'parameter_tuple: %r' % self.tokens + name = ''.join(self.tokens).strip() + self.tokens = [] + parameter_tuple = None + if self.string in (')', ','): + if name: + if self.tokens: + default_text = ''.join(self.tokens).strip() + else: + default_text = None + parameters[name] = default_text + self.tokens = [] + name = None + default = None + if self.string == ')': + break + elif self.type == token.NAME: + if name and default: + self.note_token() + else: + assert name is None, ( + 'token=%r name=%r parameters=%r stack=%r' + % (self.token, name, parameters, self.stack)) + name = self.string + #print >>sys.stderr, 'name=%r' % name + elif self.string == '=': + assert name is not None, 'token=%r' % (self.token,) + assert default is None, 'token=%r' % (self.token,) + assert self.tokens == [], 'token=%r' % (self.token,) + default = 1 + self._type = None + self._string = None + self._backquote = 0 + elif name: + self.note_token() + elif self.string == '(': + parameter_tuple = 1 + self._type = None + self._string = None + self._backquote = 0 + self.note_token() + else: # ignore these tokens: + assert (self.string in ('*', '**', '\n') + or self.type == tokenize.COMMENT), ( + 'token=%r' % (self.token,)) + else: + self.note_token() + self.next() + return parameters + + +def make_docstring(doc, lineno): + n = pynodes.docstring() + if lineno: + # Really, only module docstrings don't have a line + # (@@: but maybe they should) + n['lineno'] = lineno + n.append(Text(doc)) + return n + +def append_docstring(node, doc, lineno): + if doc: + node.append(make_docstring(doc, lineno)) + +def make_class_section(name, bases, lineno, doc): + n = pynodes.class_section() + n['lineno'] = lineno + n.append(make_object_name(name)) + for base in bases: + b = pynodes.class_base() + b.append(make_object_name(base)) + n.append(b) + append_docstring(n, doc, lineno) + return n + +def make_object_name(name): + n = pynodes.object_name() + n.append(Text(name)) + return n + +def make_function_like_section(name, lineno, doc, function_class): + n = function_class() + n['lineno'] = lineno + n.append(make_object_name(name)) + append_docstring(n, doc, lineno) + return n + +def make_import_group(names, lineno, from_name=None): + n = pynodes.import_group() + n['lineno'] = lineno + if from_name: + n_from = pynodes.import_from() + n_from.append(Text(from_name)) + n.append(n_from) + for name, alias in names: + n_name = pynodes.import_name() + n_name.append(Text(name)) + if alias: + n_alias = pynodes.import_alias() + n_alias.append(Text(alias)) + n_name.append(n_alias) + n.append(n_name) + return n + +def make_class_attribute(name, lineno): + n = pynodes.class_attribute() + n['lineno'] = lineno + n.append(Text(name)) + return n + +def make_attribute(name, lineno): + n = pynodes.attribute() + n['lineno'] = lineno + n.append(make_object_name(name)) + return n + +def make_parameter(name, excess_keyword=0, excess_positional=0): + """ + excess_keyword and excess_positional must be either 1 or 0, and + not both of them can be 1. + """ + n = pynodes.parameter() + n.append(make_object_name(name)) + assert not excess_keyword or not excess_positional + if excess_keyword: + n['excess_keyword'] = 1 + if excess_positional: + n['excess_positional'] = 1 + return n + +def trim_docstring(text): + """ + Trim indentation and blank lines from docstring text & return it. + + See PEP 257. + """ + if not text: + return text + # Convert tabs to spaces (following the normal Python rules) + # and split into a list of lines: + lines = text.expandtabs().splitlines() + # Determine minimum indentation (first line doesn't count): + indent = sys.maxint + for line in lines[1:]: + stripped = line.lstrip() + if stripped: + indent = min(indent, len(line) - len(stripped)) + # Remove indentation (first line is special): + trimmed = [lines[0].strip()] + if indent < sys.maxint: + for line in lines[1:]: + trimmed.append(line[indent:].rstrip()) + # Strip off trailing and leading blank lines: + while trimmed and not trimmed[-1]: + trimmed.pop() + while trimmed and not trimmed[0]: + trimmed.pop(0) + # Return a single string: + return '\n'.join(trimmed) + +def normalize_parameter_name(name): + """ + Converts a tuple like ``('a', ('b', 'c'), 'd')`` into ``'(a, (b, c), d)'`` + """ + if type(name) is tuple: + return '(%s)' % ', '.join([normalize_parameter_name(n) for n in name]) + else: + return name + +if __name__ == '__main__': + import sys + args = sys.argv[1:] + if args[0] == '-v': + filename = args[1] + module_text = open(filename).read() + ast = compiler.parse(module_text) + visitor = compiler.visitor.ExampleASTVisitor() + compiler.walk(ast, visitor, walker=visitor, verbose=1) + else: + filename = args[0] + content = open(filename).read() + print parse_module(content, filename).pformat() + diff -Nru zope3-3.4.0/src/docutils/readers/python/pynodes.py zope3-3.5~bzr18/src/docutils/readers/python/pynodes.py --- zope3-3.4.0/src/docutils/readers/python/pynodes.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/readers/python/pynodes.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,81 @@ +#! /usr/bin/env python +# $Id: pynodes.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +from docutils import nodes +from docutils.nodes import Element, TextElement, Structural, Inline, Part, \ + Text +import types + +# This is the parent class of all the other pynode classes: +class PythonStructural(Structural): pass + +# ===================== +# Structural Elements +# ===================== + +class module_section(PythonStructural, Element): pass +class class_section(PythonStructural, Element): pass +class class_base(PythonStructural, Element): pass +class method_section(PythonStructural, Element): pass +class attribute(PythonStructural, Element): pass +class function_section(PythonStructural, Element): pass +class class_attribute_section(PythonStructural, Element): pass +class class_attribute(PythonStructural, Element): pass +class expression_value(PythonStructural, Element): pass +class attribute(PythonStructural, Element): pass + +# Structural Support Elements +# --------------------------- + +class parameter_list(PythonStructural, Element): pass +class parameter_tuple(PythonStructural, Element): pass +class parameter_default(PythonStructural, TextElement): pass +class import_group(PythonStructural, TextElement): pass +class import_from(PythonStructural, TextElement): pass +class import_name(PythonStructural, TextElement): pass +class import_alias(PythonStructural, TextElement): pass +class docstring(PythonStructural, Element): pass + +# ================= +# Inline Elements +# ================= + +# These elements cannot become references until the second +# pass. Initially, we'll use "reference" or "name". + +class object_name(PythonStructural, TextElement): pass +class parameter_list(PythonStructural, TextElement): pass +class parameter(PythonStructural, TextElement): pass +class parameter_default(PythonStructural, TextElement): pass +class class_attribute(PythonStructural, TextElement): pass +class attribute_tuple(PythonStructural, TextElement): pass + +# ================= +# Unused Elements +# ================= + +# These were part of the model, and maybe should be in the future, but +# aren't now. +#class package_section(PythonStructural, Element): pass +#class module_attribute_section(PythonStructural, Element): pass +#class instance_attribute_section(PythonStructural, Element): pass +#class module_attribute(PythonStructural, TextElement): pass +#class instance_attribute(PythonStructural, TextElement): pass +#class exception_class(PythonStructural, TextElement): pass +#class warning_class(PythonStructural, TextElement): pass + + +# Collect all the classes we've written above +def install_node_class_names(): + node_class_names = [] + for name, var in globals().items(): + if (type(var) is types.ClassType + and issubclass(var, PythonStructural) \ + and name.lower() == name): + node_class_names.append(var.tagname or name) + # Register the new node names with GenericNodeVisitor and + # SpecificNodeVisitor: + nodes._add_node_class_names(node_class_names) +install_node_class_names() diff -Nru zope3-3.4.0/src/docutils/readers/standalone.py zope3-3.5~bzr18/src/docutils/readers/standalone.py --- zope3-3.4.0/src/docutils/readers/standalone.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/readers/standalone.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,66 @@ +# $Id: standalone.py 4802 2006-11-12 18:02:17Z goodger $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Standalone file Reader for the reStructuredText markup syntax. +""" + +__docformat__ = 'reStructuredText' + + +import sys +from docutils import frontend, readers +from docutils.transforms import frontmatter, references, misc + + +class Reader(readers.Reader): + + supported = ('standalone',) + """Contexts this reader supports.""" + + document = None + """A single document tree.""" + + settings_spec = ( + 'Standalone Reader', + None, + (('Disable the promotion of a lone top-level section title to ' + 'document title (and subsequent section title to document ' + 'subtitle promotion; enabled by default).', + ['--no-doc-title'], + {'dest': 'doctitle_xform', 'action': 'store_false', 'default': 1, + 'validator': frontend.validate_boolean}), + ('Disable the bibliographic field list transform (enabled by ' + 'default).', + ['--no-doc-info'], + {'dest': 'docinfo_xform', 'action': 'store_false', 'default': 1, + 'validator': frontend.validate_boolean}), + ('Activate the promotion of lone subsection titles to ' + 'section subtitles (disabled by default).', + ['--section-subtitles'], + {'dest': 'sectsubtitle_xform', 'action': 'store_true', 'default': 0, + 'validator': frontend.validate_boolean}), + ('Deactivate the promotion of lone subsection titles.', + ['--no-section-subtitles'], + {'dest': 'sectsubtitle_xform', 'action': 'store_false'}), + )) + + config_section = 'standalone reader' + config_section_dependencies = ('readers',) + + def get_transforms(self): + return readers.Reader.get_transforms(self) + [ + references.Substitutions, + references.PropagateTargets, + frontmatter.DocTitle, + frontmatter.SectionSubTitle, + frontmatter.DocInfo, + references.AnonymousHyperlinks, + references.IndirectHyperlinks, + references.Footnotes, + references.ExternalTargets, + references.InternalTargets, + references.DanglingReferences, + misc.Transitions, + ] diff -Nru zope3-3.4.0/src/docutils/statemachine.py zope3-3.5~bzr18/src/docutils/statemachine.py --- zope3-3.4.0/src/docutils/statemachine.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/statemachine.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1483 @@ +# $Id: statemachine.py 5968 2009-06-02 14:44:19Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A finite state machine specialized for regular-expression-based text filters, +this module defines the following classes: + +- `StateMachine`, a state machine +- `State`, a state superclass +- `StateMachineWS`, a whitespace-sensitive version of `StateMachine` +- `StateWS`, a state superclass for use with `StateMachineWS` +- `SearchStateMachine`, uses `re.search()` instead of `re.match()` +- `SearchStateMachineWS`, uses `re.search()` instead of `re.match()` +- `ViewList`, extends standard Python lists. +- `StringList`, string-specific ViewList. + +Exception classes: + +- `StateMachineError` +- `UnknownStateError` +- `DuplicateStateError` +- `UnknownTransitionError` +- `DuplicateTransitionError` +- `TransitionPatternNotFound` +- `TransitionMethodNotFound` +- `UnexpectedIndentationError` +- `TransitionCorrection`: Raised to switch to another transition. +- `StateCorrection`: Raised to switch to another state & transition. + +Functions: + +- `string2lines()`: split a multi-line string into a list of one-line strings + + +How To Use This Module +====================== +(See the individual classes, methods, and attributes for details.) + +1. Import it: ``import statemachine`` or ``from statemachine import ...``. + You will also need to ``import re``. + +2. Derive a subclass of `State` (or `StateWS`) for each state in your state + machine:: + + class MyState(statemachine.State): + + Within the state's class definition: + + a) Include a pattern for each transition, in `State.patterns`:: + + patterns = {'atransition': r'pattern', ...} + + b) Include a list of initial transitions to be set up automatically, in + `State.initial_transitions`:: + + initial_transitions = ['atransition', ...] + + c) Define a method for each transition, with the same name as the + transition pattern:: + + def atransition(self, match, context, next_state): + # do something + result = [...] # a list + return context, next_state, result + # context, next_state may be altered + + Transition methods may raise an `EOFError` to cut processing short. + + d) You may wish to override the `State.bof()` and/or `State.eof()` implicit + transition methods, which handle the beginning- and end-of-file. + + e) In order to handle nested processing, you may wish to override the + attributes `State.nested_sm` and/or `State.nested_sm_kwargs`. + + If you are using `StateWS` as a base class, in order to handle nested + indented blocks, you may wish to: + + - override the attributes `StateWS.indent_sm`, + `StateWS.indent_sm_kwargs`, `StateWS.known_indent_sm`, and/or + `StateWS.known_indent_sm_kwargs`; + - override the `StateWS.blank()` method; and/or + - override or extend the `StateWS.indent()`, `StateWS.known_indent()`, + and/or `StateWS.firstknown_indent()` methods. + +3. Create a state machine object:: + + sm = StateMachine(state_classes=[MyState, ...], + initial_state='MyState') + +4. Obtain the input text, which needs to be converted into a tab-free list of + one-line strings. For example, to read text from a file called + 'inputfile':: + + input_string = open('inputfile').read() + input_lines = statemachine.string2lines(input_string) + +5. Run the state machine on the input text and collect the results, a list:: + + results = sm.run(input_lines) + +6. Remove any lingering circular references:: + + sm.unlink() +""" + +__docformat__ = 'restructuredtext' + +import sys +import re +import types +import unicodedata + + +class StateMachine: + + """ + A finite state machine for text filters using regular expressions. + + The input is provided in the form of a list of one-line strings (no + newlines). States are subclasses of the `State` class. Transitions consist + of regular expression patterns and transition methods, and are defined in + each state. + + The state machine is started with the `run()` method, which returns the + results of processing in a list. + """ + + def __init__(self, state_classes, initial_state, debug=0): + """ + Initialize a `StateMachine` object; add state objects. + + Parameters: + + - `state_classes`: a list of `State` (sub)classes. + - `initial_state`: a string, the class name of the initial state. + - `debug`: a boolean; produce verbose output if true (nonzero). + """ + + self.input_lines = None + """`StringList` of input lines (without newlines). + Filled by `self.run()`.""" + + self.input_offset = 0 + """Offset of `self.input_lines` from the beginning of the file.""" + + self.line = None + """Current input line.""" + + self.line_offset = -1 + """Current input line offset from beginning of `self.input_lines`.""" + + self.debug = debug + """Debugging mode on/off.""" + + self.initial_state = initial_state + """The name of the initial state (key to `self.states`).""" + + self.current_state = initial_state + """The name of the current state (key to `self.states`).""" + + self.states = {} + """Mapping of {state_name: State_object}.""" + + self.add_states(state_classes) + + self.observers = [] + """List of bound methods or functions to call whenever the current + line changes. Observers are called with one argument, ``self``. + Cleared at the end of `run()`.""" + + def unlink(self): + """Remove circular references to objects no longer required.""" + for state in self.states.values(): + state.unlink() + self.states = None + + def run(self, input_lines, input_offset=0, context=None, + input_source=None, initial_state=None): + """ + Run the state machine on `input_lines`. Return results (a list). + + Reset `self.line_offset` and `self.current_state`. Run the + beginning-of-file transition. Input one line at a time and check for a + matching transition. If a match is found, call the transition method + and possibly change the state. Store the context returned by the + transition method to be passed on to the next transition matched. + Accumulate the results returned by the transition methods in a list. + Run the end-of-file transition. Finally, return the accumulated + results. + + Parameters: + + - `input_lines`: a list of strings without newlines, or `StringList`. + - `input_offset`: the line offset of `input_lines` from the beginning + of the file. + - `context`: application-specific storage. + - `input_source`: name or path of source of `input_lines`. + - `initial_state`: name of initial state. + """ + self.runtime_init() + if isinstance(input_lines, StringList): + self.input_lines = input_lines + else: + self.input_lines = StringList(input_lines, source=input_source) + self.input_offset = input_offset + self.line_offset = -1 + self.current_state = initial_state or self.initial_state + if self.debug: + print >>sys.stderr, ( + '\nStateMachine.run: input_lines (line_offset=%s):\n| %s' + % (self.line_offset, '\n| '.join(self.input_lines))) + transitions = None + results = [] + state = self.get_state() + try: + if self.debug: + print >>sys.stderr, ('\nStateMachine.run: bof transition') + context, result = state.bof(context) + results.extend(result) + while 1: + try: + try: + self.next_line() + if self.debug: + source, offset = self.input_lines.info( + self.line_offset) + print >>sys.stderr, ( + '\nStateMachine.run: line (source=%r, ' + 'offset=%r):\n| %s' + % (source, offset, self.line)) + context, next_state, result = self.check_line( + context, state, transitions) + except EOFError: + if self.debug: + print >>sys.stderr, ( + '\nStateMachine.run: %s.eof transition' + % state.__class__.__name__) + result = state.eof(context) + results.extend(result) + break + else: + results.extend(result) + except TransitionCorrection, exception: + self.previous_line() # back up for another try + transitions = (exception.args[0],) + if self.debug: + print >>sys.stderr, ( + '\nStateMachine.run: TransitionCorrection to ' + 'state "%s", transition %s.' + % (state.__class__.__name__, transitions[0])) + continue + except StateCorrection, exception: + self.previous_line() # back up for another try + next_state = exception.args[0] + if len(exception.args) == 1: + transitions = None + else: + transitions = (exception.args[1],) + if self.debug: + print >>sys.stderr, ( + '\nStateMachine.run: StateCorrection to state ' + '"%s", transition %s.' + % (next_state, transitions[0])) + else: + transitions = None + state = self.get_state(next_state) + except: + if self.debug: + self.error() + raise + self.observers = [] + return results + + def get_state(self, next_state=None): + """ + Return current state object; set it first if `next_state` given. + + Parameter `next_state`: a string, the name of the next state. + + Exception: `UnknownStateError` raised if `next_state` unknown. + """ + if next_state: + if self.debug and next_state != self.current_state: + print >>sys.stderr, \ + ('\nStateMachine.get_state: Changing state from ' + '"%s" to "%s" (input line %s).' + % (self.current_state, next_state, + self.abs_line_number())) + self.current_state = next_state + try: + return self.states[self.current_state] + except KeyError: + raise UnknownStateError(self.current_state) + + def next_line(self, n=1): + """Load `self.line` with the `n`'th next line and return it.""" + try: + try: + self.line_offset += n + self.line = self.input_lines[self.line_offset] + except IndexError: + self.line = None + raise EOFError + return self.line + finally: + self.notify_observers() + + def is_next_line_blank(self): + """Return 1 if the next line is blank or non-existant.""" + try: + return not self.input_lines[self.line_offset + 1].strip() + except IndexError: + return 1 + + def at_eof(self): + """Return 1 if the input is at or past end-of-file.""" + return self.line_offset >= len(self.input_lines) - 1 + + def at_bof(self): + """Return 1 if the input is at or before beginning-of-file.""" + return self.line_offset <= 0 + + def previous_line(self, n=1): + """Load `self.line` with the `n`'th previous line and return it.""" + self.line_offset -= n + if self.line_offset < 0: + self.line = None + else: + self.line = self.input_lines[self.line_offset] + self.notify_observers() + return self.line + + def goto_line(self, line_offset): + """Jump to absolute line offset `line_offset`, load and return it.""" + try: + try: + self.line_offset = line_offset - self.input_offset + self.line = self.input_lines[self.line_offset] + except IndexError: + self.line = None + raise EOFError + return self.line + finally: + self.notify_observers() + + def get_source(self, line_offset): + """Return source of line at absolute line offset `line_offset`.""" + return self.input_lines.source(line_offset - self.input_offset) + + def abs_line_offset(self): + """Return line offset of current line, from beginning of file.""" + return self.line_offset + self.input_offset + + def abs_line_number(self): + """Return line number of current line (counting from 1).""" + return self.line_offset + self.input_offset + 1 + + def insert_input(self, input_lines, source): + self.input_lines.insert(self.line_offset + 1, '', + source='internal padding') + self.input_lines.insert(self.line_offset + 1, '', + source='internal padding') + self.input_lines.insert(self.line_offset + 2, + StringList(input_lines, source)) + + def get_text_block(self, flush_left=0): + """ + Return a contiguous block of text. + + If `flush_left` is true, raise `UnexpectedIndentationError` if an + indented line is encountered before the text block ends (with a blank + line). + """ + try: + block = self.input_lines.get_text_block(self.line_offset, + flush_left) + self.next_line(len(block) - 1) + return block + except UnexpectedIndentationError, error: + block, source, lineno = error.args + self.next_line(len(block) - 1) # advance to last line of block + raise + + def check_line(self, context, state, transitions=None): + """ + Examine one line of input for a transition match & execute its method. + + Parameters: + + - `context`: application-dependent storage. + - `state`: a `State` object, the current state. + - `transitions`: an optional ordered list of transition names to try, + instead of ``state.transition_order``. + + Return the values returned by the transition method: + + - context: possibly modified from the parameter `context`; + - next state name (`State` subclass name); + - the result output of the transition, a list. + + When there is no match, ``state.no_match()`` is called and its return + value is returned. + """ + if transitions is None: + transitions = state.transition_order + state_correction = None + if self.debug: + print >>sys.stderr, ( + '\nStateMachine.check_line: state="%s", transitions=%r.' + % (state.__class__.__name__, transitions)) + for name in transitions: + pattern, method, next_state = state.transitions[name] + match = pattern.match(self.line) + if match: + if self.debug: + print >>sys.stderr, ( + '\nStateMachine.check_line: Matched transition ' + '"%s" in state "%s".' + % (name, state.__class__.__name__)) + return method(match, context, next_state) + else: + if self.debug: + print >>sys.stderr, ( + '\nStateMachine.check_line: No match in state "%s".' + % state.__class__.__name__) + return state.no_match(context, transitions) + + def add_state(self, state_class): + """ + Initialize & add a `state_class` (`State` subclass) object. + + Exception: `DuplicateStateError` raised if `state_class` was already + added. + """ + statename = state_class.__name__ + if statename in self.states: + raise DuplicateStateError(statename) + self.states[statename] = state_class(self, self.debug) + + def add_states(self, state_classes): + """ + Add `state_classes` (a list of `State` subclasses). + """ + for state_class in state_classes: + self.add_state(state_class) + + def runtime_init(self): + """ + Initialize `self.states`. + """ + for state in self.states.values(): + state.runtime_init() + + def error(self): + """Report error details.""" + type, value, module, line, function = _exception_data() + print >>sys.stderr, '%s: %s' % (type, value) + print >>sys.stderr, 'input line %s' % (self.abs_line_number()) + print >>sys.stderr, ('module %s, line %s, function %s' + % (module, line, function)) + + def attach_observer(self, observer): + """ + The `observer` parameter is a function or bound method which takes two + arguments, the source and offset of the current line. + """ + self.observers.append(observer) + + def detach_observer(self, observer): + self.observers.remove(observer) + + def notify_observers(self): + for observer in self.observers: + try: + info = self.input_lines.info(self.line_offset) + except IndexError: + info = (None, None) + observer(*info) + + +class State: + + """ + State superclass. Contains a list of transitions, and transition methods. + + Transition methods all have the same signature. They take 3 parameters: + + - An `re` match object. ``match.string`` contains the matched input line, + ``match.start()`` gives the start index of the match, and + ``match.end()`` gives the end index. + - A context object, whose meaning is application-defined (initial value + ``None``). It can be used to store any information required by the state + machine, and the retured context is passed on to the next transition + method unchanged. + - The name of the next state, a string, taken from the transitions list; + normally it is returned unchanged, but it may be altered by the + transition method if necessary. + + Transition methods all return a 3-tuple: + + - A context object, as (potentially) modified by the transition method. + - The next state name (a return value of ``None`` means no state change). + - The processing result, a list, which is accumulated by the state + machine. + + Transition methods may raise an `EOFError` to cut processing short. + + There are two implicit transitions, and corresponding transition methods + are defined: `bof()` handles the beginning-of-file, and `eof()` handles + the end-of-file. These methods have non-standard signatures and return + values. `bof()` returns the initial context and results, and may be used + to return a header string, or do any other processing needed. `eof()` + should handle any remaining context and wrap things up; it returns the + final processing result. + + Typical applications need only subclass `State` (or a subclass), set the + `patterns` and `initial_transitions` class attributes, and provide + corresponding transition methods. The default object initialization will + take care of constructing the list of transitions. + """ + + patterns = None + """ + {Name: pattern} mapping, used by `make_transition()`. Each pattern may + be a string or a compiled `re` pattern. Override in subclasses. + """ + + initial_transitions = None + """ + A list of transitions to initialize when a `State` is instantiated. + Each entry is either a transition name string, or a (transition name, next + state name) pair. See `make_transitions()`. Override in subclasses. + """ + + nested_sm = None + """ + The `StateMachine` class for handling nested processing. + + If left as ``None``, `nested_sm` defaults to the class of the state's + controlling state machine. Override it in subclasses to avoid the default. + """ + + nested_sm_kwargs = None + """ + Keyword arguments dictionary, passed to the `nested_sm` constructor. + + Two keys must have entries in the dictionary: + + - Key 'state_classes' must be set to a list of `State` classes. + - Key 'initial_state' must be set to the name of the initial state class. + + If `nested_sm_kwargs` is left as ``None``, 'state_classes' defaults to the + class of the current state, and 'initial_state' defaults to the name of + the class of the current state. Override in subclasses to avoid the + defaults. + """ + + def __init__(self, state_machine, debug=0): + """ + Initialize a `State` object; make & add initial transitions. + + Parameters: + + - `statemachine`: the controlling `StateMachine` object. + - `debug`: a boolean; produce verbose output if true (nonzero). + """ + + self.transition_order = [] + """A list of transition names in search order.""" + + self.transitions = {} + """ + A mapping of transition names to 3-tuples containing + (compiled_pattern, transition_method, next_state_name). Initialized as + an instance attribute dynamically (instead of as a class attribute) + because it may make forward references to patterns and methods in this + or other classes. + """ + + self.add_initial_transitions() + + self.state_machine = state_machine + """A reference to the controlling `StateMachine` object.""" + + self.debug = debug + """Debugging mode on/off.""" + + if self.nested_sm is None: + self.nested_sm = self.state_machine.__class__ + if self.nested_sm_kwargs is None: + self.nested_sm_kwargs = {'state_classes': [self.__class__], + 'initial_state': self.__class__.__name__} + + def runtime_init(self): + """ + Initialize this `State` before running the state machine; called from + `self.state_machine.run()`. + """ + pass + + def unlink(self): + """Remove circular references to objects no longer required.""" + self.state_machine = None + + def add_initial_transitions(self): + """Make and add transitions listed in `self.initial_transitions`.""" + if self.initial_transitions: + names, transitions = self.make_transitions( + self.initial_transitions) + self.add_transitions(names, transitions) + + def add_transitions(self, names, transitions): + """ + Add a list of transitions to the start of the transition list. + + Parameters: + + - `names`: a list of transition names. + - `transitions`: a mapping of names to transition tuples. + + Exceptions: `DuplicateTransitionError`, `UnknownTransitionError`. + """ + for name in names: + if name in self.transitions: + raise DuplicateTransitionError(name) + if name not in transitions: + raise UnknownTransitionError(name) + self.transition_order[:0] = names + self.transitions.update(transitions) + + def add_transition(self, name, transition): + """ + Add a transition to the start of the transition list. + + Parameter `transition`: a ready-made transition 3-tuple. + + Exception: `DuplicateTransitionError`. + """ + if name in self.transitions: + raise DuplicateTransitionError(name) + self.transition_order[:0] = [name] + self.transitions[name] = transition + + def remove_transition(self, name): + """ + Remove a transition by `name`. + + Exception: `UnknownTransitionError`. + """ + try: + del self.transitions[name] + self.transition_order.remove(name) + except: + raise UnknownTransitionError(name) + + def make_transition(self, name, next_state=None): + """ + Make & return a transition tuple based on `name`. + + This is a convenience function to simplify transition creation. + + Parameters: + + - `name`: a string, the name of the transition pattern & method. This + `State` object must have a method called '`name`', and a dictionary + `self.patterns` containing a key '`name`'. + - `next_state`: a string, the name of the next `State` object for this + transition. A value of ``None`` (or absent) implies no state change + (i.e., continue with the same state). + + Exceptions: `TransitionPatternNotFound`, `TransitionMethodNotFound`. + """ + if next_state is None: + next_state = self.__class__.__name__ + try: + pattern = self.patterns[name] + if not hasattr(pattern, 'match'): + pattern = re.compile(pattern) + except KeyError: + raise TransitionPatternNotFound( + '%s.patterns[%r]' % (self.__class__.__name__, name)) + try: + method = getattr(self, name) + except AttributeError: + raise TransitionMethodNotFound( + '%s.%s' % (self.__class__.__name__, name)) + return (pattern, method, next_state) + + def make_transitions(self, name_list): + """ + Return a list of transition names and a transition mapping. + + Parameter `name_list`: a list, where each entry is either a transition + name string, or a 1- or 2-tuple (transition name, optional next state + name). + """ + stringtype = type('') + names = [] + transitions = {} + for namestate in name_list: + if type(namestate) is stringtype: + transitions[namestate] = self.make_transition(namestate) + names.append(namestate) + else: + transitions[namestate[0]] = self.make_transition(*namestate) + names.append(namestate[0]) + return names, transitions + + def no_match(self, context, transitions): + """ + Called when there is no match from `StateMachine.check_line()`. + + Return the same values returned by transition methods: + + - context: unchanged; + - next state name: ``None``; + - empty result list. + + Override in subclasses to catch this event. + """ + return context, None, [] + + def bof(self, context): + """ + Handle beginning-of-file. Return unchanged `context`, empty result. + + Override in subclasses. + + Parameter `context`: application-defined storage. + """ + return context, [] + + def eof(self, context): + """ + Handle end-of-file. Return empty result. + + Override in subclasses. + + Parameter `context`: application-defined storage. + """ + return [] + + def nop(self, match, context, next_state): + """ + A "do nothing" transition method. + + Return unchanged `context` & `next_state`, empty result. Useful for + simple state changes (actionless transitions). + """ + return context, next_state, [] + + +class StateMachineWS(StateMachine): + + """ + `StateMachine` subclass specialized for whitespace recognition. + + There are three methods provided for extracting indented text blocks: + + - `get_indented()`: use when the indent is unknown. + - `get_known_indented()`: use when the indent is known for all lines. + - `get_first_known_indented()`: use when only the first line's indent is + known. + """ + + def get_indented(self, until_blank=0, strip_indent=1): + """ + Return a block of indented lines of text, and info. + + Extract an indented block where the indent is unknown for all lines. + + :Parameters: + - `until_blank`: Stop collecting at the first blank line if true + (1). + - `strip_indent`: Strip common leading indent if true (1, + default). + + :Return: + - the indented block (a list of lines of text), + - its indent, + - its first line offset from BOF, and + - whether or not it finished with a blank line. + """ + offset = self.abs_line_offset() + indented, indent, blank_finish = self.input_lines.get_indented( + self.line_offset, until_blank, strip_indent) + if indented: + self.next_line(len(indented) - 1) # advance to last indented line + while indented and not indented[0].strip(): + indented.trim_start() + offset += 1 + return indented, indent, offset, blank_finish + + def get_known_indented(self, indent, until_blank=0, strip_indent=1): + """ + Return an indented block and info. + + Extract an indented block where the indent is known for all lines. + Starting with the current line, extract the entire text block with at + least `indent` indentation (which must be whitespace, except for the + first line). + + :Parameters: + - `indent`: The number of indent columns/characters. + - `until_blank`: Stop collecting at the first blank line if true + (1). + - `strip_indent`: Strip `indent` characters of indentation if true + (1, default). + + :Return: + - the indented block, + - its first line offset from BOF, and + - whether or not it finished with a blank line. + """ + offset = self.abs_line_offset() + indented, indent, blank_finish = self.input_lines.get_indented( + self.line_offset, until_blank, strip_indent, + block_indent=indent) + self.next_line(len(indented) - 1) # advance to last indented line + while indented and not indented[0].strip(): + indented.trim_start() + offset += 1 + return indented, offset, blank_finish + + def get_first_known_indented(self, indent, until_blank=0, strip_indent=1, + strip_top=1): + """ + Return an indented block and info. + + Extract an indented block where the indent is known for the first line + and unknown for all other lines. + + :Parameters: + - `indent`: The first line's indent (# of columns/characters). + - `until_blank`: Stop collecting at the first blank line if true + (1). + - `strip_indent`: Strip `indent` characters of indentation if true + (1, default). + - `strip_top`: Strip blank lines from the beginning of the block. + + :Return: + - the indented block, + - its indent, + - its first line offset from BOF, and + - whether or not it finished with a blank line. + """ + offset = self.abs_line_offset() + indented, indent, blank_finish = self.input_lines.get_indented( + self.line_offset, until_blank, strip_indent, + first_indent=indent) + self.next_line(len(indented) - 1) # advance to last indented line + if strip_top: + while indented and not indented[0].strip(): + indented.trim_start() + offset += 1 + return indented, indent, offset, blank_finish + + +class StateWS(State): + + """ + State superclass specialized for whitespace (blank lines & indents). + + Use this class with `StateMachineWS`. The transitions 'blank' (for blank + lines) and 'indent' (for indented text blocks) are added automatically, + before any other transitions. The transition method `blank()` handles + blank lines and `indent()` handles nested indented blocks. Indented + blocks trigger a new state machine to be created by `indent()` and run. + The class of the state machine to be created is in `indent_sm`, and the + constructor keyword arguments are in the dictionary `indent_sm_kwargs`. + + The methods `known_indent()` and `firstknown_indent()` are provided for + indented blocks where the indent (all lines' and first line's only, + respectively) is known to the transition method, along with the attributes + `known_indent_sm` and `known_indent_sm_kwargs`. Neither transition method + is triggered automatically. + """ + + indent_sm = None + """ + The `StateMachine` class handling indented text blocks. + + If left as ``None``, `indent_sm` defaults to the value of + `State.nested_sm`. Override it in subclasses to avoid the default. + """ + + indent_sm_kwargs = None + """ + Keyword arguments dictionary, passed to the `indent_sm` constructor. + + If left as ``None``, `indent_sm_kwargs` defaults to the value of + `State.nested_sm_kwargs`. Override it in subclasses to avoid the default. + """ + + known_indent_sm = None + """ + The `StateMachine` class handling known-indented text blocks. + + If left as ``None``, `known_indent_sm` defaults to the value of + `indent_sm`. Override it in subclasses to avoid the default. + """ + + known_indent_sm_kwargs = None + """ + Keyword arguments dictionary, passed to the `known_indent_sm` constructor. + + If left as ``None``, `known_indent_sm_kwargs` defaults to the value of + `indent_sm_kwargs`. Override it in subclasses to avoid the default. + """ + + ws_patterns = {'blank': ' *$', + 'indent': ' +'} + """Patterns for default whitespace transitions. May be overridden in + subclasses.""" + + ws_initial_transitions = ('blank', 'indent') + """Default initial whitespace transitions, added before those listed in + `State.initial_transitions`. May be overridden in subclasses.""" + + def __init__(self, state_machine, debug=0): + """ + Initialize a `StateSM` object; extends `State.__init__()`. + + Check for indent state machine attributes, set defaults if not set. + """ + State.__init__(self, state_machine, debug) + if self.indent_sm is None: + self.indent_sm = self.nested_sm + if self.indent_sm_kwargs is None: + self.indent_sm_kwargs = self.nested_sm_kwargs + if self.known_indent_sm is None: + self.known_indent_sm = self.indent_sm + if self.known_indent_sm_kwargs is None: + self.known_indent_sm_kwargs = self.indent_sm_kwargs + + def add_initial_transitions(self): + """ + Add whitespace-specific transitions before those defined in subclass. + + Extends `State.add_initial_transitions()`. + """ + State.add_initial_transitions(self) + if self.patterns is None: + self.patterns = {} + self.patterns.update(self.ws_patterns) + names, transitions = self.make_transitions( + self.ws_initial_transitions) + self.add_transitions(names, transitions) + + def blank(self, match, context, next_state): + """Handle blank lines. Does nothing. Override in subclasses.""" + return self.nop(match, context, next_state) + + def indent(self, match, context, next_state): + """ + Handle an indented text block. Extend or override in subclasses. + + Recursively run the registered state machine for indented blocks + (`self.indent_sm`). + """ + indented, indent, line_offset, blank_finish = \ + self.state_machine.get_indented() + sm = self.indent_sm(debug=self.debug, **self.indent_sm_kwargs) + results = sm.run(indented, input_offset=line_offset) + return context, next_state, results + + def known_indent(self, match, context, next_state): + """ + Handle a known-indent text block. Extend or override in subclasses. + + Recursively run the registered state machine for known-indent indented + blocks (`self.known_indent_sm`). The indent is the length of the + match, ``match.end()``. + """ + indented, line_offset, blank_finish = \ + self.state_machine.get_known_indented(match.end()) + sm = self.known_indent_sm(debug=self.debug, + **self.known_indent_sm_kwargs) + results = sm.run(indented, input_offset=line_offset) + return context, next_state, results + + def first_known_indent(self, match, context, next_state): + """ + Handle an indented text block (first line's indent known). + + Extend or override in subclasses. + + Recursively run the registered state machine for known-indent indented + blocks (`self.known_indent_sm`). The indent is the length of the + match, ``match.end()``. + """ + indented, line_offset, blank_finish = \ + self.state_machine.get_first_known_indented(match.end()) + sm = self.known_indent_sm(debug=self.debug, + **self.known_indent_sm_kwargs) + results = sm.run(indented, input_offset=line_offset) + return context, next_state, results + + +class _SearchOverride: + + """ + Mix-in class to override `StateMachine` regular expression behavior. + + Changes regular expression matching, from the default `re.match()` + (succeeds only if the pattern matches at the start of `self.line`) to + `re.search()` (succeeds if the pattern matches anywhere in `self.line`). + When subclassing a `StateMachine`, list this class **first** in the + inheritance list of the class definition. + """ + + def match(self, pattern): + """ + Return the result of a regular expression search. + + Overrides `StateMachine.match()`. + + Parameter `pattern`: `re` compiled regular expression. + """ + return pattern.search(self.line) + + +class SearchStateMachine(_SearchOverride, StateMachine): + """`StateMachine` which uses `re.search()` instead of `re.match()`.""" + pass + + +class SearchStateMachineWS(_SearchOverride, StateMachineWS): + """`StateMachineWS` which uses `re.search()` instead of `re.match()`.""" + pass + + +class ViewList: + + """ + List with extended functionality: slices of ViewList objects are child + lists, linked to their parents. Changes made to a child list also affect + the parent list. A child list is effectively a "view" (in the SQL sense) + of the parent list. Changes to parent lists, however, do *not* affect + active child lists. If a parent list is changed, any active child lists + should be recreated. + + The start and end of the slice can be trimmed using the `trim_start()` and + `trim_end()` methods, without affecting the parent list. The link between + child and parent lists can be broken by calling `disconnect()` on the + child list. + + Also, ViewList objects keep track of the source & offset of each item. + This information is accessible via the `source()`, `offset()`, and + `info()` methods. + """ + + def __init__(self, initlist=None, source=None, items=None, + parent=None, parent_offset=None): + self.data = [] + """The actual list of data, flattened from various sources.""" + + self.items = [] + """A list of (source, offset) pairs, same length as `self.data`: the + source of each line and the offset of each line from the beginning of + its source.""" + + self.parent = parent + """The parent list.""" + + self.parent_offset = parent_offset + """Offset of this list from the beginning of the parent list.""" + + if isinstance(initlist, ViewList): + self.data = initlist.data[:] + self.items = initlist.items[:] + elif initlist is not None: + self.data = list(initlist) + if items: + self.items = items + else: + self.items = [(source, i) for i in range(len(initlist))] + assert len(self.data) == len(self.items), 'data mismatch' + + def __str__(self): + return str(self.data) + + def __repr__(self): + return '%s(%s, items=%s)' % (self.__class__.__name__, + self.data, self.items) + + def __lt__(self, other): return self.data < self.__cast(other) + def __le__(self, other): return self.data <= self.__cast(other) + def __eq__(self, other): return self.data == self.__cast(other) + def __ne__(self, other): return self.data != self.__cast(other) + def __gt__(self, other): return self.data > self.__cast(other) + def __ge__(self, other): return self.data >= self.__cast(other) + def __cmp__(self, other): return cmp(self.data, self.__cast(other)) + + def __cast(self, other): + if isinstance(other, ViewList): + return other.data + else: + return other + + def __contains__(self, item): return item in self.data + def __len__(self): return len(self.data) + + # The __getitem__()/__setitem__() methods check whether the index + # is a slice first, since native list objects start supporting + # them directly in Python 2.3 (no exception is raised when + # indexing a list with a slice object; they just work). + + def __getitem__(self, i): + if isinstance(i, types.SliceType): + assert i.step in (None, 1), 'cannot handle slice with stride' + return self.__class__(self.data[i.start:i.stop], + items=self.items[i.start:i.stop], + parent=self, parent_offset=i.start or 0) + else: + return self.data[i] + + def __setitem__(self, i, item): + if isinstance(i, types.SliceType): + assert i.step in (None, 1), 'cannot handle slice with stride' + if not isinstance(item, ViewList): + raise TypeError('assigning non-ViewList to ViewList slice') + self.data[i.start:i.stop] = item.data + self.items[i.start:i.stop] = item.items + assert len(self.data) == len(self.items), 'data mismatch' + if self.parent: + self.parent[(i.start or 0) + self.parent_offset + : (i.stop or len(self)) + self.parent_offset] = item + else: + self.data[i] = item + if self.parent: + self.parent[i + self.parent_offset] = item + + def __delitem__(self, i): + try: + del self.data[i] + del self.items[i] + if self.parent: + del self.parent[i + self.parent_offset] + except TypeError: + assert i.step is None, 'cannot handle slice with stride' + del self.data[i.start:i.stop] + del self.items[i.start:i.stop] + if self.parent: + del self.parent[(i.start or 0) + self.parent_offset + : (i.stop or len(self)) + self.parent_offset] + + def __add__(self, other): + if isinstance(other, ViewList): + return self.__class__(self.data + other.data, + items=(self.items + other.items)) + else: + raise TypeError('adding non-ViewList to a ViewList') + + def __radd__(self, other): + if isinstance(other, ViewList): + return self.__class__(other.data + self.data, + items=(other.items + self.items)) + else: + raise TypeError('adding ViewList to a non-ViewList') + + def __iadd__(self, other): + if isinstance(other, ViewList): + self.data += other.data + else: + raise TypeError('argument to += must be a ViewList') + return self + + def __mul__(self, n): + return self.__class__(self.data * n, items=(self.items * n)) + + __rmul__ = __mul__ + + def __imul__(self, n): + self.data *= n + self.items *= n + return self + + def extend(self, other): + if not isinstance(other, ViewList): + raise TypeError('extending a ViewList with a non-ViewList') + if self.parent: + self.parent.insert(len(self.data) + self.parent_offset, other) + self.data.extend(other.data) + self.items.extend(other.items) + + def append(self, item, source=None, offset=0): + if source is None: + self.extend(item) + else: + if self.parent: + self.parent.insert(len(self.data) + self.parent_offset, item, + source, offset) + self.data.append(item) + self.items.append((source, offset)) + + def insert(self, i, item, source=None, offset=0): + if source is None: + if not isinstance(item, ViewList): + raise TypeError('inserting non-ViewList with no source given') + self.data[i:i] = item.data + self.items[i:i] = item.items + if self.parent: + index = (len(self.data) + i) % len(self.data) + self.parent.insert(index + self.parent_offset, item) + else: + self.data.insert(i, item) + self.items.insert(i, (source, offset)) + if self.parent: + index = (len(self.data) + i) % len(self.data) + self.parent.insert(index + self.parent_offset, item, + source, offset) + + def pop(self, i=-1): + if self.parent: + index = (len(self.data) + i) % len(self.data) + self.parent.pop(index + self.parent_offset) + self.items.pop(i) + return self.data.pop(i) + + def trim_start(self, n=1): + """ + Remove items from the start of the list, without touching the parent. + """ + if n > len(self.data): + raise IndexError("Size of trim too large; can't trim %s items " + "from a list of size %s." % (n, len(self.data))) + elif n < 0: + raise IndexError('Trim size must be >= 0.') + del self.data[:n] + del self.items[:n] + if self.parent: + self.parent_offset += n + + def trim_end(self, n=1): + """ + Remove items from the end of the list, without touching the parent. + """ + if n > len(self.data): + raise IndexError("Size of trim too large; can't trim %s items " + "from a list of size %s." % (n, len(self.data))) + elif n < 0: + raise IndexError('Trim size must be >= 0.') + del self.data[-n:] + del self.items[-n:] + + def remove(self, item): + index = self.index(item) + del self[index] + + def count(self, item): return self.data.count(item) + def index(self, item): return self.data.index(item) + + def reverse(self): + self.data.reverse() + self.items.reverse() + self.parent = None + + def sort(self, *args): + tmp = zip(self.data, self.items) + tmp.sort(*args) + self.data = [entry[0] for entry in tmp] + self.items = [entry[1] for entry in tmp] + self.parent = None + + def info(self, i): + """Return source & offset for index `i`.""" + try: + return self.items[i] + except IndexError: + if i == len(self.data): # Just past the end + return self.items[i - 1][0], None + else: + raise + + def source(self, i): + """Return source for index `i`.""" + return self.info(i)[0] + + def offset(self, i): + """Return offset for index `i`.""" + return self.info(i)[1] + + def disconnect(self): + """Break link between this list and parent list.""" + self.parent = None + + +class StringList(ViewList): + + """A `ViewList` with string-specific methods.""" + + def trim_left(self, length, start=0, end=sys.maxint): + """ + Trim `length` characters off the beginning of each item, in-place, + from index `start` to `end`. No whitespace-checking is done on the + trimmed text. Does not affect slice parent. + """ + self.data[start:end] = [line[length:] + for line in self.data[start:end]] + + def get_text_block(self, start, flush_left=0): + """ + Return a contiguous block of text. + + If `flush_left` is true, raise `UnexpectedIndentationError` if an + indented line is encountered before the text block ends (with a blank + line). + """ + end = start + last = len(self.data) + while end < last: + line = self.data[end] + if not line.strip(): + break + if flush_left and (line[0] == ' '): + source, offset = self.info(end) + raise UnexpectedIndentationError(self[start:end], source, + offset + 1) + end += 1 + return self[start:end] + + def get_indented(self, start=0, until_blank=0, strip_indent=1, + block_indent=None, first_indent=None): + """ + Extract and return a StringList of indented lines of text. + + Collect all lines with indentation, determine the minimum indentation, + remove the minimum indentation from all indented lines (unless + `strip_indent` is false), and return them. All lines up to but not + including the first unindented line will be returned. + + :Parameters: + - `start`: The index of the first line to examine. + - `until_blank`: Stop collecting at the first blank line if true. + - `strip_indent`: Strip common leading indent if true (default). + - `block_indent`: The indent of the entire block, if known. + - `first_indent`: The indent of the first line, if known. + + :Return: + - a StringList of indented lines with mininum indent removed; + - the amount of the indent; + - a boolean: did the indented block finish with a blank line or EOF? + """ + indent = block_indent # start with None if unknown + end = start + if block_indent is not None and first_indent is None: + first_indent = block_indent + if first_indent is not None: + end += 1 + last = len(self.data) + while end < last: + line = self.data[end] + if line and (line[0] != ' ' + or (block_indent is not None + and line[:block_indent].strip())): + # Line not indented or insufficiently indented. + # Block finished properly iff the last indented line blank: + blank_finish = ((end > start) + and not self.data[end - 1].strip()) + break + stripped = line.lstrip() + if not stripped: # blank line + if until_blank: + blank_finish = 1 + break + elif block_indent is None: + line_indent = len(line) - len(stripped) + if indent is None: + indent = line_indent + else: + indent = min(indent, line_indent) + end += 1 + else: + blank_finish = 1 # block ends at end of lines + block = self[start:end] + if first_indent is not None and block: + block.data[0] = block.data[0][first_indent:] + if indent and strip_indent: + block.trim_left(indent, start=(first_indent is not None)) + return block, indent or 0, blank_finish + + def get_2D_block(self, top, left, bottom, right, strip_indent=1): + block = self[top:bottom] + indent = right + for i in range(len(block.data)): + block.data[i] = line = block.data[i][left:right].rstrip() + if line: + indent = min(indent, len(line) - len(line.lstrip())) + if strip_indent and 0 < indent < right: + block.data = [line[indent:] for line in block.data] + return block + + def pad_double_width(self, pad_char): + """ + Pad all double-width characters in self by appending `pad_char` to each. + For East Asian language support. + """ + if hasattr(unicodedata, 'east_asian_width'): + east_asian_width = unicodedata.east_asian_width + else: + return # new in Python 2.4 + for i in range(len(self.data)): + line = self.data[i] + if isinstance(line, unicode): + new = [] + for char in line: + new.append(char) + if east_asian_width(char) in 'WF': # 'W'ide & 'F'ull-width + new.append(pad_char) + self.data[i] = ''.join(new) + + def replace(self, old, new): + """Replace all occurrences of substring `old` with `new`.""" + for i in range(len(self.data)): + self.data[i] = self.data[i].replace(old, new) + + +class StateMachineError(Exception): pass +class UnknownStateError(StateMachineError): pass +class DuplicateStateError(StateMachineError): pass +class UnknownTransitionError(StateMachineError): pass +class DuplicateTransitionError(StateMachineError): pass +class TransitionPatternNotFound(StateMachineError): pass +class TransitionMethodNotFound(StateMachineError): pass +class UnexpectedIndentationError(StateMachineError): pass + + +class TransitionCorrection(Exception): + + """ + Raise from within a transition method to switch to another transition. + + Raise with one argument, the new transition name. + """ + + +class StateCorrection(Exception): + + """ + Raise from within a transition method to switch to another state. + + Raise with one or two arguments: new state name, and an optional new + transition name. + """ + + +def string2lines(astring, tab_width=8, convert_whitespace=0, + whitespace=re.compile('[\v\f]')): + """ + Return a list of one-line strings with tabs expanded, no newlines, and + trailing whitespace stripped. + + Each tab is expanded with between 1 and `tab_width` spaces, so that the + next character's index becomes a multiple of `tab_width` (8 by default). + + Parameters: + + - `astring`: a multi-line string. + - `tab_width`: the number of columns between tab stops. + - `convert_whitespace`: convert form feeds and vertical tabs to spaces? + """ + if convert_whitespace: + astring = whitespace.sub(' ', astring) + return [s.expandtabs(tab_width).rstrip() for s in astring.splitlines()] + +def _exception_data(): + """ + Return exception information: + + - the exception's class name; + - the exception object; + - the name of the file containing the offending code; + - the line number of the offending code; + - the function name of the offending code. + """ + type, value, traceback = sys.exc_info() + while traceback.tb_next: + traceback = traceback.tb_next + code = traceback.tb_frame.f_code + return (type.__name__, value, code.co_filename, traceback.tb_lineno, + code.co_name) diff -Nru zope3-3.4.0/src/docutils/_string_template_compat.py zope3-3.5~bzr18/src/docutils/_string_template_compat.py --- zope3-3.4.0/src/docutils/_string_template_compat.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/_string_template_compat.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# string_template_compat.py: string.Template for Python <= 2.4 +# ===================================================== + +# This is just an excerpt of the standard string module to provide backwards +# compatibility. + +import re as _re + +class _multimap: + """Helper class for combining multiple mappings. + + Used by .{safe_,}substitute() to combine the mapping and keyword + arguments. + """ + def __init__(self, primary, secondary): + self._primary = primary + self._secondary = secondary + + def __getitem__(self, key): + try: + return self._primary[key] + except KeyError: + return self._secondary[key] + + +class _TemplateMetaclass(type): + pattern = r""" + %(delim)s(?: + (?P%(delim)s) | # Escape sequence of two delimiters + (?P%(id)s) | # delimiter and a Python identifier + {(?P%(id)s)} | # delimiter and a braced identifier + (?P) # Other ill-formed delimiter exprs + ) + """ + + def __init__(cls, name, bases, dct): + super(_TemplateMetaclass, cls).__init__(name, bases, dct) + if 'pattern' in dct: + pattern = cls.pattern + else: + pattern = _TemplateMetaclass.pattern % { + 'delim' : _re.escape(cls.delimiter), + 'id' : cls.idpattern, + } + cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE) + + +class Template: + """A string class for supporting $-substitutions.""" + __metaclass__ = _TemplateMetaclass + + delimiter = '$' + idpattern = r'[_a-z][_a-z0-9]*' + + def __init__(self, template): + self.template = template + + # Search for $$, $identifier, ${identifier}, and any bare $'s + + def _invalid(self, mo): + i = mo.start('invalid') + lines = self.template[:i].splitlines(True) + if not lines: + colno = 1 + lineno = 1 + else: + colno = i - len(''.join(lines[:-1])) + lineno = len(lines) + raise ValueError('Invalid placeholder in string: line %d, col %d' % + (lineno, colno)) + + def substitute(self, *args, **kws): + if len(args) > 1: + raise TypeError('Too many positional arguments') + if not args: + mapping = kws + elif kws: + mapping = _multimap(kws, args[0]) + else: + mapping = args[0] + # Helper function for .sub() + def convert(mo): + # Check the most common path first. + named = mo.group('named') or mo.group('braced') + if named is not None: + val = mapping[named] + # We use this idiom instead of str() because the latter will + # fail if val is a Unicode containing non-ASCII characters. + return '%s' % (val,) + if mo.group('escaped') is not None: + return self.delimiter + if mo.group('invalid') is not None: + self._invalid(mo) + raise ValueError('Unrecognized named group in pattern', + self.pattern) + return self.pattern.sub(convert, self.template) + + def safe_substitute(self, *args, **kws): + if len(args) > 1: + raise TypeError('Too many positional arguments') + if not args: + mapping = kws + elif kws: + mapping = _multimap(kws, args[0]) + else: + mapping = args[0] + # Helper function for .sub() + def convert(mo): + named = mo.group('named') + if named is not None: + try: + # We use this idiom instead of str() because the latter + # will fail if val is a Unicode containing non-ASCII + return '%s' % (mapping[named],) + except KeyError: + return self.delimiter + named + braced = mo.group('braced') + if braced is not None: + try: + return '%s' % (mapping[braced],) + except KeyError: + return self.delimiter + '{' + braced + '}' + if mo.group('escaped') is not None: + return self.delimiter + if mo.group('invalid') is not None: + return self.delimiter + raise ValueError('Unrecognized named group in pattern', + self.pattern) + return self.pattern.sub(convert, self.template) + diff -Nru zope3-3.4.0/src/docutils/transforms/components.py zope3-3.5~bzr18/src/docutils/transforms/components.py --- zope3-3.4.0/src/docutils/transforms/components.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/components.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,52 @@ +# $Id: components.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Docutils component-related transforms. +""" + +__docformat__ = 'reStructuredText' + +import sys +import os +import re +import time +from docutils import nodes, utils +from docutils import ApplicationError, DataError +from docutils.transforms import Transform, TransformError + + +class Filter(Transform): + + """ + Include or exclude elements which depend on a specific Docutils component. + + For use with `nodes.pending` elements. A "pending" element's dictionary + attribute ``details`` must contain the keys "component" and "format". The + value of ``details['component']`` must match the type name of the + component the elements depend on (e.g. "writer"). The value of + ``details['format']`` is the name of a specific format or context of that + component (e.g. "html"). If the matching Docutils component supports that + format or context, the "pending" element is replaced by the contents of + ``details['nodes']`` (a list of nodes); otherwise, the "pending" element + is removed. + + For example, the reStructuredText "meta" directive creates a "pending" + element containing a "meta" element (in ``pending.details['nodes']``). + Only writers (``pending.details['component'] == 'writer'``) supporting the + "html" format (``pending.details['format'] == 'html'``) will include the + "meta" element; it will be deleted from the output of all other writers. + """ + + default_priority = 780 + + def apply(self): + pending = self.startnode + component_type = pending.details['component'] # 'reader' or 'writer' + format = pending.details['format'] + component = self.document.transformer.components[component_type] + if component.supports(format): + pending.replace_self(pending.details['nodes']) + else: + pending.parent.remove(pending) diff -Nru zope3-3.4.0/src/docutils/transforms/frontmatter.py zope3-3.5~bzr18/src/docutils/transforms/frontmatter.py --- zope3-3.4.0/src/docutils/transforms/frontmatter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/frontmatter.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,512 @@ +# $Id: frontmatter.py 5618 2008-07-28 08:37:32Z strank $ +# Author: David Goodger, Ueli Schlaepfer +# Copyright: This module has been placed in the public domain. + +""" +Transforms related to the front matter of a document or a section +(information found before the main text): + +- `DocTitle`: Used to transform a lone top level section's title to + the document title, promote a remaining lone top-level section's + title to the document subtitle, and determine the document's title + metadata (document['title']) based on the document title and/or the + "title" setting. + +- `SectionSubTitle`: Used to transform a lone subsection into a + subtitle. + +- `DocInfo`: Used to transform a bibliographic field list into docinfo + elements. +""" + +__docformat__ = 'reStructuredText' + +import re +from docutils import nodes, utils +from docutils.transforms import TransformError, Transform + + +class TitlePromoter(Transform): + + """ + Abstract base class for DocTitle and SectionSubTitle transforms. + """ + + def promote_title(self, node): + """ + Transform the following tree:: + + +
+ + ... + + into :: + + <node> + <title> + ... + + `node` is normally a document. + """ + # `node` must not have a title yet. + assert not (len(node) and isinstance(node[0], nodes.title)) + section, index = self.candidate_index(node) + if index is None: + return None + # Transfer the section's attributes to the node: + node.attributes.update(section.attributes) + # setup_child is called automatically for all nodes. + node[:] = (section[:1] # section title + + node[:index] # everything that was in the + # node before the section + + section[1:]) # everything that was in the section + assert isinstance(node[0], nodes.title) + return 1 + + def promote_subtitle(self, node): + """ + Transform the following node tree:: + + <node> + <title> + <section> + <title> + ... + + into :: + + <node> + <title> + <subtitle> + ... + """ + subsection, index = self.candidate_index(node) + if index is None: + return None + subtitle = nodes.subtitle() + # Transfer the subsection's attributes to the new subtitle: + # This causes trouble with list attributes! To do: Write a + # test case which catches direct access to the `attributes` + # dictionary and/or write a test case which shows problems in + # this particular case. + subtitle.attributes.update(subsection.attributes) + # We're losing the subtitle's attributes here! To do: Write a + # test case which shows this behavior. + # Transfer the contents of the subsection's title to the + # subtitle: + subtitle[:] = subsection[0][:] + node[:] = (node[:1] # title + + [subtitle] + # everything that was before the section: + + node[1:index] + # everything that was in the subsection: + + subsection[1:]) + return 1 + + def candidate_index(self, node): + """ + Find and return the promotion candidate and its index. + + Return (None, None) if no valid candidate was found. + """ + index = node.first_child_not_matching_class( + nodes.PreBibliographic) + if index is None or len(node) > (index + 1) or \ + not isinstance(node[index], nodes.section): + return None, None + else: + return node[index], index + + +class DocTitle(TitlePromoter): + + """ + In reStructuredText_, there is no way to specify a document title + and subtitle explicitly. Instead, we can supply the document title + (and possibly the subtitle as well) implicitly, and use this + two-step transform to "raise" or "promote" the title(s) (and their + corresponding section contents) to the document level. + + 1. If the document contains a single top-level section as its + first non-comment element, the top-level section's title + becomes the document's title, and the top-level section's + contents become the document's immediate contents. The lone + top-level section header must be the first non-comment element + in the document. + + For example, take this input text:: + + ================= + Top-Level Title + ================= + + A paragraph. + + Once parsed, it looks like this:: + + <document> + <section names="top-level title"> + <title> + Top-Level Title + <paragraph> + A paragraph. + + After running the DocTitle transform, we have:: + + <document names="top-level title"> + <title> + Top-Level Title + <paragraph> + A paragraph. + + 2. If step 1 successfully determines the document title, we + continue by checking for a subtitle. + + If the lone top-level section itself contains a single + second-level section as its first non-comment element, that + section's title is promoted to the document's subtitle, and + that section's contents become the document's immediate + contents. Given this input text:: + + ================= + Top-Level Title + ================= + + Second-Level Title + ~~~~~~~~~~~~~~~~~~ + + A paragraph. + + After parsing and running the Section Promotion transform, the + result is:: + + <document names="top-level title"> + <title> + Top-Level Title + <subtitle names="second-level title"> + Second-Level Title + <paragraph> + A paragraph. + + (Note that the implicit hyperlink target generated by the + "Second-Level Title" is preserved on the "subtitle" element + itself.) + + Any comment elements occurring before the document title or + subtitle are accumulated and inserted as the first body elements + after the title(s). + + This transform also sets the document's metadata title + (document['title']). + + .. _reStructuredText: http://docutils.sf.net/rst.html + """ + + default_priority = 320 + + def set_metadata(self): + """ + Set document['title'] metadata title from the following + sources, listed in order of priority: + + * Existing document['title'] attribute. + * "title" setting. + * Document title node (as promoted by promote_title). + """ + if not self.document.hasattr('title'): + if self.document.settings.title is not None: + self.document['title'] = self.document.settings.title + elif len(self.document) and isinstance(self.document[0], nodes.title): + self.document['title'] = self.document[0].astext() + + def apply(self): + if getattr(self.document.settings, 'doctitle_xform', 1): + # promote_(sub)title defined in TitlePromoter base class. + if self.promote_title(self.document): + # If a title has been promoted, also try to promote a + # subtitle. + self.promote_subtitle(self.document) + # Set document['title']. + self.set_metadata() + + +class SectionSubTitle(TitlePromoter): + + """ + This works like document subtitles, but for sections. For example, :: + + <section> + <title> + Title + <section> + <title> + Subtitle + ... + + is transformed into :: + + <section> + <title> + Title + <subtitle> + Subtitle + ... + + For details refer to the docstring of DocTitle. + """ + + default_priority = 350 + + def apply(self): + if not getattr(self.document.settings, 'sectsubtitle_xform', 1): + return + for section in self.document.traverse(nodes.section): + # On our way through the node tree, we are deleting + # sections, but we call self.promote_subtitle for those + # sections nonetheless. To do: Write a test case which + # shows the problem and discuss on Docutils-develop. + self.promote_subtitle(section) + + +class DocInfo(Transform): + + """ + This transform is specific to the reStructuredText_ markup syntax; + see "Bibliographic Fields" in the `reStructuredText Markup + Specification`_ for a high-level description. This transform + should be run *after* the `DocTitle` transform. + + Given a field list as the first non-comment element after the + document title and subtitle (if present), registered bibliographic + field names are transformed to the corresponding DTD elements, + becoming child elements of the "docinfo" element (except for a + dedication and/or an abstract, which become "topic" elements after + "docinfo"). + + For example, given this document fragment after parsing:: + + <document> + <title> + Document Title + <field_list> + <field> + <field_name> + Author + <field_body> + <paragraph> + A. Name + <field> + <field_name> + Status + <field_body> + <paragraph> + $RCSfile$ + ... + + After running the bibliographic field list transform, the + resulting document tree would look like this:: + + <document> + <title> + Document Title + <docinfo> + <author> + A. Name + <status> + frontmatter.py + ... + + The "Status" field contained an expanded RCS keyword, which is + normally (but optionally) cleaned up by the transform. The sole + contents of the field body must be a paragraph containing an + expanded RCS keyword of the form "$keyword: expansion text $". Any + RCS keyword can be processed in any bibliographic field. The + dollar signs and leading RCS keyword name are removed. Extra + processing is done for the following RCS keywords: + + - "RCSfile" expands to the name of the file in the RCS or CVS + repository, which is the name of the source file with a ",v" + suffix appended. The transform will remove the ",v" suffix. + + - "Date" expands to the format "YYYY/MM/DD hh:mm:ss" (in the UTC + time zone). The RCS Keywords transform will extract just the + date itself and transform it to an ISO 8601 format date, as in + "2000-12-31". + + (Since the source file for this text is itself stored under CVS, + we can't show an example of the "Date" RCS keyword because we + can't prevent any RCS keywords used in this explanation from + being expanded. Only the "RCSfile" keyword is stable; its + expansion text changes only if the file name changes.) + + .. _reStructuredText: http://docutils.sf.net/rst.html + .. _reStructuredText Markup Specification: + http://docutils.sf.net/docs/ref/rst/restructuredtext.html + """ + + default_priority = 340 + + biblio_nodes = { + 'author': nodes.author, + 'authors': nodes.authors, + 'organization': nodes.organization, + 'address': nodes.address, + 'contact': nodes.contact, + 'version': nodes.version, + 'revision': nodes.revision, + 'status': nodes.status, + 'date': nodes.date, + 'copyright': nodes.copyright, + 'dedication': nodes.topic, + 'abstract': nodes.topic} + """Canonical field name (lowcased) to node class name mapping for + bibliographic fields (field_list).""" + + def apply(self): + if not getattr(self.document.settings, 'docinfo_xform', 1): + return + document = self.document + index = document.first_child_not_matching_class( + nodes.PreBibliographic) + if index is None: + return + candidate = document[index] + if isinstance(candidate, nodes.field_list): + biblioindex = document.first_child_not_matching_class( + (nodes.Titular, nodes.Decorative)) + nodelist = self.extract_bibliographic(candidate) + del document[index] # untransformed field list (candidate) + document[biblioindex:biblioindex] = nodelist + + def extract_bibliographic(self, field_list): + docinfo = nodes.docinfo() + bibliofields = self.language.bibliographic_fields + labels = self.language.labels + topics = {'dedication': None, 'abstract': None} + for field in field_list: + try: + name = field[0][0].astext() + normedname = nodes.fully_normalize_name(name) + if not (len(field) == 2 and normedname in bibliofields + and self.check_empty_biblio_field(field, name)): + raise TransformError + canonical = bibliofields[normedname] + biblioclass = self.biblio_nodes[canonical] + if issubclass(biblioclass, nodes.TextElement): + if not self.check_compound_biblio_field(field, name): + raise TransformError + utils.clean_rcs_keywords( + field[1][0], self.rcs_keyword_substitutions) + docinfo.append(biblioclass('', '', *field[1][0])) + elif issubclass(biblioclass, nodes.authors): + self.extract_authors(field, name, docinfo) + elif issubclass(biblioclass, nodes.topic): + if topics[canonical]: + field[-1] += self.document.reporter.warning( + 'There can only be one "%s" field.' % name, + base_node=field) + raise TransformError + title = nodes.title(name, labels[canonical]) + topics[canonical] = biblioclass( + '', title, classes=[canonical], *field[1].children) + else: + docinfo.append(biblioclass('', *field[1].children)) + except TransformError: + if len(field[-1]) == 1 \ + and isinstance(field[-1][0], nodes.paragraph): + utils.clean_rcs_keywords( + field[-1][0], self.rcs_keyword_substitutions) + docinfo.append(field) + nodelist = [] + if len(docinfo) != 0: + nodelist.append(docinfo) + for name in ('dedication', 'abstract'): + if topics[name]: + nodelist.append(topics[name]) + return nodelist + + def check_empty_biblio_field(self, field, name): + if len(field[-1]) < 1: + field[-1] += self.document.reporter.warning( + 'Cannot extract empty bibliographic field "%s".' % name, + base_node=field) + return None + return 1 + + def check_compound_biblio_field(self, field, name): + if len(field[-1]) > 1: + field[-1] += self.document.reporter.warning( + 'Cannot extract compound bibliographic field "%s".' % name, + base_node=field) + return None + if not isinstance(field[-1][0], nodes.paragraph): + field[-1] += self.document.reporter.warning( + 'Cannot extract bibliographic field "%s" containing ' + 'anything other than a single paragraph.' % name, + base_node=field) + return None + return 1 + + rcs_keyword_substitutions = [ + (re.compile(r'\$' r'Date: (\d\d\d\d)[-/](\d\d)[-/](\d\d)[ T][\d:]+' + r'[^$]* \$', re.IGNORECASE), r'\1-\2-\3'), + (re.compile(r'\$' r'RCSfile: (.+),v \$', re.IGNORECASE), r'\1'), + (re.compile(r'\$[a-zA-Z]+: (.+) \$'), r'\1'),] + + def extract_authors(self, field, name, docinfo): + try: + if len(field[1]) == 1: + if isinstance(field[1][0], nodes.paragraph): + authors = self.authors_from_one_paragraph(field) + elif isinstance(field[1][0], nodes.bullet_list): + authors = self.authors_from_bullet_list(field) + else: + raise TransformError + else: + authors = self.authors_from_paragraphs(field) + authornodes = [nodes.author('', '', *author) + for author in authors if author] + if len(authornodes) >= 1: + docinfo.append(nodes.authors('', *authornodes)) + else: + raise TransformError + except TransformError: + field[-1] += self.document.reporter.warning( + 'Bibliographic field "%s" incompatible with extraction: ' + 'it must contain either a single paragraph (with authors ' + 'separated by one of "%s"), multiple paragraphs (one per ' + 'author), or a bullet list with one paragraph (one author) ' + 'per item.' + % (name, ''.join(self.language.author_separators)), + base_node=field) + raise + + def authors_from_one_paragraph(self, field): + text = field[1][0].astext().strip() + if not text: + raise TransformError + for authorsep in self.language.author_separators: + authornames = text.split(authorsep) + if len(authornames) > 1: + break + authornames = [author.strip() for author in authornames] + authors = [[nodes.Text(author)] for author in authornames if author] + return authors + + def authors_from_bullet_list(self, field): + authors = [] + for item in field[1][0]: + if len(item) != 1 or not isinstance(item[0], nodes.paragraph): + raise TransformError + authors.append(item[0].children) + if not authors: + raise TransformError + return authors + + def authors_from_paragraphs(self, field): + for item in field[1]: + if not isinstance(item, nodes.paragraph): + raise TransformError + authors = [item.children for item in field[1]] + return authors diff -Nru zope3-3.4.0/src/docutils/transforms/__init__.py zope3-3.5~bzr18/src/docutils/transforms/__init__.py --- zope3-3.4.0/src/docutils/transforms/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,172 @@ +# $Id: __init__.py 4975 2007-03-01 18:08:32Z wiemann $ +# Authors: David Goodger <goodger@python.org>; Ueli Schlaepfer +# Copyright: This module has been placed in the public domain. + +""" +This package contains modules for standard tree transforms available +to Docutils components. Tree transforms serve a variety of purposes: + +- To tie up certain syntax-specific "loose ends" that remain after the + initial parsing of the input plaintext. These transforms are used to + supplement a limited syntax. + +- To automate the internal linking of the document tree (hyperlink + references, footnote references, etc.). + +- To extract useful information from the document tree. These + transforms may be used to construct (for example) indexes and tables + of contents. + +Each transform is an optional step that a Docutils component may +choose to perform on the parsed document. +""" + +__docformat__ = 'reStructuredText' + + +from docutils import languages, ApplicationError, TransformSpec + + +class TransformError(ApplicationError): pass + + +class Transform: + + """ + Docutils transform component abstract base class. + """ + + default_priority = None + """Numerical priority of this transform, 0 through 999 (override).""" + + def __init__(self, document, startnode=None): + """ + Initial setup for in-place document transforms. + """ + + self.document = document + """The document tree to transform.""" + + self.startnode = startnode + """Node from which to begin the transform. For many transforms which + apply to the document as a whole, `startnode` is not set (i.e. its + value is `None`).""" + + self.language = languages.get_language( + document.settings.language_code) + """Language module local to this document.""" + + def apply(self, **kwargs): + """Override to apply the transform to the document tree.""" + raise NotImplementedError('subclass must override this method') + + +class Transformer(TransformSpec): + + """ + Stores transforms (`Transform` classes) and applies them to document + trees. Also keeps track of components by component type name. + """ + + def __init__(self, document): + self.transforms = [] + """List of transforms to apply. Each item is a 3-tuple: + ``(priority string, transform class, pending node or None)``.""" + + self.unknown_reference_resolvers = [] + """List of hook functions which assist in resolving references""" + + self.document = document + """The `nodes.document` object this Transformer is attached to.""" + + self.applied = [] + """Transforms already applied, in order.""" + + self.sorted = 0 + """Boolean: is `self.tranforms` sorted?""" + + self.components = {} + """Mapping of component type name to component object. Set by + `self.populate_from_components()`.""" + + self.serialno = 0 + """Internal serial number to keep track of the add order of + transforms.""" + + def add_transform(self, transform_class, priority=None, **kwargs): + """ + Store a single transform. Use `priority` to override the default. + `kwargs` is a dictionary whose contents are passed as keyword + arguments to the `apply` method of the transform. This can be used to + pass application-specific data to the transform instance. + """ + if priority is None: + priority = transform_class.default_priority + priority_string = self.get_priority_string(priority) + self.transforms.append( + (priority_string, transform_class, None, kwargs)) + self.sorted = 0 + + def add_transforms(self, transform_list): + """Store multiple transforms, with default priorities.""" + for transform_class in transform_list: + priority_string = self.get_priority_string( + transform_class.default_priority) + self.transforms.append( + (priority_string, transform_class, None, {})) + self.sorted = 0 + + def add_pending(self, pending, priority=None): + """Store a transform with an associated `pending` node.""" + transform_class = pending.transform + if priority is None: + priority = transform_class.default_priority + priority_string = self.get_priority_string(priority) + self.transforms.append( + (priority_string, transform_class, pending, {})) + self.sorted = 0 + + def get_priority_string(self, priority): + """ + Return a string, `priority` combined with `self.serialno`. + + This ensures FIFO order on transforms with identical priority. + """ + self.serialno += 1 + return '%03d-%03d' % (priority, self.serialno) + + def populate_from_components(self, components): + """ + Store each component's default transforms, with default priorities. + Also, store components by type name in a mapping for later lookup. + """ + for component in components: + if component is None: + continue + self.add_transforms(component.get_transforms()) + self.components[component.component_type] = component + self.sorted = 0 + # Set up all of the reference resolvers for this transformer. Each + # component of this transformer is able to register its own helper + # functions to help resolve references. + unknown_reference_resolvers = [] + for i in components: + unknown_reference_resolvers.extend(i.unknown_reference_resolvers) + decorated_list = [(f.priority, f) for f in unknown_reference_resolvers] + decorated_list.sort() + self.unknown_reference_resolvers.extend([f[1] for f in decorated_list]) + + def apply_transforms(self): + """Apply all of the stored transforms, in priority order.""" + self.document.reporter.attach_observer( + self.document.note_transform_message) + while self.transforms: + if not self.sorted: + # Unsorted initially, and whenever a transform is added. + self.transforms.sort() + self.transforms.reverse() + self.sorted = 1 + priority, transform_class, pending, kwargs = self.transforms.pop() + transform = transform_class(self.document, startnode=pending) + transform.apply(**kwargs) + self.applied.append((priority, transform_class, pending, kwargs)) diff -Nru zope3-3.4.0/src/docutils/transforms/misc.py zope3-3.5~bzr18/src/docutils/transforms/misc.py --- zope3-3.4.0/src/docutils/transforms/misc.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/misc.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,143 @@ +# $Id: misc.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger <goodger@python.org> +# Copyright: This module has been placed in the public domain. + +""" +Miscellaneous transforms. +""" + +__docformat__ = 'reStructuredText' + +from docutils import nodes +from docutils.transforms import Transform, TransformError + + +class CallBack(Transform): + + """ + Inserts a callback into a document. The callback is called when the + transform is applied, which is determined by its priority. + + For use with `nodes.pending` elements. Requires a ``details['callback']`` + entry, a bound method or function which takes one parameter: the pending + node. Other data can be stored in the ``details`` attribute or in the + object hosting the callback method. + """ + + default_priority = 990 + + def apply(self): + pending = self.startnode + pending.details['callback'](pending) + pending.parent.remove(pending) + + +class ClassAttribute(Transform): + + """ + Move the "class" attribute specified in the "pending" node into the + immediately following non-comment element. + """ + + default_priority = 210 + + def apply(self): + pending = self.startnode + parent = pending.parent + child = pending + while parent: + # Check for appropriate following siblings: + for index in range(parent.index(child) + 1, len(parent)): + element = parent[index] + if (isinstance(element, nodes.Invisible) or + isinstance(element, nodes.system_message)): + continue + element['classes'] += pending.details['class'] + pending.parent.remove(pending) + return + else: + # At end of section or container; apply to sibling + child = parent + parent = parent.parent + error = self.document.reporter.error( + 'No suitable element following "%s" directive' + % pending.details['directive'], + nodes.literal_block(pending.rawsource, pending.rawsource), + line=pending.line) + pending.replace_self(error) + + +class Transitions(Transform): + + """ + Move transitions at the end of sections up the tree. Complain + on transitions after a title, at the beginning or end of the + document, and after another transition. + + For example, transform this:: + + <section> + ... + <transition> + <section> + ... + + into this:: + + <section> + ... + <transition> + <section> + ... + """ + + default_priority = 830 + + def apply(self): + for node in self.document.traverse(nodes.transition): + self.visit_transition(node) + + def visit_transition(self, node): + index = node.parent.index(node) + error = None + if (index == 0 or + isinstance(node.parent[0], nodes.title) and + (index == 1 or + isinstance(node.parent[1], nodes.subtitle) and + index == 2)): + assert (isinstance(node.parent, nodes.document) or + isinstance(node.parent, nodes.section)) + error = self.document.reporter.error( + 'Document or section may not begin with a transition.', + line=node.line) + elif isinstance(node.parent[index - 1], nodes.transition): + error = self.document.reporter.error( + 'At least one body element must separate transitions; ' + 'adjacent transitions are not allowed.', line=node.line) + if error: + # Insert before node and update index. + node.parent.insert(index, error) + index += 1 + assert index < len(node.parent) + if index != len(node.parent) - 1: + # No need to move the node. + return + # Node behind which the transition is to be moved. + sibling = node + # While sibling is the last node of its parent. + while index == len(sibling.parent) - 1: + sibling = sibling.parent + # If sibling is the whole document (i.e. it has no parent). + if sibling.parent is None: + # Transition at the end of document. Do not move the + # transition up, and place an error behind. + error = self.document.reporter.error( + 'Document may not end with a transition.', + line=node.line) + node.parent.insert(node.parent.index(node) + 1, error) + return + index = sibling.parent.index(sibling) + # Remove the original transition node. + node.parent.remove(node) + # Insert the transition after the sibling. + sibling.parent.insert(index + 1, node) diff -Nru zope3-3.4.0/src/docutils/transforms/parts.py zope3-3.5~bzr18/src/docutils/transforms/parts.py --- zope3-3.4.0/src/docutils/transforms/parts.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/parts.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,180 @@ +# $Id: parts.py 6073 2009-08-06 12:21:10Z milde $ +# Authors: David Goodger <goodger@python.org>; Ueli Schlaepfer; Dmitry Jemerov +# Copyright: This module has been placed in the public domain. + +""" +Transforms related to document parts. +""" + +__docformat__ = 'reStructuredText' + + +import re +import sys +from docutils import nodes, utils +from docutils.transforms import TransformError, Transform + + +class SectNum(Transform): + + """ + Automatically assigns numbers to the titles of document sections. + + It is possible to limit the maximum section level for which the numbers + are added. For those sections that are auto-numbered, the "autonum" + attribute is set, informing the contents table generator that a different + form of the TOC should be used. + """ + + default_priority = 710 + """Should be applied before `Contents`.""" + + def apply(self): + self.maxdepth = self.startnode.details.get('depth', None) + self.startvalue = self.startnode.details.get('start', 1) + self.prefix = self.startnode.details.get('prefix', '') + self.suffix = self.startnode.details.get('suffix', '') + self.startnode.parent.remove(self.startnode) + if self.document.settings.sectnum_xform: + if self.maxdepth is None: + self.maxdepth = sys.maxint + self.update_section_numbers(self.document) + else: # store details for eventual section numbering by the writer + self.document.settings.sectnum_depth = self.maxdepth + self.document.settings.sectnum_start = self.startvalue + self.document.settings.sectnum_prefix = self.prefix + self.document.settings.sectnum_suffix = self.suffix + + def update_section_numbers(self, node, prefix=(), depth=0): + depth += 1 + if prefix: + sectnum = 1 + else: + sectnum = self.startvalue + for child in node: + if isinstance(child, nodes.section): + numbers = prefix + (str(sectnum),) + title = child[0] + # Use   for spacing: + generated = nodes.generated( + '', (self.prefix + '.'.join(numbers) + self.suffix + + u'\u00a0' * 3), + classes=['sectnum']) + title.insert(0, generated) + title['auto'] = 1 + if depth < self.maxdepth: + self.update_section_numbers(child, numbers, depth) + sectnum += 1 + + +class Contents(Transform): + + """ + This transform generates a table of contents from the entire document tree + or from a single branch. It locates "section" elements and builds them + into a nested bullet list, which is placed within a "topic" created by the + contents directive. A title is either explicitly specified, taken from + the appropriate language module, or omitted (local table of contents). + The depth may be specified. Two-way references between the table of + contents and section titles are generated (requires Writer support). + + This transform requires a startnode, which contains generation + options and provides the location for the generated table of contents (the + startnode is replaced by the table of contents "topic"). + """ + + default_priority = 720 + + def apply(self): + try: # let the writer (or output software) build the contents list? + toc_by_writer = self.document.settings.use_latex_toc + except AttributeError: + toc_by_writer = False + details = self.startnode.details + if 'local' in details: + startnode = self.startnode.parent.parent + while not (isinstance(startnode, nodes.section) + or isinstance(startnode, nodes.document)): + # find the ToC root: a direct ancestor of startnode + startnode = startnode.parent + else: + startnode = self.document + self.toc_id = self.startnode.parent['ids'][0] + if 'backlinks' in details: + self.backlinks = details['backlinks'] + else: + self.backlinks = self.document.settings.toc_backlinks + if toc_by_writer: + # move customization settings to the parent node + self.startnode.parent.attributes.update(details) + self.startnode.parent.remove(self.startnode) + else: + contents = self.build_contents(startnode) + if len(contents): + self.startnode.replace_self(contents) + else: + self.startnode.parent.parent.remove(self.startnode.parent) + + def build_contents(self, node, level=0): + level += 1 + sections = [sect for sect in node if isinstance(sect, nodes.section)] + entries = [] + autonum = 0 + depth = self.startnode.details.get('depth', sys.maxint) + for section in sections: + title = section[0] + auto = title.get('auto') # May be set by SectNum. + entrytext = self.copy_and_filter(title) + reference = nodes.reference('', '', refid=section['ids'][0], + *entrytext) + ref_id = self.document.set_id(reference) + entry = nodes.paragraph('', '', reference) + item = nodes.list_item('', entry) + if ( self.backlinks in ('entry', 'top') + and title.next_node(nodes.reference) is None): + if self.backlinks == 'entry': + title['refid'] = ref_id + elif self.backlinks == 'top': + title['refid'] = self.toc_id + if level < depth: + subsects = self.build_contents(section, level) + item += subsects + entries.append(item) + if entries: + contents = nodes.bullet_list('', *entries) + if auto: + contents['classes'].append('auto-toc') + return contents + else: + return [] + + def copy_and_filter(self, node): + """Return a copy of a title, with references, images, etc. removed.""" + visitor = ContentsFilter(self.document) + node.walkabout(visitor) + return visitor.get_entry_text() + + +class ContentsFilter(nodes.TreeCopyVisitor): + + def get_entry_text(self): + return self.get_tree_copy().children + + def visit_citation_reference(self, node): + raise nodes.SkipNode + + def visit_footnote_reference(self, node): + raise nodes.SkipNode + + def visit_image(self, node): + if node.hasattr('alt'): + self.parent.append(nodes.Text(node['alt'])) + raise nodes.SkipNode + + def ignore_node_but_process_children(self, node): + raise nodes.SkipDeparture + + visit_interpreted = ignore_node_but_process_children + visit_problematic = ignore_node_but_process_children + visit_reference = ignore_node_but_process_children + visit_target = ignore_node_but_process_children diff -Nru zope3-3.4.0/src/docutils/transforms/peps.py zope3-3.5~bzr18/src/docutils/transforms/peps.py --- zope3-3.4.0/src/docutils/transforms/peps.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/peps.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,304 @@ +# $Id: peps.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger <goodger@python.org> +# Copyright: This module has been placed in the public domain. + +""" +Transforms for PEP processing. + +- `Headers`: Used to transform a PEP's initial RFC-2822 header. It remains a + field list, but some entries get processed. +- `Contents`: Auto-inserts a table of contents. +- `PEPZero`: Special processing for PEP 0. +""" + +__docformat__ = 'reStructuredText' + +import sys +import os +import re +import time +from docutils import nodes, utils, languages +from docutils import ApplicationError, DataError +from docutils.transforms import Transform, TransformError +from docutils.transforms import parts, references, misc + + +class Headers(Transform): + + """ + Process fields in a PEP's initial RFC-2822 header. + """ + + default_priority = 360 + + pep_url = 'pep-%04d' + pep_cvs_url = ('http://svn.python.org/view/*checkout*' + '/peps/trunk/pep-%04d.txt') + rcs_keyword_substitutions = ( + (re.compile(r'\$' r'RCSfile: (.+),v \$$', re.IGNORECASE), r'\1'), + (re.compile(r'\$[a-zA-Z]+: (.+) \$$'), r'\1'),) + + def apply(self): + if not len(self.document): + # @@@ replace these DataErrors with proper system messages + raise DataError('Document tree is empty.') + header = self.document[0] + if not isinstance(header, nodes.field_list) or \ + 'rfc2822' not in header['classes']: + raise DataError('Document does not begin with an RFC-2822 ' + 'header; it is not a PEP.') + pep = None + for field in header: + if field[0].astext().lower() == 'pep': # should be the first field + value = field[1].astext() + try: + pep = int(value) + cvs_url = self.pep_cvs_url % pep + except ValueError: + pep = value + cvs_url = None + msg = self.document.reporter.warning( + '"PEP" header must contain an integer; "%s" is an ' + 'invalid value.' % pep, base_node=field) + msgid = self.document.set_id(msg) + prb = nodes.problematic(value, value or '(none)', + refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + if len(field[1]): + field[1][0][:] = [prb] + else: + field[1] += nodes.paragraph('', '', prb) + break + if pep is None: + raise DataError('Document does not contain an RFC-2822 "PEP" ' + 'header.') + if pep == 0: + # Special processing for PEP 0. + pending = nodes.pending(PEPZero) + self.document.insert(1, pending) + self.document.note_pending(pending) + if len(header) < 2 or header[1][0].astext().lower() != 'title': + raise DataError('No title!') + for field in header: + name = field[0].astext().lower() + body = field[1] + if len(body) > 1: + raise DataError('PEP header field body contains multiple ' + 'elements:\n%s' % field.pformat(level=1)) + elif len(body) == 1: + if not isinstance(body[0], nodes.paragraph): + raise DataError('PEP header field body may only contain ' + 'a single paragraph:\n%s' + % field.pformat(level=1)) + elif name == 'last-modified': + date = time.strftime( + '%d-%b-%Y', + time.localtime(os.stat(self.document['source'])[8])) + if cvs_url: + body += nodes.paragraph( + '', '', nodes.reference('', date, refuri=cvs_url)) + else: + # empty + continue + para = body[0] + if name == 'author': + for node in para: + if isinstance(node, nodes.reference): + node.replace_self(mask_email(node)) + elif name == 'discussions-to': + for node in para: + if isinstance(node, nodes.reference): + node.replace_self(mask_email(node, pep)) + elif name in ('replaces', 'replaced-by', 'requires'): + newbody = [] + space = nodes.Text(' ') + for refpep in re.split(',?\s+', body.astext()): + pepno = int(refpep) + newbody.append(nodes.reference( + refpep, refpep, + refuri=(self.document.settings.pep_base_url + + self.pep_url % pepno))) + newbody.append(space) + para[:] = newbody[:-1] # drop trailing space + elif name == 'last-modified': + utils.clean_rcs_keywords(para, self.rcs_keyword_substitutions) + if cvs_url: + date = para.astext() + para[:] = [nodes.reference('', date, refuri=cvs_url)] + elif name == 'content-type': + pep_type = para.astext() + uri = self.document.settings.pep_base_url + self.pep_url % 12 + para[:] = [nodes.reference('', pep_type, refuri=uri)] + elif name == 'version' and len(body): + utils.clean_rcs_keywords(para, self.rcs_keyword_substitutions) + + +class Contents(Transform): + + """ + Insert an empty table of contents topic and a transform placeholder into + the document after the RFC 2822 header. + """ + + default_priority = 380 + + def apply(self): + language = languages.get_language(self.document.settings.language_code) + name = language.labels['contents'] + title = nodes.title('', name) + topic = nodes.topic('', title, classes=['contents']) + name = nodes.fully_normalize_name(name) + if not self.document.has_name(name): + topic['names'].append(name) + self.document.note_implicit_target(topic) + pending = nodes.pending(parts.Contents) + topic += pending + self.document.insert(1, topic) + self.document.note_pending(pending) + + +class TargetNotes(Transform): + + """ + Locate the "References" section, insert a placeholder for an external + target footnote insertion transform at the end, and schedule the + transform to run immediately. + """ + + default_priority = 520 + + def apply(self): + doc = self.document + i = len(doc) - 1 + refsect = copyright = None + while i >= 0 and isinstance(doc[i], nodes.section): + title_words = doc[i][0].astext().lower().split() + if 'references' in title_words: + refsect = doc[i] + break + elif 'copyright' in title_words: + copyright = i + i -= 1 + if not refsect: + refsect = nodes.section() + refsect += nodes.title('', 'References') + doc.set_id(refsect) + if copyright: + # Put the new "References" section before "Copyright": + doc.insert(copyright, refsect) + else: + # Put the new "References" section at end of doc: + doc.append(refsect) + pending = nodes.pending(references.TargetNotes) + refsect.append(pending) + self.document.note_pending(pending, 0) + pending = nodes.pending(misc.CallBack, + details={'callback': self.cleanup_callback}) + refsect.append(pending) + self.document.note_pending(pending, 1) + + def cleanup_callback(self, pending): + """ + Remove an empty "References" section. + + Called after the `references.TargetNotes` transform is complete. + """ + if len(pending.parent) == 2: # <title> and <pending> + pending.parent.parent.remove(pending.parent) + + +class PEPZero(Transform): + + """ + Special processing for PEP 0. + """ + + default_priority =760 + + def apply(self): + visitor = PEPZeroSpecial(self.document) + self.document.walk(visitor) + self.startnode.parent.remove(self.startnode) + + +class PEPZeroSpecial(nodes.SparseNodeVisitor): + + """ + Perform the special processing needed by PEP 0: + + - Mask email addresses. + + - Link PEP numbers in the second column of 4-column tables to the PEPs + themselves. + """ + + pep_url = Headers.pep_url + + def unknown_visit(self, node): + pass + + def visit_reference(self, node): + node.replace_self(mask_email(node)) + + def visit_field_list(self, node): + if 'rfc2822' in node['classes']: + raise nodes.SkipNode + + def visit_tgroup(self, node): + self.pep_table = node['cols'] == 4 + self.entry = 0 + + def visit_colspec(self, node): + self.entry += 1 + if self.pep_table and self.entry == 2: + node['classes'].append('num') + + def visit_row(self, node): + self.entry = 0 + + def visit_entry(self, node): + self.entry += 1 + if self.pep_table and self.entry == 2 and len(node) == 1: + node['classes'].append('num') + p = node[0] + if isinstance(p, nodes.paragraph) and len(p) == 1: + text = p.astext() + try: + pep = int(text) + ref = (self.document.settings.pep_base_url + + self.pep_url % pep) + p[0] = nodes.reference(text, text, refuri=ref) + except ValueError: + pass + + +non_masked_addresses = ('peps@python.org', + 'python-list@python.org', + 'python-dev@python.org') + +def mask_email(ref, pepno=None): + """ + Mask the email address in `ref` and return a replacement node. + + `ref` is returned unchanged if it contains no email address. + + For email addresses such as "user@host", mask the address as "user at + host" (text) to thwart simple email address harvesters (except for those + listed in `non_masked_addresses`). If a PEP number (`pepno`) is given, + return a reference including a default email subject. + """ + if ref.hasattr('refuri') and ref['refuri'].startswith('mailto:'): + if ref['refuri'][8:] in non_masked_addresses: + replacement = ref[0] + else: + replacement_text = ref.astext().replace('@', ' at ') + replacement = nodes.raw('', replacement_text, format='html') + if pepno is None: + return replacement + else: + ref['refuri'] += '?subject=PEP%%20%s' % pepno + ref[:] = [replacement] + return ref + else: + return ref diff -Nru zope3-3.4.0/src/docutils/transforms/references.py zope3-3.5~bzr18/src/docutils/transforms/references.py --- zope3-3.4.0/src/docutils/transforms/references.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/references.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,904 @@ +# $Id: references.py 6167 2009-10-11 14:51:42Z grubert $ +# Author: David Goodger <goodger@python.org> +# Copyright: This module has been placed in the public domain. + +""" +Transforms for resolving references. +""" + +__docformat__ = 'reStructuredText' + +import sys +import re +from docutils import nodes, utils +from docutils.transforms import TransformError, Transform + + +class PropagateTargets(Transform): + + """ + Propagate empty internal targets to the next element. + + Given the following nodes:: + + <target ids="internal1" names="internal1"> + <target anonymous="1" ids="id1"> + <target ids="internal2" names="internal2"> + <paragraph> + This is a test. + + PropagateTargets propagates the ids and names of the internal + targets preceding the paragraph to the paragraph itself:: + + <target refid="internal1"> + <target anonymous="1" refid="id1"> + <target refid="internal2"> + <paragraph ids="internal2 id1 internal1" names="internal2 internal1"> + This is a test. + """ + + default_priority = 260 + + def apply(self): + for target in self.document.traverse(nodes.target): + # Only block-level targets without reference (like ".. target:"): + if (isinstance(target.parent, nodes.TextElement) or + (target.hasattr('refid') or target.hasattr('refuri') or + target.hasattr('refname'))): + continue + assert len(target) == 0, 'error: block-level target has children' + next_node = target.next_node(ascend=1) + # Do not move names and ids into Invisibles (we'd lose the + # attributes) or different Targetables (e.g. footnotes). + if (next_node is not None and + ((not isinstance(next_node, nodes.Invisible) and + not isinstance(next_node, nodes.Targetable)) or + isinstance(next_node, nodes.target))): + next_node['ids'].extend(target['ids']) + next_node['names'].extend(target['names']) + # Set defaults for next_node.expect_referenced_by_name/id. + if not hasattr(next_node, 'expect_referenced_by_name'): + next_node.expect_referenced_by_name = {} + if not hasattr(next_node, 'expect_referenced_by_id'): + next_node.expect_referenced_by_id = {} + for id in target['ids']: + # Update IDs to node mapping. + self.document.ids[id] = next_node + # If next_node is referenced by id ``id``, this + # target shall be marked as referenced. + next_node.expect_referenced_by_id[id] = target + for name in target['names']: + next_node.expect_referenced_by_name[name] = target + # If there are any expect_referenced_by_... attributes + # in target set, copy them to next_node. + next_node.expect_referenced_by_name.update( + getattr(target, 'expect_referenced_by_name', {})) + next_node.expect_referenced_by_id.update( + getattr(target, 'expect_referenced_by_id', {})) + # Set refid to point to the first former ID of target + # which is now an ID of next_node. + target['refid'] = target['ids'][0] + # Clear ids and names; they have been moved to + # next_node. + target['ids'] = [] + target['names'] = [] + self.document.note_refid(target) + + +class AnonymousHyperlinks(Transform): + + """ + Link anonymous references to targets. Given:: + + <paragraph> + <reference anonymous="1"> + internal + <reference anonymous="1"> + external + <target anonymous="1" ids="id1"> + <target anonymous="1" ids="id2" refuri="http://external"> + + Corresponding references are linked via "refid" or resolved via "refuri":: + + <paragraph> + <reference anonymous="1" refid="id1"> + text + <reference anonymous="1" refuri="http://external"> + external + <target anonymous="1" ids="id1"> + <target anonymous="1" ids="id2" refuri="http://external"> + """ + + default_priority = 440 + + def apply(self): + anonymous_refs = [] + anonymous_targets = [] + for node in self.document.traverse(nodes.reference): + if node.get('anonymous'): + anonymous_refs.append(node) + for node in self.document.traverse(nodes.target): + if node.get('anonymous'): + anonymous_targets.append(node) + if len(anonymous_refs) \ + != len(anonymous_targets): + msg = self.document.reporter.error( + 'Anonymous hyperlink mismatch: %s references but %s ' + 'targets.\nSee "backrefs" attribute for IDs.' + % (len(anonymous_refs), len(anonymous_targets))) + msgid = self.document.set_id(msg) + for ref in anonymous_refs: + prb = nodes.problematic( + ref.rawsource, ref.rawsource, refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + ref.replace_self(prb) + return + for ref, target in zip(anonymous_refs, anonymous_targets): + target.referenced = 1 + while 1: + if target.hasattr('refuri'): + ref['refuri'] = target['refuri'] + ref.resolved = 1 + break + else: + if not target['ids']: + # Propagated target. + target = self.document.ids[target['refid']] + continue + ref['refid'] = target['ids'][0] + self.document.note_refid(ref) + break + + +class IndirectHyperlinks(Transform): + + """ + a) Indirect external references:: + + <paragraph> + <reference refname="indirect external"> + indirect external + <target id="id1" name="direct external" + refuri="http://indirect"> + <target id="id2" name="indirect external" + refname="direct external"> + + The "refuri" attribute is migrated back to all indirect targets + from the final direct target (i.e. a target not referring to + another indirect target):: + + <paragraph> + <reference refname="indirect external"> + indirect external + <target id="id1" name="direct external" + refuri="http://indirect"> + <target id="id2" name="indirect external" + refuri="http://indirect"> + + Once the attribute is migrated, the preexisting "refname" attribute + is dropped. + + b) Indirect internal references:: + + <target id="id1" name="final target"> + <paragraph> + <reference refname="indirect internal"> + indirect internal + <target id="id2" name="indirect internal 2" + refname="final target"> + <target id="id3" name="indirect internal" + refname="indirect internal 2"> + + Targets which indirectly refer to an internal target become one-hop + indirect (their "refid" attributes are directly set to the internal + target's "id"). References which indirectly refer to an internal + target become direct internal references:: + + <target id="id1" name="final target"> + <paragraph> + <reference refid="id1"> + indirect internal + <target id="id2" name="indirect internal 2" refid="id1"> + <target id="id3" name="indirect internal" refid="id1"> + """ + + default_priority = 460 + + def apply(self): + for target in self.document.indirect_targets: + if not target.resolved: + self.resolve_indirect_target(target) + self.resolve_indirect_references(target) + + def resolve_indirect_target(self, target): + refname = target.get('refname') + if refname is None: + reftarget_id = target['refid'] + else: + reftarget_id = self.document.nameids.get(refname) + if not reftarget_id: + # Check the unknown_reference_resolvers + for resolver_function in \ + self.document.transformer.unknown_reference_resolvers: + if resolver_function(target): + break + else: + self.nonexistent_indirect_target(target) + return + reftarget = self.document.ids[reftarget_id] + reftarget.note_referenced_by(id=reftarget_id) + if isinstance(reftarget, nodes.target) \ + and not reftarget.resolved and reftarget.hasattr('refname'): + if hasattr(target, 'multiply_indirect'): + #and target.multiply_indirect): + #del target.multiply_indirect + self.circular_indirect_reference(target) + return + target.multiply_indirect = 1 + self.resolve_indirect_target(reftarget) # multiply indirect + del target.multiply_indirect + if reftarget.hasattr('refuri'): + target['refuri'] = reftarget['refuri'] + if 'refid' in target: + del target['refid'] + elif reftarget.hasattr('refid'): + target['refid'] = reftarget['refid'] + self.document.note_refid(target) + else: + if reftarget['ids']: + target['refid'] = reftarget_id + self.document.note_refid(target) + else: + self.nonexistent_indirect_target(target) + return + if refname is not None: + del target['refname'] + target.resolved = 1 + + def nonexistent_indirect_target(self, target): + if target['refname'] in self.document.nameids: + self.indirect_target_error(target, 'which is a duplicate, and ' + 'cannot be used as a unique reference') + else: + self.indirect_target_error(target, 'which does not exist') + + def circular_indirect_reference(self, target): + self.indirect_target_error(target, 'forming a circular reference') + + def indirect_target_error(self, target, explanation): + naming = '' + reflist = [] + if target['names']: + naming = '"%s" ' % target['names'][0] + for name in target['names']: + reflist.extend(self.document.refnames.get(name, [])) + for id in target['ids']: + reflist.extend(self.document.refids.get(id, [])) + naming += '(id="%s")' % target['ids'][0] + msg = self.document.reporter.error( + 'Indirect hyperlink target %s refers to target "%s", %s.' + % (naming, target['refname'], explanation), base_node=target) + msgid = self.document.set_id(msg) + for ref in utils.uniq(reflist): + prb = nodes.problematic( + ref.rawsource, ref.rawsource, refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + ref.replace_self(prb) + target.resolved = 1 + + def resolve_indirect_references(self, target): + if target.hasattr('refid'): + attname = 'refid' + call_method = self.document.note_refid + elif target.hasattr('refuri'): + attname = 'refuri' + call_method = None + else: + return + attval = target[attname] + for name in target['names']: + reflist = self.document.refnames.get(name, []) + if reflist: + target.note_referenced_by(name=name) + for ref in reflist: + if ref.resolved: + continue + del ref['refname'] + ref[attname] = attval + if call_method: + call_method(ref) + ref.resolved = 1 + if isinstance(ref, nodes.target): + self.resolve_indirect_references(ref) + for id in target['ids']: + reflist = self.document.refids.get(id, []) + if reflist: + target.note_referenced_by(id=id) + for ref in reflist: + if ref.resolved: + continue + del ref['refid'] + ref[attname] = attval + if call_method: + call_method(ref) + ref.resolved = 1 + if isinstance(ref, nodes.target): + self.resolve_indirect_references(ref) + + +class ExternalTargets(Transform): + + """ + Given:: + + <paragraph> + <reference refname="direct external"> + direct external + <target id="id1" name="direct external" refuri="http://direct"> + + The "refname" attribute is replaced by the direct "refuri" attribute:: + + <paragraph> + <reference refuri="http://direct"> + direct external + <target id="id1" name="direct external" refuri="http://direct"> + """ + + default_priority = 640 + + def apply(self): + for target in self.document.traverse(nodes.target): + if target.hasattr('refuri'): + refuri = target['refuri'] + for name in target['names']: + reflist = self.document.refnames.get(name, []) + if reflist: + target.note_referenced_by(name=name) + for ref in reflist: + if ref.resolved: + continue + del ref['refname'] + ref['refuri'] = refuri + ref.resolved = 1 + + +class InternalTargets(Transform): + + default_priority = 660 + + def apply(self): + for target in self.document.traverse(nodes.target): + if not target.hasattr('refuri') and not target.hasattr('refid'): + self.resolve_reference_ids(target) + + def resolve_reference_ids(self, target): + """ + Given:: + + <paragraph> + <reference refname="direct internal"> + direct internal + <target id="id1" name="direct internal"> + + The "refname" attribute is replaced by "refid" linking to the target's + "id":: + + <paragraph> + <reference refid="id1"> + direct internal + <target id="id1" name="direct internal"> + """ + for name in target['names']: + refid = self.document.nameids[name] + reflist = self.document.refnames.get(name, []) + if reflist: + target.note_referenced_by(name=name) + for ref in reflist: + if ref.resolved: + continue + del ref['refname'] + ref['refid'] = refid + ref.resolved = 1 + + +class Footnotes(Transform): + + """ + Assign numbers to autonumbered footnotes, and resolve links to footnotes, + citations, and their references. + + Given the following ``document`` as input:: + + <document> + <paragraph> + A labeled autonumbered footnote referece: + <footnote_reference auto="1" id="id1" refname="footnote"> + <paragraph> + An unlabeled autonumbered footnote referece: + <footnote_reference auto="1" id="id2"> + <footnote auto="1" id="id3"> + <paragraph> + Unlabeled autonumbered footnote. + <footnote auto="1" id="footnote" name="footnote"> + <paragraph> + Labeled autonumbered footnote. + + Auto-numbered footnotes have attribute ``auto="1"`` and no label. + Auto-numbered footnote_references have no reference text (they're + empty elements). When resolving the numbering, a ``label`` element + is added to the beginning of the ``footnote``, and reference text + to the ``footnote_reference``. + + The transformed result will be:: + + <document> + <paragraph> + A labeled autonumbered footnote referece: + <footnote_reference auto="1" id="id1" refid="footnote"> + 2 + <paragraph> + An unlabeled autonumbered footnote referece: + <footnote_reference auto="1" id="id2" refid="id3"> + 1 + <footnote auto="1" id="id3" backrefs="id2"> + <label> + 1 + <paragraph> + Unlabeled autonumbered footnote. + <footnote auto="1" id="footnote" name="footnote" backrefs="id1"> + <label> + 2 + <paragraph> + Labeled autonumbered footnote. + + Note that the footnotes are not in the same order as the references. + + The labels and reference text are added to the auto-numbered ``footnote`` + and ``footnote_reference`` elements. Footnote elements are backlinked to + their references via "refids" attributes. References are assigned "id" + and "refid" attributes. + + After adding labels and reference text, the "auto" attributes can be + ignored. + """ + + default_priority = 620 + + autofootnote_labels = None + """Keep track of unlabeled autonumbered footnotes.""" + + symbols = [ + # Entries 1-4 and 6 below are from section 12.51 of + # The Chicago Manual of Style, 14th edition. + '*', # asterisk/star + u'\u2020', # dagger † + u'\u2021', # double dagger ‡ + u'\u00A7', # section mark § + u'\u00B6', # paragraph mark (pilcrow) ¶ + # (parallels ['||'] in CMoS) + '#', # number sign + # The entries below were chosen arbitrarily. + u'\u2660', # spade suit ♠ + u'\u2665', # heart suit ♥ + u'\u2666', # diamond suit ♦ + u'\u2663', # club suit ♣ + ] + + def apply(self): + self.autofootnote_labels = [] + startnum = self.document.autofootnote_start + self.document.autofootnote_start = self.number_footnotes(startnum) + self.number_footnote_references(startnum) + self.symbolize_footnotes() + self.resolve_footnotes_and_citations() + + def number_footnotes(self, startnum): + """ + Assign numbers to autonumbered footnotes. + + For labeled autonumbered footnotes, copy the number over to + corresponding footnote references. + """ + for footnote in self.document.autofootnotes: + while 1: + label = str(startnum) + startnum += 1 + if label not in self.document.nameids: + break + footnote.insert(0, nodes.label('', label)) + for name in footnote['names']: + for ref in self.document.footnote_refs.get(name, []): + ref += nodes.Text(label) + ref.delattr('refname') + assert len(footnote['ids']) == len(ref['ids']) == 1 + ref['refid'] = footnote['ids'][0] + footnote.add_backref(ref['ids'][0]) + self.document.note_refid(ref) + ref.resolved = 1 + if not footnote['names'] and not footnote['dupnames']: + footnote['names'].append(label) + self.document.note_explicit_target(footnote, footnote) + self.autofootnote_labels.append(label) + return startnum + + def number_footnote_references(self, startnum): + """Assign numbers to autonumbered footnote references.""" + i = 0 + for ref in self.document.autofootnote_refs: + if ref.resolved or ref.hasattr('refid'): + continue + try: + label = self.autofootnote_labels[i] + except IndexError: + msg = self.document.reporter.error( + 'Too many autonumbered footnote references: only %s ' + 'corresponding footnotes available.' + % len(self.autofootnote_labels), base_node=ref) + msgid = self.document.set_id(msg) + for ref in self.document.autofootnote_refs[i:]: + if ref.resolved or ref.hasattr('refname'): + continue + prb = nodes.problematic( + ref.rawsource, ref.rawsource, refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + ref.replace_self(prb) + break + ref += nodes.Text(label) + id = self.document.nameids[label] + footnote = self.document.ids[id] + ref['refid'] = id + self.document.note_refid(ref) + assert len(ref['ids']) == 1 + footnote.add_backref(ref['ids'][0]) + ref.resolved = 1 + i += 1 + + def symbolize_footnotes(self): + """Add symbols indexes to "[*]"-style footnotes and references.""" + labels = [] + for footnote in self.document.symbol_footnotes: + reps, index = divmod(self.document.symbol_footnote_start, + len(self.symbols)) + labeltext = self.symbols[index] * (reps + 1) + labels.append(labeltext) + footnote.insert(0, nodes.label('', labeltext)) + self.document.symbol_footnote_start += 1 + self.document.set_id(footnote) + i = 0 + for ref in self.document.symbol_footnote_refs: + try: + ref += nodes.Text(labels[i]) + except IndexError: + msg = self.document.reporter.error( + 'Too many symbol footnote references: only %s ' + 'corresponding footnotes available.' % len(labels), + base_node=ref) + msgid = self.document.set_id(msg) + for ref in self.document.symbol_footnote_refs[i:]: + if ref.resolved or ref.hasattr('refid'): + continue + prb = nodes.problematic( + ref.rawsource, ref.rawsource, refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + ref.replace_self(prb) + break + footnote = self.document.symbol_footnotes[i] + assert len(footnote['ids']) == 1 + ref['refid'] = footnote['ids'][0] + self.document.note_refid(ref) + footnote.add_backref(ref['ids'][0]) + i += 1 + + def resolve_footnotes_and_citations(self): + """ + Link manually-labeled footnotes and citations to/from their + references. + """ + for footnote in self.document.footnotes: + for label in footnote['names']: + if label in self.document.footnote_refs: + reflist = self.document.footnote_refs[label] + self.resolve_references(footnote, reflist) + for citation in self.document.citations: + for label in citation['names']: + if label in self.document.citation_refs: + reflist = self.document.citation_refs[label] + self.resolve_references(citation, reflist) + + def resolve_references(self, note, reflist): + assert len(note['ids']) == 1 + id = note['ids'][0] + for ref in reflist: + if ref.resolved: + continue + ref.delattr('refname') + ref['refid'] = id + assert len(ref['ids']) == 1 + note.add_backref(ref['ids'][0]) + ref.resolved = 1 + note.resolved = 1 + + +class CircularSubstitutionDefinitionError(Exception): pass + + +class Substitutions(Transform): + + """ + Given the following ``document`` as input:: + + <document> + <paragraph> + The + <substitution_reference refname="biohazard"> + biohazard + symbol is deservedly scary-looking. + <substitution_definition name="biohazard"> + <image alt="biohazard" uri="biohazard.png"> + + The ``substitution_reference`` will simply be replaced by the + contents of the corresponding ``substitution_definition``. + + The transformed result will be:: + + <document> + <paragraph> + The + <image alt="biohazard" uri="biohazard.png"> + symbol is deservedly scary-looking. + <substitution_definition name="biohazard"> + <image alt="biohazard" uri="biohazard.png"> + """ + + default_priority = 220 + """The Substitutions transform has to be applied very early, before + `docutils.tranforms.frontmatter.DocTitle` and others.""" + + def apply(self): + defs = self.document.substitution_defs + normed = self.document.substitution_names + subreflist = self.document.traverse(nodes.substitution_reference) + nested = {} + for ref in subreflist: + refname = ref['refname'] + key = None + if refname in defs: + key = refname + else: + normed_name = refname.lower() + if normed_name in normed: + key = normed[normed_name] + if key is None: + msg = self.document.reporter.error( + 'Undefined substitution referenced: "%s".' + % refname, base_node=ref) + msgid = self.document.set_id(msg) + prb = nodes.problematic( + ref.rawsource, ref.rawsource, refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + ref.replace_self(prb) + else: + subdef = defs[key] + parent = ref.parent + index = parent.index(ref) + if ('ltrim' in subdef.attributes + or 'trim' in subdef.attributes): + if index > 0 and isinstance(parent[index - 1], + nodes.Text): + parent.replace(parent[index - 1], + parent[index - 1].rstrip()) + if ('rtrim' in subdef.attributes + or 'trim' in subdef.attributes): + if (len(parent) > index + 1 + and isinstance(parent[index + 1], nodes.Text)): + parent.replace(parent[index + 1], + parent[index + 1].lstrip()) + subdef_copy = subdef.deepcopy() + try: + # Take care of nested substitution references: + for nested_ref in subdef_copy.traverse( + nodes.substitution_reference): + nested_name = normed[nested_ref['refname'].lower()] + if nested_name in nested.setdefault(nested_name, []): + raise CircularSubstitutionDefinitionError + else: + nested[nested_name].append(key) + subreflist.append(nested_ref) + except CircularSubstitutionDefinitionError: + parent = ref.parent + if isinstance(parent, nodes.substitution_definition): + msg = self.document.reporter.error( + 'Circular substitution definition detected:', + nodes.literal_block(parent.rawsource, + parent.rawsource), + line=parent.line, base_node=parent) + parent.replace_self(msg) + else: + msg = self.document.reporter.error( + 'Circular substitution definition referenced: "%s".' + % refname, base_node=ref) + msgid = self.document.set_id(msg) + prb = nodes.problematic( + ref.rawsource, ref.rawsource, refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + ref.replace_self(prb) + else: + ref.replace_self(subdef_copy.children) + # register refname of the replacment node(s) + # (needed for resolution of references) + for node in subdef_copy.children: + if isinstance(node, nodes.Referential): + # HACK: verify refname attribute exists. + # Test with docs/dev/todo.txt, see. |donate| + if 'refname' in node: + self.document.note_refname(node) + + +class TargetNotes(Transform): + + """ + Creates a footnote for each external target in the text, and corresponding + footnote references after each reference. + """ + + default_priority = 540 + """The TargetNotes transform has to be applied after `IndirectHyperlinks` + but before `Footnotes`.""" + + + def __init__(self, document, startnode): + Transform.__init__(self, document, startnode=startnode) + + self.classes = startnode.details.get('class', []) + + def apply(self): + notes = {} + nodelist = [] + for target in self.document.traverse(nodes.target): + # Only external targets. + if not target.hasattr('refuri'): + continue + names = target['names'] + refs = [] + for name in names: + refs.extend(self.document.refnames.get(name, [])) + if not refs: + continue + footnote = self.make_target_footnote(target['refuri'], refs, + notes) + if target['refuri'] not in notes: + notes[target['refuri']] = footnote + nodelist.append(footnote) + # Take care of anonymous references. + for ref in self.document.traverse(nodes.reference): + if not ref.get('anonymous'): + continue + if ref.hasattr('refuri'): + footnote = self.make_target_footnote(ref['refuri'], [ref], + notes) + if ref['refuri'] not in notes: + notes[ref['refuri']] = footnote + nodelist.append(footnote) + self.startnode.replace_self(nodelist) + + def make_target_footnote(self, refuri, refs, notes): + if refuri in notes: # duplicate? + footnote = notes[refuri] + assert len(footnote['names']) == 1 + footnote_name = footnote['names'][0] + else: # original + footnote = nodes.footnote() + footnote_id = self.document.set_id(footnote) + # Use uppercase letters and a colon; they can't be + # produced inside names by the parser. + footnote_name = 'TARGET_NOTE: ' + footnote_id + footnote['auto'] = 1 + footnote['names'] = [footnote_name] + footnote_paragraph = nodes.paragraph() + footnote_paragraph += nodes.reference('', refuri, refuri=refuri) + footnote += footnote_paragraph + self.document.note_autofootnote(footnote) + self.document.note_explicit_target(footnote, footnote) + for ref in refs: + if isinstance(ref, nodes.target): + continue + refnode = nodes.footnote_reference( + refname=footnote_name, auto=1) + refnode['classes'] += self.classes + self.document.note_autofootnote_ref(refnode) + self.document.note_footnote_ref(refnode) + index = ref.parent.index(ref) + 1 + reflist = [refnode] + if not utils.get_trim_footnote_ref_space(self.document.settings): + if self.classes: + reflist.insert(0, nodes.inline(text=' ', Classes=self.classes)) + else: + reflist.insert(0, nodes.Text(' ')) + ref.parent.insert(index, reflist) + return footnote + + +class DanglingReferences(Transform): + + """ + Check for dangling references (incl. footnote & citation) and for + unreferenced targets. + """ + + default_priority = 850 + + def apply(self): + visitor = DanglingReferencesVisitor( + self.document, + self.document.transformer.unknown_reference_resolvers) + self.document.walk(visitor) + # *After* resolving all references, check for unreferenced + # targets: + for target in self.document.traverse(nodes.target): + if not target.referenced: + if target.get('anonymous'): + # If we have unreferenced anonymous targets, there + # is already an error message about anonymous + # hyperlink mismatch; no need to generate another + # message. + continue + if target['names']: + naming = target['names'][0] + elif target['ids']: + naming = target['ids'][0] + else: + # Hack: Propagated targets always have their refid + # attribute set. + naming = target['refid'] + self.document.reporter.info( + 'Hyperlink target "%s" is not referenced.' + % naming, base_node=target) + + +class DanglingReferencesVisitor(nodes.SparseNodeVisitor): + + def __init__(self, document, unknown_reference_resolvers): + nodes.SparseNodeVisitor.__init__(self, document) + self.document = document + self.unknown_reference_resolvers = unknown_reference_resolvers + + def unknown_visit(self, node): + pass + + def visit_reference(self, node): + if node.resolved or not node.hasattr('refname'): + return + refname = node['refname'] + id = self.document.nameids.get(refname) + if id is None: + for resolver_function in self.unknown_reference_resolvers: + if resolver_function(node): + break + else: + if refname in self.document.nameids: + msg = self.document.reporter.error( + 'Duplicate target name, cannot be used as a unique ' + 'reference: "%s".' % (node['refname']), base_node=node) + else: + msg = self.document.reporter.error( + 'Unknown target name: "%s".' % (node['refname']), + base_node=node) + msgid = self.document.set_id(msg) + prb = nodes.problematic( + node.rawsource, node.rawsource, refid=msgid) + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + node.replace_self(prb) + else: + del node['refname'] + node['refid'] = id + self.document.ids[id].note_referenced_by(id=id) + node.resolved = 1 + + visit_footnote_reference = visit_citation_reference = visit_reference diff -Nru zope3-3.4.0/src/docutils/transforms/universal.py zope3-3.5~bzr18/src/docutils/transforms/universal.py --- zope3-3.4.0/src/docutils/transforms/universal.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/universal.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,203 @@ +# $Id: universal.py 6112 2009-09-03 07:27:59Z milde $ +# Authors: David Goodger <goodger@python.org>; Ueli Schlaepfer +# Copyright: This module has been placed in the public domain. + +""" +Transforms needed by most or all documents: + +- `Decorations`: Generate a document's header & footer. +- `Messages`: Placement of system messages stored in + `nodes.document.transform_messages`. +- `TestMessages`: Like `Messages`, used on test runs. +- `FinalReferences`: Resolve remaining references. +""" + +__docformat__ = 'reStructuredText' + +import re +import sys +import time +from docutils import nodes, utils +from docutils.transforms import TransformError, Transform + + +class Decorations(Transform): + + """ + Populate a document's decoration element (header, footer). + """ + + default_priority = 820 + + def apply(self): + header_nodes = self.generate_header() + if header_nodes: + decoration = self.document.get_decoration() + header = decoration.get_header() + header.extend(header_nodes) + footer_nodes = self.generate_footer() + if footer_nodes: + decoration = self.document.get_decoration() + footer = decoration.get_footer() + footer.extend(footer_nodes) + + def generate_header(self): + return None + + def generate_footer(self): + # @@@ Text is hard-coded for now. + # Should be made dynamic (language-dependent). + settings = self.document.settings + if settings.generator or settings.datestamp or settings.source_link \ + or settings.source_url: + text = [] + if settings.source_link and settings._source \ + or settings.source_url: + if settings.source_url: + source = settings.source_url + else: + source = utils.relative_path(settings._destination, + settings._source) + text.extend([ + nodes.reference('', 'View document source', + refuri=source), + nodes.Text('.\n')]) + if settings.datestamp: + datestamp = time.strftime(settings.datestamp, time.gmtime()) + text.append(nodes.Text('Generated on: ' + datestamp + '.\n')) + if settings.generator: + text.extend([ + nodes.Text('Generated by '), + nodes.reference('', 'Docutils', refuri= + 'http://docutils.sourceforge.net/'), + nodes.Text(' from '), + nodes.reference('', 'reStructuredText', refuri='http://' + 'docutils.sourceforge.net/rst.html'), + nodes.Text(' source.\n')]) + return [nodes.paragraph('', '', *text)] + else: + return None + + +class ExposeInternals(Transform): + + """ + Expose internal attributes if ``expose_internals`` setting is set. + """ + + default_priority = 840 + + def not_Text(self, node): + return not isinstance(node, nodes.Text) + + def apply(self): + if self.document.settings.expose_internals: + for node in self.document.traverse(self.not_Text): + for att in self.document.settings.expose_internals: + value = getattr(node, att, None) + if value is not None: + node['internal:' + att] = value + + +class Messages(Transform): + + """ + Place any system messages generated after parsing into a dedicated section + of the document. + """ + + default_priority = 860 + + def apply(self): + unfiltered = self.document.transform_messages + threshold = self.document.reporter.report_level + messages = [] + for msg in unfiltered: + if msg['level'] >= threshold and not msg.parent: + messages.append(msg) + if messages: + section = nodes.section(classes=['system-messages']) + # @@@ get this from the language module? + section += nodes.title('', 'Docutils System Messages') + section += messages + self.document.transform_messages[:] = [] + self.document += section + + +class FilterMessages(Transform): + + """ + Remove system messages below verbosity threshold. + """ + + default_priority = 870 + + def apply(self): + for node in self.document.traverse(nodes.system_message): + if node['level'] < self.document.reporter.report_level: + node.parent.remove(node) + + +class TestMessages(Transform): + + """ + Append all post-parse system messages to the end of the document. + + Used for testing purposes. + """ + + default_priority = 880 + + def apply(self): + for msg in self.document.transform_messages: + if not msg.parent: + self.document += msg + + +class StripComments(Transform): + + """ + Remove comment elements from the document tree (only if the + ``strip_comments`` setting is enabled). + """ + + default_priority = 740 + + def apply(self): + if self.document.settings.strip_comments: + for node in self.document.traverse(nodes.comment): + node.parent.remove(node) + + +class StripClassesAndElements(Transform): + + """ + Remove from the document tree all elements with classes in + `self.document.settings.strip_elements_with_classes` and all "classes" + attribute values in `self.document.settings.strip_classes`. + """ + + default_priority = 420 + + def apply(self): + if not (self.document.settings.strip_elements_with_classes + or self.document.settings.strip_classes): + return + # prepare dicts for lookup (not sets, for Python 2.2 compatibility): + self.strip_elements = dict( + [(key, None) + for key in (self.document.settings.strip_elements_with_classes + or [])]) + self.strip_classes = dict( + [(key, None) for key in (self.document.settings.strip_classes + or [])]) + for node in self.document.traverse(self.check_classes): + node.parent.remove(node) + + def check_classes(self, node): + if isinstance(node, nodes.Element): + for class_value in node['classes'][:]: + if class_value in self.strip_classes: + node['classes'].remove(class_value) + if class_value in self.strip_elements: + return 1 diff -Nru zope3-3.4.0/src/docutils/transforms/writer_aux.py zope3-3.5~bzr18/src/docutils/transforms/writer_aux.py --- zope3-3.4.0/src/docutils/transforms/writer_aux.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/transforms/writer_aux.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,88 @@ +# $Id: writer_aux.py 5174 2007-05-31 00:01:52Z wiemann $ +# Author: Lea Wiemann <LeWiemann@gmail.com> +# Copyright: This module has been placed in the public domain. + +""" +Auxiliary transforms mainly to be used by Writer components. + +This module is called "writer_aux" because otherwise there would be +conflicting imports like this one:: + + from docutils import writers + from docutils.transforms import writers +""" + +__docformat__ = 'reStructuredText' + +from docutils import nodes, utils, languages +from docutils.transforms import Transform + + +class Compound(Transform): + + """ + Flatten all compound paragraphs. For example, transform :: + + <compound> + <paragraph> + <literal_block> + <paragraph> + + into :: + + <paragraph> + <literal_block classes="continued"> + <paragraph classes="continued"> + """ + + default_priority = 910 + + def apply(self): + for compound in self.document.traverse(nodes.compound): + first_child = 1 + for child in compound: + if first_child: + if not isinstance(child, nodes.Invisible): + first_child = 0 + else: + child['classes'].append('continued') + # Substitute children for compound. + compound.replace_self(compound[:]) + + +class Admonitions(Transform): + + """ + Transform specific admonitions, like this: + + <note> + <paragraph> + Note contents ... + + into generic admonitions, like this:: + + <admonition classes="note"> + <title> + Note + <paragraph> + Note contents ... + + The admonition title is localized. + """ + + default_priority = 920 + + def apply(self): + lcode = self.document.settings.language_code + language = languages.get_language(lcode) + for node in self.document.traverse(nodes.Admonition): + node_name = node.__class__.__name__ + # Set class, so that we know what node this admonition came from. + node['classes'].append(node_name) + if not isinstance(node, nodes.admonition): + # Specific admonition. Transform into a generic admonition. + admonition = nodes.admonition(node.rawsource, *node.children, + **node.attributes) + title = nodes.title('', language.labels[node_name]) + admonition.insert(0, title) + node.replace_self(admonition) diff -Nru zope3-3.4.0/src/docutils/urischemes.py zope3-3.5~bzr18/src/docutils/urischemes.py --- zope3-3.4.0/src/docutils/urischemes.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/urischemes.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,136 @@ +# $Id: urischemes.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger <goodger@python.org> +# Copyright: This module has been placed in the public domain. + +""" +`schemes` is a dictionary with lowercase URI addressing schemes as +keys and descriptions as values. It was compiled from the index at +http://www.iana.org/assignments/uri-schemes (revised 2005-11-28) +and an older list at http://www.w3.org/Addressing/schemes.html. +""" + +# Many values are blank and should be filled in with useful descriptions. + +schemes = { + 'about': 'provides information on Navigator', + 'acap': 'Application Configuration Access Protocol; RFC 2244', + 'addbook': "To add vCard entries to Communicator's Address Book", + 'afp': 'Apple Filing Protocol', + 'afs': 'Andrew File System global file names', + 'aim': 'AOL Instant Messenger', + 'callto': 'for NetMeeting links', + 'castanet': 'Castanet Tuner URLs for Netcaster', + 'chttp': 'cached HTTP supported by RealPlayer', + 'cid': 'content identifier; RFC 2392', + 'crid': 'TV-Anytime Content Reference Identifier; RFC 4078', + 'data': ('allows inclusion of small data items as "immediate" data; ' + 'RFC 2397'), + 'dav': 'Distributed Authoring and Versioning Protocol; RFC 2518', + 'dict': 'dictionary service protocol; RFC 2229', + 'dns': 'Domain Name System resources', + 'eid': ('External ID; non-URL data; general escape mechanism to allow ' + 'access to information for applications that are too ' + 'specialized to justify their own schemes'), + 'fax': ('a connection to a terminal that can handle telefaxes ' + '(facsimiles); RFC 2806'), + 'feed' : 'NetNewsWire feed', + 'file': 'Host-specific file names; RFC 1738', + 'finger': '', + 'freenet': '', + 'ftp': 'File Transfer Protocol; RFC 1738', + 'go': 'go; RFC 3368', + 'gopher': 'The Gopher Protocol', + 'gsm-sms': ('Global System for Mobile Communications Short Message ' + 'Service'), + 'h323': ('video (audiovisual) communication on local area networks; ' + 'RFC 3508'), + 'h324': ('video and audio communications over low bitrate connections ' + 'such as POTS modem connections'), + 'hdl': 'CNRI handle system', + 'hnews': 'an HTTP-tunneling variant of the NNTP news protocol', + 'http': 'Hypertext Transfer Protocol; RFC 2616', + 'https': 'HTTP over SSL; RFC 2818', + 'hydra': 'SubEthaEdit URI. See http://www.codingmonkeys.de/subethaedit.', + 'iioploc': 'Internet Inter-ORB Protocol Location?', + 'ilu': 'Inter-Language Unification', + 'im': 'Instant Messaging; RFC 3860', + 'imap': 'Internet Message Access Protocol; RFC 2192', + 'info': 'Information Assets with Identifiers in Public Namespaces', + 'ior': 'CORBA interoperable object reference', + 'ipp': 'Internet Printing Protocol; RFC 3510', + 'irc': 'Internet Relay Chat', + 'iris.beep': 'iris.beep; RFC 3983', + 'iseek' : 'See www.ambrosiasw.com; a little util for OS X.', + 'jar': 'Java archive', + 'javascript': ('JavaScript code; evaluates the expression after the ' + 'colon'), + 'jdbc': 'JDBC connection URI.', + 'ldap': 'Lightweight Directory Access Protocol', + 'lifn': '', + 'livescript': '', + 'lrq': '', + 'mailbox': 'Mail folder access', + 'mailserver': 'Access to data available from mail servers', + 'mailto': 'Electronic mail address; RFC 2368', + 'md5': '', + 'mid': 'message identifier; RFC 2392', + 'mocha': '', + 'modem': ('a connection to a terminal that can handle incoming data ' + 'calls; RFC 2806'), + 'mtqp': 'Message Tracking Query Protocol; RFC 3887', + 'mupdate': 'Mailbox Update (MUPDATE) Protocol; RFC 3656', + 'news': 'USENET news; RFC 1738', + 'nfs': 'Network File System protocol; RFC 2224', + 'nntp': 'USENET news using NNTP access; RFC 1738', + 'opaquelocktoken': 'RFC 2518', + 'phone': '', + 'pop': 'Post Office Protocol; RFC 2384', + 'pop3': 'Post Office Protocol v3', + 'pres': 'Presence; RFC 3859', + 'printer': '', + 'prospero': 'Prospero Directory Service; RFC 4157', + 'rdar' : ('URLs found in Darwin source ' + '(http://www.opensource.apple.com/darwinsource/).'), + 'res': '', + 'rtsp': 'real time streaming protocol; RFC 2326', + 'rvp': '', + 'rwhois': '', + 'rx': 'Remote Execution', + 'sdp': '', + 'service': 'service location; RFC 2609', + 'shttp': 'secure hypertext transfer protocol', + 'sip': 'Session Initiation Protocol; RFC 3261', + 'sips': 'secure session intitiaion protocol; RFC 3261', + 'smb': 'SAMBA filesystems.', + 'snews': 'For NNTP postings via SSL', + 'snmp': 'Simple Network Management Protocol; RFC 4088', + 'soap.beep': 'RFC 3288', + 'soap.beeps': 'RFC 3288', + 'ssh': 'Reference to interactive sessions via ssh.', + 't120': 'real time data conferencing (audiographics)', + 'tag': 'RFC 4151', + 'tcp': '', + 'tel': ('a connection to a terminal that handles normal voice ' + 'telephone calls, a voice mailbox or another voice messaging ' + 'system or a service that can be operated using DTMF tones; ' + 'RFC 2806.'), + 'telephone': 'telephone', + 'telnet': 'Reference to interactive sessions; RFC 4248', + 'tftp': 'Trivial File Transfer Protocol; RFC 3617', + 'tip': 'Transaction Internet Protocol; RFC 2371', + 'tn3270': 'Interactive 3270 emulation sessions', + 'tv': '', + 'urn': 'Uniform Resource Name; RFC 2141', + 'uuid': '', + 'vemmi': 'versatile multimedia interface; RFC 2122', + 'videotex': '', + 'view-source': 'displays HTML code that was generated with JavaScript', + 'wais': 'Wide Area Information Servers; RFC 4156', + 'whodp': '', + 'whois++': 'Distributed directory service.', + 'x-man-page': ('Opens man page in Terminal.app on OS X ' + '(see macosxhints.com)'), + 'xmlrpc.beep': 'RFC 3529', + 'xmlrpc.beeps': 'RFC 3529', + 'z39.50r': 'Z39.50 Retrieval; RFC 2056', + 'z39.50s': 'Z39.50 Session; RFC 2056',} diff -Nru zope3-3.4.0/src/docutils/utils.py zope3-3.5~bzr18/src/docutils/utils.py --- zope3-3.4.0/src/docutils/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/utils.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,650 @@ +# $Id: utils.py 6120 2009-09-10 11:02:27Z milde $ +# Author: David Goodger <goodger@python.org> +# Copyright: This module has been placed in the public domain. + +""" +Miscellaneous utilities for the documentation utilities. +""" + +__docformat__ = 'reStructuredText' + +import sys +import os +import os.path +import warnings +import unicodedata +from docutils import ApplicationError, DataError +from docutils import nodes +from docutils._compat import b + + +class SystemMessage(ApplicationError): + + def __init__(self, system_message, level): + Exception.__init__(self, system_message.astext()) + self.level = level + + +class SystemMessagePropagation(ApplicationError): pass + + +class Reporter: + + """ + Info/warning/error reporter and ``system_message`` element generator. + + Five levels of system messages are defined, along with corresponding + methods: `debug()`, `info()`, `warning()`, `error()`, and `severe()`. + + There is typically one Reporter object per process. A Reporter object is + instantiated with thresholds for reporting (generating warnings) and + halting processing (raising exceptions), a switch to turn debug output on + or off, and an I/O stream for warnings. These are stored as instance + attributes. + + When a system message is generated, its level is compared to the stored + thresholds, and a warning or error is generated as appropriate. Debug + messages are produced iff the stored debug switch is on, independently of + other thresholds. Message output is sent to the stored warning stream if + not set to ''. + + The Reporter class also employs a modified form of the "Observer" pattern + [GoF95]_ to track system messages generated. The `attach_observer` method + should be called before parsing, with a bound method or function which + accepts system messages. The observer can be removed with + `detach_observer`, and another added in its place. + + .. [GoF95] Gamma, Helm, Johnson, Vlissides. *Design Patterns: Elements of + Reusable Object-Oriented Software*. Addison-Wesley, Reading, MA, USA, + 1995. + """ + + levels = 'DEBUG INFO WARNING ERROR SEVERE'.split() + """List of names for system message levels, indexed by level.""" + + # system message level constants: + (DEBUG_LEVEL, + INFO_LEVEL, + WARNING_LEVEL, + ERROR_LEVEL, + SEVERE_LEVEL) = range(5) + + def __init__(self, source, report_level, halt_level, stream=None, + debug=0, encoding=None, error_handler='replace'): + """ + :Parameters: + - `source`: The path to or description of the source data. + - `report_level`: The level at or above which warning output will + be sent to `stream`. + - `halt_level`: The level at or above which `SystemMessage` + exceptions will be raised, halting execution. + - `debug`: Show debug (level=0) system messages? + - `stream`: Where warning output is sent. Can be file-like (has a + ``.write`` method), a string (file name, opened for writing), + '' (empty string, for discarding all stream messages) or + `None` (implies `sys.stderr`; default). + - `encoding`: The output encoding. + - `error_handler`: The error handler for stderr output encoding. + """ + + self.source = source + """The path to or description of the source data.""" + + self.error_handler = error_handler + """The character encoding error handler.""" + + self.debug_flag = debug + """Show debug (level=0) system messages?""" + + self.report_level = report_level + """The level at or above which warning output will be sent + to `self.stream`.""" + + self.halt_level = halt_level + """The level at or above which `SystemMessage` exceptions + will be raised, halting execution.""" + + if stream is None: + stream = sys.stderr + elif type(stream) in (str, unicode): + # Leave stream untouched if it's ''. + if stream != '': + if type(stream) == str: + stream = open(stream, 'w') + elif type(stream) == unicode: + stream = open(stream.encode(), 'w') + + self.stream = stream + """Where warning output is sent.""" + + if encoding is None: + try: + encoding = stream.encoding + except AttributeError: + pass + + self.encoding = encoding or 'ascii' + """The output character encoding.""" + + self.observers = [] + """List of bound methods or functions to call with each system_message + created.""" + + self.max_level = -1 + """The highest level system message generated so far.""" + + def set_conditions(self, category, report_level, halt_level, + stream=None, debug=0): + warnings.warn('docutils.utils.Reporter.set_conditions deprecated; ' + 'set attributes via configuration settings or directly', + DeprecationWarning, stacklevel=2) + self.report_level = report_level + self.halt_level = halt_level + if stream is None: + stream = sys.stderr + self.stream = stream + self.debug_flag = debug + + def attach_observer(self, observer): + """ + The `observer` parameter is a function or bound method which takes one + argument, a `nodes.system_message` instance. + """ + self.observers.append(observer) + + def detach_observer(self, observer): + self.observers.remove(observer) + + def notify_observers(self, message): + for observer in self.observers: + observer(message) + + def system_message(self, level, message, *children, **kwargs): + """ + Return a system_message object. + + Raise an exception or generate a warning if appropriate. + """ + attributes = kwargs.copy() + if 'base_node' in kwargs: + source, line = get_source_line(kwargs['base_node']) + del attributes['base_node'] + if source is not None: + attributes.setdefault('source', source) + if line is not None: + attributes.setdefault('line', line) + attributes.setdefault('source', self.source) + msg = nodes.system_message(message, level=level, + type=self.levels[level], + *children, **attributes) + if self.stream and (level >= self.report_level + or self.debug_flag and level == self.DEBUG_LEVEL + or level >= self.halt_level): + msgtext = msg.astext().encode(self.encoding, self.error_handler) + self.stream.write(msgtext) + self.stream.write(b('\n')) + if level >= self.halt_level: + raise SystemMessage(msg, level) + if level > self.DEBUG_LEVEL or self.debug_flag: + self.notify_observers(msg) + self.max_level = max(level, self.max_level) + return msg + + def debug(self, *args, **kwargs): + """ + Level-0, "DEBUG": an internal reporting issue. Typically, there is no + effect on the processing. Level-0 system messages are handled + separately from the others. + """ + if self.debug_flag: + return self.system_message(self.DEBUG_LEVEL, *args, **kwargs) + + def info(self, *args, **kwargs): + """ + Level-1, "INFO": a minor issue that can be ignored. Typically there is + no effect on processing, and level-1 system messages are not reported. + """ + return self.system_message(self.INFO_LEVEL, *args, **kwargs) + + def warning(self, *args, **kwargs): + """ + Level-2, "WARNING": an issue that should be addressed. If ignored, + there may be unpredictable problems with the output. + """ + return self.system_message(self.WARNING_LEVEL, *args, **kwargs) + + def error(self, *args, **kwargs): + """ + Level-3, "ERROR": an error that should be addressed. If ignored, the + output will contain errors. + """ + return self.system_message(self.ERROR_LEVEL, *args, **kwargs) + + def severe(self, *args, **kwargs): + """ + Level-4, "SEVERE": a severe error that must be addressed. If ignored, + the output will contain severe errors. Typically level-4 system + messages are turned into exceptions which halt processing. + """ + return self.system_message(self.SEVERE_LEVEL, *args, **kwargs) + + +class ExtensionOptionError(DataError): pass +class BadOptionError(ExtensionOptionError): pass +class BadOptionDataError(ExtensionOptionError): pass +class DuplicateOptionError(ExtensionOptionError): pass + + +def extract_extension_options(field_list, options_spec): + """ + Return a dictionary mapping extension option names to converted values. + + :Parameters: + - `field_list`: A flat field list without field arguments, where each + field body consists of a single paragraph only. + - `options_spec`: Dictionary mapping known option names to a + conversion function such as `int` or `float`. + + :Exceptions: + - `KeyError` for unknown option names. + - `ValueError` for invalid option values (raised by the conversion + function). + - `TypeError` for invalid option value types (raised by conversion + function). + - `DuplicateOptionError` for duplicate options. + - `BadOptionError` for invalid fields. + - `BadOptionDataError` for invalid option data (missing name, + missing data, bad quotes, etc.). + """ + option_list = extract_options(field_list) + option_dict = assemble_option_dict(option_list, options_spec) + return option_dict + +def extract_options(field_list): + """ + Return a list of option (name, value) pairs from field names & bodies. + + :Parameter: + `field_list`: A flat field list, where each field name is a single + word and each field body consists of a single paragraph only. + + :Exceptions: + - `BadOptionError` for invalid fields. + - `BadOptionDataError` for invalid option data (missing name, + missing data, bad quotes, etc.). + """ + option_list = [] + for field in field_list: + if len(field[0].astext().split()) != 1: + raise BadOptionError( + 'extension option field name may not contain multiple words') + name = str(field[0].astext().lower()) + body = field[1] + if len(body) == 0: + data = None + elif len(body) > 1 or not isinstance(body[0], nodes.paragraph) \ + or len(body[0]) != 1 or not isinstance(body[0][0], nodes.Text): + raise BadOptionDataError( + 'extension option field body may contain\n' + 'a single paragraph only (option "%s")' % name) + else: + data = body[0][0].astext() + option_list.append((name, data)) + return option_list + +def assemble_option_dict(option_list, options_spec): + """ + Return a mapping of option names to values. + + :Parameters: + - `option_list`: A list of (name, value) pairs (the output of + `extract_options()`). + - `options_spec`: Dictionary mapping known option names to a + conversion function such as `int` or `float`. + + :Exceptions: + - `KeyError` for unknown option names. + - `DuplicateOptionError` for duplicate options. + - `ValueError` for invalid option values (raised by conversion + function). + - `TypeError` for invalid option value types (raised by conversion + function). + """ + options = {} + for name, value in option_list: + convertor = options_spec[name] # raises KeyError if unknown + if convertor is None: + raise KeyError(name) # or if explicitly disabled + if name in options: + raise DuplicateOptionError('duplicate option "%s"' % name) + try: + options[name] = convertor(value) + except (ValueError, TypeError), detail: + raise detail.__class__('(option: "%s"; value: %r)\n%s' + % (name, value, ' '.join(detail.args))) + return options + + +class NameValueError(DataError): pass + + +def decode_path(path): + """ + Decode file/path string. Return `nodes.reprunicode` object. + + Provides a conversion to unicode without the UnicodeDecode error of the + implicit 'ascii:strict' decoding. + """ + # see also http://article.gmane.org/gmane.text.docutils.user/2905 + try: + path = path.decode(sys.getfilesystemencoding(), 'strict') + except AttributeError: # default value None has no decode method + return nodes.reprunicode(path) + except UnicodeDecodeError: + path = path.decode('utf-8', 'strict') + try: + path = path.decode(sys.getfilesystemencoding(), 'strict') + except UnicodeDecodeError: + path = path.decode('ascii', 'replace') + return nodes.reprunicode(path) + + +def extract_name_value(line): + """ + Return a list of (name, value) from a line of the form "name=value ...". + + :Exception: + `NameValueError` for invalid input (missing name, missing data, bad + quotes, etc.). + """ + attlist = [] + while line: + equals = line.find('=') + if equals == -1: + raise NameValueError('missing "="') + attname = line[:equals].strip() + if equals == 0 or not attname: + raise NameValueError( + 'missing attribute name before "="') + line = line[equals+1:].lstrip() + if not line: + raise NameValueError( + 'missing value after "%s="' % attname) + if line[0] in '\'"': + endquote = line.find(line[0], 1) + if endquote == -1: + raise NameValueError( + 'attribute "%s" missing end quote (%s)' + % (attname, line[0])) + if len(line) > endquote + 1 and line[endquote + 1].strip(): + raise NameValueError( + 'attribute "%s" end quote (%s) not followed by ' + 'whitespace' % (attname, line[0])) + data = line[1:endquote] + line = line[endquote+1:].lstrip() + else: + space = line.find(' ') + if space == -1: + data = line + line = '' + else: + data = line[:space] + line = line[space+1:].lstrip() + attlist.append((attname.lower(), data)) + return attlist + +def new_reporter(source_path, settings): + """ + Return a new Reporter object. + + :Parameters: + `source` : string + The path to or description of the source text of the document. + `settings` : optparse.Values object + Runtime settings. + """ + reporter = Reporter( + source_path, settings.report_level, settings.halt_level, + stream=settings.warning_stream, debug=settings.debug, + encoding=settings.error_encoding, + error_handler=settings.error_encoding_error_handler) + return reporter + +def new_document(source_path, settings=None): + """ + Return a new empty document object. + + :Parameters: + `source_path` : string + The path to or description of the source text of the document. + `settings` : optparse.Values object + Runtime settings. If none provided, a default set will be used. + """ + from docutils import frontend + if settings is None: + settings = frontend.OptionParser().get_default_values() + source_path = decode_path(source_path) + reporter = new_reporter(source_path, settings) + document = nodes.document(settings, reporter, source=source_path) + document.note_source(source_path, -1) + return document + +def clean_rcs_keywords(paragraph, keyword_substitutions): + if len(paragraph) == 1 and isinstance(paragraph[0], nodes.Text): + textnode = paragraph[0] + for pattern, substitution in keyword_substitutions: + match = pattern.search(textnode) + if match: + paragraph[0] = nodes.Text(pattern.sub(substitution, textnode)) + return + +def relative_path(source, target): + """ + Build and return a path to `target`, relative to `source` (both files). + + If there is no common prefix, return the absolute path to `target`. + """ + source_parts = os.path.abspath(source or 'dummy_file').split(os.sep) + target_parts = os.path.abspath(target).split(os.sep) + # Check first 2 parts because '/dir'.split('/') == ['', 'dir']: + if source_parts[:2] != target_parts[:2]: + # Nothing in common between paths. + # Return absolute path, using '/' for URLs: + return '/'.join(target_parts) + source_parts.reverse() + target_parts.reverse() + while (source_parts and target_parts + and source_parts[-1] == target_parts[-1]): + # Remove path components in common: + source_parts.pop() + target_parts.pop() + target_parts.reverse() + parts = ['..'] * (len(source_parts) - 1) + target_parts + return '/'.join(parts) + +def get_stylesheet_reference(settings, relative_to=None): + """ + Retrieve a stylesheet reference from the settings object. + + Deprecated. Use get_stylesheet_reference_list() instead to + enable specification of multiple stylesheets as a comma-separated + list. + """ + if settings.stylesheet_path: + assert not settings.stylesheet, ( + 'stylesheet and stylesheet_path are mutually exclusive.') + if relative_to == None: + relative_to = settings._destination + return relative_path(relative_to, settings.stylesheet_path) + else: + return settings.stylesheet + +# Return 'stylesheet' or 'stylesheet_path' arguments as list. +# +# The original settings arguments are kept unchanged: you can test +# with e.g. ``if settings.stylesheet_path:`` +# +# Differences to ``get_stylesheet_reference``: +# * return value is a list +# * no re-writing of the path (and therefore no optional argument) +# (if required, use ``utils.relative_path(source, target)`` +# in the calling script) +def get_stylesheet_list(settings): + """ + Retrieve list of stylesheet references from the settings object. + """ + if settings.stylesheet_path: + assert not settings.stylesheet, ( + 'stylesheet and stylesheet_path are mutually exclusive.') + return settings.stylesheet_path.split(",") + elif settings.stylesheet: + return settings.stylesheet.split(",") + else: + return [] + +def get_trim_footnote_ref_space(settings): + """ + Return whether or not to trim footnote space. + + If trim_footnote_reference_space is not None, return it. + + If trim_footnote_reference_space is None, return False unless the + footnote reference style is 'superscript'. + """ + if settings.trim_footnote_reference_space is None: + return hasattr(settings, 'footnote_references') and \ + settings.footnote_references == 'superscript' + else: + return settings.trim_footnote_reference_space + +def get_source_line(node): + """ + Return the "source" and "line" attributes from the `node` given or from + its closest ancestor. + """ + while node: + if node.source or node.line: + return node.source, node.line + node = node.parent + return None, None + +def escape2null(text): + """Return a string with escape-backslashes converted to nulls.""" + parts = [] + start = 0 + while 1: + found = text.find('\\', start) + if found == -1: + parts.append(text[start:]) + return ''.join(parts) + parts.append(text[start:found]) + parts.append('\x00' + text[found+1:found+2]) + start = found + 2 # skip character after escape + +def unescape(text, restore_backslashes=0): + """ + Return a string with nulls removed or restored to backslashes. + Backslash-escaped spaces are also removed. + """ + if restore_backslashes: + return text.replace('\x00', '\\') + else: + for sep in ['\x00 ', '\x00\n', '\x00']: + text = ''.join(text.split(sep)) + return text + +east_asian_widths = {'W': 2, # Wide + 'F': 2, # Full-width (wide) + 'Na': 1, # Narrow + 'H': 1, # Half-width (narrow) + 'N': 1, # Neutral (not East Asian, treated as narrow) + 'A': 1} # Ambiguous (s/b wide in East Asian context, + # narrow otherwise, but that doesn't work) +"""Mapping of result codes from `unicodedata.east_asian_width()` to character +column widths.""" + +def east_asian_column_width(text): + if isinstance(text, unicode): + total = 0 + for c in text: + total += east_asian_widths[unicodedata.east_asian_width(c)] + return total + else: + return len(text) + +if hasattr(unicodedata, 'east_asian_width'): + column_width = east_asian_column_width +else: + column_width = len + +def uniq(L): + r = [] + for item in L: + if not item in r: + r.append(item) + return r + + +class DependencyList: + + """ + List of dependencies, with file recording support. + + Note that the output file is not automatically closed. You have + to explicitly call the close() method. + """ + + def __init__(self, output_file=None, dependencies=[]): + """ + Initialize the dependency list, automatically setting the + output file to `output_file` (see `set_output()`) and adding + all supplied dependencies. + """ + self.set_output(output_file) + for i in dependencies: + self.add(i) + + def set_output(self, output_file): + """ + Set the output file and clear the list of already added + dependencies. + + `output_file` must be a string. The specified file is + immediately overwritten. + + If output_file is '-', the output will be written to stdout. + If it is None, no file output is done when calling add(). + """ + self.list = [] + if output_file == '-': + self.file = sys.stdout + elif output_file: + self.file = open(output_file, 'w') + else: + self.file = None + + def add(self, *filenames): + """ + If the dependency `filename` has not already been added, + append it to self.list and print it to self.file if self.file + is not None. + """ + for filename in filenames: + if not filename in self.list: + self.list.append(filename) + if self.file is not None: + print >>self.file, filename + + def close(self): + """ + Close the output file. + """ + self.file.close() + self.file = None + + def __repr__(self): + if self.file: + output_file = self.file.name + else: + output_file = None + return '%s(%r, %s)' % (self.__class__.__name__, output_file, self.list) diff -Nru zope3-3.4.0/src/docutils/writers/docutils_xml.py zope3-3.5~bzr18/src/docutils/writers/docutils_xml.py --- zope3-3.4.0/src/docutils/writers/docutils_xml.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/docutils_xml.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,73 @@ +# $Id: docutils_xml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger <goodger@python.org> +# Copyright: This module has been placed in the public domain. + +""" +Simple internal document tree Writer, writes Docutils XML. +""" + +__docformat__ = 'reStructuredText' + + +import docutils +from docutils import frontend, writers + + +class Writer(writers.Writer): + + supported = ('xml',) + """Formats this writer supports.""" + + settings_spec = ( + '"Docutils XML" Writer Options', + 'Warning: the --newlines and --indents options may adversely affect ' + 'whitespace; use them only for reading convenience.', + (('Generate XML with newlines before and after tags.', + ['--newlines'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Generate XML with indents and newlines.', + ['--indents'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Omit the XML declaration. Use with caution.', + ['--no-xml-declaration'], + {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Omit the DOCTYPE declaration.', + ['--no-doctype'], + {'dest': 'doctype_declaration', 'default': 1, + 'action': 'store_false', 'validator': frontend.validate_boolean}),)) + + settings_defaults = {'output_encoding_error_handler': 'xmlcharrefreplace'} + + config_section = 'docutils_xml writer' + config_section_dependencies = ('writers',) + + output = None + """Final translated form of `document`.""" + + xml_declaration = '<?xml version="1.0" encoding="%s"?>\n' + #xml_stylesheet = '<?xml-stylesheet type="text/xsl" href="%s"?>\n' + doctype = ( + '<!DOCTYPE document PUBLIC' + ' "+//IDN docutils.sourceforge.net//DTD Docutils Generic//EN//XML"' + ' "http://docutils.sourceforge.net/docs/ref/docutils.dtd">\n') + generator = '<!-- Generated by Docutils %s -->\n' + + def translate(self): + settings = self.document.settings + indent = newline = '' + if settings.newlines: + newline = '\n' + if settings.indents: + newline = '\n' + indent = ' ' + output_prefix = [] + if settings.xml_declaration: + output_prefix.append( + self.xml_declaration % settings.output_encoding) + if settings.doctype_declaration: + output_prefix.append(self.doctype) + output_prefix.append(self.generator % docutils.__version__) + docnode = self.document.asdom().childNodes[0] + self.output = (''.join(output_prefix) + + docnode.toprettyxml(indent, newline)) diff -Nru zope3-3.4.0/src/docutils/writers/html4css1/html4css1.css zope3-3.5~bzr18/src/docutils/writers/html4css1/html4css1.css --- zope3-3.4.0/src/docutils/writers/html4css1/html4css1.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/html4css1/html4css1.css 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,293 @@ +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: html4css1.css 5951 2009-05-18 18:03:10Z milde $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left{ + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: left } + +/* div.align-center * { */ +/* text-align: left } */ + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } diff -Nru zope3-3.4.0/src/docutils/writers/html4css1/__init__.py zope3-3.5~bzr18/src/docutils/writers/html4css1/__init__.py --- zope3-3.4.0/src/docutils/writers/html4css1/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/html4css1/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1551 @@ +# $Id: __init__.py 6153 2009-10-05 13:37:10Z milde $ +# Author: David Goodger <goodger@python.org> +# Copyright: This module has been placed in the public domain. + +""" +Simple HyperText Markup Language document tree Writer. + +The output conforms to the XHTML version 1.0 Transitional DTD +(*almost* strict). The output contains a minimum of formatting +information. The cascading style sheet "html4css1.css" is required +for proper viewing with a modern graphical browser. +""" + +__docformat__ = 'reStructuredText' + + +import sys +import os +import os.path +import time +import re +try: + import Image # check for the Python Imaging Library +except ImportError: + Image = None +import docutils +from docutils import frontend, nodes, utils, writers, languages +from docutils.transforms import writer_aux + + +class Writer(writers.Writer): + + supported = ('html', 'html4css1', 'xhtml') + """Formats this writer supports.""" + + default_stylesheet = 'html4css1.css' + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + default_template = 'template.txt' + + default_template_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_template)) + + settings_spec = ( + 'HTML-Specific Options', + None, + (('Specify the template file (UTF-8 encoded). Default is "%s".' + % default_template_path, + ['--template'], + {'default': default_template_path, 'metavar': '<file>'}), + ('Specify comma separated list of stylesheet URLs. ' + 'Overrides previous --stylesheet and --stylesheet-path settings.', + ['--stylesheet'], + {'metavar': '<URL>', 'overrides': 'stylesheet_path'}), + ('Specify comma separated list of stylesheet paths. ' + 'With --link-stylesheet, ' + 'the path is rewritten relative to the output HTML file. ' + 'Default: "%s"' % default_stylesheet_path, + ['--stylesheet-path'], + {'metavar': '<file>', 'overrides': 'stylesheet', + 'default': default_stylesheet_path}), + ('Embed the stylesheet(s) in the output HTML file. The stylesheet ' + 'files must be accessible during processing. This is the default.', + ['--embed-stylesheet'], + {'default': 1, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Link to the stylesheet(s) in the output HTML file. ' + 'Default: embed stylesheets.', + ['--link-stylesheet'], + {'dest': 'embed_stylesheet', 'action': 'store_false'}), + ('Specify the initial header level. Default is 1 for "<h1>". ' + 'Does not affect document title & subtitle (see --no-doc-title).', + ['--initial-header-level'], + {'choices': '1 2 3 4 5 6'.split(), 'default': '1', + 'metavar': '<level>'}), + ('Specify the maximum width (in characters) for one-column field ' + 'names. Longer field names will span an entire row of the table ' + 'used to render the field list. Default is 14 characters. ' + 'Use 0 for "no limit".', + ['--field-name-limit'], + {'default': 14, 'metavar': '<level>', + 'validator': frontend.validate_nonnegative_int}), + ('Specify the maximum width (in characters) for options in option ' + 'lists. Longer options will span an entire row of the table used ' + 'to render the option list. Default is 14 characters. ' + 'Use 0 for "no limit".', + ['--option-limit'], + {'default': 14, 'metavar': '<level>', + 'validator': frontend.validate_nonnegative_int}), + ('Format for footnote references: one of "superscript" or ' + '"brackets". Default is "brackets".', + ['--footnote-references'], + {'choices': ['superscript', 'brackets'], 'default': 'brackets', + 'metavar': '<format>', + 'overrides': 'trim_footnote_reference_space'}), + ('Format for block quote attributions: one of "dash" (em-dash ' + 'prefix), "parentheses"/"parens", or "none". Default is "dash".', + ['--attribution'], + {'choices': ['dash', 'parentheses', 'parens', 'none'], + 'default': 'dash', 'metavar': '<format>'}), + ('Remove extra vertical whitespace between items of "simple" bullet ' + 'lists and enumerated lists. Default: enabled.', + ['--compact-lists'], + {'default': 1, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Disable compact simple bullet and enumerated lists.', + ['--no-compact-lists'], + {'dest': 'compact_lists', 'action': 'store_false'}), + ('Remove extra vertical whitespace between items of simple field ' + 'lists. Default: enabled.', + ['--compact-field-lists'], + {'default': 1, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Disable compact simple field lists.', + ['--no-compact-field-lists'], + {'dest': 'compact_field_lists', 'action': 'store_false'}), + ('Added to standard table classes. ' + 'Defined styles: "borderless". Default: ""', + ['--table-style'], + {'default': ''}), + ('Omit the XML declaration. Use with caution.', + ['--no-xml-declaration'], + {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Obfuscate email addresses to confuse harvesters while still ' + 'keeping email links usable with standards-compliant browsers.', + ['--cloak-email-addresses'], + {'action': 'store_true', 'validator': frontend.validate_boolean}),)) + + settings_defaults = {'output_encoding_error_handler': 'xmlcharrefreplace'} + + relative_path_settings = ('stylesheet_path',) + + config_section = 'html4css1 writer' + config_section_dependencies = ('writers',) + + visitor_attributes = ( + 'head_prefix', 'head', 'stylesheet', 'body_prefix', + 'body_pre_docinfo', 'docinfo', 'body', 'body_suffix', + 'title', 'subtitle', 'header', 'footer', 'meta', 'fragment', + 'html_prolog', 'html_head', 'html_title', 'html_subtitle', + 'html_body') + + def get_transforms(self): + return writers.Writer.get_transforms(self) + [writer_aux.Admonitions] + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = HTMLTranslator + + def translate(self): + self.visitor = visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + for attr in self.visitor_attributes: + setattr(self, attr, getattr(visitor, attr)) + self.output = self.apply_template() + + def apply_template(self): + template_file = open(self.document.settings.template, 'rb') + template = unicode(template_file.read(), 'utf-8') + template_file.close() + subs = self.interpolation_dict() + return template % subs + + def interpolation_dict(self): + subs = {} + settings = self.document.settings + for attr in self.visitor_attributes: + subs[attr] = ''.join(getattr(self, attr)).rstrip('\n') + subs['encoding'] = settings.output_encoding + subs['version'] = docutils.__version__ + return subs + + def assemble_parts(self): + writers.Writer.assemble_parts(self) + for part in self.visitor_attributes: + self.parts[part] = ''.join(getattr(self, part)) + + +class HTMLTranslator(nodes.NodeVisitor): + + """ + This HTML writer has been optimized to produce visually compact + lists (less vertical whitespace). HTML's mixed content models + allow list items to contain "<li><p>body elements</p></li>" or + "<li>just text</li>" or even "<li>text<p>and body + elements</p>combined</li>", each with different effects. It would + be best to stick with strict body elements in list items, but they + affect vertical spacing in browsers (although they really + shouldn't). + + Here is an outline of the optimization: + + - Check for and omit <p> tags in "simple" lists: list items + contain either a single paragraph, a nested simple list, or a + paragraph followed by a nested simple list. This means that + this list can be compact: + + - Item 1. + - Item 2. + + But this list cannot be compact: + + - Item 1. + + This second paragraph forces space between list items. + + - Item 2. + + - In non-list contexts, omit <p> tags on a paragraph if that + paragraph is the only child of its parent (footnotes & citations + are allowed a label first). + + - Regardless of the above, in definitions, table cells, field bodies, + option descriptions, and list items, mark the first child with + 'class="first"' and the last child with 'class="last"'. The stylesheet + sets the margins (top & bottom respectively) to 0 for these elements. + + The ``no_compact_lists`` setting (``--no-compact-lists`` command-line + option) disables list whitespace optimization. + """ + + xml_declaration = '<?xml version="1.0" encoding="%s" ?>\n' + doctype = ( + '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' + ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n') + head_prefix_template = ('<html xmlns="http://www.w3.org/1999/xhtml"' + ' xml:lang="%s" lang="%s">\n<head>\n') + content_type = ('<meta http-equiv="Content-Type"' + ' content="text/html; charset=%s" />\n') + generator = ('<meta name="generator" content="Docutils %s: ' + 'http://docutils.sourceforge.net/" />\n') + stylesheet_link = '<link rel="stylesheet" href="%s" type="text/css" />\n' + embedded_stylesheet = '<style type="text/css">\n\n%s\n</style>\n' + words_and_spaces = re.compile(r'\S+| +|\n') + sollbruchstelle = re.compile(r'.+\W\W.+|[-?].+', re.U) # wrap point inside word + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.settings = settings = document.settings + lcode = settings.language_code + self.language = languages.get_language(lcode) + self.meta = [self.content_type % settings.output_encoding, + self.generator % docutils.__version__] + self.head_prefix = [] + self.html_prolog = [] + if settings.xml_declaration: + self.head_prefix.append(self.xml_declaration + % settings.output_encoding) + # encoding not interpolated: + self.html_prolog.append(self.xml_declaration) + self.head_prefix.extend([self.doctype, + self.head_prefix_template % (lcode, lcode)]) + self.html_prolog.append(self.doctype) + self.head = self.meta[:] + # stylesheets + styles = utils.get_stylesheet_list(settings) + if settings.stylesheet_path and not(settings.embed_stylesheet): + styles = [utils.relative_path(settings._destination, sheet) + for sheet in styles] + if settings.embed_stylesheet: + settings.record_dependencies.add(*styles) + self.stylesheet = [self.embedded_stylesheet % + unicode(open(sheet).read(), 'utf-8') + for sheet in styles] + else: # link to stylesheets + self.stylesheet = [self.stylesheet_link % self.encode(stylesheet) + for stylesheet in styles] + self.body_prefix = ['</head>\n<body>\n'] + # document title, subtitle display + self.body_pre_docinfo = [] + # author, date, etc. + self.docinfo = [] + self.body = [] + self.fragment = [] + self.body_suffix = ['</body>\n</html>\n'] + self.section_level = 0 + self.initial_header_level = int(settings.initial_header_level) + # A heterogenous stack used in conjunction with the tree traversal. + # Make sure that the pops correspond to the pushes: + self.context = [] + self.topic_classes = [] + self.colspecs = [] + self.compact_p = 1 + self.compact_simple = None + self.compact_field_list = None + self.in_docinfo = None + self.in_sidebar = None + self.title = [] + self.subtitle = [] + self.header = [] + self.footer = [] + self.html_head = [self.content_type] # charset not interpolated + self.html_title = [] + self.html_subtitle = [] + self.html_body = [] + self.in_document_title = 0 + self.in_mailto = 0 + self.author_in_authors = None + + def astext(self): + return ''.join(self.head_prefix + self.head + + self.stylesheet + self.body_prefix + + self.body_pre_docinfo + self.docinfo + + self.body + self.body_suffix) + + def encode(self, text): + """Encode special characters in `text` & return.""" + # @@@ A codec to do these and all other HTML entities would be nice. + text = unicode(text) + return text.translate({ + ord('&'): u'&', + ord('<'): u'<', + ord('"'): u'"', + ord('>'): u'>', + ord('@'): u'@', # may thwart some address harvesters + # TODO: convert non-breaking space only if needed? + 0xa0: u' '}) # non-breaking space + + def cloak_mailto(self, uri): + """Try to hide a mailto: URL from harvesters.""" + # Encode "@" using a URL octet reference (see RFC 1738). + # Further cloaking with HTML entities will be done in the + # `attval` function. + return uri.replace('@', '%40') + + def cloak_email(self, addr): + """Try to hide the link text of a email link from harversters.""" + # Surround at-signs and periods with <span> tags. ("@" has + # already been encoded to "@" by the `encode` method.) + addr = addr.replace('@', '<span>@</span>') + addr = addr.replace('.', '<span>.</span>') + return addr + + def attval(self, text, + whitespace=re.compile('[\n\r\t\v\f]')): + """Cleanse, HTML encode, and return attribute value text.""" + encoded = self.encode(whitespace.sub(' ', text)) + if self.in_mailto and self.settings.cloak_email_addresses: + # Cloak at-signs ("%40") and periods with HTML entities. + encoded = encoded.replace('%40', '%40') + encoded = encoded.replace('.', '.') + return encoded + + def starttag(self, node, tagname, suffix='\n', empty=0, **attributes): + """ + Construct and return a start tag given a node (id & class attributes + are extracted), tag name, and optional attributes. + """ + tagname = tagname.lower() + prefix = [] + atts = {} + ids = [] + for (name, value) in attributes.items(): + atts[name.lower()] = value + classes = node.get('classes', []) + if 'class' in atts: + classes.append(atts['class']) + if classes: + atts['class'] = ' '.join(classes) + assert 'id' not in atts + ids.extend(node.get('ids', [])) + if 'ids' in atts: + ids.extend(atts['ids']) + del atts['ids'] + if ids: + atts['id'] = ids[0] + for id in ids[1:]: + # Add empty "span" elements for additional IDs. Note + # that we cannot use empty "a" elements because there + # may be targets inside of references, but nested "a" + # elements aren't allowed in XHTML (even if they do + # not all have a "href" attribute). + if empty: + # Empty tag. Insert target right in front of element. + prefix.append('<span id="%s"></span>' % id) + else: + # Non-empty tag. Place the auxiliary <span> tag + # *inside* the element, as the first child. + suffix += '<span id="%s"></span>' % id + attlist = atts.items() + attlist.sort() + parts = [tagname] + for name, value in attlist: + # value=None was used for boolean attributes without + # value, but this isn't supported by XHTML. + assert value is not None + if isinstance(value, list): + values = [unicode(v) for v in value] + parts.append('%s="%s"' % (name.lower(), + self.attval(' '.join(values)))) + else: + parts.append('%s="%s"' % (name.lower(), + self.attval(unicode(value)))) + if empty: + infix = ' /' + else: + infix = '' + return ''.join(prefix) + '<%s%s>' % (' '.join(parts), infix) + suffix + + def emptytag(self, node, tagname, suffix='\n', **attributes): + """Construct and return an XML-compatible empty tag.""" + return self.starttag(node, tagname, suffix, empty=1, **attributes) + + def set_class_on_child(self, node, class_, index=0): + """ + Set class `class_` on the visible child no. index of `node`. + Do nothing if node has fewer children than `index`. + """ + children = [n for n in node if not isinstance(n, nodes.Invisible)] + try: + child = children[index] + except IndexError: + return + child['classes'].append(class_) + + def set_first_last(self, node): + self.set_class_on_child(node, 'first', 0) + self.set_class_on_child(node, 'last', -1) + + def visit_Text(self, node): + text = node.astext() + encoded = self.encode(text) + if self.in_mailto and self.settings.cloak_email_addresses: + encoded = self.cloak_email(encoded) + self.body.append(encoded) + + def depart_Text(self, node): + pass + + def visit_abbreviation(self, node): + # @@@ implementation incomplete ("title" attribute) + self.body.append(self.starttag(node, 'abbr', '')) + + def depart_abbreviation(self, node): + self.body.append('</abbr>') + + def visit_acronym(self, node): + # @@@ implementation incomplete ("title" attribute) + self.body.append(self.starttag(node, 'acronym', '')) + + def depart_acronym(self, node): + self.body.append('</acronym>') + + def visit_address(self, node): + self.visit_docinfo_item(node, 'address', meta=None) + self.body.append(self.starttag(node, 'pre', CLASS='address')) + + def depart_address(self, node): + self.body.append('\n</pre>\n') + self.depart_docinfo_item() + + def visit_admonition(self, node): + self.body.append(self.starttag(node, 'div')) + self.set_first_last(node) + + def depart_admonition(self, node=None): + self.body.append('</div>\n') + + attribution_formats = {'dash': ('—', ''), + 'parentheses': ('(', ')'), + 'parens': ('(', ')'), + 'none': ('', '')} + + def visit_attribution(self, node): + prefix, suffix = self.attribution_formats[self.settings.attribution] + self.context.append(suffix) + self.body.append( + self.starttag(node, 'p', prefix, CLASS='attribution')) + + def depart_attribution(self, node): + self.body.append(self.context.pop() + '</p>\n') + + def visit_author(self, node): + if isinstance(node.parent, nodes.authors): + if self.author_in_authors: + self.body.append('\n<br />') + else: + self.visit_docinfo_item(node, 'author') + + def depart_author(self, node): + if isinstance(node.parent, nodes.authors): + self.author_in_authors += 1 + else: + self.depart_docinfo_item() + + def visit_authors(self, node): + self.visit_docinfo_item(node, 'authors') + self.author_in_authors = 0 # initialize counter + + def depart_authors(self, node): + self.depart_docinfo_item() + self.author_in_authors = None + + def visit_block_quote(self, node): + self.body.append(self.starttag(node, 'blockquote')) + + def depart_block_quote(self, node): + self.body.append('</blockquote>\n') + + def check_simple_list(self, node): + """Check for a simple list that can be rendered compactly.""" + visitor = SimpleListChecker(self.document) + try: + node.walk(visitor) + except nodes.NodeFound: + return None + else: + return 1 + + def is_compactable(self, node): + return ('compact' in node['classes'] + or (self.settings.compact_lists + and 'open' not in node['classes'] + and (self.compact_simple + or self.topic_classes == ['contents'] + or self.check_simple_list(node)))) + + def visit_bullet_list(self, node): + atts = {} + old_compact_simple = self.compact_simple + self.context.append((self.compact_simple, self.compact_p)) + self.compact_p = None + self.compact_simple = self.is_compactable(node) + if self.compact_simple and not old_compact_simple: + atts['class'] = 'simple' + self.body.append(self.starttag(node, 'ul', **atts)) + + def depart_bullet_list(self, node): + self.compact_simple, self.compact_p = self.context.pop() + self.body.append('</ul>\n') + + def visit_caption(self, node): + self.body.append(self.starttag(node, 'p', '', CLASS='caption')) + + def depart_caption(self, node): + self.body.append('</p>\n') + + def visit_citation(self, node): + self.body.append(self.starttag(node, 'table', + CLASS='docutils citation', + frame="void", rules="none")) + self.body.append('<colgroup><col class="label" /><col /></colgroup>\n' + '<tbody valign="top">\n' + '<tr>') + self.footnote_backrefs(node) + + def depart_citation(self, node): + self.body.append('</td></tr>\n' + '</tbody>\n</table>\n') + + def visit_citation_reference(self, node): + href = '#' + node['refid'] + self.body.append(self.starttag( + node, 'a', '[', CLASS='citation-reference', href=href)) + + def depart_citation_reference(self, node): + self.body.append(']</a>') + + def visit_classifier(self, node): + self.body.append(' <span class="classifier-delimiter">:</span> ') + self.body.append(self.starttag(node, 'span', '', CLASS='classifier')) + + def depart_classifier(self, node): + self.body.append('</span>') + + def visit_colspec(self, node): + self.colspecs.append(node) + # "stubs" list is an attribute of the tgroup element: + node.parent.stubs.append(node.attributes.get('stub')) + + def depart_colspec(self, node): + pass + + def write_colspecs(self): + width = 0 + for node in self.colspecs: + width += node['colwidth'] + for node in self.colspecs: + colwidth = int(node['colwidth'] * 100.0 / width + 0.5) + self.body.append(self.emptytag(node, 'col', + width='%i%%' % colwidth)) + self.colspecs = [] + + def visit_comment(self, node, + sub=re.compile('-(?=-)').sub): + """Escape double-dashes in comment text.""" + self.body.append('<!-- %s -->\n' % sub('- ', node.astext())) + # Content already processed: + raise nodes.SkipNode + + def visit_compound(self, node): + self.body.append(self.starttag(node, 'div', CLASS='compound')) + if len(node) > 1: + node[0]['classes'].append('compound-first') + node[-1]['classes'].append('compound-last') + for child in node[1:-1]: + child['classes'].append('compound-middle') + + def depart_compound(self, node): + self.body.append('</div>\n') + + def visit_container(self, node): + self.body.append(self.starttag(node, 'div', CLASS='container')) + + def depart_container(self, node): + self.body.append('</div>\n') + + def visit_contact(self, node): + self.visit_docinfo_item(node, 'contact', meta=None) + + def depart_contact(self, node): + self.depart_docinfo_item() + + def visit_copyright(self, node): + self.visit_docinfo_item(node, 'copyright') + + def depart_copyright(self, node): + self.depart_docinfo_item() + + def visit_date(self, node): + self.visit_docinfo_item(node, 'date') + + def depart_date(self, node): + self.depart_docinfo_item() + + def visit_decoration(self, node): + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + self.body.append('</dt>\n') + self.body.append(self.starttag(node, 'dd', '')) + self.set_first_last(node) + + def depart_definition(self, node): + self.body.append('</dd>\n') + + def visit_definition_list(self, node): + self.body.append(self.starttag(node, 'dl', CLASS='docutils')) + + def depart_definition_list(self, node): + self.body.append('</dl>\n') + + def visit_definition_list_item(self, node): + pass + + def depart_definition_list_item(self, node): + pass + + def visit_description(self, node): + self.body.append(self.starttag(node, 'td', '')) + self.set_first_last(node) + + def depart_description(self, node): + self.body.append('</td>') + + def visit_docinfo(self, node): + self.context.append(len(self.body)) + self.body.append(self.starttag(node, 'table', + CLASS='docinfo', + frame="void", rules="none")) + self.body.append('<col class="docinfo-name" />\n' + '<col class="docinfo-content" />\n' + '<tbody valign="top">\n') + self.in_docinfo = 1 + + def depart_docinfo(self, node): + self.body.append('</tbody>\n</table>\n') + self.in_docinfo = None + start = self.context.pop() + self.docinfo = self.body[start:] + self.body = [] + + def visit_docinfo_item(self, node, name, meta=1): + if meta: + meta_tag = '<meta name="%s" content="%s" />\n' \ + % (name, self.attval(node.astext())) + self.add_meta(meta_tag) + self.body.append(self.starttag(node, 'tr', '')) + self.body.append('<th class="docinfo-name">%s:</th>\n<td>' + % self.language.labels[name]) + if len(node): + if isinstance(node[0], nodes.Element): + node[0]['classes'].append('first') + if isinstance(node[-1], nodes.Element): + node[-1]['classes'].append('last') + + def depart_docinfo_item(self): + self.body.append('</td></tr>\n') + + def visit_doctest_block(self, node): + self.body.append(self.starttag(node, 'pre', CLASS='doctest-block')) + + def depart_doctest_block(self, node): + self.body.append('\n</pre>\n') + + def visit_document(self, node): + self.head.append('<title>%s\n' + % self.encode(node.get('title', ''))) + + def depart_document(self, node): + self.fragment.extend(self.body) + self.body_prefix.append(self.starttag(node, 'div', CLASS='document')) + self.body_suffix.insert(0, '\n') + # skip content-type meta tag with interpolated charset value: + self.html_head.extend(self.head[1:]) + self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo + + self.docinfo + self.body + + self.body_suffix[:-1]) + assert not self.context, 'len(context) = %s' % len(self.context) + + def visit_emphasis(self, node): + self.body.append(self.starttag(node, 'em', '')) + + def depart_emphasis(self, node): + self.body.append('') + + def visit_entry(self, node): + atts = {'class': []} + if isinstance(node.parent.parent, nodes.thead): + atts['class'].append('head') + if node.parent.parent.parent.stubs[node.parent.column]: + # "stubs" list is an attribute of the tgroup element + atts['class'].append('stub') + if atts['class']: + tagname = 'th' + atts['class'] = ' '.join(atts['class']) + else: + tagname = 'td' + del atts['class'] + node.parent.column += 1 + if 'morerows' in node: + atts['rowspan'] = node['morerows'] + 1 + if 'morecols' in node: + atts['colspan'] = node['morecols'] + 1 + node.parent.column += node['morecols'] + self.body.append(self.starttag(node, tagname, '', **atts)) + self.context.append('\n' % tagname.lower()) + if len(node) == 0: # empty cell + self.body.append(' ') + self.set_first_last(node) + + def depart_entry(self, node): + self.body.append(self.context.pop()) + + def visit_enumerated_list(self, node): + """ + The 'start' attribute does not conform to HTML 4.01's strict.dtd, but + CSS1 doesn't help. CSS2 isn't widely enough supported yet to be + usable. + """ + atts = {} + if 'start' in node: + atts['start'] = node['start'] + if 'enumtype' in node: + atts['class'] = node['enumtype'] + # @@@ To do: prefix, suffix. How? Change prefix/suffix to a + # single "format" attribute? Use CSS2? + old_compact_simple = self.compact_simple + self.context.append((self.compact_simple, self.compact_p)) + self.compact_p = None + self.compact_simple = self.is_compactable(node) + if self.compact_simple and not old_compact_simple: + atts['class'] = (atts.get('class', '') + ' simple').strip() + self.body.append(self.starttag(node, 'ol', **atts)) + + def depart_enumerated_list(self, node): + self.compact_simple, self.compact_p = self.context.pop() + self.body.append('\n') + + def visit_field(self, node): + self.body.append(self.starttag(node, 'tr', '', CLASS='field')) + + def depart_field(self, node): + self.body.append('\n') + + def visit_field_body(self, node): + self.body.append(self.starttag(node, 'td', '', CLASS='field-body')) + self.set_class_on_child(node, 'first', 0) + field = node.parent + if (self.compact_field_list or + isinstance(field.parent, nodes.docinfo) or + field.parent.index(field) == len(field.parent) - 1): + # If we are in a compact list, the docinfo, or if this is + # the last field of the field list, do not add vertical + # space after last element. + self.set_class_on_child(node, 'last', -1) + + def depart_field_body(self, node): + self.body.append('\n') + + def visit_field_list(self, node): + self.context.append((self.compact_field_list, self.compact_p)) + self.compact_p = None + if 'compact' in node['classes']: + self.compact_field_list = 1 + elif (self.settings.compact_field_lists + and 'open' not in node['classes']): + self.compact_field_list = 1 + if self.compact_field_list: + for field in node: + field_body = field[-1] + assert isinstance(field_body, nodes.field_body) + children = [n for n in field_body + if not isinstance(n, nodes.Invisible)] + if not (len(children) == 0 or + len(children) == 1 and + isinstance(children[0], + (nodes.paragraph, nodes.line_block))): + self.compact_field_list = 0 + break + self.body.append(self.starttag(node, 'table', frame='void', + rules='none', + CLASS='docutils field-list')) + self.body.append('\n' + '\n' + '\n') + + def depart_field_list(self, node): + self.body.append('\n\n') + self.compact_field_list, self.compact_p = self.context.pop() + + def visit_field_name(self, node): + atts = {} + if self.in_docinfo: + atts['class'] = 'docinfo-name' + else: + atts['class'] = 'field-name' + if ( self.settings.field_name_limit + and len(node.astext()) > self.settings.field_name_limit): + atts['colspan'] = 2 + self.context.append('\n ') + else: + self.context.append('') + self.body.append(self.starttag(node, 'th', '', **atts)) + + def depart_field_name(self, node): + self.body.append(':') + self.body.append(self.context.pop()) + + def visit_figure(self, node): + atts = {'class': 'figure'} + if node.get('width'): + atts['style'] = 'width: %s' % node['width'] + if node.get('align'): + atts['class'] += " align-" + node['align'] + self.body.append(self.starttag(node, 'div', **atts)) + + def depart_figure(self, node): + self.body.append('\n') + + def visit_footer(self, node): + self.context.append(len(self.body)) + + def depart_footer(self, node): + start = self.context.pop() + footer = [self.starttag(node, 'div', CLASS='footer'), + '\n'] + footer.extend(self.body[start:]) + footer.append('\n\n') + self.footer.extend(footer) + self.body_suffix[:0] = footer + del self.body[start:] + + def visit_footnote(self, node): + self.body.append(self.starttag(node, 'table', + CLASS='docutils footnote', + frame="void", rules="none")) + self.body.append('\n' + '\n' + '') + self.footnote_backrefs(node) + + def footnote_backrefs(self, node): + backlinks = [] + backrefs = node['backrefs'] + if self.settings.footnote_backlinks and backrefs: + if len(backrefs) == 1: + self.context.append('') + self.context.append('') + self.context.append('' + % backrefs[0]) + else: + i = 1 + for backref in backrefs: + backlinks.append('%s' + % (backref, i)) + i += 1 + self.context.append('(%s) ' % ', '.join(backlinks)) + self.context += ['', ''] + else: + self.context.append('') + self.context += ['', ''] + # If the node does not only consist of a label. + if len(node) > 1: + # If there are preceding backlinks, we do not set class + # 'first', because we need to retain the top-margin. + if not backlinks: + node[1]['classes'].append('first') + node[-1]['classes'].append('last') + + def depart_footnote(self, node): + self.body.append('\n' + '\n\n') + + def visit_footnote_reference(self, node): + href = '#' + node['refid'] + format = self.settings.footnote_references + if format == 'brackets': + suffix = '[' + self.context.append(']') + else: + assert format == 'superscript' + suffix = '' + self.context.append('') + self.body.append(self.starttag(node, 'a', suffix, + CLASS='footnote-reference', href=href)) + + def depart_footnote_reference(self, node): + self.body.append(self.context.pop() + '') + + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def visit_header(self, node): + self.context.append(len(self.body)) + + def depart_header(self, node): + start = self.context.pop() + header = [self.starttag(node, 'div', CLASS='header')] + header.extend(self.body[start:]) + header.append('\n
\n\n') + self.body_prefix.extend(header) + self.header.extend(header) + del self.body[start:] + + def visit_image(self, node): + atts = {} + atts['src'] = node['uri'] + if 'width' in node: + atts['width'] = node['width'] + if 'height' in node: + atts['height'] = node['height'] + if 'scale' in node: + if Image and not ('width' in node + and 'height' in node): + try: + im = Image.open(str(atts['src'])) + except (IOError, # Source image can't be found or opened + UnicodeError): # PIL doesn't like Unicode paths. + pass + else: + if 'width' not in atts: + atts['width'] = str(im.size[0]) + if 'height' not in atts: + atts['height'] = str(im.size[1]) + del im + for att_name in 'width', 'height': + if att_name in atts: + match = re.match(r'([0-9.]+)(\S*)$', atts[att_name]) + assert match + atts[att_name] = '%s%s' % ( + float(match.group(1)) * (float(node['scale']) / 100), + match.group(2)) + style = [] + for att_name in 'width', 'height': + if att_name in atts: + if re.match(r'^[0-9.]+$', atts[att_name]): + # Interpret unitless values as pixels. + atts[att_name] += 'px' + style.append('%s: %s;' % (att_name, atts[att_name])) + del atts[att_name] + if style: + atts['style'] = ' '.join(style) + atts['alt'] = node.get('alt', atts['src']) + if (isinstance(node.parent, nodes.TextElement) or + (isinstance(node.parent, nodes.reference) and + not isinstance(node.parent.parent, nodes.TextElement))): + # Inline context or surrounded by .... + suffix = '' + else: + suffix = '\n' + if 'classes' in node and 'align-center' in node['classes']: + node['align'] = 'center' + if 'align' in node: + if node['align'] == 'center': + # "align" attribute is set in surrounding "div" element. + self.body.append('
') + self.context.append('
\n') + suffix = '' + else: + # "align" attribute is set in "img" element. + atts['align'] = node['align'] + self.context.append('') + atts['class'] = 'align-%s' % node['align'] + else: + self.context.append('') + self.body.append(self.emptytag(node, 'img', suffix, **atts)) + + def depart_image(self, node): + self.body.append(self.context.pop()) + + def visit_inline(self, node): + self.body.append(self.starttag(node, 'span', '')) + + def depart_inline(self, node): + self.body.append('') + + def visit_label(self, node): + # Context added in footnote_backrefs. + self.body.append(self.starttag(node, 'td', '%s[' % self.context.pop(), + CLASS='label')) + + def depart_label(self, node): + # Context added in footnote_backrefs. + self.body.append(']%s%s' % (self.context.pop(), self.context.pop())) + + def visit_legend(self, node): + self.body.append(self.starttag(node, 'div', CLASS='legend')) + + def depart_legend(self, node): + self.body.append('\n') + + def visit_line(self, node): + self.body.append(self.starttag(node, 'div', suffix='', CLASS='line')) + if not len(node): + self.body.append('
') + + def depart_line(self, node): + self.body.append('\n') + + def visit_line_block(self, node): + self.body.append(self.starttag(node, 'div', CLASS='line-block')) + + def depart_line_block(self, node): + self.body.append('\n') + + def visit_list_item(self, node): + self.body.append(self.starttag(node, 'li', '')) + if len(node): + node[0]['classes'].append('first') + + def depart_list_item(self, node): + self.body.append('\n') + + def visit_literal(self, node): + """Process text to prevent tokens from wrapping.""" + self.body.append( + self.starttag(node, 'tt', '', CLASS='docutils literal')) + text = node.astext() + for token in self.words_and_spaces.findall(text): + if token.strip(): + # Protect text like "--an-option" and the regular expression + # ``[+]?(\d+(\.\d*)?|\.\d+)`` from bad line wrapping + if self.sollbruchstelle.search(token): + self.body.append('%s' + % self.encode(token)) + else: + self.body.append(self.encode(token)) + elif token in ('\n', ' '): + # Allow breaks at whitespace: + self.body.append(token) + else: + # Protect runs of multiple spaces; the last space can wrap: + self.body.append(' ' * (len(token) - 1) + ' ') + self.body.append('') + # Content already processed: + raise nodes.SkipNode + + def visit_literal_block(self, node): + self.body.append(self.starttag(node, 'pre', CLASS='literal-block')) + + def depart_literal_block(self, node): + self.body.append('\n\n') + + def visit_meta(self, node): + meta = self.emptytag(node, 'meta', **node.non_default_attributes()) + self.add_meta(meta) + + def depart_meta(self, node): + pass + + def add_meta(self, tag): + self.meta.append(tag) + self.head.append(tag) + + def visit_option(self, node): + if self.context[-1]: + self.body.append(', ') + self.body.append(self.starttag(node, 'span', '', CLASS='option')) + + def depart_option(self, node): + self.body.append('') + self.context[-1] += 1 + + def visit_option_argument(self, node): + self.body.append(node.get('delimiter', ' ')) + self.body.append(self.starttag(node, 'var', '')) + + def depart_option_argument(self, node): + self.body.append('') + + def visit_option_group(self, node): + atts = {} + if ( self.settings.option_limit + and len(node.astext()) > self.settings.option_limit): + atts['colspan'] = 2 + self.context.append('\n ') + else: + self.context.append('') + self.body.append( + self.starttag(node, 'td', CLASS='option-group', **atts)) + self.body.append('') + self.context.append(0) # count number of options + + def depart_option_group(self, node): + self.context.pop() + self.body.append('\n') + self.body.append(self.context.pop()) + + def visit_option_list(self, node): + self.body.append( + self.starttag(node, 'table', CLASS='docutils option-list', + frame="void", rules="none")) + self.body.append('\n' + '\n' + '\n') + + def depart_option_list(self, node): + self.body.append('\n\n') + + def visit_option_list_item(self, node): + self.body.append(self.starttag(node, 'tr', '')) + + def depart_option_list_item(self, node): + self.body.append('\n') + + def visit_option_string(self, node): + pass + + def depart_option_string(self, node): + pass + + def visit_organization(self, node): + self.visit_docinfo_item(node, 'organization') + + def depart_organization(self, node): + self.depart_docinfo_item() + + def should_be_compact_paragraph(self, node): + """ + Determine if the

tags around paragraph ``node`` can be omitted. + """ + if (isinstance(node.parent, nodes.document) or + isinstance(node.parent, nodes.compound)): + # Never compact paragraphs in document or compound. + return 0 + for key, value in node.attlist(): + if (node.is_not_default(key) and + not (key == 'classes' and value in + ([], ['first'], ['last'], ['first', 'last']))): + # Attribute which needs to survive. + return 0 + first = isinstance(node.parent[0], nodes.label) # skip label + for child in node.parent.children[first:]: + # only first paragraph can be compact + if isinstance(child, nodes.Invisible): + continue + if child is node: + break + return 0 + parent_length = len([n for n in node.parent if not isinstance( + n, (nodes.Invisible, nodes.label))]) + if ( self.compact_simple + or self.compact_field_list + or self.compact_p and parent_length == 1): + return 1 + return 0 + + def visit_paragraph(self, node): + if self.should_be_compact_paragraph(node): + self.context.append('') + else: + self.body.append(self.starttag(node, 'p', '')) + self.context.append('

\n') + + def depart_paragraph(self, node): + self.body.append(self.context.pop()) + + def visit_problematic(self, node): + if node.hasattr('refid'): + self.body.append('' % node['refid']) + self.context.append('') + else: + self.context.append('') + self.body.append(self.starttag(node, 'span', '', CLASS='problematic')) + + def depart_problematic(self, node): + self.body.append('') + self.body.append(self.context.pop()) + + def visit_raw(self, node): + if 'html' in node.get('format', '').split(): + t = isinstance(node.parent, nodes.TextElement) and 'span' or 'div' + if node['classes']: + self.body.append(self.starttag(node, t, suffix='')) + self.body.append(node.astext()) + if node['classes']: + self.body.append('' % t) + # Keep non-HTML raw text out of output: + raise nodes.SkipNode + + def visit_reference(self, node): + atts = {'class': 'reference'} + if 'refuri' in node: + atts['href'] = node['refuri'] + if ( self.settings.cloak_email_addresses + and atts['href'].startswith('mailto:')): + atts['href'] = self.cloak_mailto(atts['href']) + self.in_mailto = 1 + atts['class'] += ' external' + else: + assert 'refid' in node, \ + 'References must have "refuri" or "refid" attribute.' + atts['href'] = '#' + node['refid'] + atts['class'] += ' internal' + if not isinstance(node.parent, nodes.TextElement): + assert len(node) == 1 and isinstance(node[0], nodes.image) + atts['class'] += ' image-reference' + self.body.append(self.starttag(node, 'a', '', **atts)) + + def depart_reference(self, node): + self.body.append('') + if not isinstance(node.parent, nodes.TextElement): + self.body.append('\n') + self.in_mailto = 0 + + def visit_revision(self, node): + self.visit_docinfo_item(node, 'revision', meta=None) + + def depart_revision(self, node): + self.depart_docinfo_item() + + def visit_row(self, node): + self.body.append(self.starttag(node, 'tr', '')) + node.column = 0 + + def depart_row(self, node): + self.body.append('\n') + + def visit_rubric(self, node): + self.body.append(self.starttag(node, 'p', '', CLASS='rubric')) + + def depart_rubric(self, node): + self.body.append('

\n') + + def visit_section(self, node): + self.section_level += 1 + self.body.append( + self.starttag(node, 'div', CLASS='section')) + + def depart_section(self, node): + self.section_level -= 1 + self.body.append('\n') + + def visit_sidebar(self, node): + self.body.append( + self.starttag(node, 'div', CLASS='sidebar')) + self.set_first_last(node) + self.in_sidebar = 1 + + def depart_sidebar(self, node): + self.body.append('\n') + self.in_sidebar = None + + def visit_status(self, node): + self.visit_docinfo_item(node, 'status', meta=None) + + def depart_status(self, node): + self.depart_docinfo_item() + + def visit_strong(self, node): + self.body.append(self.starttag(node, 'strong', '')) + + def depart_strong(self, node): + self.body.append('
') + + def visit_subscript(self, node): + self.body.append(self.starttag(node, 'sub', '')) + + def depart_subscript(self, node): + self.body.append('') + + def visit_substitution_definition(self, node): + """Internal only.""" + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + self.unimplemented_visit(node) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.sidebar): + self.body.append(self.starttag(node, 'p', '', + CLASS='sidebar-subtitle')) + self.context.append('

\n') + elif isinstance(node.parent, nodes.document): + self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle')) + self.context.append('

\n') + self.in_document_title = len(self.body) + elif isinstance(node.parent, nodes.section): + tag = 'h%s' % (self.section_level + self.initial_header_level - 1) + self.body.append( + self.starttag(node, tag, '', CLASS='section-subtitle') + + self.starttag({}, 'span', '', CLASS='section-subtitle')) + self.context.append('\n' % tag) + + def depart_subtitle(self, node): + self.body.append(self.context.pop()) + if self.in_document_title: + self.subtitle = self.body[self.in_document_title:-1] + self.in_document_title = 0 + self.body_pre_docinfo.extend(self.body) + self.html_subtitle.extend(self.body) + del self.body[:] + + def visit_superscript(self, node): + self.body.append(self.starttag(node, 'sup', '')) + + def depart_superscript(self, node): + self.body.append('') + + def visit_system_message(self, node): + self.body.append(self.starttag(node, 'div', CLASS='system-message')) + self.body.append('

') + backref_text = '' + if len(node['backrefs']): + backrefs = node['backrefs'] + if len(backrefs) == 1: + backref_text = ('; backlink' + % backrefs[0]) + else: + i = 1 + backlinks = [] + for backref in backrefs: + backlinks.append('%s' % (backref, i)) + i += 1 + backref_text = ('; backlinks: %s' + % ', '.join(backlinks)) + if node.hasattr('line'): + line = ', line %s' % node['line'] + else: + line = '' + self.body.append('System Message: %s/%s ' + '(%s%s)%s

\n' + % (node['type'], node['level'], + self.encode(node['source']), line, backref_text)) + + def depart_system_message(self, node): + self.body.append('\n') + + def visit_table(self, node): + classes = ' '.join(['docutils', self.settings.table_style]).strip() + self.body.append( + self.starttag(node, 'table', CLASS=classes, border="1")) + + def depart_table(self, node): + self.body.append('\n') + + def visit_target(self, node): + if not ('refuri' in node or 'refid' in node + or 'refname' in node): + self.body.append(self.starttag(node, 'span', '', CLASS='target')) + self.context.append('') + else: + self.context.append('') + + def depart_target(self, node): + self.body.append(self.context.pop()) + + def visit_tbody(self, node): + self.write_colspecs() + self.body.append(self.context.pop()) # '\n' or '' + self.body.append(self.starttag(node, 'tbody', valign='top')) + + def depart_tbody(self, node): + self.body.append('\n') + + def visit_term(self, node): + self.body.append(self.starttag(node, 'dt', '')) + + def depart_term(self, node): + """ + Leave the end tag to `self.visit_definition()`, in case there's a + classifier. + """ + pass + + def visit_tgroup(self, node): + # Mozilla needs : + self.body.append(self.starttag(node, 'colgroup')) + # Appended by thead or tbody: + self.context.append('\n') + node.stubs = [] + + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + self.write_colspecs() + self.body.append(self.context.pop()) # '\n' + # There may or may not be a ; this is for to use: + self.context.append('') + self.body.append(self.starttag(node, 'thead', valign='bottom')) + + def depart_thead(self, node): + self.body.append('\n') + + def visit_title(self, node): + """Only 6 section levels are supported by HTML.""" + check_id = 0 + close_tag = '

\n' + if isinstance(node.parent, nodes.topic): + self.body.append( + self.starttag(node, 'p', '', CLASS='topic-title first')) + elif isinstance(node.parent, nodes.sidebar): + self.body.append( + self.starttag(node, 'p', '', CLASS='sidebar-title')) + elif isinstance(node.parent, nodes.Admonition): + self.body.append( + self.starttag(node, 'p', '', CLASS='admonition-title')) + elif isinstance(node.parent, nodes.table): + self.body.append( + self.starttag(node, 'caption', '')) + close_tag = '\n' + elif isinstance(node.parent, nodes.document): + self.body.append(self.starttag(node, 'h1', '', CLASS='title')) + close_tag = '\n' + self.in_document_title = len(self.body) + else: + assert isinstance(node.parent, nodes.section) + h_level = self.section_level + self.initial_header_level - 1 + atts = {} + if (len(node.parent) >= 2 and + isinstance(node.parent[1], nodes.subtitle)): + atts['CLASS'] = 'with-subtitle' + self.body.append( + self.starttag(node, 'h%s' % h_level, '', **atts)) + atts = {} + if node.hasattr('refid'): + atts['class'] = 'toc-backref' + atts['href'] = '#' + node['refid'] + if atts: + self.body.append(self.starttag({}, 'a', '', **atts)) + close_tag = '\n' % (h_level) + else: + close_tag = '\n' % (h_level) + self.context.append(close_tag) + + def depart_title(self, node): + self.body.append(self.context.pop()) + if self.in_document_title: + self.title = self.body[self.in_document_title:-1] + self.in_document_title = 0 + self.body_pre_docinfo.extend(self.body) + self.html_title.extend(self.body) + del self.body[:] + + def visit_title_reference(self, node): + self.body.append(self.starttag(node, 'cite', '')) + + def depart_title_reference(self, node): + self.body.append('') + + def visit_topic(self, node): + self.body.append(self.starttag(node, 'div', CLASS='topic')) + self.topic_classes = node['classes'] + + def depart_topic(self, node): + self.body.append('\n') + self.topic_classes = [] + + def visit_transition(self, node): + self.body.append(self.emptytag(node, 'hr', CLASS='docutils')) + + def depart_transition(self, node): + pass + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version', meta=None) + + def depart_version(self, node): + self.depart_docinfo_item() + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' + % node.__class__.__name__) + + +class SimpleListChecker(nodes.GenericNodeVisitor): + + """ + Raise `nodes.NodeFound` if non-simple list item is encountered. + + Here "simple" means a list item containing nothing other than a single + paragraph, a simple list, or a paragraph followed by a simple list. + """ + + def default_visit(self, node): + raise nodes.NodeFound + + def visit_bullet_list(self, node): + pass + + def visit_enumerated_list(self, node): + pass + + def visit_list_item(self, node): + children = [] + for child in node.children: + if not isinstance(child, nodes.Invisible): + children.append(child) + if (children and isinstance(children[0], nodes.paragraph) + and (isinstance(children[-1], nodes.bullet_list) + or isinstance(children[-1], nodes.enumerated_list))): + children.pop() + if len(children) <= 1: + return + else: + raise nodes.NodeFound + + def visit_paragraph(self, node): + raise nodes.SkipNode + + def invisible_visit(self, node): + """Invisible nodes should be ignored.""" + raise nodes.SkipNode + + visit_comment = invisible_visit + visit_substitution_definition = invisible_visit + visit_target = invisible_visit + visit_pending = invisible_visit diff -Nru zope3-3.4.0/src/docutils/writers/html4css1/template.txt zope3-3.5~bzr18/src/docutils/writers/html4css1/template.txt --- zope3-3.4.0/src/docutils/writers/html4css1/template.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/html4css1/template.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ +%(head_prefix)s +%(head)s +%(stylesheet)s +%(body_prefix)s +%(body_pre_docinfo)s +%(docinfo)s +%(body)s +%(body_suffix)s diff -Nru zope3-3.4.0/src/docutils/writers/__init__.py zope3-3.5~bzr18/src/docutils/writers/__init__.py --- zope3-3.4.0/src/docutils/writers/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,133 @@ +# $Id: __init__.py 6111 2009-09-02 21:36:05Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +This package contains Docutils Writer modules. +""" + +__docformat__ = 'reStructuredText' + + +import os.path +import docutils +from docutils import languages, Component +from docutils.transforms import universal + + +class Writer(Component): + + """ + Abstract base class for docutils Writers. + + Each writer module or package must export a subclass also called 'Writer'. + Each writer must support all standard node types listed in + `docutils.nodes.node_class_names`. + + The `write()` method is the main entry point. + """ + + component_type = 'writer' + config_section = 'writers' + + def get_transforms(self): + return Component.get_transforms(self) + [ + universal.Messages, + universal.FilterMessages, + universal.StripClassesAndElements,] + + document = None + """The document to write (Docutils doctree); set by `write`.""" + + output = None + """Final translated form of `document` (Unicode string for text, binary + string for other forms); set by `translate`.""" + + language = None + """Language module for the document; set by `write`.""" + + destination = None + """`docutils.io` Output object; where to write the document. + Set by `write`.""" + + def __init__(self): + + # Used by HTML and LaTex writer for output fragments: + self.parts = {} + """Mapping of document part names to fragments of `self.output`. + Values are Unicode strings; encoding is up to the client. The 'whole' + key should contain the entire document output. + """ + + def write(self, document, destination): + """ + Process a document into its final form. + + Translate `document` (a Docutils document tree) into the Writer's + native format, and write it out to its `destination` (a + `docutils.io.Output` subclass object). + + Normally not overridden or extended in subclasses. + """ + self.document = document + self.language = languages.get_language( + document.settings.language_code) + self.destination = destination + self.translate() + output = self.destination.write(self.output) + return output + + def translate(self): + """ + Do final translation of `self.document` into `self.output`. Called + from `write`. Override in subclasses. + + Usually done with a `docutils.nodes.NodeVisitor` subclass, in + combination with a call to `docutils.nodes.Node.walk()` or + `docutils.nodes.Node.walkabout()`. The ``NodeVisitor`` subclass must + support all standard elements (listed in + `docutils.nodes.node_class_names`) and possibly non-standard elements + used by the current Reader as well. + """ + raise NotImplementedError('subclass must override this method') + + def assemble_parts(self): + """Assemble the `self.parts` dictionary. Extend in subclasses.""" + self.parts['whole'] = self.output + self.parts['encoding'] = self.document.settings.output_encoding + self.parts['version'] = docutils.__version__ + + +class UnfilteredWriter(Writer): + + """ + A writer that passes the document tree on unchanged (e.g. a + serializer.) + + Documents written by UnfilteredWriters are typically reused at a + later date using a subclass of `readers.ReReader`. + """ + + def get_transforms(self): + # Do not add any transforms. When the document is reused + # later, the then-used writer will add the appropriate + # transforms. + return Component.get_transforms(self) + + +_writer_aliases = { + 'html': 'html4css1', + 'latex': 'latex2e', + 'pprint': 'pseudoxml', + 'pformat': 'pseudoxml', + 'pdf': 'rlpdf', + 'xml': 'docutils_xml', + 's5': 's5_html'} + +def get_writer_class(writer_name): + """Return the Writer class from the `writer_name` module.""" + writer_name = writer_name.lower() + if writer_name in _writer_aliases: + writer_name = _writer_aliases[writer_name] + module = __import__(writer_name, globals(), locals()) + return module.Writer diff -Nru zope3-3.4.0/src/docutils/writers/latex2e/default.tex zope3-3.5~bzr18/src/docutils/writers/latex2e/default.tex --- zope3-3.4.0/src/docutils/writers/latex2e/default.tex 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/latex2e/default.tex 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,12 @@ +% generated by Docutils +$head_prefix\usepackage{fixltx2e} % LaTeX patches, \textsubscript +\usepackage{cmap} % fix search and cut-and-paste in PDF +$requirements +%%% User specified packages and stylesheets +$stylesheet +%%% Fallback definitions for Docutils-specific commands +$fallbacks$pdfsetup +%%% Body +\begin{document} +$body_pre_docinfo$docinfo$dedication$abstract$body +\end{document} diff -Nru zope3-3.4.0/src/docutils/writers/latex2e/__init__.py zope3-3.5~bzr18/src/docutils/writers/latex2e/__init__.py --- zope3-3.4.0/src/docutils/writers/latex2e/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/latex2e/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2714 @@ +# -*- coding: utf8 -*- +# $Id: __init__.py 6156 2009-10-08 09:42:38Z milde $ +# Author: Engelbert Gruber +# Copyright: This module has been placed in the public domain. + +"""LaTeX2e document tree Writer.""" + +__docformat__ = 'reStructuredText' + +# code contributions from several people included, thanks to all. +# some named: David Abrahams, Julien Letessier, Lele Gaifax, and others. +# +# convention deactivate code by two # i.e. ##. + +import sys +import os +import time +import re +import string +from docutils import frontend, nodes, languages, writers, utils, transforms +from docutils.writers.newlatex2e import unicode_map + +# compatibility module for Python <= 2.4 +if not hasattr(string, 'Template'): + import docutils._string_template_compat + string.Template = docutils._string_template_compat.Template + +class Writer(writers.Writer): + + supported = ('latex','latex2e') + """Formats this writer supports.""" + + default_template = 'default.tex' + default_template_path = os.path.dirname(__file__) + + settings_spec = ( + 'LaTeX-Specific Options', + 'The LaTeX "--output-encoding" default is "latin-1:strict".', + (('Specify documentclass. Default is "article".', + ['--documentclass'], + {'default': 'article', }), + ('Specify document options. Multiple options can be given, ' + 'separated by commas. Default is "a4paper".', + ['--documentoptions'], + {'default': 'a4paper', }), + ('Use LaTeX footnotes. (default)', + ['--use-latex-footnotes'], + {'default': 1, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Use figure floats for footnote text.', + ['--figure-footnotes'], + {'dest': 'use_latex_footnotes', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Format for footnote references: one of "superscript" or ' + '"brackets". Default is "superscript".', + ['--footnote-references'], + {'choices': ['superscript', 'brackets'], 'default': 'superscript', + 'metavar': '', + 'overrides': 'trim_footnote_reference_space'}), + ('Use \\cite command for citations. ', + ['--use-latex-citations'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Use figure floats for citations ' + '(might get mixed with real figures). (default)', + ['--figure-citations'], + {'dest': 'use_latex_citations', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Format for block quote attributions: one of "dash" (em-dash ' + 'prefix), "parentheses"/"parens", or "none". Default is "dash".', + ['--attribution'], + {'choices': ['dash', 'parentheses', 'parens', 'none'], + 'default': 'dash', 'metavar': ''}), + ('Specify LaTeX packages/stylesheets. ' + ' A style is referenced with \\usepackage if extension is ' + '".sty" or omitted and with \\input else. ' + ' Overrides previous --stylesheet and --stylesheet-path settings.', + ['--stylesheet'], + {'default': '', 'metavar': '', + 'overrides': 'stylesheet_path'}), + ('Like --stylesheet, but the path is rewritten ' + 'relative to the output file. ', + ['--stylesheet-path'], + {'metavar': '', 'overrides': 'stylesheet'}), + ('Link to the stylesheet(s) in the output file. (default)', + ['--link-stylesheet'], + {'dest': 'embed_stylesheet', 'action': 'store_false'}), + ('Embed the stylesheet(s) in the output file. ' + 'Stylesheets must be accessible during processing. ', + ['--embed-stylesheet'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Specify the template file. Default: "%s".' % default_template, + ['--template'], + {'default': default_template, 'metavar': ''}), + ('Table of contents by LaTeX. (default) ', + ['--use-latex-toc'], + {'default': 1, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Table of contents by Docutils (without page numbers). ', + ['--use-docutils-toc'], + {'dest': 'use_latex_toc', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Add parts on top of the section hierarchy.', + ['--use-part-section'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Attach author and date to the document info table. (default) ', + ['--use-docutils-docinfo'], + {'dest': 'use_latex_docinfo', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ('Attach author and date to the document title.', + ['--use-latex-docinfo'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ("Typeset abstract as topic. (default)", + ['--topic-abstract'], + {'dest': 'use_latex_abstract', 'action': 'store_false', + 'validator': frontend.validate_boolean}), + ("Use LaTeX abstract environment for the document's abstract. ", + ['--use-latex-abstract'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Color of any hyperlinks embedded in text ' + '(default: "blue", "0" to disable).', + ['--hyperlink-color'], {'default': 'blue'}), + ('Enable compound enumerators for nested enumerated lists ' + '(e.g. "1.2.a.ii"). Default: disabled.', + ['--compound-enumerators'], + {'default': None, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Disable compound enumerators for nested enumerated lists. ' + 'This is the default.', + ['--no-compound-enumerators'], + {'action': 'store_false', 'dest': 'compound_enumerators'}), + ('Enable section ("." subsection ...) prefixes for compound ' + 'enumerators. This has no effect without --compound-enumerators.' + 'Default: disabled.', + ['--section-prefix-for-enumerators'], + {'default': None, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Disable section prefixes for compound enumerators. ' + 'This is the default.', + ['--no-section-prefix-for-enumerators'], + {'action': 'store_false', 'dest': 'section_prefix_for_enumerators'}), + ('Set the separator between section number and enumerator ' + 'for compound enumerated lists. Default is "-".', + ['--section-enumerator-separator'], + {'default': '-', 'metavar': ''}), + ('When possibile, use the specified environment for literal-blocks. ' + 'Default is quoting of whitespace and special chars.', + ['--literal-block-env'], + {'default': ''}), + ('When possibile, use verbatim for literal-blocks. ' + 'Compatibility alias for "--literal-block-env=verbatim".', + ['--use-verbatim-when-possible'], + {'default': 0, 'action': 'store_true', + 'validator': frontend.validate_boolean}), + ('Table style. "standard" with horizontal and vertical lines, ' + '"booktabs" (LaTeX booktabs style) only horizontal lines ' + 'above and below the table and below the header or "borderless". ' + 'Default: "standard"', + ['--table-style'], + {'choices': ['standard', 'booktabs','nolines', 'borderless'], + 'default': 'standard', + 'metavar': ''}), + ('LaTeX graphicx package option. ' + 'Possible values are "dvips", "pdftex". "auto" includes LaTeX code ' + 'to use "pdftex" if processing with pdf(la)tex and dvips otherwise. ' + 'Default is no option.', + ['--graphicx-option'], + {'default': ''}), + ('LaTeX font encoding. ' + 'Possible values are "", "T1" (default), "OT1", "LGR,T1" or ' + 'any other combination of options to the `fontenc` package. ', + ['--font-encoding'], + {'default': 'T1'}), + ('Per default the latex-writer puts the reference title into ' + 'hyperreferences. Specify "ref*" or "pageref*" to get the section ' + 'number or the page number.', + ['--reference-label'], + {'default': None, }), + ('Specify style and database for bibtex, for example ' + '"--use-bibtex=mystyle,mydb1,mydb2".', + ['--use-bibtex'], + {'default': None, }), + ),) + + settings_defaults = {'output_encoding': 'latin-1', + 'sectnum_depth': 0 # updated by SectNum transform + } + + relative_path_settings = ('stylesheet_path',) + + config_section = 'latex2e writer' + config_section_dependencies = ('writers',) + + head_parts = ('head_prefix', 'requirements', 'stylesheet', + 'fallbacks', 'pdfsetup', 'title', 'subtitle') + visitor_attributes = head_parts + ('body_pre_docinfo', 'docinfo', + 'dedication', 'abstract', 'body') + + output = None + """Final translated form of `document`.""" + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = LaTeXTranslator + + # Override parent method to add latex-specific transforms + ## def get_transforms(self): + ## # call the parent class' method + ## transforms = writers.Writer.get_transforms(self) + ## # print transforms + ## # TODO: footnote collection transform + ## # transforms.append(footnotes.collect) + ## return transforms + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + # copy parts + for part in self.visitor_attributes: + setattr(self, part, getattr(visitor, part)) + # get template string from file + try: + file = open(self.document.settings.template, 'rb') + except IOError: + file = open(os.path.join(os.path.dirname(__file__), + self.document.settings.template), 'rb') + template = string.Template(unicode(file.read(), 'utf-8')) + file.close() + # fill template + self.assemble_parts() # create dictionary of parts + self.output = template.substitute(self.parts) + + def assemble_parts(self): + """Assemble the `self.parts` dictionary of output fragments.""" + writers.Writer.assemble_parts(self) + for part in self.visitor_attributes: + lines = getattr(self, part) + if part in self.head_parts: + if lines: + lines.append('') # to get a trailing newline + self.parts[part] = '\n'.join(lines) + else: + # body contains inline elements, so join without newline + self.parts[part] = ''.join(lines) + + +class Babel(object): + """Language specifics for LaTeX.""" + # country code by a.schlock. + # partly manually converted from iso and babel stuff, dialects and some + _ISO639_TO_BABEL = { + 'no': 'norsk', #XXX added by hand ( forget about nynorsk?) + 'gd': 'scottish', #XXX added by hand + 'hu': 'magyar', #XXX added by hand + 'pt': 'portuguese',#XXX added by hand + 'sl': 'slovenian', + 'af': 'afrikaans', + 'bg': 'bulgarian', + 'br': 'breton', + 'ca': 'catalan', + 'cs': 'czech', + 'cy': 'welsh', + 'da': 'danish', + 'fr': 'french', + # french, francais, canadien, acadian + 'de': 'ngerman', #XXX rather than german + # ngerman, naustrian, german, germanb, austrian + 'el': 'greek', + 'en': 'english', + # english, USenglish, american, UKenglish, british, canadian + 'eo': 'esperanto', + 'es': 'spanish', + 'et': 'estonian', + 'eu': 'basque', + 'fi': 'finnish', + 'ga': 'irish', + 'gl': 'galician', + 'he': 'hebrew', + 'hr': 'croatian', + 'hu': 'hungarian', + 'is': 'icelandic', + 'it': 'italian', + 'la': 'latin', + 'nl': 'dutch', + 'pl': 'polish', + 'pt': 'portuguese', + 'ro': 'romanian', + 'ru': 'russian', + 'sk': 'slovak', + 'sr': 'serbian', + 'sv': 'swedish', + 'tr': 'turkish', + 'uk': 'ukrainian' + } + + def __init__(self, lang): + self.language = lang + self.quote_index = 0 + self.quotes = ('``', "''") + self.setup = '' # language dependent configuration code + # double quotes are "active" in some languages (e.g. German). + # TODO: use \textquotedbl in OT1 font encoding? + self.literal_double_quote = u'"' + if self.language.startswith('de'): + self.quotes = (r'\glqq{}', r'\grqq{}') + self.literal_double_quote = ur'\dq{}' + if self.language.startswith('it'): + self.literal_double_quote = ur'{\char`\"}' + if self.language.startswith('es'): + # reset tilde ~ to the original binding (nobreakspace): + self.setup = ('\n' + r'\addto\shorthandsspanish{\spanishdeactivate{."~<>}}') + + def next_quote(self): + q = self.quotes[self.quote_index] + self.quote_index = (self.quote_index+1) % 2 + return q + + def quote_quotes(self,text): + t = None + for part in text.split('"'): + if t == None: + t = part + else: + t += self.next_quote() + part + return t + + def get_language(self): + lang = self.language.split('_')[0] # filter dialects + return self._ISO639_TO_BABEL.get(lang, "") + +# Building blocks for the latex preamble +# -------------------------------------- + +class SortableDict(dict): + """Dictionary with additional sorting methods""" + + def sortedkeys(self): + """Return sorted list of keys""" + keys = self.keys() + keys.sort() + return keys + + def sortedvalues(self): + """Return list of values sorted by keys""" + return [self[key] for key in self.sortedkeys()] + + +# PreambleCmds +# ````````````` +# A container for LaTeX code snippets that can be +# inserted into the preamble if required in the document. +# +# .. The package 'makecmds' would enable shorter definitions using the +# \providelength and \provideenvironment commands. +# However, it is pretty non-standard (texlive-latex-extra). + +class PreambleCmds(object): + """Building blocks for the latex preamble.""" + +PreambleCmds.abstract = r""" +% abstract title +\providecommand*{\DUtitleabstract}[1]{\centerline{\textbf{#1}}}""" + +PreambleCmds.admonition = r""" +% admonition (specially marked topic) +\providecommand{\DUadmonition}[2][class-arg]{% + % try \DUadmonition#1{#2}: + \ifcsname DUadmonition#1\endcsname% + \csname DUadmonition#1\endcsname{#2}% + \else + \begin{center} + \fbox{\parbox{0.9\textwidth}{#2}} + \end{center} + \fi +}""" + +## PreambleCmds.caption = r"""% configure caption layout +## \usepackage{caption} +## \captionsetup{singlelinecheck=false}% no exceptions for one-liners""" + +PreambleCmds.color = r"""\usepackage{color}""" + +PreambleCmds.docinfo = r""" +% docinfo (width of docinfo table) +\DUprovidelength{\DUdocinfowidth}{0.9\textwidth}""" +# PreambleCmds.docinfo._depends = 'providelength' + +PreambleCmds.embedded_package_wrapper = r"""\makeatletter +%% embedded stylesheet: %s +%s +\makeatother""" + +PreambleCmds.dedication = r""" +% dedication topic +\providecommand{\DUtopicdedication}[1]{\begin{center}#1\end{center}}""" + +PreambleCmds.error = r""" +% error admonition title +\providecommand*{\DUtitleerror}[1]{\DUtitle{\color{red}#1}}""" +# PreambleCmds.errortitle._depends = 'color' + +PreambleCmds.fieldlist = r""" +% fieldlist environment +\ifthenelse{\isundefined{\DUfieldlist}}{ + \newenvironment{DUfieldlist}% + {\quote\description} + {\enddescription\endquote} +}{}""" + +PreambleCmds.float_settings = r"""\usepackage{float} % float configuration +\floatplacement{figure}{H} % place figures here definitely""" + +PreambleCmds.footnotes = r"""% numeric or symbol footnotes with hyperlinks +\providecommand*{\DUfootnotemark}[3]{% + \raisebox{1em}{\hypertarget{#1}{}}% + \hyperlink{#2}{\textsuperscript{#3}}% +} +\providecommand{\DUfootnotetext}[4]{% + \begingroup% + \renewcommand{\thefootnote}{% + \protect\raisebox{1em}{\protect\hypertarget{#1}{}}% + \protect\hyperlink{#2}{#3}}% + \footnotetext{#4}% + \endgroup% +}""" + +PreambleCmds.footnote_floats = r"""% settings for footnotes as floats: +\setlength{\floatsep}{0.5em} +\setlength{\textfloatsep}{\fill} +\addtolength{\textfloatsep}{3em} +\renewcommand{\textfraction}{0.5} +\renewcommand{\topfraction}{0.5} +\renewcommand{\bottomfraction}{0.5} +\setcounter{totalnumber}{50} +\setcounter{topnumber}{50} +\setcounter{bottomnumber}{50}""" + +PreambleCmds.graphicx_auto = r"""% Check output format +\ifx\pdftexversion\undefined + \usepackage{graphicx} +\else + \usepackage[pdftex]{graphicx} +\fi'))""" + + +PreambleCmds.inline = r""" +% inline markup (custom roles) +% \DUrole{#1}{#2} tries \DUrole#1{#2} +\providecommand*{\DUrole}[2]{% + \ifcsname DUrole#1\endcsname% + \csname DUrole#1\endcsname{#2}% + \else% backwards compatibility: try \docutilsrole#1{#2} + \ifcsname docutilsrole#1\endcsname% + \csname docutilsrole#1\endcsname{#2}% + \else% + #2% + \fi% + \fi% +}""" + +PreambleCmds.legend = r""" +% legend environment +\ifthenelse{\isundefined{\DUlegend}}{ + \newenvironment{DUlegend}{\small}{} +}{}""" + +PreambleCmds.lineblock = r""" +% lineblock environment +\DUprovidelength{\DUlineblockindent}{2.5em} +\ifthenelse{\isundefined{\DUlineblock}}{ + \newenvironment{DUlineblock}[1]{% + \list{}{\setlength{\partopsep}{\parskip} + \addtolength{\partopsep}{\baselineskip} + \setlength{\topsep}{0pt} + \setlength{\itemsep}{0.15\baselineskip} + \setlength{\parsep}{0pt} + \setlength{\leftmargin}{#1}} + \raggedright + } + {\endlist} +}{}""" +# PreambleCmds.lineblock._depends = 'providelength' + +PreambleCmds.linking = r""" +%% hyperlinks: +\ifthenelse{\isundefined{\hypersetup}}{ + \usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref} +}{}""" + +PreambleCmds.minitoc = r"""%% local table of contents +\usepackage{minitoc}""" + +PreambleCmds.optionlist = r""" +% optionlist environment +\providecommand*{\DUoptionlistlabel}[1]{\bf #1 \hfill} +\DUprovidelength{\DUoptionlistindent}{3cm} +\ifthenelse{\isundefined{\DUoptionlist}}{ + \newenvironment{DUoptionlist}{% + \list{}{\setlength{\labelwidth}{\DUoptionlistindent} + \setlength{\rightmargin}{1cm} + \setlength{\leftmargin}{\rightmargin} + \addtolength{\leftmargin}{\labelwidth} + \addtolength{\leftmargin}{\labelsep} + \renewcommand{\makelabel}{\DUoptionlistlabel}} + } + {\endlist} +}{}""" +# PreambleCmds.optionlist._depends = 'providelength' + +PreambleCmds.providelength = r""" +% providelength (provide a length variable and set default, if it is new) +\providecommand*{\DUprovidelength}[2]{ + \ifthenelse{\isundefined{#1}}{\newlength{#1}\setlength{#1}{#2}}{} +}""" + +PreambleCmds.rubric = r""" +% rubric (informal heading) +\providecommand*{\DUrubric}[2][class-arg]{% + \subsubsection*{\centering\textit{\textmd{#2}}}}""" + +PreambleCmds.sidebar = r""" +% sidebar (text outside the main text flow) +\providecommand{\DUsidebar}[2][class-arg]{% + \begin{center} + \colorbox[gray]{0.80}{\parbox{0.9\textwidth}{#2}} + \end{center} +}""" + +PreambleCmds.subtitle = r""" +% subtitle (for topic/sidebar) +\providecommand*{\DUsubtitle}[2][class-arg]{\par\emph{#2}\smallskip}""" + +PreambleCmds.table = r"""\usepackage{longtable} +\usepackage{array} +\setlength{\extrarowheight}{2pt} +\newlength{\DUtablewidth} % internal use in tables""" + +PreambleCmds.documenttitle = r""" +%% Document title +\title{%s} +\author{%s} +\date{%s} +\maketitle +""" + +PreambleCmds.titlereference = r""" +% titlereference role +\providecommand*{\DUroletitlereference}[1]{\textsl{#1}}""" + +PreambleCmds.title = r""" +% title for topics, admonitions and sidebar +\providecommand*{\DUtitle}[2][class-arg]{% + % call \DUtitle#1{#2} if it exists: + \ifcsname DUtitle#1\endcsname% + \csname DUtitle#1\endcsname{#2}% + \else + \smallskip\noindent\textbf{#2}\smallskip% + \fi +}""" + +PreambleCmds.topic = r""" +% topic (quote with heading) +\providecommand{\DUtopic}[2][class-arg]{% + \ifcsname DUtopic#1\endcsname% + \csname DUtopic#1\endcsname{#2}% + \else + \begin{quote}#2\end{quote} + \fi +}""" + +PreambleCmds.transition = r""" +% transition (break, fancybreak, anonymous section) +\providecommand*{\DUtransition}[1][class-arg]{% + \hspace*{\fill}\hrulefill\hspace*{\fill} + \vskip 0.5\baselineskip +}""" + + +class DocumentClass(object): + """Details of a LaTeX document class.""" + + def __init__(self, document_class, with_part=False): + self.document_class = document_class + self._with_part = with_part + self.sections = ['section', 'subsection', 'subsubsection', + 'paragraph', 'subparagraph'] + if self.document_class in ('book', 'memoir', 'report', + 'scrbook', 'scrreprt'): + self.sections.insert(0, 'chapter') + if self._with_part: + self.sections.insert(0, 'part') + + def section(self, level): + """Return the LaTeX section name for section `level`. + + The name depends on the specific document class. + Level is 1,2,3..., as level 0 is the title. + """ + + if level <= len(self.sections): + return self.sections[level-1] + else: + return self.sections[-1] + + +class Table(object): + """Manage a table while traversing. + + Maybe change to a mixin defining the visit/departs, but then + class Table internal variables are in the Translator. + + Table style might be + + :standard: horizontal and vertical lines + :booktabs: only horizontal lines (requires "booktabs" LaTeX package) + :nolines: (or borderless) no lines + """ + def __init__(self,translator,latex_type,table_style): + self._translator = translator + self._latex_type = latex_type + self._table_style = table_style + self._open = 0 + # miscellaneous attributes + self._attrs = {} + self._col_width = [] + self._rowspan = [] + self.stubs = [] + self._in_thead = 0 + + def open(self): + self._open = 1 + self._col_specs = [] + self.caption = [] + self._attrs = {} + self._in_head = 0 # maybe context with search + def close(self): + self._open = 0 + self._col_specs = None + self.caption = [] + self._attrs = {} + self.stubs = [] + def is_open(self): + return self._open + + def set_table_style(self, table_style): + if not table_style in ('standard','booktabs','borderless','nolines'): + return + self._table_style = table_style + + def get_latex_type(self): + return self._latex_type + + def set(self,attr,value): + self._attrs[attr] = value + def get(self,attr): + if attr in self._attrs: + return self._attrs[attr] + return None + + def get_vertical_bar(self): + if self._table_style == 'standard': + return '|' + return '' + + # horizontal lines are drawn below a row, + def get_opening(self): + if self._latex_type == 'longtable': + # otherwise longtable might move before paragraph and subparagraph + prefix = '\\leavevmode\n' + else: + prefix = '' + prefix += '\setlength{\DUtablewidth}{\linewidth}' + return '%s\n\\begin{%s}[c]' % (prefix, self._latex_type) + + def get_closing(self): + line = '' + if self._table_style == 'booktabs': + line = '\\bottomrule\n' + elif self._table_style == 'standard': + lines = '\\hline\n' + return '%s\\end{%s}' % (line,self._latex_type) + + def visit_colspec(self, node): + self._col_specs.append(node) + # "stubs" list is an attribute of the tgroup element: + self.stubs.append(node.attributes.get('stub')) + + def get_colspecs(self): + """Return column specification for longtable. + + Assumes reST line length being 80 characters. + Table width is hairy. + + === === + ABC DEF + === === + + usually gets to narrow, therefore we add 1 (fiddlefactor). + """ + width = 80 + + total_width = 0.0 + # first see if we get too wide. + for node in self._col_specs: + colwidth = float(node['colwidth']+1) / width + total_width += colwidth + self._col_width = [] + self._rowspan = [] + # donot make it full linewidth + factor = 0.93 + if total_width > 1.0: + factor /= total_width + bar = self.get_vertical_bar() + latex_table_spec = '' + for node in self._col_specs: + colwidth = factor * float(node['colwidth']+1) / width + self._col_width.append(colwidth+0.005) + self._rowspan.append(0) + latex_table_spec += '%sp{%.3f\\DUtablewidth}' % (bar, colwidth+0.005) + return latex_table_spec+bar + + def get_column_width(self): + """Return columnwidth for current cell (not multicell).""" + return '%.2f\\DUtablewidth' % self._col_width[self._cell_in_row-1] + + def get_caption(self): + if not self.caption: + return '' + caption = ''.join(self.caption) + if 1 == self._translator.thead_depth(): + return r'\caption{%s}\\' '\n' % caption + return r'\caption[]{%s (... continued)}\\' '\n' % caption + + def need_recurse(self): + if self._latex_type == 'longtable': + return 1 == self._translator.thead_depth() + return 0 + + def visit_thead(self): + self._in_thead += 1 + if self._table_style == 'standard': + return ['\\hline\n'] + elif self._table_style == 'booktabs': + return ['\\toprule\n'] + return [] + def depart_thead(self): + a = [] + #if self._table_style == 'standard': + # a.append('\\hline\n') + if self._table_style == 'booktabs': + a.append('\\midrule\n') + if self._latex_type == 'longtable': + if 1 == self._translator.thead_depth(): + a.append('\\endfirsthead\n') + else: + a.append('\\endhead\n') + a.append(r'\multicolumn{%d}{c}' % len(self._col_specs) + + r'{\hfill ... continued on next page} \\') + a.append('\n\\endfoot\n\\endlastfoot\n') + # for longtable one could add firsthead, foot and lastfoot + self._in_thead -= 1 + return a + def visit_row(self): + self._cell_in_row = 0 + def depart_row(self): + res = [' \\\\\n'] + self._cell_in_row = None # remove cell counter + for i in range(len(self._rowspan)): + if (self._rowspan[i]>0): + self._rowspan[i] -= 1 + + if self._table_style == 'standard': + rowspans = [i+1 for i in range(len(self._rowspan)) + if (self._rowspan[i]<=0)] + if len(rowspans)==len(self._rowspan): + res.append('\\hline\n') + else: + cline = '' + rowspans.reverse() + # TODO merge clines + while 1: + try: + c_start = rowspans.pop() + except: + break + cline += '\\cline{%d-%d}\n' % (c_start,c_start) + res.append(cline) + return res + + def set_rowspan(self,cell,value): + try: + self._rowspan[cell] = value + except: + pass + def get_rowspan(self,cell): + try: + return self._rowspan[cell] + except: + return 0 + def get_entry_number(self): + return self._cell_in_row + def visit_entry(self): + self._cell_in_row += 1 + def is_stub_column(self): + if len(self.stubs) >= self._cell_in_row: + return self.stubs[self._cell_in_row-1] + return False + + +class LaTeXTranslator(nodes.NodeVisitor): + + # When options are given to the documentclass, latex will pass them + # to other packages, as done with babel. + # Dummy settings might be taken from document settings + + # Config setting defaults + # ----------------------- + + # TODO: use mixins for different implementations. + # list environment for docinfo. else tabularx + ## use_optionlist_for_docinfo = False # TODO: NOT YET IN USE + + # Use compound enumerations (1.A.1.) + compound_enumerators = 0 + + # If using compound enumerations, include section information. + section_prefix_for_enumerators = 0 + + # This is the character that separates the section ("." subsection ...) + # prefix from the regular list enumerator. + section_enumerator_separator = '-' + + # default link color + hyperlink_color = 'blue' + + # Auxiliary variables + # ------------------- + + has_latex_toc = False # is there a toc in the doc? (needed by minitoc) + is_toc_list = False # is the current bullet_list a ToC? + section_level = 0 + + # Flags to encode(): + # inside citation reference labels underscores dont need to be escaped + inside_citation_reference_label = False + verbatim = False # do not encode + insert_non_breaking_blanks = False # replace blanks by "~" + insert_newline = False # add latex newline commands + literal = False # teletype: replace underscores + literal_block = False # inside literal block: no quote mangling + + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + # Settings + # ~~~~~~~~ + self.settings = settings = document.settings + self.latex_encoding = self.to_latex_encoding(settings.output_encoding) + self.use_latex_toc = settings.use_latex_toc + self.use_latex_docinfo = settings.use_latex_docinfo + self.use_latex_footnotes = settings.use_latex_footnotes + self._use_latex_citations = settings.use_latex_citations + self.embed_stylesheet = settings.embed_stylesheet + self._reference_label = settings.reference_label + self.hyperlink_color = settings.hyperlink_color + self.compound_enumerators = settings.compound_enumerators + self.font_encoding = settings.font_encoding + self.section_prefix_for_enumerators = ( + settings.section_prefix_for_enumerators) + self.section_enumerator_separator = ( + settings.section_enumerator_separator.replace('_', '\\_')) + # literal blocks: + self.literal_block_env = '' + self.literal_block_options = '' + if settings.literal_block_env != '': + (none, + self.literal_block_env, + self.literal_block_options, + none ) = re.split('(\w+)(.*)', settings.literal_block_env) + elif settings.use_verbatim_when_possible: + self.literal_block_env = 'verbatim' + # + if self.settings.use_bibtex: + self.bibtex = self.settings.use_bibtex.split(',',1) + # TODO avoid errors on not declared citations. + else: + self.bibtex = None + # language: + # (labels, bibliographic_fields, and author_separators) + self.language = languages.get_language(settings.language_code) + self.babel = Babel(settings.language_code) + self.author_separator = self.language.author_separators[0] + self.d_options = [self.settings.documentoptions, + self.babel.get_language()] + self.d_options = ','.join([opt for opt in self.d_options if opt]) + self.d_class = DocumentClass(settings.documentclass, + settings.use_part_section) + + if self.settings.graphicx_option == '': + self.graphicx_package = r'\usepackage{graphicx}' + elif self.settings.graphicx_option.lower() == 'auto': + self.graphicx_package = PreambleCmds.graphicx_auto + else: + self.graphicx_package = (r'\usepackage[%s]{graphicx}' % + self.settings.graphicx_option) + + + # Output collection stacks + # ~~~~~~~~~~~~~~~~~~~~~~~~ + + # Document parts + self.head_prefix = [r'\documentclass[%s]{%s}' % + (self.d_options, self.settings.documentclass)] + self.requirements = SortableDict() # made a list in depart_document() + self.stylesheet = [] + self.fallbacks = SortableDict() # made a list in depart_document() + self.pdfsetup = [] # PDF properties (hyperref package) + self.title = [] + self.subtitle = [] + ## self.body_prefix = ['\\begin{document}\n'] + self.body_pre_docinfo = [] # title data and \maketitle + self.docinfo = [] + self.dedication = [] + self.abstract = [] + self.body = [] + ## self.body_suffix = ['\\end{document}\n'] + + # A heterogenous stack used in conjunction with the tree traversal. + # Make sure that the pops correspond to the pushes: + self.context = [] + + # Title metadata: + self.title_labels = [] + self.subtitle_labels = [] + # (if use_latex_docinfo: collects lists of + # author/organization/contact/address lines) + self.author_stack = [] + # date (the default supresses the "auto-date" feature of \maketitle) + self.date = [] + + # PDF properties: pdftitle, pdfauthor + # TODO?: pdfcreator, pdfproducer, pdfsubject, pdfkeywords + self.pdfinfo = [] + self.pdfauthor = [] + + # Stack of section counters so that we don't have to use_latex_toc. + # This will grow and shrink as processing occurs. + # Initialized for potential first-level sections. + self._section_number = [0] + + # The current stack of enumerations so that we can expand + # them into a compound enumeration. + self._enumeration_counters = [] + # The maximum number of enumeration counters we've used. + # If we go beyond this number, we need to create a new + # counter; otherwise, just reuse an old one. + self._max_enumeration_counters = 0 + + self._bibitems = [] + self.literal_block_stack = [] + + # object for a table while proccessing. + self.table_stack = [] + self.active_table = Table(self, 'longtable', settings.table_style) + + # Where to collect the output of visitor methods (default: body) + self.out = self.body + self.out_stack = [] # stack of output collectors + + # Process settings + # ~~~~~~~~~~~~~~~~ + + # persistent requirements + if self.font_encoding == '': + fontenc_header = r'%\usepackage[OT1]{fontenc}' + else: + fontenc_header = r'\usepackage[%s]{fontenc}' % self.font_encoding + self.requirements['_persistent'] = '\n'.join([ + # multi-language support (language is in document settings) + '\\usepackage{babel}%s' % self.babel.setup, + fontenc_header, + r'\usepackage[%s]{inputenc}' % self.latex_encoding, + r'\usepackage{ifthen}', + ]) + # page layout with typearea (if there are relevant document options). + if (settings.documentclass.find('scr') == -1 and + (self.d_options.find('DIV') != -1 or + self.d_options.find('BCOR') != -1)): + self.requirements['typearea'] = r'\usepackage{typearea}' + + # Stylesheets + # get list of style sheets from settings + styles = utils.get_stylesheet_list(settings) + # adapt path if --stylesheet_path is used + if settings.stylesheet_path and not(self.embed_stylesheet): + styles = [utils.relative_path(settings._destination, sheet) + for sheet in styles] + for sheet in styles: + (base, ext) = os.path.splitext(sheet) + is_package = ext in ['.sty', ''] + if self.embed_stylesheet: + if is_package: + sheet = base + '.sty' # adapt package name + # wrap in \makeatletter, \makeatother + wrapper = PreambleCmds.embedded_package_wrapper + else: + wrapper = '%% embedded stylesheet: %s\n%s' + settings.record_dependencies.add(sheet) + self.stylesheet.append(wrapper % + (sheet, unicode(open(sheet).read(), 'utf-8'))) + else: # link to style sheet + if is_package: + self.stylesheet.append(r'\usepackage{%s}' % base) + else: + self.stylesheet.append(r'\input{%s}' % sheet) + + # PDF setup + if self.hyperlink_color == '0': + self.hyperlink_color = 'black' + self.colorlinks = 'false' + else: + self.colorlinks = 'true' + + # LaTeX Toc + # include all supported sections in toc and PDF bookmarks + # (or use documentclass-default (as currently))? + ## if self.use_latex_toc: + ## self.requirements['tocdepth'] = (r'\setcounter{tocdepth}{%d}' % + ## len(self.d_class.sections)) + + # LaTeX section numbering + if not self.settings.sectnum_xform: # section numbering by LaTeX: + # sectnum_depth: + # None "sectnum" directive without depth arg -> LaTeX default + # 0 no "sectnum" directive -> no section numbers + # else value of the "depth" argument -> limit to supported + # section levels + if settings.sectnum_depth is not None: + sectnum_depth = min(settings.sectnum_depth, + len(self.d_class.sections)) + self.requirements['sectnum_depth'] = ( + r'\setcounter{secnumdepth}{%d}' % sectnum_depth) + # start with specified number: + if (hasattr(settings, 'sectnum_start') and + settings.sectnum_start != 1): + self.requirements['sectnum_start'] = ( + r'\setcounter{%s}{%d}' % (self.d_class.sections[0], + settings.sectnum_start-1)) + # currently ignored (configure in a stylesheet): + ## settings.sectnum_prefix + ## settings.sectnum_suffix + + + # Auxiliary Methods + # ----------------- + + def to_latex_encoding(self,docutils_encoding): + """Translate docutils encoding name into LaTeX's. + + Default method is remove "-" and "_" chars from docutils_encoding. + + """ + tr = { 'iso-8859-1': 'latin1', # west european + 'iso-8859-2': 'latin2', # east european + 'iso-8859-3': 'latin3', # esperanto, maltese + 'iso-8859-4': 'latin4', # north european, scandinavian, baltic + 'iso-8859-5': 'iso88595', # cyrillic (ISO) + 'iso-8859-9': 'latin5', # turkish + 'iso-8859-15': 'latin9', # latin9, update to latin1. + 'mac_cyrillic': 'maccyr', # cyrillic (on Mac) + 'windows-1251': 'cp1251', # cyrillic (on Windows) + 'koi8-r': 'koi8-r', # cyrillic (Russian) + 'koi8-u': 'koi8-u', # cyrillic (Ukrainian) + 'windows-1250': 'cp1250', # + 'windows-1252': 'cp1252', # + 'us-ascii': 'ascii', # ASCII (US) + # unmatched encodings + #'': 'applemac', + #'': 'ansinew', # windows 3.1 ansi + #'': 'ascii', # ASCII encoding for the range 32--127. + #'': 'cp437', # dos latin us + #'': 'cp850', # dos latin 1 + #'': 'cp852', # dos latin 2 + #'': 'decmulti', + #'': 'latin10', + #'iso-8859-6': '' # arabic + #'iso-8859-7': '' # greek + #'iso-8859-8': '' # hebrew + #'iso-8859-10': '' # latin6, more complete iso-8859-4 + } + encoding = docutils_encoding.lower() + if encoding in tr: + return tr[encoding] + # convert: latin-1, latin_1, utf-8 and similar things + encoding = encoding.replace('_', '').replace('-', '') + # strip the error handler + return encoding.split(':')[0] + + def language_label(self, docutil_label): + return self.language.labels[docutil_label] + + def ensure_math(self, text): + if not hasattr(self, 'ensure_math_re'): + chars = { # lnot,pm,twosuperior,threesuperior,mu,onesuperior,times,div + 'latin1' : '\xac\xb1\xb2\xb3\xb5\xb9\xd7\xf7' , # ¬±²³µ¹×÷ + # TODO?: use texcomp instead. + } + self.ensure_math_re = re.compile('([%s])' % chars['latin1']) + text = self.ensure_math_re.sub(r'\\ensuremath{\1}', text) + return text + + def encode(self, text): + """Return text with 'problematic' characters escaped. + + Escape the ten special printing characters ``# $ % & ~ _ ^ \ { }``, + square brackets ``[ ]``, double quotes and (in OT1) ``< | >``. + + Separate ``-`` (and more in literal text) to prevent input ligatures. + + Translate non-supported Unicode characters. + """ + if self.verbatim: + return text + # Separate compound characters, e.g. '--' to '-{}-'. + separate_chars = '-' + # In monospace-font, we also separate ',,', '``' and "''" and some + # other characters which can't occur in non-literal text. + if self.literal_block or self.literal: + separate_chars += ',`\'"<>' + # LaTeX encoding maps: + special_chars = { + ord('#'): ur'\#', + ord('$'): ur'\$', + ord('%'): ur'\%', + ord('&'): ur'\&', + ord('~'): ur'\textasciitilde{}', + ord('_'): ur'\_', + ord('^'): ur'\textasciicircum{}', + ord('\\'): ur'\textbackslash{}', + ord('{'): ur'\{', + ord('}'): ur'\}', + # Square brackets are ordinary chars and cannot be escaped with '\', + # so we put them in a group '{[}'. (Alternative: ensure that all + # macros with optional arguments are terminated with {} and text + # inside any optional argument is put in a group ``[{text}]``). + # Commands with optional args inside an optional arg must be put + # in a group, e.g. ``\item[{\hyperref[label]{text}}]``. + ord('['): ur'{[}', + ord(']'): ur'{]}' + } + # Unicode chars that are not recognized by LaTeX's utf8 encoding + unsupported_unicode_chars = { + 0x00A0: ur'~', # NO-BREAK SPACE + 0x00AD: ur'\-', # SOFT HYPHEN + 0x21d4: ur'$\Leftrightarrow$', + # Docutils footnote symbols: + 0x2660: ur'$\spadesuit$', + 0x2663: ur'$\clubsuit$', + } + # Unicode chars that are recognized by LaTeX's utf8 encoding + unicode_chars = { + 0x2013: ur'\textendash{}', + 0x2014: ur'\textemdash{}', + 0x2018: ur'`', + 0x2019: ur"'", + 0x201A: ur'\quotesinglbase{}', # SINGLE LOW-9 QUOTATION MARK + 0x201C: ur'\textquotedblleft{}', + 0x201D: ur'\textquotedblright{}', + 0x201E: ur'\quotedblbase', # DOUBLE LOW-9 QUOTATION MARK + 0x2020: ur'\dag{}', + 0x2021: ur'\ddag{}', + 0x2026: ur'\dots{}', + 0x2122: ur'\texttrademark{}', + } + # Unicode chars that require a feature/package to render + pifont_chars = { + 0x2665: ur'\ding{170}', # black heartsuit + 0x2666: ur'\ding{169}', # black diamondsuit + } + # TODO: greek alphabet ... ? + # see also LaTeX codec + # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124 + # and unimap.py from TeXML + + # set up the translation table: + table = special_chars + # keep the underscore in citation references + if self.inside_citation_reference_label: + del(table[ord('_')]) + # Workarounds for OT1 font-encoding + if self.font_encoding in ['OT1', '']: + # * out-of-order characters in cmtt + if self.literal_block or self.literal: + # replace underscore by underlined blank, + # because this has correct width. + table[ord('_')] = u'\\underline{~}' + # the backslash doesn't work, so we use a mirrored slash. + # \reflectbox is provided by graphicx: + self.requirements['graphicx'] = self.graphicx_package + table[ord('\\')] = ur'\reflectbox{/}' + # * ``< | >`` come out as different chars (except for cmtt): + else: + table[ord('|')] = ur'\textbar{}' + table[ord('<')] = ur'\textless{}' + table[ord('>')] = ur'\textgreater{}' + if self.insert_non_breaking_blanks: + table[ord(' ')] = ur'~' + if self.literal_block or self.literal: + # double quotes are 'active' in some languages + table[ord('"')] = self.babel.literal_double_quote + else: + text = self.babel.quote_quotes(text) + # Unicode chars: + table.update(unsupported_unicode_chars) + if not self.latex_encoding.startswith('utf8'): + table.update(unicode_chars) + # Unicode chars that require a feature/package to render + if [ch for ch in pifont_chars.keys() if unichr(ch) in text]: + self.requirements['pifont'] = '\\usepackage{pifont}' + table.update(pifont_chars) + + text = text.translate(table) + + # Break up input ligatures + for char in separate_chars * 2: + # Do it twice ("* 2") because otherwise we would replace + # '---' by '-{}--'. + text = text.replace(char + char, char + '{}' + char) + # Literal line breaks (in address or literal blocks): + if self.insert_newline or self.literal_block: + # for blank lines, insert a protected space, to avoid + # ! LaTeX Error: There's no line here to end. + textlines = [line + '~'*(not line.lstrip()) + for line in text.split('\n')] + text = '\\\\\n'.join(textlines) + if not self.latex_encoding.startswith('utf8'): + text = self.ensure_math(text) + return text + + def attval(self, text, + whitespace=re.compile('[\n\r\t\v\f]')): + """Cleanse, encode, and return attribute value text.""" + return self.encode(whitespace.sub(' ', text)) + + # TODO: is this used anywhere? (update or delete) + ## def astext(self): + ## """Assemble document parts and return as string.""" + ## head = '\n'.join(self.head_prefix + self.stylesheet + self.head) + ## body = ''.join(self.body_prefix + self.body + self.body_suffix) + ## return head + '\n' + body + + def is_inline(self, node): + """Check whether a node represents an inline element""" + return isinstance(node.parent, nodes.TextElement) + + def append_hypertargets(self, node): + """Append hypertargets for all ids of `node`""" + # hypertarget places the anchor at the target's baseline, + # so we raise it explicitely + self.out.append('%\n'.join(['\\raisebox{1em}{\\hypertarget{%s}{}}' % + id for id in node['ids']])) + + def ids_to_labels(self, node, set_anchor=True): + """Return list of label definitions for all ids of `node` + + If `set_anchor` is True, an anchor is set with \phantomsection. + """ + labels = ['\\label{%s}' % id for id in node.get('ids', [])] + if set_anchor and labels: + labels.insert(0, '\\phantomsection') + return labels + + def push_output_collector(self, new_out): + self.out_stack.append(self.out) + self.out = new_out + + def pop_output_collector(self): + self.out = self.out_stack.pop() + + # Visitor methods + # --------------- + + def visit_Text(self, node): + self.out.append(self.encode(node.astext())) + + def depart_Text(self, node): + pass + + def visit_address(self, node): + self.visit_docinfo_item(node, 'address') + + def depart_address(self, node): + self.depart_docinfo_item(node) + + def visit_admonition(self, node): + self.fallbacks['admonition'] = PreambleCmds.admonition + if node.tagname is 'admonition': + classes = ','.join(node['classes']) + title = '' + else: # specific admonitions + self.fallbacks['title'] = PreambleCmds.title + classes = node.tagname.replace('_', '-') + title = '\\DUtitle[%s]{%s}\n' % ( + classes, self.language.labels.get(classes, classes)) + self.out.append('\n\\DUadmonition[%s]{\n%s' % (classes, title)) + + def depart_admonition(self, node=None): + self.out.append('}\n') + + def visit_attention(self, node): + self.visit_admonition(node) + + def depart_attention(self, node): + self.depart_admonition() + + def visit_author(self, node): + self.visit_docinfo_item(node, 'author') + + def depart_author(self, node): + self.depart_docinfo_item(node) + + def visit_authors(self, node): + # not used: visit_author is called anyway for each author. + pass + + def depart_authors(self, node): + pass + + def visit_block_quote(self, node): + self.out.append( '%\n\\begin{quote}\n') + + def depart_block_quote(self, node): + self.out.append( '\n\\end{quote}\n') + + def visit_bullet_list(self, node): + if self.is_toc_list: + self.out.append( '%\n\\begin{list}{}{}\n' ) + else: + self.out.append( '%\n\\begin{itemize}\n' ) + + def depart_bullet_list(self, node): + if self.is_toc_list: + self.out.append( '\n\\end{list}\n' ) + else: + self.out.append( '\n\\end{itemize}\n' ) + + def visit_superscript(self, node): + self.out.append(r'\textsuperscript{') + if node['classes']: + self.visit_inline(node) + + def depart_superscript(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + def visit_subscript(self, node): + self.out.append(r'\textsubscript{') # requires `fixltx2e` + if node['classes']: + self.visit_inline(node) + + def depart_subscript(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + def visit_caption(self, node): + self.out.append( '\\caption{' ) + + def depart_caption(self, node): + self.out.append('}\n') + + def visit_caution(self, node): + self.visit_admonition(node) + + def depart_caution(self, node): + self.depart_admonition() + + def visit_title_reference(self, node): + self.fallbacks['titlereference'] = PreambleCmds.titlereference + self.out.append(r'\DUroletitlereference{') + if node['classes']: + self.visit_inline(node) + + def depart_title_reference(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append( '}' ) + + def visit_citation(self, node): + # TODO maybe use cite bibitems + if self._use_latex_citations: + self.context.append(len(self.body)) + else: + self.out.append(r'\begin{figure}[b]') + self.append_hypertargets(node) + + def depart_citation(self, node): + if self._use_latex_citations: + size = self.context.pop() + label = self.body[size] + text = ''.join(self.body[size+1:]) + del self.body[size:] + self._bibitems.append([label, text]) + else: + self.out.append('\\end{figure}\n') + + def visit_citation_reference(self, node): + if self._use_latex_citations: + if not self.inside_citation_reference_label: + self.out.append(r'\cite{') + self.inside_citation_reference_label = 1 + else: + assert self.body[-1] in (' ', '\n'),\ + 'unexpected non-whitespace while in reference label' + del self.body[-1] + else: + href = '' + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + self.out.append('[\\hyperlink{%s}{' % href) + + def depart_citation_reference(self, node): + if self._use_latex_citations: + followup_citation = False + # check for a following citation separated by a space or newline + next_siblings = node.traverse(descend=0, siblings=1, + include_self=0) + if len(next_siblings) > 1: + next = next_siblings[0] + if (isinstance(next, nodes.Text) and + next.astext() in (' ', '\n')): + if next_siblings[1].__class__ == node.__class__: + followup_citation = True + if followup_citation: + self.out.append(',') + else: + self.out.append('}') + self.inside_citation_reference_label = False + else: + self.out.append('}]') + + def visit_classifier(self, node): + self.out.append( '(\\textbf{' ) + + def depart_classifier(self, node): + self.out.append( '})\n' ) + + def visit_colspec(self, node): + self.active_table.visit_colspec(node) + + def depart_colspec(self, node): + pass + + def visit_comment(self, node): + # Precede every line with a comment sign, wrap in newlines + self.out.append('\n%% %s\n' % node.astext().replace('\n', '\n% ')) + raise nodes.SkipNode + + def depart_comment(self, node): + pass + + def visit_compound(self, node): + pass + + def depart_compound(self, node): + pass + + def visit_contact(self, node): + self.visit_docinfo_item(node, 'contact') + + def depart_contact(self, node): + self.depart_docinfo_item(node) + + def visit_container(self, node): + pass + + def depart_container(self, node): + pass + + def visit_copyright(self, node): + self.visit_docinfo_item(node, 'copyright') + + def depart_copyright(self, node): + self.depart_docinfo_item(node) + + def visit_danger(self, node): + self.visit_admonition(node) + + def depart_danger(self, node): + self.depart_admonition() + + def visit_date(self, node): + self.visit_docinfo_item(node, 'date') + + def depart_date(self, node): + self.depart_docinfo_item(node) + + def visit_decoration(self, node): + # header and footer + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + pass + + def depart_definition(self, node): + self.out.append('\n') + + def visit_definition_list(self, node): + self.out.append( '%\n\\begin{description}\n' ) + + def depart_definition_list(self, node): + self.out.append( '\\end{description}\n' ) + + def visit_definition_list_item(self, node): + pass + + def depart_definition_list_item(self, node): + pass + + def visit_description(self, node): + self.out.append(' ') + + def depart_description(self, node): + pass + + def visit_docinfo(self, node): + self.push_output_collector(self.docinfo) + + def depart_docinfo(self, node): + self.pop_output_collector() + # Some itmes (e.g. author) end up at other places + if self.docinfo: + # tabularx: automatic width of columns, no page breaks allowed. + self.requirements['tabularx'] = r'\usepackage{tabularx}' + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['docinfo'] = PreambleCmds.docinfo + # + self.docinfo.insert(0, '\n% Docinfo\n' + '\\begin{center}\n' + '\\begin{tabularx}{\\DUdocinfowidth}{lX}\n') + self.docinfo.append('\\end{tabularx}\n' + '\\end{center}\n') + + def visit_docinfo_item(self, node, name): + if name == 'author': + self.pdfauthor.append(self.attval(node.astext())) + if self.use_latex_docinfo: + if name in ('author', 'organization', 'contact', 'address'): + # We attach these to the last author. If any of them precedes + # the first author, put them in a separate "author" group + # (in lack of better semantics). + if name == 'author' or not self.author_stack: + self.author_stack.append([]) + if name == 'address': # newlines are meaningful + self.insert_newline = 1 + text = self.encode(node.astext()) + self.insert_newline = False + else: + text = self.attval(node.astext()) + self.author_stack[-1].append(text) + raise nodes.SkipNode + elif name == 'date': + self.date.append(self.attval(node.astext())) + raise nodes.SkipNode + self.out.append('\\textbf{%s}: &\n\t' % self.language_label(name)) + if name == 'address': + self.insert_newline = 1 + self.out.append('{\\raggedright\n') + self.context.append(' } \\\\\n') + else: + self.context.append(' \\\\\n') + + def depart_docinfo_item(self, node): + self.out.append(self.context.pop()) + # for address we did set insert_newline + self.insert_newline = False + + def visit_doctest_block(self, node): + self.visit_literal_block(node) + + def depart_doctest_block(self, node): + self.depart_literal_block(node) + + def visit_document(self, node): + # titled document? + if (self.use_latex_docinfo or len(node) and + isinstance(node[0], nodes.title)): + self.title_labels += self.ids_to_labels(node) + + def depart_document(self, node): + # Complete header with information gained from walkabout + # a) conditional requirements (before style sheet) + self.requirements = self.requirements.sortedvalues() + # b) coditional fallback definitions (after style sheet) + self.fallbacks = self.fallbacks.sortedvalues() + # c) PDF properties + self.pdfsetup.append(PreambleCmds.linking % (self.colorlinks, + self.hyperlink_color, + self.hyperlink_color)) + if self.pdfauthor: + authors = self.author_separator.join(self.pdfauthor) + self.pdfinfo.append(' pdfauthor={%s}' % authors) + if self.pdfinfo: + self.pdfsetup += [r'\hypersetup{'] + self.pdfinfo + ['}'] + # Complete body + # a) document title (part 'body_prefix'): + # NOTE: Docutils puts author/date into docinfo, so normally + # we do not want LaTeX author/date handling (via \maketitle). + # To deactivate it, we add \title, \author, \date, + # even if the arguments are empty strings. + if self.title or self.author_stack or self.date: + authors = ['\\\\\n'.join(author_entry) + for author_entry in self.author_stack] + title = self.title + self.title_labels + if self.subtitle: + title += [r'\\ % subtitle', + r'\large{%s}' % self.subtitle[0] + ] + self.subtitle_labels + self.body_pre_docinfo.append(PreambleCmds.documenttitle % ( + '%\n '.join(title), + ' \\and\n'.join(authors), + ', '.join(self.date))) + # b) bibliography + # TODO insertion point of bibliography should be configurable. + if self._use_latex_citations and len(self._bibitems)>0: + if not self.bibtex: + widest_label = '' + for bi in self._bibitems: + if len(widest_label) self._max_enumeration_counters: + self._max_enumeration_counters = len(self._enumeration_counters) + self.out.append('\\newcounter{%s}\n' % counter_name) + else: + self.out.append('\\setcounter{%s}{0}\n' % counter_name) + + self.out.append('\\begin{list}{%s\\%s{%s}%s}\n' % + (enum_prefix,enum_type,counter_name,enum_suffix)) + self.out.append('{\n') + self.out.append('\\usecounter{%s}\n' % counter_name) + # set start after usecounter, because it initializes to zero. + if 'start' in node: + self.out.append('\\addtocounter{%s}{%d}\n' % + (counter_name,node['start']-1)) + ## set rightmargin equal to leftmargin + self.out.append('\\setlength{\\rightmargin}{\\leftmargin}\n') + self.out.append('}\n') + + def depart_enumerated_list(self, node): + self.out.append('\\end{list}\n') + self._enumeration_counters.pop() + + def visit_error(self, node): + self.fallbacks['error'] = PreambleCmds.error + self.visit_admonition(node) + + def depart_error(self, node): + self.depart_admonition() + + def visit_field(self, node): + # real output is done in siblings: _argument, _body, _name + pass + + def depart_field(self, node): + self.out.append('\n') + ##self.out.append('%[depart_field]\n') + + def visit_field_argument(self, node): + self.out.append('%[visit_field_argument]\n') + + def depart_field_argument(self, node): + self.out.append('%[depart_field_argument]\n') + + def visit_field_body(self, node): + pass + + def depart_field_body(self, node): + if self.out is self.docinfo: + self.out.append(r'\\') + + def visit_field_list(self, node): + if self.out is not self.docinfo: + self.fallbacks['fieldlist'] = PreambleCmds.fieldlist + self.out.append('%\n\\begin{DUfieldlist}\n') + + def depart_field_list(self, node): + if self.out is not self.docinfo: + self.out.append('\\end{DUfieldlist}\n') + + def visit_field_name(self, node): + if self.out is self.docinfo: + self.out.append('\\textbf{') + else: + # Commands with optional args inside an optional arg must be put + # in a group, e.g. ``\item[{\hyperref[label]{text}}]``. + self.out.append('\\item[{') + + def depart_field_name(self, node): + if self.out is self.docinfo: + self.out.append('}: &') + else: + self.out.append(':}]') + + def visit_figure(self, node): + self.requirements['float_settings'] = PreambleCmds.float_settings + # ! the 'align' attribute should set "outer alignment" ! + # For "inner alignment" use LaTeX default alignment (similar to HTML) + ## if ('align' not in node.attributes or + ## node.attributes['align'] == 'center'): + ## align = '\n\\centering' + ## align_end = '' + ## else: + ## # TODO non vertical space for other alignments. + ## align = '\\begin{flush%s}' % node.attributes['align'] + ## align_end = '\\end{flush%s}' % node.attributes['align'] + ## self.out.append( '\\begin{figure}%s\n' % align ) + ## self.context.append( '%s\\end{figure}\n' % align_end ) + self.out.append('\\begin{figure}') + self.context.append('\\end{figure}\n') + + def depart_figure(self, node): + self.out.append(self.context.pop()) + + def visit_footer(self, node): + self.push_output_collector([]) + self.out.append(r'\newcommand{\DUfooter}{') + + def depart_footer(self, node): + self.out.append('}') + self.requirements['~footer'] = ''.join(self.out) + self.pop_output_collector() + + def visit_footnote(self, node): + try: + backref = node['backrefs'][0] + except IndexError: + backref = node['ids'][0] # no backref, use self-ref instead + if self.use_latex_footnotes: + self.fallbacks['footnotes'] = PreambleCmds.footnotes + num,text = node.astext().split(None,1) + if self.settings.footnote_references == 'brackets': + num = '[%s]' % num + self.out.append('%%\n\\DUfootnotetext{%s}{%s}{%s}{' % + (node['ids'][0], backref, self.encode(num))) + if node['ids'] == node['names']: + self.out += self.ids_to_labels(node) + else: + # use key starting with ~ for sorting after small letters + self.requirements['~fnt_floats'] = PreambleCmds.footnote_floats + self.out.append('\\begin{figure}[b]') + self.append_hypertargets(node) + if node.get('id') == node.get('name'): # explicite label + self.out += self.ids_to_labels(node) + + def depart_footnote(self, node): + if self.use_latex_footnotes: + self.out.append('}\n') + else: + self.out.append('\\end{figure}\n') + + def visit_footnote_reference(self, node): + href = '' + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + # if self.use_latex_footnotes: + # TODO: insert footnote content at (or near) this place + # print "footnote-ref to", node['refid'] + # footnotes = (self.document.footnotes + + # self.document.autofootnotes + + # self.document.symbol_footnotes) + # for footnote in footnotes: + # # print footnote['ids'] + # if node.get('refid', '') in footnote['ids']: + # print 'matches', footnote['ids'] + format = self.settings.footnote_references + if format == 'brackets': + self.append_hypertargets(node) + self.out.append('\\hyperlink{%s}{[' % href) + self.context.append(']}') + else: + self.fallbacks['footnotes'] = PreambleCmds.footnotes + # TODO: second argument = backlink id + self.out.append(r'\DUfootnotemark{%s}{%s}{' % + (node['ids'][0], href)) + self.context.append('}') + + def depart_footnote_reference(self, node): + self.out.append(self.context.pop()) + + # footnote/citation label + def label_delim(self, node, bracket, superscript): + if isinstance(node.parent, nodes.footnote): + if self.use_latex_footnotes: + raise nodes.SkipNode + if self.settings.footnote_references == 'brackets': + self.out.append(bracket) + else: + self.out.append(superscript) + else: + assert isinstance(node.parent, nodes.citation) + if not self._use_latex_citations: + self.out.append(bracket) + + def visit_label(self, node): + """footnote or citation label: in brackets or as superscript""" + self.label_delim(node, '[', '\\textsuperscript{') + + def depart_label(self, node): + self.label_delim(node, ']', '}') + + # elements generated by the framework e.g. section numbers. + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def visit_header(self, node): + self.push_output_collector([]) + self.out.append(r'\newcommand{\DUheader}{') + + def depart_header(self, node): + self.out.append('}') + self.requirements['~header'] = ''.join(self.out) + self.pop_output_collector() + + def visit_hint(self, node): + self.visit_admonition(node) + + def depart_hint(self, node): + self.depart_admonition() + + def to_latex_length(self, length_str): + """Convert string with rst lenght to LaTeX""" + match = re.match('(\d*\.?\d*)\s*(\S*)', length_str) + if not match: + return length_str + value, unit = match.groups()[:2] + # no unit or "DTP" points (called 'bp' in TeX): + if unit in ('', 'pt'): + length_str = '%sbp' % value + # percentage: relate to current line width + elif unit == '%': + length_str = '%.3f\\linewidth' % (float(value)/100.0) + return length_str + + def visit_image(self, node): + self.requirements['graphicx'] = self.graphicx_package + attrs = node.attributes + # Add image URI to dependency list, assuming that it's + # referring to a local file. + self.settings.record_dependencies.add(attrs['uri']) + # alignment defaults: + if not 'align' in attrs: + # Set default align of image in a figure to 'center' + if isinstance(node.parent, nodes.figure): + attrs['align'] = 'center' + # query 'align-*' class argument + for cls in node['classes']: + if cls.startswith('align-'): + attrs['align'] = cls.split('-')[1] + # pre- and postfix (prefix inserted in reverse order) + pre = [] + post = [] + include_graphics_options = [] + is_inline = self.is_inline(node) + align_prepost = { + # key == (, ) + # By default latex aligns the bottom of an image. + (True, 'bottom'): ('', ''), + (True, 'middle'): (r'\raisebox{-0.5\height}{', '}'), + (True, 'top'): (r'\raisebox{-\height}{', '}'), + (False, 'center'): (r'\noindent\makebox[\textwidth][c]{', '}'), + (False, 'left'): (r'\noindent{', r'\hfill}'), + (False, 'right'): (r'\noindent{\hfill', '}'),} + if 'align' in attrs: + try: + pre.append(align_prepost[is_inline, attrs['align']][0]) + post.append(align_prepost[is_inline, attrs['align']][1]) + except KeyError: + pass # TODO: warn? + if 'height' in attrs: + include_graphics_options.append('height=%s' % + self.to_latex_length(attrs['height'])) + if 'scale' in attrs: + include_graphics_options.append('scale=%f' % + (attrs['scale'] / 100.0)) + ## # Could also be done with ``scale`` option to + ## # ``\includegraphics``; doing it this way for consistency. + ## pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,)) + ## post.append('}') + if 'width' in attrs: + include_graphics_options.append('width=%s' % + self.to_latex_length(attrs['width'])) + if not is_inline: + pre.append('\n') + post.append('\n') + pre.reverse() + self.out.extend(pre) + self.append_hypertargets(node) + options = '' + if include_graphics_options: + options = '[%s]' % (','.join(include_graphics_options)) + self.out.append('\\includegraphics%s{%s}' % (options, attrs['uri'])) + self.out.extend(post) + + def depart_image(self, node): + pass + + def visit_important(self, node): + self.visit_admonition(node) + + def depart_important(self, node): + self.depart_admonition() + + def visit_interpreted(self, node): + # @@@ Incomplete, pending a proper implementation on the + # Parser/Reader end. + self.visit_literal(node) + + def depart_interpreted(self, node): + self.depart_literal(node) + + def visit_legend(self, node): + self.fallbacks['legend'] = PreambleCmds.legend + self.out.append('\\begin{DUlegend}') + + def depart_legend(self, node): + self.out.append('\\end{DUlegend}\n') + + def visit_line(self, node): + self.out.append('\item[] ') + + def depart_line(self, node): + self.out.append('\n') + + def visit_line_block(self, node): + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['lineblock'] = PreambleCmds.lineblock + if isinstance(node.parent, nodes.line_block): + self.out.append('\\item[]\n' + '\\begin{DUlineblock}{\\DUlineblockindent}\n') + else: + self.out.append('\n\\begin{DUlineblock}{0em}\n') + + def depart_line_block(self, node): + self.out.append('\\end{DUlineblock}\n') + + def visit_list_item(self, node): + self.out.append('\n\\item ') + + def depart_list_item(self, node): + pass + + def visit_literal(self, node): + self.literal = True + self.out.append('\\texttt{') + if node['classes']: + self.visit_inline(node) + + def depart_literal(self, node): + self.literal = False + if node['classes']: + self.depart_inline(node) + self.out.append('}') + + # Literal blocks are used for '::'-prefixed literal-indented + # blocks of text, where the inline markup is not recognized, + # but are also the product of the "parsed-literal" directive, + # where the markup is respected. + # + # In both cases, we want to use a typewriter/monospaced typeface. + # For "real" literal-blocks, we can use \verbatim, while for all + # the others we must use \mbox or \alltt. + # + # We can distinguish between the two kinds by the number of + # siblings that compose this node: if it is composed by a + # single element, it's either + # * a real one, + # * a parsed-literal that does not contain any markup, or + # * a parsed-literal containing just one markup construct. + def is_plaintext(self, node): + """Check whether a node can be typeset verbatim""" + return (len(node) == 1) and isinstance(node[0], nodes.Text) + + def visit_literal_block(self, node): + """Render a literal block.""" + # environments and packages to typeset literal blocks + packages = {'listing': r'\usepackage{moreverb}', + 'lstlisting': r'\usepackage{listings}', + 'Verbatim': r'\usepackage{fancyvrb}', + # 'verbatim': '', + 'verbatimtab': r'\usepackage{moreverb}'} + + if not self.active_table.is_open(): + # no quote inside tables, to avoid vertical space between + # table border and literal block. + # BUG: fails if normal text preceeds the literal block. + self.out.append('%\n\\begin{quote}') + self.context.append('\n\\end{quote}\n') + else: + self.out.append('\n') + self.context.append('\n') + if self.literal_block_env != '' and self.is_plaintext(node): + self.requirements['literal_block'] = packages.get( + self.literal_block_env, '') + self.verbatim = True + self.out.append('\\begin{%s}%s\n' % (self.literal_block_env, + self.literal_block_options)) + else: + self.literal_block = 1 + self.insert_non_breaking_blanks = 1 + self.out.append('{\\ttfamily \\raggedright \\noindent\n') + + def depart_literal_block(self, node): + if self.verbatim: + self.out.append('\n\\end{%s}\n' % self.literal_block_env) + self.verbatim = False + else: + self.out.append('\n}') + self.insert_non_breaking_blanks = False + self.literal_block = False + self.out.append(self.context.pop()) + + ## def visit_meta(self, node): + ## self.out.append('[visit_meta]\n') + # TODO: set keywords for pdf? + # But: + # The reStructuredText "meta" directive creates a "pending" node, + # which contains knowledge that the embedded "meta" node can only + # be handled by HTML-compatible writers. The "pending" node is + # resolved by the docutils.transforms.components.Filter transform, + # which checks that the calling writer supports HTML; if it doesn't, + # the "pending" node (and enclosed "meta" node) is removed from the + # document. + # --- docutils/docs/peps/pep-0258.html#transformer + + ## def depart_meta(self, node): + ## self.out.append('[depart_meta]\n') + + def visit_note(self, node): + self.visit_admonition(node) + + def depart_note(self, node): + self.depart_admonition() + + def visit_option(self, node): + if self.context[-1]: + # this is not the first option + self.out.append(', ') + + def depart_option(self, node): + # flag tha the first option is done. + self.context[-1] += 1 + + def visit_option_argument(self, node): + """Append the delimiter betweeen an option and its argument to body.""" + self.out.append(node.get('delimiter', ' ')) + + def depart_option_argument(self, node): + pass + + def visit_option_group(self, node): + self.out.append('\n\\item[') + # flag for first option + self.context.append(0) + + def depart_option_group(self, node): + self.context.pop() # the flag + self.out.append('] ') + + def visit_option_list(self, node): + self.fallbacks['_providelength'] = PreambleCmds.providelength + self.fallbacks['optionlist'] = PreambleCmds.optionlist + self.out.append('%\n\\begin{DUoptionlist}\n') + + def depart_option_list(self, node): + self.out.append('\n\\end{DUoptionlist}\n') + + def visit_option_list_item(self, node): + pass + + def depart_option_list_item(self, node): + pass + + def visit_option_string(self, node): + ##self.out.append(self.starttag(node, 'span', '', CLASS='option')) + pass + + def depart_option_string(self, node): + ##self.out.append('') + pass + + def visit_organization(self, node): + self.visit_docinfo_item(node, 'organization') + + def depart_organization(self, node): + self.depart_docinfo_item(node) + + def visit_paragraph(self, node): + # no newline if the paragraph is first in a list item + if ((isinstance(node.parent, nodes.list_item) or + isinstance(node.parent, nodes.description)) and + node is node.parent[0]): + return + index = node.parent.index(node) + if (isinstance(node.parent, nodes.compound) and + index > 0 and + not isinstance(node.parent[index - 1], nodes.paragraph) and + not isinstance(node.parent[index - 1], nodes.compound)): + return + self.out.append('\n') + if node.get('ids'): + self.out += self.ids_to_labels(node) + ['\n'] + + def depart_paragraph(self, node): + self.out.append('\n') + + def visit_problematic(self, node): + self.requirements['color'] = PreambleCmds.color + self.out.append('%\n') + self.append_hypertargets(node) + self.out.append(r'\hyperlink{%s}{\textbf{\color{red}' % node['refid']) + + def depart_problematic(self, node): + self.out.append('}}') + + def visit_raw(self, node): + if not 'latex' in node.get('format', '').split(): + raise nodes.SkipNode + if node['classes']: + self.visit_inline(node) + # append "as-is" skipping any LaTeX-encoding + self.verbatim = True + + def depart_raw(self, node): + self.verbatim = False + if node['classes']: + self.depart_inline(node) + + def visit_reference(self, node): + # BUG: hash_char '#' is troublesome in LaTeX. + # mbox and other environments do not like the '#'. + hash_char = '\\#' + if 'refuri' in node: + href = node['refuri'].replace('#', hash_char) + self.out.append('\\href{%s}{' % href.replace('%', '\\%')) + return + if 'refid' in node: + href = node['refid'] + elif 'refname' in node: + href = self.document.nameids[node['refname']] + else: + raise AssertionError('Unknown reference.') + if not self.is_inline(node): + self.out.append('\n') + + self.out.append('\\hyperref[%s]{' % href) + if self._reference_label and 'refuri' not in node: + self.out.append('\\%s{%s}}' % (self._reference_label, + href.replace(hash_char, ''))) + raise nodes.SkipNode + + def depart_reference(self, node): + self.out.append('}') + if not self.is_inline(node): + self.out.append('\n') + + def visit_revision(self, node): + self.visit_docinfo_item(node, 'revision') + + def depart_revision(self, node): + self.depart_docinfo_item(node) + + def visit_section(self, node): + self.section_level += 1 + # Initialize counter for potential subsections: + self._section_number.append(0) + # Counter for this section's level (initialized by parent section): + self._section_number[self.section_level - 1] += 1 + + def depart_section(self, node): + # Remove counter for potential subsections: + self._section_number.pop() + self.section_level -= 1 + + def visit_sidebar(self, node): + self.requirements['color'] = PreambleCmds.color + self.fallbacks['sidebar'] = PreambleCmds.sidebar + self.out.append('\n\\DUsidebar{\n') + + def depart_sidebar(self, node): + self.out.append('}\n') + + attribution_formats = {'dash': ('---', ''), + 'parentheses': ('(', ')'), + 'parens': ('(', ')'), + 'none': ('', '')} + + def visit_attribution(self, node): + prefix, suffix = self.attribution_formats[self.settings.attribution] + self.out.append('\n\\begin{flushright}\n') + self.out.append(prefix) + self.context.append(suffix) + + def depart_attribution(self, node): + self.out.append(self.context.pop() + '\n') + self.out.append('\\end{flushright}\n') + + def visit_status(self, node): + self.visit_docinfo_item(node, 'status') + + def depart_status(self, node): + self.depart_docinfo_item(node) + + def visit_strong(self, node): + self.out.append('\\textbf{') + self.literal_block_stack.append('\\textbf{') + if node['classes']: + self.visit_inline(node) + + def depart_strong(self, node): + if node['classes']: + self.depart_inline(node) + self.out.append('}') + self.literal_block_stack.pop() + + def visit_substitution_definition(self, node): + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + self.unimplemented_visit(node) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.document): + self.subtitle += [self.encode(node.astext())] + self.subtitle_labels += self.ids_to_labels(node, set_anchor=False) + raise nodes.SkipNode + # section subtitle: "starred" (no number, not in ToC) + elif isinstance(node.parent, nodes.section): + self.out.append(r'\%s*{' % + self.d_class.section(self.section_level + 1)) + else: + self.fallbacks['subtitle'] = PreambleCmds.subtitle + self.out.append('\n\\DUsubtitle[%s]{' % node.parent.tagname) + + def depart_subtitle(self, node): + self.out.append('}\n') + + def visit_system_message(self, node): + self.requirements['color'] = PreambleCmds.color + self.fallbacks['error'] = PreambleCmds.error + self.visit_admonition(node) # error or warning + self.append_hypertargets(node) + try: + line = ', line~%s' % node['line'] + except KeyError: + line = '' + self.out.append('\n\n{\color{red}%s/%s} in \\texttt{%s}%s\n' % + (node['type'], node['level'], + self.encode(node['source']), line)) + if len(node['backrefs']) == 1: + self.out.append('\n\\hyperlink{%s}{' % node['backrefs'][0]) + self.context.append('}') + else: + backrefs = ['\\hyperlink{%s}{%d}' % (href, i+1) + for (i, href) in enumerate(node['backrefs'])] + self.context.append('backrefs: ' + ' '.join(backrefs)) + + def depart_system_message(self, node): + self.out.append(self.context.pop()) + self.depart_admonition() + + def visit_table(self, node): + self.requirements['table'] = PreambleCmds.table + if self.active_table.is_open(): + self.table_stack.append(self.active_table) + # nesting longtable does not work (e.g. 2007-04-18) + self.active_table = Table(self,'tabular',self.settings.table_style) + self.active_table.open() + for cls in node['classes']: + self.active_table.set_table_style(cls) + if self.active_table._table_style == 'booktabs': + self.requirements['booktabs'] = r'\usepackage{booktabs}' + self.out.append('\n' + self.active_table.get_opening()) + + def depart_table(self, node): + self.out.append(self.active_table.get_closing() + '\n') + self.active_table.close() + if len(self.table_stack)>0: + self.active_table = self.table_stack.pop() + else: + self.active_table.set_table_style(self.settings.table_style) + + def visit_target(self, node): + # Skip indirect targets: + if ('refuri' in node # external hyperlink + or 'refid' in node # resolved internal link + or 'refname' in node): # unresolved internal link + ## self.out.append('%% %s\n' % node) # for debugging + return + self.out.append('%\n') + # do we need an anchor (\phantomsection)? + set_anchor = not(isinstance(node.parent, nodes.caption) or + isinstance(node.parent, nodes.title)) + # TODO: where else can/must we omit the \phantomsection? + self.out += self.ids_to_labels(node, set_anchor) + + def depart_target(self, node): + pass + + def visit_tbody(self, node): + # BUG write preamble if not yet done (colspecs not []) + # for tables without heads. + if not self.active_table.get('preamble written'): + self.visit_thead(None) + self.depart_thead(None) + + def depart_tbody(self, node): + pass + + def visit_term(self, node): + """definition list term""" + # Commands with optional args inside an optional arg must be put + # in a group, e.g. ``\item[{\hyperref[label]{text}}]``. + self.out.append('\\item[{') + + def depart_term(self, node): + # \leavevmode results in a line break if the + # term is followed by an item list. + self.out.append('}] \leavevmode ') + + def visit_tgroup(self, node): + #self.out.append(self.starttag(node, 'colgroup')) + #self.context.append('\n') + pass + + def depart_tgroup(self, node): + pass + + _thead_depth = 0 + def thead_depth (self): + return self._thead_depth + + def visit_thead(self, node): + self._thead_depth += 1 + if 1 == self.thead_depth(): + self.out.append('{%s}\n' % self.active_table.get_colspecs()) + self.active_table.set('preamble written',1) + self.out.append(self.active_table.get_caption()) + self.out.extend(self.active_table.visit_thead()) + + def depart_thead(self, node): + if node is not None: + self.out.extend(self.active_table.depart_thead()) + if self.active_table.need_recurse(): + node.walkabout(self) + self._thead_depth -= 1 + + def visit_tip(self, node): + self.visit_admonition(node) + + def depart_tip(self, node): + self.depart_admonition() + + def bookmark(self, node): + """Return label and pdfbookmark string for titles.""" + result = [''] + if self.settings.sectnum_xform: # "starred" section cmd + # add to the toc and pdfbookmarks + section_name = self.d_class.section(max(self.section_level, 1)) + section_title = self.encode(node.astext()) + result.append(r'\phantomsection') + result.append(r'\addcontentsline{toc}{%s}{%s}' % + (section_name, section_title)) + result += self.ids_to_labels(node.parent, set_anchor=False) + return '%\n '.join(result) + '%\n' + + def visit_title(self, node): + """Append section and other titles.""" + # Document title + if node.parent.tagname == 'document': + title = self.encode(node.astext()) + self.title.append(title) + self.pdfinfo.append(' pdftitle={%s},' % title) + raise nodes.SkipNode + # Topic titles (topic, admonition, sidebar) + elif (isinstance(node.parent, nodes.topic) or + isinstance(node.parent, nodes.admonition) or + isinstance(node.parent, nodes.sidebar)): + self.fallbacks['title'] = PreambleCmds.title + classes = ','.join(node.parent['classes']) + if not classes: + classes = node.tagname + self.out.append('\\DUtitle[%s]{' % classes) + self.context.append('}\n') + # Table caption + elif isinstance(node.parent, nodes.table): + self.push_output_collector(self.active_table.caption) + self.context.append('') + # Section title + else: + self.out.append('\n\n') + self.out.append('%' + '_' * 75) + self.out.append('\n\n') + # + section_name = self.d_class.section(self.section_level) + # number sections? + if (self.settings.sectnum_xform # numbering by Docutils + or (self.section_level > len(self.d_class.sections))): + section_star = '*' + else: # LaTeX numbered sections + section_star = '' + self.out.append(r'\%s%s{' % (section_name, section_star)) + # System messages heading in red: + if ('system-messages' in node.parent['classes']): + self.requirements['color'] = PreambleCmds.color + self.out.append('\color{red}') + # label and ToC entry: + self.context.append(self.bookmark(node) + '}\n') + # MAYBE postfix paragraph and subparagraph with \leavemode to + # ensure floats stay in the section and text starts on a new line. + + def depart_title(self, node): + self.out.append(self.context.pop()) + if isinstance(node.parent, nodes.table): + self.pop_output_collector() + + def minitoc(self, title, depth): + """Generate a local table of contents with LaTeX package minitoc""" + section_name = self.d_class.section(self.section_level) + # name-prefix for current section level + minitoc_names = {'part': 'part', 'chapter': 'mini'} + if 'chapter' not in self.d_class.sections: + minitoc_names['section'] = 'sect' + try: + minitoc_name = minitoc_names[section_name] + except KeyError: # minitoc only supports part- and toplevel + warn = self.document.reporter.warning + warn('Skipping local ToC at %s level.\n' % section_name + + ' Feature not supported with option "use-latex-toc"') + return + # Requirements/Setup + self.requirements['minitoc'] = PreambleCmds.minitoc + self.requirements['minitoc-'+minitoc_name] = (r'\do%stoc' % + minitoc_name) + # depth: (Docutils defaults to unlimited depth) + maxdepth = len(self.d_class.sections) + self.requirements['minitoc-%s-depth' % minitoc_name] = ( + r'\mtcsetdepth{%stoc}{%d}' % (minitoc_name, maxdepth)) + # Process 'depth' argument (!Docutils stores a relative depth while + # minitoc expects an absolute depth!): + offset = {'sect': 1, 'mini': 0, 'part': 0} + if 'chapter' in self.d_class.sections: + offset['part'] = -1 + if depth: + self.out.append('\\setcounter{%stocdepth}{%d}' % + (minitoc_name, depth + offset[minitoc_name])) + # title: + self.out.append('\\mtcsettitle{%stoc}{%s}\n' % (minitoc_name, title)) + # the toc-generating command: + self.out.append('\\%stoc\n' % minitoc_name) + + def visit_topic(self, node): + # Topic nodes can be generic topic, abstract, dedication, or ToC. + # table of contents: + if 'contents' in node['classes']: + self.out.append('\n') + self.out += self.ids_to_labels(node) + # add contents to PDF bookmarks sidebar + if isinstance(node.next_node(), nodes.title): + self.out.append('\n\\pdfbookmark[%d]{%s}{%s}\n' % + (self.section_level+1, + node.next_node().astext(), + node.get('ids', ['contents'])[0] + )) + if self.use_latex_toc: + title = '' + if isinstance(node.next_node(), nodes.title): + title = self.encode(node.pop(0).astext()) + depth = node.get('depth', 0) + if 'local' in node['classes']: + self.minitoc(title, depth) + self.context.append('') + return + if depth: + self.out.append('\\setcounter{tocdepth}{%d}\n' % depth) + if title != 'Contents': + self.out.append('\\renewcommand{\\contentsname}{%s}\n' % + title) + self.out.append('\\tableofcontents\n\n') + self.has_latex_toc = True + else: # Docutils generated contents list + # set flag for visit_bullet_list() and visit_title() + self.is_toc_list = True + self.context.append('') + elif ('abstract' in node['classes'] and + self.settings.use_latex_abstract): + self.push_output_collector(self.abstract) + self.out.append('\\begin{abstract}') + self.context.append('\\end{abstract}\n') + if isinstance(node.next_node(), nodes.title): + node.pop(0) # LaTeX provides its own title + else: + self.fallbacks['topic'] = PreambleCmds.topic + # special topics: + if 'abstract' in node['classes']: + self.fallbacks['abstract'] = PreambleCmds.abstract + self.push_output_collector(self.abstract) + if 'dedication' in node['classes']: + self.fallbacks['dedication'] = PreambleCmds.dedication + self.push_output_collector(self.dedication) + self.out.append('\n\\DUtopic[%s]{\n' % ','.join(node['classes'])) + self.context.append('}\n') + + def depart_topic(self, node): + self.out.append(self.context.pop()) + self.is_toc_list = False + if ('abstract' in node['classes'] or + 'dedication' in node['classes']): + self.pop_output_collector() + + def visit_inline(self, node): # , i.e. custom roles + # insert fallback definition + self.fallbacks['inline'] = PreambleCmds.inline + self.out += [r'\DUrole{%s}{' % cls for cls in node['classes']] + self.context.append('}' * (len(node['classes']))) + + def depart_inline(self, node): + self.out.append(self.context.pop()) + + def visit_rubric(self, node): + self.fallbacks['rubric'] = PreambleCmds.rubric + self.out.append('\n\\DUrubric{') + self.context.append('}\n') + + def depart_rubric(self, node): + self.out.append(self.context.pop()) + + def visit_transition(self, node): + self.fallbacks['transition'] = PreambleCmds.transition + self.out.append('\n\n') + self.out.append('%' + '_' * 75 + '\n') + self.out.append(r'\DUtransition') + self.out.append('\n\n') + + def depart_transition(self, node): + pass + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version') + + def depart_version(self, node): + self.depart_docinfo_item(node) + + def visit_warning(self, node): + self.visit_admonition(node) + + def depart_warning(self, node): + self.depart_admonition() + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' % + node.__class__.__name__) + +# def unknown_visit(self, node): +# def default_visit(self, node): + +# vim: set ts=4 et ai : diff -Nru zope3-3.4.0/src/docutils/writers/latex2e/titlepage.tex zope3-3.5~bzr18/src/docutils/writers/latex2e/titlepage.tex --- zope3-3.4.0/src/docutils/writers/latex2e/titlepage.tex 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/latex2e/titlepage.tex 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,14 @@ +% generated by Docutils +$head_prefix$requirements +%%% User specified packages and stylesheets +$stylesheet +%%% Fallback definitions for Docutils-specific commands +$fallbacks$pdfsetup +%%% Body +\begin{document} +\begin{titlepage} +$body_pre_docinfo$docinfo$dedication$abstract +\thispagestyle{empty} +\end{titlepage} +$body +\end{document} diff -Nru zope3-3.4.0/src/docutils/writers/manpage.py zope3-3.5~bzr18/src/docutils/writers/manpage.py --- zope3-3.4.0/src/docutils/writers/manpage.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/manpage.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1104 @@ +# -*- coding: utf-8 -*- +# $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $ +# Author: Engelbert Gruber +# Copyright: This module is put into the public domain. + +""" +Simple man page writer for reStructuredText. + +Man pages (short for "manual pages") contain system documentation on unix-like +systems. The pages are grouped in numbered sections: + + 1 executable programs and shell commands + 2 system calls + 3 library functions + 4 special files + 5 file formats + 6 games + 7 miscellaneous + 8 system administration + +Man pages are written *troff*, a text file formatting system. + +See http://www.tldp.org/HOWTO/Man-Page for a start. + +Man pages have no subsection only parts. +Standard parts + + NAME , + SYNOPSIS , + DESCRIPTION , + OPTIONS , + FILES , + SEE ALSO , + BUGS , + +and + + AUTHOR . + +A unix-like system keeps an index of the DESCRIPTIONs, which is accesable +by the command whatis or apropos. + +""" + +__docformat__ = 'reStructuredText' + +import sys +import os +import time +import re +from types import ListType + +import docutils +from docutils import nodes, utils, writers, languages +import roman + +FIELD_LIST_INDENT = 7 +DEFINITION_LIST_INDENT = 7 +OPTION_LIST_INDENT = 7 +BLOCKQOUTE_INDENT = 3.5 + +# Define two macros so man/roff can calculate the +# indent/unindent margins by itself +MACRO_DEF = (r""". +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +""") + +class Writer(writers.Writer): + + supported = ('manpage') + """Formats this writer supports.""" + + output = None + """Final translated form of `document`.""" + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = Translator + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + self.output = visitor.astext() + + +class Table: + def __init__(self): + self._rows = [] + self._options = ['center', ] + self._tab_char = '\t' + self._coldefs = [] + def new_row(self): + self._rows.append([]) + def append_separator(self, separator): + """Append the separator for table head.""" + self._rows.append([separator]) + def append_cell(self, cell_lines): + """cell_lines is an array of lines""" + start = 0 + if len(cell_lines)>0 and cell_lines[0] == '.sp\n': + start = 1 + self._rows[-1].append(cell_lines[start:]) + if len(self._coldefs) < len(self._rows[-1]): + self._coldefs.append('l') + def _minimize_cell(self, cell_lines): + """Remove leading and trailing blank and ``.sp`` lines""" + while (cell_lines and cell_lines[0] in ('\n', '.sp\n')): + del cell_lines[0] + while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')): + del cell_lines[-1] + def as_list(self): + text = ['.TS\n'] + text.append(' '.join(self._options) + ';\n') + text.append('|%s|.\n' % ('|'.join(self._coldefs))) + for row in self._rows: + # row = array of cells. cell = array of lines. + text.append('_\n') # line above + text.append('T{\n') + for i in range(len(row)): + cell = row[i] + self._minimize_cell(cell) + text.extend(cell) + if not text[-1].endswith('\n'): + text[-1] += '\n' + if i < len(row)-1: + text.append('T}'+self._tab_char+'T{\n') + else: + text.append('T}\n') + text.append('_\n') + text.append('.TE\n') + return text + +class Translator(nodes.NodeVisitor): + """""" + + words_and_spaces = re.compile(r'\S+| +|\n') + document_start = """Man page generated from reStructeredText.""" + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.settings = settings = document.settings + lcode = settings.language_code + self.language = languages.get_language(lcode) + self.head = [] + self.body = [] + self.foot = [] + self.section_level = 0 + self.context = [] + self.topic_class = '' + self.colspecs = [] + self.compact_p = 1 + self.compact_simple = None + # the list style "*" bullet or "#" numbered + self._list_char = [] + # writing the header .TH and .SH NAME is postboned after + # docinfo. + self._docinfo = { + "title" : "", "title_upper": "", + "subtitle" : "", + "manual_section" : "", "manual_group" : "", + "author" : [], + "date" : "", + "copyright" : "", + "version" : "", + } + self._docinfo_keys = [] # a list to keep the sequence as in source. + self._docinfo_names = {} # to get name from text not normalized. + self._in_docinfo = None + self._active_table = None + self._in_literal = False + self.header_written = 0 + self._line_block = 0 + self.authors = [] + self.section_level = 0 + self._indent = [0] + # central definition of simple processing rules + # what to output on : visit, depart + # Do not use paragraph requests ``.PP`` because these set indentation. + # use ``.sp``. Remove superfluous ``.sp`` in ``astext``. + # + # Fonts are put on a stack, the top one is used. + # ``.ft P`` or ``\\fP`` pop from stack. + # ``B`` bold, ``I`` italic, ``R`` roman should be available. + # Hopefully ``C`` courier too. + self.defs = { + 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'), + 'definition_list_item' : ('.TP', ''), + 'field_name' : ('.TP\n.B ', '\n'), + 'literal' : ('\\fC', '\\fP'), + 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'), + + 'option_list_item' : ('.TP\n', ''), + + 'reference' : (r'\fI\%', r'\fP'), + 'emphasis': ('\\fI', '\\fP'), + 'strong' : ('\\fB', '\\fP'), + 'term' : ('\n.B ', '\n'), + 'title_reference' : ('\\fI', '\\fP'), + + 'topic-title' : ('.SS ', ), + 'sidebar-title' : ('.SS ', ), + + 'problematic' : ('\n.nf\n', '\n.fi\n'), + } + # NOTE dont specify the newline before a dot-command, but ensure + # it is there. + + def comment_begin(self, text): + """Return commented version of the passed text WITHOUT end of + line/comment.""" + prefix = '.\\" ' + out_text = ''.join( + [(prefix + in_line + '\n') + for in_line in text.split('\n')]) + return out_text + + def comment(self, text): + """Return commented version of the passed text.""" + return self.comment_begin(text)+'.\n' + + def ensure_eol(self): + """Ensure the last line in body is terminated by new line.""" + if self.body[-1][-1] != '\n': + self.body.append('\n') + + def astext(self): + """Return the final formatted document as a string.""" + if not self.header_written: + # ensure we get a ".TH" as viewers require it. + self.head.append(self.header()) + # filter body + for i in xrange(len(self.body)-1,0,-1): + # remove superfluous vertical gaps. + if self.body[i] == '.sp\n': + if self.body[i-1][:4] in ('.BI ','.IP '): + self.body[i] = '.\n' + elif (self.body[i-1][:3] == '.B ' and + self.body[i-2][:4] == '.TP\n'): + self.body[i] = '.\n' + elif (self.body[i-1] == '\n' and + self.body[i-2][0] != '.' and + (self.body[i-3][:7] == '.TP\n.B ' + or self.body[i-3][:4] == '\n.B ') + ): + self.body[i] = '.\n' + return ''.join(self.head + self.body + self.foot) + + def deunicode(self, text): + text = text.replace(u'\xa0', '\\ ') + text = text.replace(u'\u2020', '\\(dg') + return text + + def visit_Text(self, node): + text = node.astext() + text = text.replace('\\','\\e') + replace_pairs = [ + (u'-', ur'\-'), + (u'\'', ur'\(aq'), + (u'´', ur'\''), + (u'`', ur'\(ga'), + ] + for (in_char, out_markup) in replace_pairs: + text = text.replace(in_char, out_markup) + # unicode + text = self.deunicode(text) + if self._in_literal: + # prevent interpretation of "." at line start + if text[0] == '.': + text = '\\&' + text + text = text.replace('\n.', '\n\\&.') + self.body.append(text) + + def depart_Text(self, node): + pass + + def list_start(self, node): + class enum_char: + enum_style = { + 'bullet' : '\\(bu', + 'emdash' : '\\(em', + } + + def __init__(self, style): + self._style = style + if node.has_key('start'): + self._cnt = node['start'] - 1 + else: + self._cnt = 0 + self._indent = 2 + if style == 'arabic': + # indentation depends on number of childrens + # and start value. + self._indent = len(str(len(node.children))) + self._indent += len(str(self._cnt)) + 1 + elif style == 'loweralpha': + self._cnt += ord('a') - 1 + self._indent = 3 + elif style == 'upperalpha': + self._cnt += ord('A') - 1 + self._indent = 3 + elif style.endswith('roman'): + self._indent = 5 + + def next(self): + if self._style == 'bullet': + return self.enum_style[self._style] + elif self._style == 'emdash': + return self.enum_style[self._style] + self._cnt += 1 + # TODO add prefix postfix + if self._style == 'arabic': + return "%d." % self._cnt + elif self._style in ('loweralpha', 'upperalpha'): + return "%c." % self._cnt + elif self._style.endswith('roman'): + res = roman.toRoman(self._cnt) + '.' + if self._style.startswith('upper'): + return res.upper() + return res.lower() + else: + return "%d." % self._cnt + def get_width(self): + return self._indent + def __repr__(self): + return 'enum_style-%s' % list(self._style) + + if node.has_key('enumtype'): + self._list_char.append(enum_char(node['enumtype'])) + else: + self._list_char.append(enum_char('bullet')) + if len(self._list_char) > 1: + # indent nested lists + self.indent(self._list_char[-2].get_width()) + else: + self.indent(self._list_char[-1].get_width()) + + def list_end(self): + self.dedent() + self._list_char.pop() + + def header(self): + tmpl = (".TH %(title_upper)s %(manual_section)s" + " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n" + ".SH NAME\n" + "%(title)s \- %(subtitle)s\n") + return tmpl % self._docinfo + + def append_header(self): + """append header with .TH and .SH NAME""" + # NOTE before everything + # .TH title_upper section date source manual + if self.header_written: + return + self.body.append(self.header()) + self.body.append(MACRO_DEF) + self.header_written = 1 + + def visit_address(self, node): + self.visit_docinfo_item(node, 'address') + + def depart_address(self, node): + pass + + def visit_admonition(self, node, name=None): + if name: + self.body.append('.IP %s\n' % + self.language.labels.get(name, name)) + + def depart_admonition(self, node): + self.body.append('.RE\n') + + def visit_attention(self, node): + self.visit_admonition(node, 'attention') + + depart_attention = depart_admonition + + def visit_docinfo_item(self, node, name): + if name == 'author': + self._docinfo[name].append(node.astext()) + else: + self._docinfo[name] = node.astext() + self._docinfo_keys.append(name) + raise nodes.SkipNode + + def depart_docinfo_item(self, node): + pass + + def visit_author(self, node): + self.visit_docinfo_item(node, 'author') + + depart_author = depart_docinfo_item + + def visit_authors(self, node): + # _author is called anyway. + pass + + def depart_authors(self, node): + pass + + def visit_block_quote(self, node): + # BUG/HACK: indent alway uses the _last_ indention, + # thus we need two of them. + self.indent(BLOCKQOUTE_INDENT) + self.indent(0) + + def depart_block_quote(self, node): + self.dedent() + self.dedent() + + def visit_bullet_list(self, node): + self.list_start(node) + + def depart_bullet_list(self, node): + self.list_end() + + def visit_caption(self, node): + pass + + def depart_caption(self, node): + pass + + def visit_caution(self, node): + self.visit_admonition(node, 'caution') + + depart_caution = depart_admonition + + def visit_citation(self, node): + num,text = node.astext().split(None,1) + num = num.strip() + self.body.append('.IP [%s] 5\n' % num) + + def depart_citation(self, node): + pass + + def visit_citation_reference(self, node): + self.body.append('['+node.astext()+']') + raise nodes.SkipNode + + def visit_classifier(self, node): + pass + + def depart_classifier(self, node): + pass + + def visit_colspec(self, node): + self.colspecs.append(node) + + def depart_colspec(self, node): + pass + + def write_colspecs(self): + self.body.append("%s.\n" % ('L '*len(self.colspecs))) + + def visit_comment(self, node, + sub=re.compile('-(?=-)').sub): + self.body.append(self.comment(node.astext())) + raise nodes.SkipNode + + def visit_contact(self, node): + self.visit_docinfo_item(node, 'contact') + + depart_contact = depart_docinfo_item + + def visit_container(self, node): + pass + + def depart_container(self, node): + pass + + def visit_compound(self, node): + pass + + def depart_compound(self, node): + pass + + def visit_copyright(self, node): + self.visit_docinfo_item(node, 'copyright') + + def visit_danger(self, node): + self.visit_admonition(node, 'danger') + + depart_danger = depart_admonition + + def visit_date(self, node): + self.visit_docinfo_item(node, 'date') + + def visit_decoration(self, node): + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + pass + + def depart_definition(self, node): + pass + + def visit_definition_list(self, node): + self.indent(DEFINITION_LIST_INDENT) + + def depart_definition_list(self, node): + self.dedent() + + def visit_definition_list_item(self, node): + self.body.append(self.defs['definition_list_item'][0]) + + def depart_definition_list_item(self, node): + self.body.append(self.defs['definition_list_item'][1]) + + def visit_description(self, node): + pass + + def depart_description(self, node): + pass + + def visit_docinfo(self, node): + self._in_docinfo = 1 + + def depart_docinfo(self, node): + self._in_docinfo = None + # NOTE nothing should be written before this + self.append_header() + + def visit_doctest_block(self, node): + self.body.append(self.defs['literal_block'][0]) + self._in_literal = True + + def depart_doctest_block(self, node): + self._in_literal = False + self.body.append(self.defs['literal_block'][1]) + + def visit_document(self, node): + # no blank line between comment and header. + self.body.append(self.comment(self.document_start).rstrip()+'\n') + # writing header is postboned + self.header_written = 0 + + def depart_document(self, node): + if self._docinfo['author']: + self.body.append('.SH AUTHOR\n%s\n' + % ', '.join(self._docinfo['author'])) + skip = ('author', 'copyright', 'date', + 'manual_group', 'manual_section', + 'subtitle', + 'title', 'title_upper', 'version') + for name in self._docinfo_keys: + if name == 'address': + self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % ( + self.language.labels.get(name, name), + self.defs['indent'][0] % 0, + self.defs['indent'][0] % BLOCKQOUTE_INDENT, + self._docinfo[name], + self.defs['indent'][1], + self.defs['indent'][1], + ) ) + elif not name in skip: + if name in self._docinfo_names: + label = self._docinfo_names[name] + else: + label = self.language.labels.get(name, name) + self.body.append("\n%s: %s\n" % (label, self._docinfo[name]) ) + if self._docinfo['copyright']: + self.body.append('.SH COPYRIGHT\n%s\n' + % self._docinfo['copyright']) + self.body.append( self.comment( + 'Generated by docutils manpage writer.\n' ) ) + + def visit_emphasis(self, node): + self.body.append(self.defs['emphasis'][0]) + + def depart_emphasis(self, node): + self.body.append(self.defs['emphasis'][1]) + + def visit_entry(self, node): + # a cell in a table row + if 'morerows' in node: + self.document.reporter.warning('"table row spanning" not supported', + base_node=node) + if 'morecols' in node: + self.document.reporter.warning( + '"table cell spanning" not supported', base_node=node) + self.context.append(len(self.body)) + + def depart_entry(self, node): + start = self.context.pop() + self._active_table.append_cell(self.body[start:]) + del self.body[start:] + + def visit_enumerated_list(self, node): + self.list_start(node) + + def depart_enumerated_list(self, node): + self.list_end() + + def visit_error(self, node): + self.visit_admonition(node, 'error') + + depart_error = depart_admonition + + def visit_field(self, node): + pass + + def depart_field(self, node): + pass + + def visit_field_body(self, node): + if self._in_docinfo: + name_normalized = self._field_name.lower().replace(" ","_") + self._docinfo_names[name_normalized] = self._field_name + self.visit_docinfo_item(node, name_normalized) + raise nodes.SkipNode + + def depart_field_body(self, node): + pass + + def visit_field_list(self, node): + self.indent(FIELD_LIST_INDENT) + + def depart_field_list(self, node): + self.dedent() + + def visit_field_name(self, node): + if self._in_docinfo: + self._field_name = node.astext() + raise nodes.SkipNode + else: + self.body.append(self.defs['field_name'][0]) + + def depart_field_name(self, node): + self.body.append(self.defs['field_name'][1]) + + def visit_figure(self, node): + self.indent(2.5) + self.indent(0) + + def depart_figure(self, node): + self.dedent() + self.dedent() + + def visit_footer(self, node): + self.document.reporter.warning('"footer" not supported', + base_node=node) + + def depart_footer(self, node): + pass + + def visit_footnote(self, node): + num,text = node.astext().split(None,1) + num = num.strip() + self.body.append('.IP [%s] 5\n' % self.deunicode(num)) + + def depart_footnote(self, node): + pass + + def footnote_backrefs(self, node): + self.document.reporter.warning('"footnote_backrefs" not supported', + base_node=node) + + def visit_footnote_reference(self, node): + self.body.append('['+self.deunicode(node.astext())+']') + raise nodes.SkipNode + + def depart_footnote_reference(self, node): + pass + + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def visit_header(self, node): + raise NotImplementedError, node.astext() + + def depart_header(self, node): + pass + + def visit_hint(self, node): + self.visit_admonition(node, 'hint') + + depart_hint = depart_admonition + + def visit_subscript(self, node): + self.body.append('\\s-2\\d') + + def depart_subscript(self, node): + self.body.append('\\u\\s0') + + def visit_superscript(self, node): + self.body.append('\\s-2\\u') + + def depart_superscript(self, node): + self.body.append('\\d\\s0') + + def visit_attribution(self, node): + self.body.append('\\(em ') + + def depart_attribution(self, node): + self.body.append('\n') + + def visit_image(self, node): + self.document.reporter.warning('"image" not supported', + base_node=node) + text = [] + if 'alt' in node.attributes: + text.append(node.attributes['alt']) + if 'uri' in node.attributes: + text.append(node.attributes['uri']) + self.body.append('[image: %s]\n' % ('/'.join(text))) + raise nodes.SkipNode + + def visit_important(self, node): + self.visit_admonition(node, 'important') + + depart_important = depart_admonition + + def visit_label(self, node): + # footnote and citation + if (isinstance(node.parent, nodes.footnote) + or isinstance(node.parent, nodes.citation)): + raise nodes.SkipNode + self.document.reporter.warning('"unsupported "label"', + base_node=node) + self.body.append('[') + + def depart_label(self, node): + self.body.append(']\n') + + def visit_legend(self, node): + pass + + def depart_legend(self, node): + pass + + # WHAT should we use .INDENT, .UNINDENT ? + def visit_line_block(self, node): + self._line_block += 1 + if self._line_block == 1: + self.body.append('.nf\n') + else: + self.body.append('.in +2\n') + + def depart_line_block(self, node): + self._line_block -= 1 + if self._line_block == 0: + self.body.append('.fi\n') + self.body.append('.sp\n') + else: + self.body.append('.in -2\n') + + def visit_line(self, node): + pass + + def depart_line(self, node): + self.body.append('\n') + + def visit_list_item(self, node): + # man 7 man argues to use ".IP" instead of ".TP" + self.body.append('.IP %s %d\n' % ( + self._list_char[-1].next(), + self._list_char[-1].get_width(),) ) + + def depart_list_item(self, node): + pass + + def visit_literal(self, node): + self.body.append(self.defs['literal'][0]) + + def depart_literal(self, node): + self.body.append(self.defs['literal'][1]) + + def visit_literal_block(self, node): + self.body.append(self.defs['literal_block'][0]) + self._in_literal = True + + def depart_literal_block(self, node): + self._in_literal = False + self.body.append(self.defs['literal_block'][1]) + + def visit_meta(self, node): + raise NotImplementedError, node.astext() + + def depart_meta(self, node): + pass + + def visit_note(self, node): + self.visit_admonition(node, 'note') + + depart_note = depart_admonition + + def indent(self, by=0.5): + # if we are in a section ".SH" there already is a .RS + step = self._indent[-1] + self._indent.append(by) + self.body.append(self.defs['indent'][0] % step) + + def dedent(self): + self._indent.pop() + self.body.append(self.defs['indent'][1]) + + def visit_option_list(self, node): + self.indent(OPTION_LIST_INDENT) + + def depart_option_list(self, node): + self.dedent() + + def visit_option_list_item(self, node): + # one item of the list + self.body.append(self.defs['option_list_item'][0]) + + def depart_option_list_item(self, node): + self.body.append(self.defs['option_list_item'][1]) + + def visit_option_group(self, node): + # as one option could have several forms it is a group + # options without parameter bold only, .B, -v + # options with parameter bold italic, .BI, -f file + # + # we do not know if .B or .BI + self.context.append('.B') # blind guess + self.context.append(len(self.body)) # to be able to insert later + self.context.append(0) # option counter + + def depart_option_group(self, node): + self.context.pop() # the counter + start_position = self.context.pop() + text = self.body[start_position:] + del self.body[start_position:] + self.body.append('%s%s\n' % (self.context.pop(), ''.join(text))) + + def visit_option(self, node): + # each form of the option will be presented separately + if self.context[-1]>0: + self.body.append(', ') + if self.context[-3] == '.BI': + self.body.append('\\') + self.body.append(' ') + + def depart_option(self, node): + self.context[-1] += 1 + + def visit_option_string(self, node): + # do not know if .B or .BI + pass + + def depart_option_string(self, node): + pass + + def visit_option_argument(self, node): + self.context[-3] = '.BI' # bold/italic alternate + if node['delimiter'] != ' ': + self.body.append('\\fB%s ' % node['delimiter'] ) + elif self.body[len(self.body)-1].endswith('='): + # a blank only means no blank in output, just changing font + self.body.append(' ') + else: + # blank backslash blank, switch font then a blank + self.body.append(' \\ ') + + def depart_option_argument(self, node): + pass + + def visit_organization(self, node): + self.visit_docinfo_item(node, 'organization') + + def depart_organization(self, node): + pass + + def visit_paragraph(self, node): + # ``.PP`` : Start standard indented paragraph. + # ``.LP`` : Start block paragraph, all except the first. + # ``.P [type]`` : Start paragraph type. + # NOTE dont use paragraph starts because they reset indentation. + # ``.sp`` is only vertical space + self.ensure_eol() + self.body.append('.sp\n') + + def depart_paragraph(self, node): + self.body.append('\n') + + def visit_problematic(self, node): + self.body.append(self.defs['problematic'][0]) + + def depart_problematic(self, node): + self.body.append(self.defs['problematic'][1]) + + def visit_raw(self, node): + if node.get('format') == 'manpage': + self.body.append(node.astext() + "\n") + # Keep non-manpage raw text out of output: + raise nodes.SkipNode + + def visit_reference(self, node): + """E.g. link or email address.""" + self.body.append(self.defs['reference'][0]) + + def depart_reference(self, node): + self.body.append(self.defs['reference'][1]) + + def visit_revision(self, node): + self.visit_docinfo_item(node, 'revision') + + depart_revision = depart_docinfo_item + + def visit_row(self, node): + self._active_table.new_row() + + def depart_row(self, node): + pass + + def visit_section(self, node): + self.section_level += 1 + + def depart_section(self, node): + self.section_level -= 1 + + def visit_status(self, node): + self.visit_docinfo_item(node, 'status') + + depart_status = depart_docinfo_item + + def visit_strong(self, node): + self.body.append(self.defs['strong'][0]) + + def depart_strong(self, node): + self.body.append(self.defs['strong'][1]) + + def visit_substitution_definition(self, node): + """Internal only.""" + raise nodes.SkipNode + + def visit_substitution_reference(self, node): + self.document.reporter.warning('"substitution_reference" not supported', + base_node=node) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.sidebar): + self.body.append(self.defs['strong'][0]) + elif isinstance(node.parent, nodes.document): + self.visit_docinfo_item(node, 'subtitle') + elif isinstance(node.parent, nodes.section): + self.body.append(self.defs['strong'][0]) + + def depart_subtitle(self, node): + # document subtitle calls SkipNode + self.body.append(self.defs['strong'][1]+'\n.PP\n') + + def visit_system_message(self, node): + # TODO add report_level + #if node['level'] < self.document.reporter['writer'].report_level: + # Level is too low to display: + # raise nodes.SkipNode + attr = {} + backref_text = '' + if node.hasattr('id'): + attr['name'] = node['id'] + if node.hasattr('line'): + line = ', line %s' % node['line'] + else: + line = '' + self.body.append('.IP "System Message: %s/%s (%s:%s)"\n' + % (node['type'], node['level'], node['source'], line)) + + def depart_system_message(self, node): + pass + + def visit_table(self, node): + self._active_table = Table() + + def depart_table(self, node): + self.ensure_eol() + self.body.extend(self._active_table.as_list()) + self._active_table = None + + def visit_target(self, node): + # targets are in-document hyper targets, without any use for man-pages. + raise nodes.SkipNode + + def visit_tbody(self, node): + pass + + def depart_tbody(self, node): + pass + + def visit_term(self, node): + self.body.append(self.defs['term'][0]) + + def depart_term(self, node): + self.body.append(self.defs['term'][1]) + + def visit_tgroup(self, node): + pass + + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + # MAYBE double line '=' + pass + + def depart_thead(self, node): + # MAYBE double line '=' + pass + + def visit_tip(self, node): + self.visit_admonition(node, 'tip') + + depart_tip = depart_admonition + + def visit_title(self, node): + if isinstance(node.parent, nodes.topic): + self.body.append(self.defs['topic-title'][0]) + elif isinstance(node.parent, nodes.sidebar): + self.body.append(self.defs['sidebar-title'][0]) + elif isinstance(node.parent, nodes.admonition): + self.body.append('.IP "') + elif self.section_level == 0: + self._docinfo['title'] = node.astext() + # document title for .TH + self._docinfo['title_upper'] = node.astext().upper() + raise nodes.SkipNode + elif self.section_level == 1: + self.body.append('.SH ') + else: + self.body.append('.SS ') + + def depart_title(self, node): + if isinstance(node.parent, nodes.admonition): + self.body.append('"') + self.body.append('\n') + + def visit_title_reference(self, node): + """inline citation reference""" + self.body.append(self.defs['title_reference'][0]) + + def depart_title_reference(self, node): + self.body.append(self.defs['title_reference'][1]) + + def visit_topic(self, node): + pass + + def depart_topic(self, node): + pass + + def visit_sidebar(self, node): + pass + + def depart_sidebar(self, node): + pass + + def visit_rubric(self, node): + pass + + def depart_rubric(self, node): + pass + + def visit_transition(self, node): + # .PP Begin a new paragraph and reset prevailing indent. + # .sp N leaves N lines of blank space. + # .ce centers the next line + self.body.append('\n.sp\n.ce\n----\n') + + def depart_transition(self, node): + self.body.append('\n.ce 0\n.sp\n') + + def visit_version(self, node): + self.visit_docinfo_item(node, 'version') + + def visit_warning(self, node): + self.visit_admonition(node, 'warning') + + depart_warning = depart_admonition + + def unimplemented_visit(self, node): + raise NotImplementedError('visiting unimplemented node type: %s' + % node.__class__.__name__) + +# vim: set fileencoding=utf-8 et ts=4 ai : diff -Nru zope3-3.4.0/src/docutils/writers/newlatex2e/base.tex zope3-3.5~bzr18/src/docutils/writers/newlatex2e/base.tex --- zope3-3.4.0/src/docutils/writers/newlatex2e/base.tex 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/newlatex2e/base.tex 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1176 @@ +% System stylesheet for the new LaTeX writer, newlatex2e. + +% Major parts of the rendering are done in this stylesheet and not in the +% Python module. + +% For development notes, see notes.txt. + +% User documentation (in the stylesheet for now; that may change though): + +% Naming conventions: +% All uppercase letters in macro names have a specific meaning. +% \D...: All macros introduced by the Docutils LaTeX writer start with "D". +% \DS: Setup function (called at the bottom of this stylesheet). +% \DN{}: Handler for Docutils document tree node `node`; called by +% the Python module. +% \DEV: External variable, set by the Python module. +% \DEC: External command. It is called by the Python module and must be +% defined in this stylesheet. +% \DNA{}{}{}{}{}: +% Attribute handler for `attribute` set on nodes of type `nodename`. +% See below for a discussion of attribute handlers. +% \DA{}{}{}{}{}: +% Attribute handler for all `attribute`. Called only when no specific +% \DNA handler is defined. +% \DNC{}: +% Handler for `class`, when set on nodes of type `nodename`. +% \DC{}: +% Handler for `class`. Called only when no specific \DNC +% handler is defined. +% \D: Generic variable or function. + +% Attribute handlers: +% TODO + +% --------------------------------------------------------------------------- + +% Having to intersperse code with \makeatletter-\makeatother pairs is very +% annoying, so we call \makeatletter at the top and \makeatother at the +% bottom. Just be aware that you cannot use "@" as a text character inside +% this stylesheet. +\makeatletter + +% Print-mode (as opposed to online mode e.g. with Adobe Reader). +% This causes for example blue hyperlinks. +\providecommand{\Dprinting}{false} + +% \DSearly is called right after \documentclass. +\providecommand{\DSearly}{} +% \DSlate is called at the end of the stylesheet (right before the document +% tree). +\providecommand{\DSlate}{} + +% Use the KOMA script article class. +\providecommand{\Ddocumentclass}{scrartcl} +\providecommand{\Ddocumentoptions}{a4paper} +\providecommand{\DSdocumentclass}{ + \documentclass[\Ddocumentoptions]{\Ddocumentclass} } + +% Todo: This should be movable to the bottom, but it isn't as long as +% we use \usepackage commands at the top level of this stylesheet +% (which we shouldn't). +\DSdocumentclass + +\providecommand{\DSpackages}{ + % Load miscellaneous packages. + % Note 1: Many of the packages loaded here are used throughout this stylesheet. + % If one of these packages does not work on your system or in your scenario, + % please let us know, so we can consider making the package optional. + % Note 2: It would appear cleaner to load packages where they are used. + % However, since using a wrong package loading order can lead to *very* + % subtle bugs, we centralize the loading of most packages here. + \DSfontencoding % load font encoding packages + \DSlanguage % load babel + % Using \ifthenelse conditionals. + \usepackage{ifthen} % before hyperref (really!) + % There is not support for *not* using hyperref because it's used in many + % places. If this is a problem (e.g. because hyperref doesn't work on your + % system), please let us know. + \usepackage[colorlinks=false,pdfborder={0 0 0}]{hyperref} + % Get color, e.g. for links and system messages. + \usepackage{color} + % Get \textnhtt macro (non-hyphenating type writer). + \usepackage{hyphenat} + % We use longtable to create tables. + \usepackage{longtable} + % Images. + \usepackage{graphicx} + % These packages might be useful (some just add magic pixie dust), so + % evaluate them: + %\usepackage{fixmath} + %\usepackage{amsmath} + % Add some missing symbols like \textonehalf. + \usepackage{textcomp} +} + +\providecommand{\DSfontencoding}{ + % Set up font encoding. Called by \DSpackages. + % AE is a T1 emulation. It provides mostly the same characters and + % features as T1-encoded fonts but doesn't use bitmap fonts (which are + % unsuitable for online reading and subtle for printers). + \usepackage{ae} + % Provide the characters not contained in AE from EC bitmap fonts. + \usepackage{aecompl} + % Guillemets ("<<", ">>") in AE. + \usepackage{aeguill} +} + +\providecommand{\DSsymbols}{% + % Fix up symbols. + % The Euro symbol in Computer Modern looks, um, funny. Let's get a + % proper Euro symbol. + \usepackage{eurosym}% + \renewcommand{\texteuro}{\euro}% +} + +% Taken from +% +% and modified. Used with permission. +\providecommand{\Dprovidelength}[2]{% + \begingroup% + \escapechar\m@ne% + \xdef\@gtempa{{\string#1}}% + \endgroup% + \expandafter\@ifundefined\@gtempa% + {\newlength{#1}\setlength{#1}{#2}}% + {}% +} + +\providecommand{\Dprovidecounter}[2]{% + % Like \newcounter except that it doesn't crash if the counter + % already exists. + \@ifundefined{c@#1}{\newcounter{#1}\setcounter{#1}{#2}}{} +} + +\Dprovidelength{\Dboxparindent}{\parindent} + +\providecommand{\Dmakebox}[1]{% + % Make a centered, frameless box. Useful e.g. for block quotes. + % Do not use minipages here, but create pseudo-lists to allow + % page-breaking. (Don't use KOMA-script's addmargin environment + % because it messes up bullet lists.) + \Dmakelistenvironment{}{}{% + \setlength{\parskip}{0pt}% + \setlength{\parindent}{\Dboxparindent}% + \item{#1}% + }% +} + +\providecommand{\Dmakefbox}[1]{% + % Make a centered, framed box. Useful e.g. for admonitions. + \vspace{0.4\baselineskip}% + \begin{center}% + \fbox{% + \begin{minipage}[t]{0.9\linewidth}% + \setlength{\parindent}{\Dboxparindent}% + #1% + \end{minipage}% + }% + \end{center}% + \vspace{0.4\baselineskip}% +} + +% We do not currently recognize the difference between an end-sentence and a +% mid-sentence period (". " vs. ". " in plain text). So \frenchspacing is +% appropriate. +\providecommand{\DSfrenchspacing}{\frenchspacing} + + +\Dprovidelength{\Dblocklevelvspace}{% + % Space between block-level elements other than paragraphs. + 0.7\baselineskip plus 0.3\baselineskip minus 0.2\baselineskip% +} +\providecommand{\DECauxiliaryspace}{% + \ifthenelse{\equal{\Dneedvspace}{true}}{\vspace{\Dblocklevelvspace}}{}% + \par\noindent% +} +\providecommand{\DECparagraphspace}{\par} +\providecommand{\Dneedvspace}{true} + +\providecommand{\DSlanguage}{% + % Set up babel. + \usepackage[\DEVlanguagebabel]{babel} +} + +\providecommand{\Difdefined}[3]{\@ifundefined{#1}{#3}{#2}} + +% Handler for 'classes' attribute (called for each class attribute). +\providecommand{\DAclasses}[5]{% + % Dispatch to \DNC. + \Difdefined{DN#4C#3}{% + % Pass only contents, nothing else! + \csname DN#4C#3\endcsname{#5}% + }{% + % Otherwise, dispatch to \DC. + \Difdefined{DC#3}{% + \csname DC#3\endcsname{#5}% + }{% + #5% + }% + }% +} + +\providecommand{\DECattr}[5]{% + % Global attribute dispatcher, called inside the document tree. + % Parameters: + % 1. Attribute number. + % 2. Attribute name. + % 3. Attribute value. + % 4. Node name. + % 5. Node contents. + \Difdefined{DN#4A#2}{% + % Dispatch to \DNA. + \csname DN#4A#2\endcsname{#1}{#2}{#3}{#4}{#5}% + }{\Difdefined{DA#2}{% + % Otherwise dispatch to \DA. + \csname DA#2\endcsname{#1}{#2}{#3}{#4}{#5}% + }{% + % Otherwise simply run the contents without calling a handler. + #5% + }}% +} + +% ---------- Link handling ---------- +% Targets and references. + +\providecommand{\Draisedlink}[1]{% + % Anchors are placed on the base line by default. This is a bad thing for + % inline context, so we raise the anchor (normally by \baselineskip). + \Hy@raisedlink{#1}% +} + +% References. +% We're assuming here that the "refid" and "refuri" attributes occur +% only in inline context (in TextElements). +\providecommand{\DArefid}[5]{% + \ifthenelse{\equal{#4}{reference}}{% + \Dexplicitreference{\##3}{#5}% + }{% + % If this is not a target node (targets with refids are + % uninteresting and should be silently dropped). + \ifthenelse{\not\equal{#4}{target}}{% + % If this is a footnote reference, call special macro. + \ifthenelse{\equal{#4}{footnotereference}}{% + \Dimplicitfootnotereference{\##3}{#5}% + }{% + \ifthenelse{\equal{#4}{citationreference}}{% + \Dimplicitcitationreference{\##3}{#5}% + }{% + \Dimplicitreference{\##3}{#5}% + }% + }% + }{}% + }% +} +\providecommand{\DArefuri}[5]{% + \ifthenelse{\equal{#4}{target}}{% + % The node name is 'target', so this is a hyperlink target, like this: + % .. _mytarget: URI + % Hyperlink targets are ignored because they are invisible. + }{% + % If a non-target node has a refuri attribute, it must be an explicit URI + % reference (i.e. node name is 'reference'). + \Durireference{#3}{#5}% + }% +} +% Targets. +\providecommand{\DAids}[5]{% + \label{#3}% + \ifthenelse{\equal{#4}{footnotereference}}{% + {% + \renewcommand{\HyperRaiseLinkDefault}{% + % Dirty hack to make backrefs to footnote references work. + % For some reason, \baselineskip is 0pt in fn references. + 0.5\Doriginalbaselineskip% + }% + \Draisedlink{\hypertarget{#3}{}}#5% + }% + }{% + \Draisedlink{\hypertarget{#3}{}}#5% + }% +} +\providecommand{\Dimplicitreference}[2]{% + % Create implicit reference to ID. Implicit references occur + % e.g. in TOC-backlinks of section titles. Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{#2}% +} +\providecommand{\Dimplicitfootnotereference}[2]{% + % Ditto, but for the special case of footnotes. + % We want them to be rendered like explicit references. + \Dexplicitreference{#1}{#2}% +} +\providecommand{\Dimplicitcitationreference}[2]{% + % Ditto for citation references. + \Dimplicitfootnotereference{#1}{#2}% +} +\providecommand{\Dcolorexplicitreference}{% + \ifthenelse{\equal{\Dprinting}{true}}{\color{black}}{\color{blue}}% +} +\providecommand{\Dexplicitreference}[2]{% + % Create explicit reference to ID, e.g. created with "foo_". + % Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{{\Dcolorexplicitreference#2}}% +} +\providecommand{\Dcolorurireference}{\Dcolorexplicitreference} +\providecommand{\Durireference}[2]{% + % Create reference to URI. Parameters: + % 1. Target. + % 2. Link text. + \href{#1}{{\Dcolorurireference#2}}% +} + +\Dprovidecounter{Dpdfbookmarkid}{0}% +\providecommand{\Dpdfbookmark}[1]{% + % Temporarily decrement Desctionlevel counter. + \addtocounter{Dsectionlevel}{-1}% + %\typeout{\arabic{Dsectionlevel}}% + %\typeout{#1}% + %\typeout{docutils\roman{Dpdfbookmarkid}}% + %\typeout{}% + \pdfbookmark[\arabic{Dsectionlevel}]{#1}{docutils\arabic{Dpdfbookmarkid}}% + \addtocounter{Dsectionlevel}{1}% + \addtocounter{Dpdfbookmarkid}{1}% +} +% ---------- End of Link Handling ---------- + +\providecommand{\DNparagraph}[1]{% + \ifthenelse{\equal{\DEVparagraphindented}{true}}{\indent}{\noindent}% + #1% +} +\providecommand{\Dformatboxtitle}[1]{{\Large\textbf{#1}}} +\providecommand{\Dformatboxsubtitle}[1]{{\large\textbf{#1}}} +\providecommand{\Dtopictitle}[1]{% + \Difinsidetoc{\vspace{1em}\par}{}% + \noindent\Dformatboxtitle{#1}% + \ifthenelse{\equal{\DEVhassubtitle}{false}}{\vspace{1em}}{\vspace{0.5em}}% + \par% +} +\providecommand{\Dadmonitiontitle}[1]{% + \Dtopictitle{#1}% +} +\providecommand{\Dtopicsubtitle}[1]{% + \noindent\Dformatboxsubtitle{#1}% + \vspace{1em}% + \par% +} +\providecommand{\Dsidebartitle}[1]{\Dtopictitle{#1}} +\providecommand{\Dsidebarsubtitle}[1]{\Dtopicsubtitle{#1}} +\providecommand{\Ddocumenttitle}[1]{% + \begin{center}{\Huge#1}\end{center}% + \ifthenelse{\equal{\DEVhassubtitle}{true}}{\vspace{0.1cm}}{\vspace{1cm}}% +} +\providecommand{\Ddocumentsubtitle}[1]{% + \begin{center}{\huge#1}\end{center}% + \vspace{1cm}% +} +% Can be overwritten by user stylesheet. +\providecommand{\Dformatsectiontitle}[1]{#1} +\providecommand{\Dformatsectionsubtitle}[1]{\Dformatsectiontitle{#1}} +\providecommand{\Dbookmarksectiontitle}[1]{% + % Return text suitable for use in \section*, \subsection*, etc., + % containing a PDF bookmark. Parameter: The title (as node tree). + \Draisedlink{\Dpdfbookmark{\DEVtitleastext}}% + #1% +} +\providecommand{\Dsectiontitlehook}[1]{#1} +\providecommand{\Dsectiontitle}[1]{% + \Dsectiontitlehook{% + \Ddispatchsectiontitle{\Dbookmarksectiontitle{\Dformatsectiontitle{#1}}}% + }% +} +\providecommand{\Ddispatchsectiontitle}[1]{% + \@ifundefined{Dsectiontitle\roman{Dsectionlevel}}{% + \Ddeepsectiontitle{#1}% + }{% + \csname Dsectiontitle\roman{Dsectionlevel}\endcsname{#1}% + }% +} +\providecommand{\Ddispatchsectionsubtitle}[1]{% + \Ddispatchsectiontitle{#1}% +} +\providecommand{\Dsectiontitlei}[1]{\section*{#1}} +\providecommand{\Dsectiontitleii}[1]{\subsection*{#1}} +\providecommand{\Ddeepsectiontitle}[1]{% + % Anything below \subsubsection (like \paragraph or \subparagraph) + % is useless because it uses the same font. The only way to + % (visually) distinguish such deeply nested sections is to use + % section numbering. + \subsubsection*{#1}% +} +\providecommand{\Dsectionsubtitlehook}[1]{#1} +\Dprovidelength{\Dsectionsubtitleraisedistance}{0.7em} +\providecommand{\Dsectionsubtitlescaling}{0.85} +\providecommand{\Dsectionsubtitle}[1]{% + \Dsectionsubtitlehook{% + % Move the subtitle nearer to the title. + \vspace{-\Dsectionsubtitleraisedistance}% + % Don't create a PDF bookmark. + \Ddispatchsectionsubtitle{% + \Dformatsectionsubtitle{\scalebox{\Dsectionsubtitlescaling}{#1}}% + }% + }% +} +\providecommand{\DNtitle}[1]{% + % Dispatch to \Dtitle. + \csname D\DEVparent title\endcsname{#1}% +} +\providecommand{\DNsubtitle}[1]{% + % Dispatch to \Dsubtitle. + \csname D\DEVparent subtitle\endcsname{#1}% +} + +\providecommand{\DNliteralblock}[1]{% + \Dmakelistenvironment{}{% + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + \setlength{\leftmargin}{0pt}% + }{}% + \setlength{\rightmargin}{0pt}% + }{% + \raggedright\item\noindent\nohyphens{\textnhtt{#1\Dfinalstrut}}% + }% +} +\providecommand{\DNdoctestblock}[1]{\DNliteralblock{#1}} +\providecommand{\DNliteral}[1]{\textnhtt{#1}} +\providecommand{\DNemphasis}[1]{\emph{#1}} +\providecommand{\DNstrong}[1]{\textbf{#1}} +\providecommand{\DECvisitdocument}{\begin{document}\noindent} +\providecommand{\DECdepartdocument}{\end{document}} +\providecommand{\DNtopic}[1]{% + \ifthenelse{\equal{\DEVcurrentNtopicAcontents}{1}}{% + \addtocounter{Dtoclevel}{1}% + \par\noindent% + #1% + \addtocounter{Dtoclevel}{-1}% + }{% + \par\noindent% + \Dmakebox{#1}% + }% +} +\providecommand{\DNadmonition}[1]{% + \DNtopic{#1}% +} +\providecommand{\Dformatrubric}[1]{\textbf{#1}} +\Dprovidelength{\Dprerubricspace}{0.3em} +\providecommand{\DNrubric}[1]{% + \vspace{\Dprerubricspace}\par\noindent\Dformatrubric{#1}\par% +} + +\providecommand{\Dbullet}{} +\providecommand{\DECsetbullet}[1]{\renewcommand{\Dbullet}{#1}} +\providecommand{\DNbulletlist}[1]{% + \Difinsidetoc{% + \Dtocbulletlist{#1}% + }{% + \Dmakelistenvironment{\Dbullet}{}{#1}% + }% +} +% Todo: So what on earth is @pnumwidth? +\renewcommand{\@pnumwidth}{2.2em} +\providecommand{\DNlistitem}[1]{% + \Difinsidetoc{% + \ifthenelse{\equal{\theDtoclevel}{1}\and\equal{\Dlocaltoc}{false}}{% + {% + \par\addvspace{1em}\noindent% + \sectfont% + #1\hfill\pageref{\DEVcurrentNlistitemAtocrefid}% + }% + }{% + \@dottedtocline{0}{\Dtocindent}{0em}{#1}{% + \pageref{\DEVcurrentNlistitemAtocrefid}% + }% + }% + }{% + \item{#1}% + }% +} +\providecommand{\DNenumeratedlist}[1]{#1} +\Dprovidecounter{Dsectionlevel}{0} +\providecommand{\Dvisitsectionhook}{} +\providecommand{\Ddepartsectionhook}{} +\providecommand{\DECvisitsection}{% + \addtocounter{Dsectionlevel}{1}% + \Dvisitsectionhook% +} +\providecommand{\DECdepartsection}{% + \Ddepartsectionhook% + \addtocounter{Dsectionlevel}{-1}% +} + +% Using \_ will cause hyphenation after _ even in \textnhtt-typewriter +% because the hyphenat package redefines \_. So we use +% \textunderscore here. +\providecommand{\DECtextunderscore}{\textunderscore} + +\providecommand{\Dtextinlineliteralfirstspace}{{ }} +\providecommand{\Dtextinlineliteralsecondspace}{{~}} + +\Dprovidelength{\Dlistspacing}{0.8\baselineskip} + +\providecommand{\Dsetlistrightmargin}{% + \ifthenelse{\lengthtest{\linewidth>12em}}{% + % Equal margins. + \setlength{\rightmargin}{\leftmargin}% + }{% + % If the line is narrower than 10em, we don't remove any further + % space from the right. + \setlength{\rightmargin}{0pt}% + }% +} +\providecommand{\Dresetlistdepth}{false} +\Dprovidelength{\Doriginallabelsep}{\labelsep} +\providecommand{\Dmakelistenvironment}[3]{% + % Make list environment with support for unlimited nesting and with + % reasonable default lengths. Parameters: + % 1. Label (same as in list environment). + % 2. Spacing (same as in list environment). + % 3. List contents (contents of list environment). + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + % Unfortunately, vertical spacing doesn't work correctly when + % using lists inside tabular environments, so we use a minipage. + \begin{minipage}[t]{\linewidth}% + }{}% + {% + \renewcommand{\Dneedvspace}{false}% + % \parsep0.5\baselineskip + \renewcommand{\Dresetlistdepth}{false}% + \ifnum \@listdepth>5% + \protect\renewcommand{\Dresetlistdepth}{true}% + \@listdepth=5% + \fi% + \begin{list}{% + #1% + }{% + \setlength{\itemsep}{0pt}% + \setlength{\partopsep}{0pt}% + \setlength{\topsep}{0pt}% + % List should take 90% of total width. + \setlength{\leftmargin}{0.05\linewidth}% + \ifthenelse{\lengthtest{\leftmargin<1.8em}}{% + \setlength{\leftmargin}{1.8em}% + }{}% + \setlength{\labelsep}{\Doriginallabelsep}% + \Dsetlistrightmargin% + #2% + }{% + #3% + }% + \end{list}% + \ifthenelse{\equal{\Dresetlistdepth}{true}}{\@listdepth=5}{}% + }% + \ifthenelse{\equal{\Dinsidetabular}{true}}{\end{minipage}}{}% +} +\providecommand{\Dfinalstrut}{\@finalstrut\@arstrutbox} +\providecommand{\DAlastitem}[5]{#5\Dfinalstrut} + +\Dprovidelength{\Ditemsep}{0pt} +\providecommand{\DECmakeenumeratedlist}[6]{% + % Make enumerated list. + % Parameters: + % - prefix + % - type (\arabic, \roman, ...) + % - suffix + % - suggested counter name + % - start number - 1 + % - list contents + \newcounter{#4}% + \Dmakelistenvironment{#1#2{#4}#3}{% + % Use as much space as needed for the label. + \setlength{\labelwidth}{10em}% + % Reserve enough space so that the label doesn't go beyond the + % left margin of preceding paragraphs. Like that: + % + % A paragraph. + % + % 1. First item. + \setlength{\leftmargin}{2.5em}% + \Dsetlistrightmargin% + \setlength{\itemsep}{\Ditemsep}% + % Use counter recommended by Python module. + \usecounter{#4}% + % Set start value. + \addtocounter{#4}{#5}% + }{% + % The list contents. + #6% + }% +} + + +% Single quote in literal mode. \textquotesingle from package +% textcomp has wrong width when using package ae, so we use a normal +% single curly quote here. +\providecommand{\DECtextliteralsinglequote}{'} + + +% "Tabular lists" are field lists and options lists (not definition +% lists because there the term always appears on its own line). We'll +% use the terminology of field lists now ("field", "field name", +% "field body"), but the same is also analogously applicable to option +% lists. +% +% We want these lists to be breakable across pages. We cannot +% automatically get the narrowest possible size for the left column +% (i.e. the field names or option groups) because tabularx does not +% support multi-page tables, ltxtable needs to have the table in an +% external file and we don't want to clutter the user's directories +% with auxiliary files created by the filecontents environment, and +% ltablex is not included in teTeX. +% +% Thus we set a fixed length for the left column and use list +% environments. This also has the nice side effect that breaking is +% now possible anywhere, not just between fields. +% +% Note that we are creating a distinct list environment for each +% field. There is no macro for a whole tabular list! +\Dprovidelength{\Dtabularlistfieldnamewidth}{6em} +\Dprovidelength{\Dtabularlistfieldnamesep}{0.5em} +\providecommand{\Dinsidetabular}{false} +\providecommand{\Dsavefieldname}{} +\providecommand{\Dsavefieldbody}{} +\Dprovidelength{\Dusedfieldnamewidth}{0pt} +\Dprovidelength{\Drealfieldnamewidth}{0pt} +\providecommand{\Dtabularlistfieldname}[1]{\renewcommand{\Dsavefieldname}{#1}} +\providecommand{\Dtabularlistfieldbody}[1]{\renewcommand{\Dsavefieldbody}{#1}} +\Dprovidelength{\Dparskiptemp}{0pt} +\providecommand{\Dtabularlistfield}[1]{% + {% + % This only saves field name and field body in \Dsavefieldname and + % \Dsavefieldbody, resp. It does not insert any text into the + % document. + #1% + % Recalculate the real field name width everytime we encounter a + % tabular list field because it may have been changed using a + % "raw" node. + \setlength{\Drealfieldnamewidth}{\Dtabularlistfieldnamewidth}% + \addtolength{\Drealfieldnamewidth}{\Dtabularlistfieldnamesep}% + \Dmakelistenvironment{% + \makebox[\Drealfieldnamewidth][l]{\Dsavefieldname}% + }{% + \setlength{\labelwidth}{\Drealfieldnamewidth}% + \setlength{\leftmargin}{\Drealfieldnamewidth}% + \setlength{\rightmargin}{0pt}% + \setlength{\labelsep}{0pt}% + }{% + \item% + \settowidth{\Dusedfieldnamewidth}{\Dsavefieldname}% + \setlength{\Dparskiptemp}{\parskip}% + \ifthenelse{% + \lengthtest{\Dusedfieldnamewidth>\Dtabularlistfieldnamewidth}% + }{% + \mbox{}\par% + \setlength{\parskip}{0pt}% + }{}% + \Dsavefieldbody% + \setlength{\parskip}{\Dparskiptemp}% + %XXX Why did we need this? + %\@finalstrut\@arstrutbox% + }% + \par% + }% +} + +\providecommand{\Dformatfieldname}[1]{\textbf{#1:}} +\providecommand{\DNfieldlist}[1]{#1} +\providecommand{\DNfield}[1]{\Dtabularlistfield{#1}} +\providecommand{\DNfieldname}[1]{% + \Dtabularlistfieldname{% + \Dformatfieldname{#1}% + }% +} +\providecommand{\DNfieldbody}[1]{\Dtabularlistfieldbody{#1}} + +\providecommand{\Dformatoptiongroup}[1]{% + % Format option group, e.g. "-f file, --input file". + \texttt{#1}% +} +\providecommand{\Dformatoption}[1]{% + % Format option, e.g. "-f file". + % Put into mbox to avoid line-breaking at spaces. + \mbox{#1}% +} +\providecommand{\Dformatoptionstring}[1]{% + % Format option string, e.g. "-f". + #1% +} +\providecommand{\Dformatoptionargument}[1]{% + % Format option argument, e.g. "file". + \textsl{#1}% +} +\providecommand{\Dformatoptiondescription}[1]{% + % Format option description, e.g. + % "\DNparagraph{Read input data from file.}" + #1% +} +\providecommand{\DNoptionlist}[1]{#1} +\providecommand{\Doptiongroupjoiner}{,{ }} +\providecommand{\Disfirstoption}{% + % Auxiliary macro indicating if a given option is the first child + % of its option group (if it's not, it has to preceded by + % \Doptiongroupjoiner). + false% +} +\providecommand{\DNoptionlistitem}[1]{% + \Dtabularlistfield{#1}% +} +\providecommand{\DNoptiongroup}[1]{% + \renewcommand{\Disfirstoption}{true}% + \Dtabularlistfieldname{\Dformatoptiongroup{#1}}% +} +\providecommand{\DNoption}[1]{% + % If this is not the first option in this option group, add a + % joiner. + \ifthenelse{\equal{\Disfirstoption}{true}}{% + \renewcommand{\Disfirstoption}{false}% + }{% + \Doptiongroupjoiner% + }% + \Dformatoption{#1}% +} +\providecommand{\DNoptionstring}[1]{\Dformatoptionstring{#1}} +\providecommand{\DNoptionargument}[1]{{ }\Dformatoptionargument{#1}} +\providecommand{\DNdescription}[1]{% + \Dtabularlistfieldbody{\Dformatoptiondescription{#1}}% +} + +\providecommand{\DNdefinitionlist}[1]{% + \begin{description}% + \parskip0pt% + #1% + \end{description}% +} +\providecommand{\DNdefinitionlistitem}[1]{% + % LaTeX expects the label in square brackets; we provide an empty + % label. + \item[]#1% +} +\providecommand{\Dformatterm}[1]{#1} +\providecommand{\DNterm}[1]{\hspace{-5pt}\Dformatterm{#1}} +% I'm still not sure what's the best rendering for classifiers. The +% colon syntax is used by reStructuredText, so it's at least WYSIWYG. +% Use slanted text because italic would cause too much emphasis. +\providecommand{\Dformatclassifier}[1]{\textsl{#1}} +\providecommand{\DNclassifier}[1]{~:~\Dformatclassifier{#1}} +\providecommand{\Dformatdefinition}[1]{#1} +\providecommand{\DNdefinition}[1]{\par\Dformatdefinition{#1}} + +\providecommand{\Dlineblockindentation}{2.5em} +\providecommand{\DNlineblock}[1]{% + \Dmakelistenvironment{}{% + \ifthenelse{\equal{\DEVparent}{lineblock}}{% + % Parent is a line block, so indent. + \setlength{\leftmargin}{\Dlineblockindentation}% + }{% + % At top level; don't indent. + \setlength{\leftmargin}{0pt}% + }% + \setlength{\rightmargin}{0pt}% + \setlength{\parsep}{0pt}% + }{% + #1% + }% +} +\providecommand{\DNline}[1]{\item#1} + +\providecommand{\DNtransition}{% + \raisebox{0.25em}{\parbox{\linewidth}{\hspace*{\fill}\hrulefill\hrulefill\hspace*{\fill}}}% +} + +\providecommand{\Dformatblockquote}[1]{% + % Format contents of block quote. + % This occurs in block-level context, so we cannot use \textsl. + {\slshape#1}% +} +\providecommand{\Dformatattribution}[1]{---\textup{#1}} +\providecommand{\DNblockquote}[1]{% + \Dmakebox{% + \Dformatblockquote{#1} + }% +} +\providecommand{\DNattribution}[1]{% + \par% + \begin{flushright}\Dformatattribution{#1}\end{flushright}% +} + + +% Sidebars: +% Vertical and horizontal margins. +\Dprovidelength{\Dsidebarvmargin}{0.5em} +\Dprovidelength{\Dsidebarhmargin}{1em} +% Padding (space between contents and frame). +\Dprovidelength{\Dsidebarpadding}{1em} +% Frame width. +\Dprovidelength{\Dsidebarframewidth}{2\fboxrule} +% Position ("l" or "r"). +\providecommand{\Dsidebarposition}{r} +% Width. +\Dprovidelength{\Dsidebarwidth}{0.45\linewidth} +\providecommand{\DNsidebar}[1]{ + \begin{minipage}[t]{\Dsidebarwidth}% + % Doing this with nested minipages is ugly, but I haven't found + % another way to place vertical space before and after the fbox. + \vspace{\Dsidebarvmargin}% + {% + \setlength{\fboxrule}{\Dsidebarframewidth}% + \setlength{\fboxsep}{\Dsidebarpadding}% + \fbox{% + \begin{minipage}[t]{\linewidth}% + \setlength{\parindent}{\Dboxparindent}% + #1% + \end{minipage}% + }% + }% + \vspace{\Dsidebarvmargin}% + \end{minipage}% +} + + +% Citations and footnotes. +\providecommand{\Dformatfootnote}[1]{% + % Format footnote. + {% + \footnotesize#1% + % \par is necessary for LaTeX to adjust baselineskip to the + % changed font size. + \par% + }% +} +\providecommand{\Dformatcitation}[1]{\Dformatfootnote{#1}} +\Dprovidelength{\Doriginalbaselineskip}{0pt} +\providecommand{\DNfootnotereference}[1]{% + {% + % \baselineskip is 0pt in \textsuperscript, so we save it here. + \setlength{\Doriginalbaselineskip}{\baselineskip}% + \textsuperscript{#1}% + }% +} +\providecommand{\DNcitationreference}[1]{{[}#1{]}} +\Dprovidelength{\Dfootnotesep}{3.5pt} +\providecommand{\Dsetfootnotespacing}{% + % Spacing commands executed at the beginning of footnotes. + \setlength{\parindent}{0pt}% + \hspace{1em}% +} +\providecommand{\DNfootnote}[1]{% + % See ltfloat.dtx for details. + {% + \insert\footins{% + % BUG: This is too small if the user adds + % \onehalfspacing or \doublespace. + \vspace{\Dfootnotesep}% + \Dsetfootnotespacing% + \Dformatfootnote{#1}% + }% + }% +} +\providecommand{\DNcitation}[1]{\DNfootnote{#1}} +\providecommand{\Dformatfootnotelabel}[1]{% + % Keep \footnotesize in footnote labels (\textsuperscript would + % reduce the font size even more). + \textsuperscript{\footnotesize#1{ }}% +} +\providecommand{\Dformatcitationlabel}[1]{{[}#1{]}{ }} +\providecommand{\Dformatmultiplebackrefs}[1]{% + % If in printing mode, do not write out multiple backrefs. + \ifthenelse{\equal{\Dprinting}{true}}{}{\textsl{#1}}% +} +\providecommand{\Dthislabel}{} +\providecommand{\DNlabel}[1]{% + % Footnote or citatation label. + \renewcommand{\Dthislabel}{#1}% + \ifthenelse{\not\equal{\DEVsinglebackref}{}}{% + \let\Doriginallabel=\Dthislabel% + \def\Dthislabel{% + \Dsinglefootnotebacklink{\DEVsinglebackref}{\Doriginallabel}% + }% + }{}% + \ifthenelse{\equal{\DEVparent}{footnote}}{% + % Footnote label. + \Dformatfootnotelabel{\Dthislabel}% + }{% + \ifthenelse{\equal{\DEVparent}{citation}}{% + % Citation label. + \Dformatcitationlabel{\Dthislabel}% + }{}% + }% + % If there are multiple backrefs, add them now. + \Dformatmultiplebackrefs{\DEVmultiplebackrefs}% +} +\providecommand{\Dsinglefootnotebacklink}[2]{% + % Create normal backlink of a footnote label. Parameters: + % 1. ID. + % 2. Link text. + % Treat like a footnote reference. + \Dimplicitfootnotereference{\##1}{#2}% +} +\providecommand{\DECmultifootnotebacklink}[2]{% + % Create generated backlink, as in (1, 2). Parameters: + % 1. ID. + % 2. Link text. + % Treat like a footnote reference. + \Dimplicitfootnotereference{\##1}{#2}% +} +\providecommand{\Dsinglecitationbacklink}[2]{\Dsinglefootnotebacklink{#1}{#2}} +\providecommand{\DECmulticitationbacklink}[2]{\DECmultifootnotebacklink{#1}{#2}} + + +\providecommand{\DECmaketable}[2]{% + % Make table. Parameters: + % 1. Table spec (like "|p|p|"). + % 2. Table contents. + {% + \ifthenelse{\equal{\Dinsidetabular}{true}}{% + % Inside longtable; we cannot have nested longtables. + \begin{tabular}{#1}% + \hline% + #2% + \end{tabular}% + }{% + \renewcommand{\Dinsidetabular}{true}% + \begin{longtable}{#1}% + \hline% + #2% + \end{longtable}% + }% + }% +} +\providecommand{\DNthead}[1]{% + #1% + \endhead% +} +\providecommand{\DNrow}[1]{% + #1\tabularnewline% + \hline% +} +\providecommand{\Dinsidemulticolumn}{false} +\providecommand{\Dcompensatingmulticol}[3]{% + \multicolumn{#1}{#2}{% + {% + \renewcommand{\Dinsidemulticolumn}{true}% + % Compensate for weird missing vertical space at top of paragraph. + \raisebox{-2.5pt}{#3}% + }% + }% +} +\providecommand{\DECcolspan}[2]{% + % Take care of the morecols attribute (but incremented by 1). + &% + \Dcompensatingmulticol{#1}{l|}{#2}% +} +\providecommand{\DECcolspanleft}[2]{% + % Like \Dmorecols, but called for the leftmost entries in a table + % row. + \Dcompensatingmulticol{#1}{|l|}{#2}% +} +\providecommand{\DECsubsequententry}[1]{% + % +} +\providecommand{\DNentry}[1]{% + % The following sequence adds minimal vertical space above the top + % lines of the first cell paragraph, so that vertical space is + % balanced at the top and bottom of table cells. + \ifthenelse{\equal{\Dinsidemulticolumn}{false}}{% + \vspace{-1em}\vspace{-\parskip}\par% + }{}% + #1% + % No need to add an ampersand ("&"); that's done by \DECsubsequententry. +} +\providecommand{\DAtableheaderentry}[5]{\Dformattableheaderentry{#5}} +\providecommand{\Dformattableheaderentry}[1]{{\bfseries#1}} + + +\providecommand{\DNsystemmessage}[1]{% + {% + \ifthenelse{\equal{\Dprinting}{false}}{\color{red}}{}% + \bfseries% + #1% + }% +} + + +\providecommand{\Dinsidehalign}{false} +\newsavebox{\Dalignedimagebox} +\Dprovidelength{\Dalignedimagewidth}{0pt} +\providecommand{\Dhalign}[2]{% + % Horizontally align the contents to the left or right so that the + % text flows around it. + % Parameters: + % 1. l or r + % 2. Contents. + \renewcommand{\Dinsidehalign}{true}% + % For some obscure reason \parpic consumes some vertical space. + \vspace{-3pt}% + % Now we do something *really* ugly, but this enables us to wrap the + % image in a minipage while still allowing tight frames when + % class=border (see \DNimageCborder). + \sbox{\Dalignedimagebox}{#2}% + \settowidth{\Dalignedimagewidth}{\usebox{\Dalignedimagebox}}% + \parpic[#1]{% + \begin{minipage}[b]{\Dalignedimagewidth}% + % Compensate for previously added space, but not entirely. + \vspace*{2.0pt}% + \vspace*{\Dfloatimagetopmargin}% + \usebox{\Dalignedimagebox}% + \vspace*{1.5pt}% + \vspace*{\Dfloatimagebottommargin}% + \end{minipage}% + }% + \renewcommand{\Dinsidehalign}{false}% +} + + +% Maximum width of an image. +\providecommand{\Dimagemaxwidth}{\linewidth} +\providecommand{\Dfloatimagemaxwidth}{0.5\linewidth} +% Auxiliary variable. +\Dprovidelength{\Dcurrentimagewidth}{0pt} +\providecommand{\DNimageAalign}[5]{% + \ifthenelse{\equal{#3}{left}}{% + \Dhalign{l}{#5}% + }{% + \ifthenelse{\equal{#3}{right}}{% + \Dhalign{r}{#5}% + }{% + \ifthenelse{\equal{#3}{center}}{% + % Text floating around centered figures is a bad idea. Thus + % we use a center environment. Note that no extra space is + % added by the writer, so the space added by the center + % environment is fine. + \begin{center}#5\end{center}% + }{% + #5% + }% + }% + }% +} +% Base path for images. +\providecommand{\Dimagebase}{} +% Auxiliary command. Current image path. +\providecommand{\Dimagepath}{} +\providecommand{\DNimageAuri}[5]{% + % Insert image. We treat the URI like a path here. + \renewcommand{\Dimagepath}{\Dimagebase#3}% + \Difdefined{DcurrentNimageAwidth}{% + \Dwidthimage{\DEVcurrentNimageAwidth}{\Dimagepath}% + }{% + \Dsimpleimage{\Dimagepath}% + }% +} +\Dprovidelength{\Dfloatimagevmargin}{0pt} +\providecommand{\Dfloatimagetopmargin}{\Dfloatimagevmargin} +\providecommand{\Dfloatimagebottommargin}{\Dfloatimagevmargin} +\providecommand{\Dwidthimage}[2]{% + % Image with specified width. + % Parameters: + % 1. Image width. + % 2. Image path. + % Need to make bottom-alignment dependent on align attribute (add + % functional test first). Need to observe height attribute. + %\begin{minipage}[b]{#1}% + \includegraphics[width=#1,height=\textheight,keepaspectratio]{#2}% + %\end{minipage}% +} +\providecommand{\Dcurrentimagemaxwidth}{} +\providecommand{\Dsimpleimage}[1]{% + % Insert image, without much parametrization. + \settowidth{\Dcurrentimagewidth}{\includegraphics{#1}}% + \ifthenelse{\equal{\Dinsidehalign}{true}}{% + \renewcommand{\Dcurrentimagemaxwidth}{\Dfloatimagemaxwidth}% + }{% + \renewcommand{\Dcurrentimagemaxwidth}{\Dimagemaxwidth}% + }% + \ifthenelse{\lengthtest{\Dcurrentimagewidth>\Dcurrentimagemaxwidth}}{% + \Dwidthimage{\Dcurrentimagemaxwidth}{#1}% + }{% + \Dwidthimage{\Dcurrentimagewidth}{#1}% + }% +} +\providecommand{\Dwidthimage}[2]{% + % Image with specified width. + % Parameters: + % 1. Image width. + % 2. Image path. + \Dwidthimage{#1}{#2}% +} + +% Figures. +\providecommand{\DNfigureAalign}[5]{% + % Hack to make it work Right Now. + %\def\DEVcurrentNimageAwidth{\DEVcurrentNfigureAwidth}% + % + %\def\DEVcurrentNimageAwidth{\linewidth}% + \DNimageAalign{#1}{#2}{#3}{#4}{% + \begin{minipage}[b]{0.4\linewidth}#5\end{minipage}}% + %\let\DEVcurrentNimageAwidth=\relax% + % + %\let\DEVcurrentNimageAwidth=\relax% +} +\providecommand{\DNcaption}[1]{\par\noindent{\slshape#1}} +\providecommand{\DNlegend}[1]{\DECauxiliaryspace#1} + +\providecommand{\DCborder}[1]{\fbox{#1}} +% No padding between image and border. +\providecommand{\DNimageCborder}[1]{\frame{#1}} + + +% Need to replace with language-specific stuff. Maybe look at +% csquotes.sty and ask the author for permission to use parts of it. +\providecommand{\DECtextleftdblquote}{``} +\providecommand{\DECtextrightdblquote}{''} + +% Table of contents: +\Dprovidelength{\Dtocininitialsectnumwidth}{2.4em} +\Dprovidelength{\Dtocadditionalsectnumwidth}{0.7em} +% Level inside a table of contents. While this is at -1, we are not +% inside a TOC. +\Dprovidecounter{Dtoclevel}{-1}% +\providecommand{\Dlocaltoc}{false}% +\providecommand{\DNtopicClocal}[1]{% + \renewcommand{\Dlocaltoc}{true}% + \addtolength{\Dtocsectnumwidth}{2\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocindent}{-2\Dtocadditionalsectnumwidth}% + #1% + \addtolength{\Dtocindent}{2\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocsectnumwidth}{-2\Dtocadditionalsectnumwidth}% + \renewcommand{\Dlocaltoc}{false}% +} +\Dprovidelength{\Dtocindent}{0pt}% +\Dprovidelength{\Dtocsectnumwidth}{\Dtocininitialsectnumwidth} +% Compensate for one additional TOC indentation space so that the +% top-level is unindented. +\addtolength{\Dtocsectnumwidth}{-\Dtocadditionalsectnumwidth} +\addtolength{\Dtocindent}{-\Dtocsectnumwidth} +\providecommand{\Difinsidetoc}[2]{% + \ifthenelse{\not\equal{\theDtoclevel}{-1}}{#1}{#2}% +} +\providecommand{\DNgeneratedCsectnum}[1]{% + \Difinsidetoc{% + % Section number inside TOC. + \makebox[\Dtocsectnumwidth][l]{#1}% + }{% + % Section number inside section title. + #1\quad% + }% +} +\providecommand{\Dtocbulletlist}[1]{% + \addtocounter{Dtoclevel}{1}% + \addtolength{\Dtocindent}{\Dtocsectnumwidth}% + \addtolength{\Dtocsectnumwidth}{\Dtocadditionalsectnumwidth}% + #1% + \addtolength{\Dtocsectnumwidth}{-\Dtocadditionalsectnumwidth}% + \addtolength{\Dtocindent}{-\Dtocsectnumwidth}% + \addtocounter{Dtoclevel}{-1}% +} + + +% For \DECpixelunit, the length value is pre-multiplied with 0.75, so by +% specifying "pt" we get the same notion of "pixel" as graphicx. +\providecommand{\DECpixelunit}{pt} +% Normally lengths are relative to the current linewidth. +\providecommand{\DECrelativeunit}{\linewidth} + + +% ACTION: These commands actually *do* something. +% Ultimately, everything should be done here, and no active content should be +% above (not even \usepackage). + +\DSearly +\DSpackages +\DSfrenchspacing +\DSsymbols +\DSlate + +\makeatother diff -Nru zope3-3.4.0/src/docutils/writers/newlatex2e/__init__.py zope3-3.5~bzr18/src/docutils/writers/newlatex2e/__init__.py --- zope3-3.4.0/src/docutils/writers/newlatex2e/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/newlatex2e/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,836 @@ +# $Id: __init__.py 5738 2008-11-30 08:59:04Z grubert $ +# Author: Lea Wiemann +# Copyright: This module has been placed in the public domain. + +""" +LaTeX2e document tree Writer. +""" + +# Thanks to Engelbert Gruber and various contributors for the original +# LaTeX writer, some code and many ideas of which have been used for +# this writer. + +__docformat__ = 'reStructuredText' + + +import re +import os.path + +import docutils +from docutils import nodes, writers, utils +from docutils.writers.newlatex2e import unicode_map +from docutils.transforms import writer_aux + + +class Writer(writers.Writer): + + supported = ('newlatex', 'newlatex2e') + """Formats this writer supports.""" + + default_stylesheet = 'base.tex' + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + settings_spec = ( + 'LaTeX-Specific Options', + 'Note that this LaTeX writer is still EXPERIMENTAL and not ' + 'feature-complete. ', + (('Specify a stylesheet file. The path is used verbatim to include ' + 'the file. Overrides --stylesheet-path.', + ['--stylesheet'], + {'default': '', 'metavar': '', + 'overrides': 'stylesheet_path'}), + ('Specify a stylesheet file, relative to the current working ' + 'directory. Overrides --stylesheet. Default: "%s"' + % default_stylesheet_path, + ['--stylesheet-path'], + {'metavar': '', 'overrides': 'stylesheet', + 'default': default_stylesheet_path}), + ('Specify a user stylesheet file. See --stylesheet.', + ['--user-stylesheet'], + {'default': '', 'metavar': '', + 'overrides': 'user_stylesheet_path'}), + ('Specify a user stylesheet file. See --stylesheet-path.', + ['--user-stylesheet-path'], + {'metavar': '', 'overrides': 'user_stylesheet'}) + ),) + + settings_defaults = { + # Many Unicode characters are provided by unicode_map.py, so + # we can default to latin-1. + 'output_encoding': 'latin-1', + 'output_encoding_error_handler': 'strict', + # Since we are using superscript footnotes, it is necessary to + # trim whitespace in front of footnote references. + 'trim_footnote_reference_space': 1, + # Currently unsupported: + 'docinfo_xform': 0, + # During development: + 'traceback': 1 + } + + relative_path_settings = ('stylesheet_path', 'user_stylesheet_path') + + config_section = 'newlatex2e writer' + config_section_dependencies = ('writers',) + + output = None + """Final translated form of `document`.""" + + def get_transforms(self): + return writers.Writer.get_transforms(self) + [ + writer_aux.Compound, writer_aux.Admonitions] + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = LaTeXTranslator + + def translate(self): + visitor = self.translator_class(self.document) + self.document.walkabout(visitor) + assert not visitor.context, 'context not empty: %s' % visitor.context + self.output = visitor.astext() + self.head = visitor.header + self.body = visitor.body + + +class LaTeXException(Exception): + """ + Exception base class to for exceptions which influence the + automatic generation of LaTeX code. + """ + + +class SkipAttrParentLaTeX(LaTeXException): + """ + Do not generate ``\DECattr`` and ``\renewcommand{\DEVparent}{...}`` for this + node. + + To be raised from ``before_...`` methods. + """ + + +class SkipParentLaTeX(LaTeXException): + """ + Do not generate ``\renewcommand{\DEVparent}{...}`` for this node. + + To be raised from ``before_...`` methods. + """ + + +class LaTeXTranslator(nodes.SparseNodeVisitor): + + # Country code by a.schlock. + # Partly manually converted from iso and babel stuff. + iso639_to_babel = { + 'no': 'norsk', # added by hand + 'gd': 'scottish', # added by hand + 'sl': 'slovenian', + 'af': 'afrikaans', + 'bg': 'bulgarian', + 'br': 'breton', + 'ca': 'catalan', + 'cs': 'czech', + 'cy': 'welsh', + 'da': 'danish', + 'fr': 'french', + # french, francais, canadien, acadian + 'de': 'ngerman', + # ngerman, naustrian, german, germanb, austrian + 'el': 'greek', + 'en': 'english', + # english, USenglish, american, UKenglish, british, canadian + 'eo': 'esperanto', + 'es': 'spanish', + 'et': 'estonian', + 'eu': 'basque', + 'fi': 'finnish', + 'ga': 'irish', + 'gl': 'galician', + 'he': 'hebrew', + 'hr': 'croatian', + 'hu': 'hungarian', + 'is': 'icelandic', + 'it': 'italian', + 'la': 'latin', + 'nl': 'dutch', + 'pl': 'polish', + 'pt': 'portuguese', + 'ro': 'romanian', + 'ru': 'russian', + 'sk': 'slovak', + 'sr': 'serbian', + 'sv': 'swedish', + 'tr': 'turkish', + 'uk': 'ukrainian' + } + + # Start with left double quote. + left_quote = 1 + + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.settings = document.settings + self.header = [] + self.body = [] + self.context = [] + self.stylesheet_path = utils.get_stylesheet_reference( + self.settings, os.path.join(os.getcwd(), 'dummy')) + if self.stylesheet_path: + self.settings.record_dependencies.add(self.stylesheet_path) + # This ugly hack will be cleaned up when refactoring the + # stylesheet mess. + self.settings.stylesheet = self.settings.user_stylesheet + self.settings.stylesheet_path = self.settings.user_stylesheet_path + self.user_stylesheet_path = utils.get_stylesheet_reference( + self.settings, os.path.join(os.getcwd(), 'dummy')) + if self.user_stylesheet_path: + self.settings.record_dependencies.add(self.user_stylesheet_path) + + lang = self.settings.language_code or '' + if lang.startswith('de'): + self.double_quote_replacment = "{\\dq}" + elif lang.startswith('it'): + self.double_quote_replacment = r'{\char`\"}' + else: + self.double_quote_replacment = None + + self.write_header() + + def write_header(self): + a = self.header.append + a('%% Generated by Docutils %s .' + % docutils.__version__) + a('') + a('% Docutils settings:') + lang = self.settings.language_code or '' + a(r'\providecommand{\DEVlanguageiso}{%s}' % lang) + a(r'\providecommand{\DEVlanguagebabel}{%s}' % self.iso639_to_babel.get( + lang, self.iso639_to_babel.get(lang.split('_')[0], ''))) + a('') + if self.user_stylesheet_path: + a('% User stylesheet:') + a(r'\input{%s}' % self.user_stylesheet_path) + a('% Docutils stylesheet:') + a(r'\input{%s}' % self.stylesheet_path) + a('') + a('% Default definitions for Docutils nodes:') + for node_name in nodes.node_class_names: + a(r'\providecommand{\DN%s}[1]{#1}' % node_name.replace('_', '')) + a('') + a('% Auxiliary definitions:') + for attr in (r'\DEVparent \DEVattrlen \DEVtitleastext ' + r'\DEVsinglebackref \DEVmultiplebackrefs' + ).split(): + # Later set using \renewcommand. + a(r'\providecommand{%s}{DOCUTILSUNINITIALIZEDVARIABLE}' % attr) + for attr in (r'\DEVparagraphindented \DEVhassubtitle').split(): + # Initialize as boolean variables. + a(r'\providecommand{%s}{false}' % attr) + a('\n\n') + + unicode_map = unicode_map.unicode_map # comprehensive Unicode map + # Fix problems with unimap.py. + unicode_map.update({ + # We have AE or T1 encoding, so "``" etc. work. The macros + # from unimap.py may *not* work. + u'\u201C': '{``}', + u'\u201D': "{''}", + u'\u201E': '{,,}', + }) + + character_map = { + '\\': r'{\textbackslash}', + '{': r'{\{}', + '}': r'{\}}', + '$': r'{\$}', + '&': r'{\&}', + '%': r'{\%}', + '#': r'{\#}', + '[': r'{[}', + ']': r'{]}', + '-': r'{-}', + '`': r'{`}', + "'": r"{'}", + ',': r'{,}', + '"': r'{"}', + '|': r'{\textbar}', + '<': r'{\textless}', + '>': r'{\textgreater}', + '^': r'{\textasciicircum}', + '~': r'{\textasciitilde}', + '_': r'{\DECtextunderscore}', + } + character_map.update(unicode_map) + #character_map.update(special_map) + + # `att_map` is for encoding attributes. According to + # , + # the following characters are special: # $ % & ~ _ ^ \ { } + # These work without special treatment in macro parameters: + # $, &, ~, _, ^ + att_map = {'#': '\\#', + '%': '\\%', + # We cannot do anything about backslashes. + '\\': '', + '{': '\\{', + '}': '\\}', + # The quotation mark may be redefined by babel. + '"': '"{}', + } + att_map.update(unicode_map) + + def encode(self, text, attval=None): + """ + Encode special characters in ``text`` and return it. + + If attval is true, preserve as much as possible verbatim (used + in attribute value encoding). If attval is 'width' or + 'height', `text` is interpreted as a length value. + """ + if attval in ('width', 'height'): + match = re.match(r'([0-9.]+)(\S*)$', text) + assert match, '%s="%s" must be a length' % (attval, text) + value, unit = match.groups() + if unit == '%': + value = str(float(value) / 100) + unit = r'\DECrelativeunit' + elif unit in ('', 'px'): + # If \DECpixelunit is "pt", this gives the same notion + # of pixels as graphicx. This is a bit of a hack. + value = str(float(value) * 0.75) + unit = '\DECpixelunit' + return '%s%s' % (value, unit) + if attval: + get = self.att_map.get + else: + get = self.character_map.get + text = ''.join([get(c, c) for c in text]) + if (self.literal_block or self.inline_literal) and not attval: + # NB: We can have inline literals within literal blocks. + # Shrink '\r\n'. + text = text.replace('\r\n', '\n') + # Convert space. If "{ }~~~~~" is wrapped (at the + # brace-enclosed space "{ }"), the following non-breaking + # spaces ("~~~~") do *not* wind up at the beginning of the + # next line. Also note that no hyphenation is done if the + # breaking space ("{ }") comes *after* the non-breaking + # spaces. + if self.literal_block: + # Replace newlines with real newlines. + text = text.replace('\n', '\mbox{}\\\\{}') + replace_fn = self.encode_replace_for_literal_block_spaces + else: + replace_fn = self.encode_replace_for_inline_literal_spaces + text = re.sub(r'\s+', replace_fn, text) + # Protect hyphens; if we don't, line breaks will be + # possible at the hyphens and even the \textnhtt macro + # from the hyphenat package won't change that. + text = text.replace('-', r'\mbox{-}') + text = text.replace("'", r'{\DECtextliteralsinglequote}') + if self.double_quote_replacment is not None: + text = text.replace('"', self.double_quote_replacment) + + return text + else: + if not attval: + # Replace space with single protected space. + text = re.sub(r'\s+', '{ }', text) + # Replace double quotes with macro calls. + L = [] + for part in text.split(self.character_map['"']): + if L: + # Insert quote. + L.append(self.left_quote and r'{\DECtextleftdblquote}' + or r'{\DECtextrightdblquote}') + self.left_quote = not self.left_quote + L.append(part) + return ''.join(L) + else: + return text + + def encode_replace_for_literal_block_spaces(self, match): + return '~' * len(match.group()) + + def encode_replace_for_inline_literal_spaces(self, match): + return '{ }' + '~' * (len(match.group()) - 1) + + def astext(self): + return '\n'.join(self.header) + (''.join(self.body)) + + def append(self, text, newline='%\n'): + """ + Append text, stripping newlines, producing nice LaTeX code. + """ + lines = [' ' * self.indentation_level + line + newline + for line in text.splitlines(0)] + self.body.append(''.join(lines)) + + def visit_Text(self, node): + self.append(self.encode(node.astext())) + + def depart_Text(self, node): + pass + + def is_indented(self, paragraph): + """Return true if `paragraph` should be first-line-indented.""" + assert isinstance(paragraph, nodes.paragraph) + siblings = [n for n in paragraph.parent if + self.is_visible(n) and not isinstance(n, nodes.Titular)] + index = siblings.index(paragraph) + if ('continued' in paragraph['classes'] or + index > 0 and isinstance(siblings[index-1], nodes.transition)): + return 0 + # Indent all but the first paragraphs. + return index > 0 + + def before_paragraph(self, node): + self.append(r'\renewcommand{\DEVparagraphindented}{%s}' + % (self.is_indented(node) and 'true' or 'false')) + + def before_title(self, node): + self.append(r'\renewcommand{\DEVtitleastext}{%s}' + % self.encode(node.astext())) + self.append(r'\renewcommand{\DEVhassubtitle}{%s}' + % ((len(node.parent) > 2 and + isinstance(node.parent[1], nodes.subtitle)) + and 'true' or 'false')) + + def before_generated(self, node): + if 'sectnum' in node['classes']: + node[0] = node[0].strip() + + literal_block = 0 + + def visit_literal_block(self, node): + self.literal_block = 1 + + def depart_literal_block(self, node): + self.literal_block = 0 + + visit_doctest_block = visit_literal_block + depart_doctest_block = depart_literal_block + + inline_literal = 0 + + def visit_literal(self, node): + self.inline_literal += 1 + + def depart_literal(self, node): + self.inline_literal -= 1 + + def _make_encodable(self, text): + """ + Return text (a unicode object) with all unencodable characters + replaced with '?'. + + Thus, the returned unicode string is guaranteed to be encodable. + """ + encoding = self.settings.output_encoding + return text.encode(encoding, 'replace').decode(encoding) + + def visit_comment(self, node): + """ + Insert the comment unchanged into the document, replacing + unencodable characters with '?'. + + (This is done in order not to fail if comments contain unencodable + characters, because our default encoding is not UTF-8.) + """ + self.append('\n'.join(['% ' + self._make_encodable(line) for line + in node.astext().splitlines(0)]), newline='\n') + raise nodes.SkipChildren + + def before_topic(self, node): + if 'contents' in node['classes']: + for bullet_list in list(node.traverse(nodes.bullet_list)): + p = bullet_list.parent + if isinstance(p, nodes.list_item): + p.parent.insert(p.parent.index(p) + 1, bullet_list) + del p[1] + for paragraph in node.traverse(nodes.paragraph): + paragraph.attributes.update(paragraph[0].attributes) + paragraph[:] = paragraph[0] + paragraph.parent['tocrefid'] = paragraph['refid'] + node['contents'] = 1 + else: + node['contents'] = 0 + + bullet_list_level = 0 + + def visit_bullet_list(self, node): + self.append(r'\DECsetbullet{\labelitem%s}' % + ['i', 'ii', 'iii', 'iv'][min(self.bullet_list_level, 3)]) + self.bullet_list_level += 1 + + def depart_bullet_list(self, node): + self.bullet_list_level -= 1 + + enum_styles = {'arabic': 'arabic', 'loweralpha': 'alph', 'upperalpha': + 'Alph', 'lowerroman': 'roman', 'upperroman': 'Roman'} + + enum_counter = 0 + + def visit_enumerated_list(self, node): + # We create our own enumeration list environment. This allows + # to set the style and starting value and unlimited nesting. + # Maybe the actual creation (\DEC) can be moved to the + # stylesheet? + self.enum_counter += 1 + enum_prefix = self.encode(node['prefix']) + enum_suffix = self.encode(node['suffix']) + enum_type = '\\' + self.enum_styles.get(node['enumtype'], r'arabic') + start = node.get('start', 1) - 1 + counter = 'Denumcounter%d' % self.enum_counter + self.append(r'\DECmakeenumeratedlist{%s}{%s}{%s}{%s}{%s}{' + % (enum_prefix, enum_type, enum_suffix, counter, start)) + # for Emacs: } + + def depart_enumerated_list(self, node): + self.append('}') # for Emacs: { + + def before_list_item(self, node): + # XXX needs cleanup. + if (len(node) and (isinstance(node[-1], nodes.TextElement) or + isinstance(node[-1], nodes.Text)) and + node.parent.index(node) == len(node.parent) - 1): + node['lastitem'] = 'true' + + before_line = before_list_item + + def before_raw(self, node): + if 'latex' in node.get('format', '').split(): + # We're inserting the text in before_raw and thus outside + # of \DN... and \DECattr in order to make grouping with + # curly brackets work. + self.append(node.astext()) + raise nodes.SkipChildren + + def process_backlinks(self, node, type): + """ + Add LaTeX handling code for backlinks of footnote or citation + node `node`. `type` is either 'footnote' or 'citation'. + """ + self.append(r'\renewcommand{\DEVsinglebackref}{}') + self.append(r'\renewcommand{\DEVmultiplebackrefs}{}') + if len(node['backrefs']) > 1: + refs = [] + for i in range(len(node['backrefs'])): + # \DECmulticitationbacklink or \DECmultifootnotebacklink. + refs.append(r'\DECmulti%sbacklink{%s}{%s}' + % (type, node['backrefs'][i], i + 1)) + self.append(r'\renewcommand{\DEVmultiplebackrefs}{(%s){ }}' + % ', '.join(refs)) + elif len(node['backrefs']) == 1: + self.append(r'\renewcommand{\DEVsinglebackref}{%s}' + % node['backrefs'][0]) + + def visit_footnote(self, node): + self.process_backlinks(node, 'footnote') + + def visit_citation(self, node): + self.process_backlinks(node, 'citation') + + def before_table(self, node): + # A table contains exactly one tgroup. See before_tgroup. + pass + + def before_tgroup(self, node): + widths = [] + total_width = 0 + for i in range(int(node['cols'])): + assert isinstance(node[i], nodes.colspec) + widths.append(int(node[i]['colwidth']) + 1) + total_width += widths[-1] + del node[:len(widths)] + tablespec = '|' + for w in widths: + # 0.93 is probably wrong in many cases. XXX Find a + # solution which works *always*. + tablespec += r'p{%s\textwidth}|' % (0.93 * w / + max(total_width, 60)) + self.append(r'\DECmaketable{%s}{' % tablespec) + self.context.append('}') + raise SkipAttrParentLaTeX + + def depart_tgroup(self, node): + self.append(self.context.pop()) + + def before_row(self, node): + raise SkipAttrParentLaTeX + + def before_thead(self, node): + raise SkipAttrParentLaTeX + + def before_tbody(self, node): + raise SkipAttrParentLaTeX + + def is_simply_entry(self, node): + return (len(node) == 1 and isinstance(node[0], nodes.paragraph) or + len(node) == 0) + + def before_entry(self, node): + is_leftmost = 0 + if node.hasattr('morerows'): + self.document.reporter.severe('Rowspans are not supported.') + # Todo: Add empty cells below rowspanning cell and issue + # warning instead of severe. + if node.hasattr('morecols'): + # The author got a headache trying to implement + # multicolumn support. + if not self.is_simply_entry(node): + self.document.reporter.severe( + 'Colspanning table cells may only contain one paragraph.') + # Todo: Same as above. + # The number of columns this entry spans (as a string). + colspan = int(node['morecols']) + 1 + del node['morecols'] + else: + colspan = 1 + # Macro to call -- \DECcolspan or \DECcolspanleft. + macro_name = r'\DECcolspan' + if node.parent.index(node) == 0: + # Leftmost column. + macro_name += 'left' + is_leftmost = 1 + if colspan > 1: + self.append('%s{%s}{' % (macro_name, colspan)) + self.context.append('}') + else: + # Do not add a multicolumn with colspan 1 beacuse we need + # at least one non-multicolumn cell per column to get the + # desired column widths, and we can only do colspans with + # cells consisting of only one paragraph. + if not is_leftmost: + self.append(r'\DECsubsequententry{') + self.context.append('}') + else: + self.context.append('') + if isinstance(node.parent.parent, nodes.thead): + node['tableheaderentry'] = 'true' + + # Don't add \renewcommand{\DEVparent}{...} because there must + # not be any non-expandable commands in front of \multicolumn. + raise SkipParentLaTeX + + def depart_entry(self, node): + self.append(self.context.pop()) + + def before_substitution_definition(self, node): + raise nodes.SkipNode + + indentation_level = 0 + + def node_name(self, node): + return node.__class__.__name__.replace('_', '') + + # Attribute propagation order. + attribute_order = ['align', 'classes', 'ids'] + + def attribute_cmp(self, a1, a2): + """ + Compare attribute names `a1` and `a2`. Used in + propagate_attributes to determine propagation order. + + See built-in function `cmp` for return value. + """ + if a1 in self.attribute_order and a2 in self.attribute_order: + return cmp(self.attribute_order.index(a1), + self.attribute_order.index(a2)) + if (a1 in self.attribute_order) != (a2 in self.attribute_order): + # Attributes not in self.attribute_order come last. + return a1 in self.attribute_order and -1 or 1 + else: + return cmp(a1, a2) + + def propagate_attributes(self, node): + # Propagate attributes using \DECattr macros. + node_name = self.node_name(node) + attlist = [] + if isinstance(node, nodes.Element): + attlist = node.attlist() + attlist.sort(lambda pair1, pair2: self.attribute_cmp(pair1[0], + pair2[0])) + # `numatts` may be greater than len(attlist) due to list + # attributes. + numatts = 0 + pass_contents = self.pass_contents(node) + for key, value in attlist: + if isinstance(value, list): + self.append(r'\renewcommand{\DEVattrlen}{%s}' % len(value)) + for i in range(len(value)): + self.append(r'\DECattr{%s}{%s}{%s}{%s}{' % + (i+1, key, self.encode(value[i], attval=key), + node_name)) + if not pass_contents: + self.append('}') + numatts += len(value) + else: + self.append(r'\DECattr{}{%s}{%s}{%s}{' % + (key, self.encode(unicode(value), attval=key), + node_name)) + if not pass_contents: + self.append('}') + numatts += 1 + if pass_contents: + self.context.append('}' * numatts) # for Emacs: { + else: + self.context.append('') + + def visit_docinfo(self, node): + raise NotImplementedError('Docinfo not yet implemented.') + + def visit_document(self, node): + document = node + # Move IDs into TextElements. This won't work for images. + # Need to review this. + for node in document.traverse(nodes.Element): + if 'ids' in node and not isinstance(node, + nodes.TextElement): + next_text_element = node.next_node(nodes.TextElement) + if next_text_element: + next_text_element['ids'].extend(node['ids']) + node['ids'] = [] + + def pass_contents(self, node): + r""" + Return True if the node contents should be passed in + \DN{} and \DECattr{}{}{}{}{}. + Return False if the node contents should be passed in + \DECvisit \DECdepart, and no + attribute handler should be called. + """ + # Passing the whole document or whole sections as parameters + # to \DN... or \DECattr causes LaTeX to run out of memory. + return not isinstance(node, (nodes.document, nodes.section)) + + def dispatch_visit(self, node): + skip_attr = skip_parent = 0 + # TreePruningException to be propagated. + tree_pruning_exception = None + if hasattr(self, 'before_' + node.__class__.__name__): + try: + getattr(self, 'before_' + node.__class__.__name__)(node) + except SkipParentLaTeX: + skip_parent = 1 + except SkipAttrParentLaTeX: + skip_attr = 1 + skip_parent = 1 + except nodes.SkipNode: + raise + except (nodes.SkipChildren, nodes.SkipSiblings), instance: + tree_pruning_exception = instance + except nodes.SkipDeparture: + raise NotImplementedError( + 'SkipDeparture not usable in LaTeX writer') + + if not isinstance(node, nodes.Text): + node_name = self.node_name(node) + # attribute_deleters will be appended to self.context. + attribute_deleters = [] + if not skip_parent and not isinstance(node, nodes.document): + self.append(r'\renewcommand{\DEVparent}{%s}' + % self.node_name(node.parent)) + for name, value in node.attlist(): + if not isinstance(value, list) and not ':' in name: + # For non-list and non-special (like + # 'xml:preserve') attributes, set + # \DEVcurrentNA to the + # attribute value, so that the value of the + # attribute is available in the node handler + # and all children. + macro = r'\DEVcurrentN%sA%s' % (node_name, name) + self.append(r'\def%s{%s}' % ( + macro, self.encode(unicode(value), attval=name))) + # Make the attribute undefined afterwards. + attribute_deleters.append(r'\let%s=\relax' % macro) + self.context.append('\n'.join(attribute_deleters)) + if self.pass_contents(node): + # Call \DN{}. + self.append(r'\DN%s{' % node_name) + self.context.append('}') + else: + # Call \DECvisit + # \DECdepart. (Maybe we should use LaTeX + # environments for this?) + self.append(r'\DECvisit%s' % node_name) + self.context.append(r'\DECdepart%s' % node_name) + self.indentation_level += 1 + if not skip_attr: + self.propagate_attributes(node) + else: + self.context.append('') + + if (isinstance(node, nodes.TextElement) and + not isinstance(node.parent, nodes.TextElement)): + # Reset current quote to left. + self.left_quote = 1 + + # Call visit_... method. + try: + nodes.SparseNodeVisitor.dispatch_visit(self, node) + except LaTeXException: + raise NotImplementedError( + 'visit_... methods must not raise LaTeXExceptions') + + if tree_pruning_exception: + # Propagate TreePruningException raised in before_... method. + raise tree_pruning_exception + + def is_invisible(self, node): + # Return true if node is invisible or moved away in the LaTeX + # rendering. + return (not isinstance(node, nodes.Text) and + (isinstance(node, nodes.Invisible) or + isinstance(node, nodes.footnote) or + isinstance(node, nodes.citation) or + # Assume raw nodes to be invisible. + isinstance(node, nodes.raw) or + # Floating image or figure. + node.get('align') in ('left', 'right'))) + + def is_visible(self, node): + return not self.is_invisible(node) + + def needs_space(self, node): + """Two nodes for which `needs_space` is true need auxiliary space.""" + # Return true if node is a visible block-level element. + return ((isinstance(node, nodes.Body) or + isinstance(node, nodes.topic)) and + not (self.is_invisible(node) or + isinstance(node.parent, nodes.TextElement))) + + def always_needs_space(self, node): + """ + Always add space around nodes for which `always_needs_space()` + is true, regardless of whether the other node needs space as + well. (E.g. transition next to section.) + """ + return isinstance(node, nodes.transition) + + def dispatch_departure(self, node): + # Call departure method. + nodes.SparseNodeVisitor.dispatch_departure(self, node) + + if not isinstance(node, nodes.Text): + # Close attribute and node handler call (\DN...{...}). + self.indentation_level -= 1 + self.append(self.context.pop() + self.context.pop()) + # Delete \DECcurrentN... attribute macros. + self.append(self.context.pop()) + # Get next sibling. + next_node = node.next_node( + ascend=0, siblings=1, descend=0, + condition=self.is_visible) + # Insert space if necessary. + if (self.needs_space(node) and self.needs_space(next_node) or + self.always_needs_space(node) or + self.always_needs_space(next_node)): + if isinstance(node, nodes.paragraph) and isinstance(next_node, nodes.paragraph): + # Space between paragraphs. + self.append(r'\DECparagraphspace') + else: + # One of the elements is not a paragraph. + self.append(r'\DECauxiliaryspace') diff -Nru zope3-3.4.0/src/docutils/writers/newlatex2e/unicode_map.py zope3-3.5~bzr18/src/docutils/writers/newlatex2e/unicode_map.py --- zope3-3.4.0/src/docutils/writers/newlatex2e/unicode_map.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/newlatex2e/unicode_map.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2369 @@ +# $Id$ +# Author: Lea Wiemann +# Copyright: This file has been placed in the public domain. + +# This is a mapping of Unicode characters to LaTeX equivalents. +# The information has been extracted from +# , written by +# David Carlisle and Sebastian Rahtz. +# +# The extraction has been done by the "create_unimap.py" script +# located at . + +unicode_map = {u'\xa0': '$~$', +u'\xa1': '{\\textexclamdown}', +u'\xa2': '{\\textcent}', +u'\xa3': '{\\textsterling}', +u'\xa4': '{\\textcurrency}', +u'\xa5': '{\\textyen}', +u'\xa6': '{\\textbrokenbar}', +u'\xa7': '{\\textsection}', +u'\xa8': '{\\textasciidieresis}', +u'\xa9': '{\\textcopyright}', +u'\xaa': '{\\textordfeminine}', +u'\xab': '{\\guillemotleft}', +u'\xac': '$\\lnot$', +u'\xad': '$\\-$', +u'\xae': '{\\textregistered}', +u'\xaf': '{\\textasciimacron}', +u'\xb0': '{\\textdegree}', +u'\xb1': '$\\pm$', +u'\xb2': '${^2}$', +u'\xb3': '${^3}$', +u'\xb4': '{\\textasciiacute}', +u'\xb5': '$\\mathrm{\\mu}$', +u'\xb6': '{\\textparagraph}', +u'\xb7': '$\\cdot$', +u'\xb8': '{\\c{}}', +u'\xb9': '${^1}$', +u'\xba': '{\\textordmasculine}', +u'\xbb': '{\\guillemotright}', +u'\xbc': '{\\textonequarter}', +u'\xbd': '{\\textonehalf}', +u'\xbe': '{\\textthreequarters}', +u'\xbf': '{\\textquestiondown}', +u'\xc0': '{\\`{A}}', +u'\xc1': "{\\'{A}}", +u'\xc2': '{\\^{A}}', +u'\xc3': '{\\~{A}}', +u'\xc4': '{\\"{A}}', +u'\xc5': '{\\AA}', +u'\xc6': '{\\AE}', +u'\xc7': '{\\c{C}}', +u'\xc8': '{\\`{E}}', +u'\xc9': "{\\'{E}}", +u'\xca': '{\\^{E}}', +u'\xcb': '{\\"{E}}', +u'\xcc': '{\\`{I}}', +u'\xcd': "{\\'{I}}", +u'\xce': '{\\^{I}}', +u'\xcf': '{\\"{I}}', +u'\xd0': '{\\DH}', +u'\xd1': '{\\~{N}}', +u'\xd2': '{\\`{O}}', +u'\xd3': "{\\'{O}}", +u'\xd4': '{\\^{O}}', +u'\xd5': '{\\~{O}}', +u'\xd6': '{\\"{O}}', +u'\xd7': '{\\texttimes}', +u'\xd8': '{\\O}', +u'\xd9': '{\\`{U}}', +u'\xda': "{\\'{U}}", +u'\xdb': '{\\^{U}}', +u'\xdc': '{\\"{U}}', +u'\xdd': "{\\'{Y}}", +u'\xde': '{\\TH}', +u'\xdf': '{\\ss}', +u'\xe0': '{\\`{a}}', +u'\xe1': "{\\'{a}}", +u'\xe2': '{\\^{a}}', +u'\xe3': '{\\~{a}}', +u'\xe4': '{\\"{a}}', +u'\xe5': '{\\aa}', +u'\xe6': '{\\ae}', +u'\xe7': '{\\c{c}}', +u'\xe8': '{\\`{e}}', +u'\xe9': "{\\'{e}}", +u'\xea': '{\\^{e}}', +u'\xeb': '{\\"{e}}', +u'\xec': '{\\`{\\i}}', +u'\xed': "{\\'{\\i}}", +u'\xee': '{\\^{\\i}}', +u'\xef': '{\\"{\\i}}', +u'\xf0': '{\\dh}', +u'\xf1': '{\\~{n}}', +u'\xf2': '{\\`{o}}', +u'\xf3': "{\\'{o}}", +u'\xf4': '{\\^{o}}', +u'\xf5': '{\\~{o}}', +u'\xf6': '{\\"{o}}', +u'\xf7': '$\\div$', +u'\xf8': '{\\o}', +u'\xf9': '{\\`{u}}', +u'\xfa': "{\\'{u}}", +u'\xfb': '{\\^{u}}', +u'\xfc': '{\\"{u}}', +u'\xfd': "{\\'{y}}", +u'\xfe': '{\\th}', +u'\xff': '{\\"{y}}', +u'\u0100': '{\\={A}}', +u'\u0101': '{\\={a}}', +u'\u0102': '{\\u{A}}', +u'\u0103': '{\\u{a}}', +u'\u0104': '{\\k{A}}', +u'\u0105': '{\\k{a}}', +u'\u0106': "{\\'{C}}", +u'\u0107': "{\\'{c}}", +u'\u0108': '{\\^{C}}', +u'\u0109': '{\\^{c}}', +u'\u010a': '{\\.{C}}', +u'\u010b': '{\\.{c}}', +u'\u010c': '{\\v{C}}', +u'\u010d': '{\\v{c}}', +u'\u010e': '{\\v{D}}', +u'\u010f': '{\\v{d}}', +u'\u0110': '{\\DJ}', +u'\u0111': '{\\dj}', +u'\u0112': '{\\={E}}', +u'\u0113': '{\\={e}}', +u'\u0114': '{\\u{E}}', +u'\u0115': '{\\u{e}}', +u'\u0116': '{\\.{E}}', +u'\u0117': '{\\.{e}}', +u'\u0118': '{\\k{E}}', +u'\u0119': '{\\k{e}}', +u'\u011a': '{\\v{E}}', +u'\u011b': '{\\v{e}}', +u'\u011c': '{\\^{G}}', +u'\u011d': '{\\^{g}}', +u'\u011e': '{\\u{G}}', +u'\u011f': '{\\u{g}}', +u'\u0120': '{\\.{G}}', +u'\u0121': '{\\.{g}}', +u'\u0122': '{\\c{G}}', +u'\u0123': '{\\c{g}}', +u'\u0124': '{\\^{H}}', +u'\u0125': '{\\^{h}}', +u'\u0126': '{{\\fontencoding{LELA}\\selectfont\\char40}}', +u'\u0127': '$\\Elzxh$', +u'\u0128': '{\\~{I}}', +u'\u0129': '{\\~{\\i}}', +u'\u012a': '{\\={I}}', +u'\u012b': '{\\={\\i}}', +u'\u012c': '{\\u{I}}', +u'\u012d': '{\\u{\\i}}', +u'\u012e': '{\\k{I}}', +u'\u012f': '{\\k{i}}', +u'\u0130': '{\\.{I}}', +u'\u0131': '{\\i}', +u'\u0132': '{IJ}', +u'\u0133': '{ij}', +u'\u0134': '{\\^{J}}', +u'\u0135': '{\\^{\\j}}', +u'\u0136': '{\\c{K}}', +u'\u0137': '{\\c{k}}', +u'\u0138': '{{\\fontencoding{LELA}\\selectfont\\char91}}', +u'\u0139': "{\\'{L}}", +u'\u013a': "{\\'{l}}", +u'\u013b': '{\\c{L}}', +u'\u013c': '{\\c{l}}', +u'\u013d': '{\\v{L}}', +u'\u013e': '{\\v{l}}', +u'\u013f': '{{\\fontencoding{LELA}\\selectfont\\char201}}', +u'\u0140': '{{\\fontencoding{LELA}\\selectfont\\char202}}', +u'\u0141': '{\\L}', +u'\u0142': '{\\l}', +u'\u0143': "{\\'{N}}", +u'\u0144': "{\\'{n}}", +u'\u0145': '{\\c{N}}', +u'\u0146': '{\\c{n}}', +u'\u0147': '{\\v{N}}', +u'\u0148': '{\\v{n}}', +u'\u0149': "{'n}", +u'\u014a': '{\\NG}', +u'\u014b': '{\\ng}', +u'\u014c': '{\\={O}}', +u'\u014d': '{\\={o}}', +u'\u014e': '{\\u{O}}', +u'\u014f': '{\\u{o}}', +u'\u0150': '{\\H{O}}', +u'\u0151': '{\\H{o}}', +u'\u0152': '{\\OE}', +u'\u0153': '{\\oe}', +u'\u0154': "{\\'{R}}", +u'\u0155': "{\\'{r}}", +u'\u0156': '{\\c{R}}', +u'\u0157': '{\\c{r}}', +u'\u0158': '{\\v{R}}', +u'\u0159': '{\\v{r}}', +u'\u015a': "{\\'{S}}", +u'\u015b': "{\\'{s}}", +u'\u015c': '{\\^{S}}', +u'\u015d': '{\\^{s}}', +u'\u015e': '{\\c{S}}', +u'\u015f': '{\\c{s}}', +u'\u0160': '{\\v{S}}', +u'\u0161': '{\\v{s}}', +u'\u0162': '{\\c{T}}', +u'\u0163': '{\\c{t}}', +u'\u0164': '{\\v{T}}', +u'\u0165': '{\\v{t}}', +u'\u0166': '{{\\fontencoding{LELA}\\selectfont\\char47}}', +u'\u0167': '{{\\fontencoding{LELA}\\selectfont\\char63}}', +u'\u0168': '{\\~{U}}', +u'\u0169': '{\\~{u}}', +u'\u016a': '{\\={U}}', +u'\u016b': '{\\={u}}', +u'\u016c': '{\\u{U}}', +u'\u016d': '{\\u{u}}', +u'\u016e': '{\\r{U}}', +u'\u016f': '{\\r{u}}', +u'\u0170': '{\\H{U}}', +u'\u0171': '{\\H{u}}', +u'\u0172': '{\\k{U}}', +u'\u0173': '{\\k{u}}', +u'\u0174': '{\\^{W}}', +u'\u0175': '{\\^{w}}', +u'\u0176': '{\\^{Y}}', +u'\u0177': '{\\^{y}}', +u'\u0178': '{\\"{Y}}', +u'\u0179': "{\\'{Z}}", +u'\u017a': "{\\'{z}}", +u'\u017b': '{\\.{Z}}', +u'\u017c': '{\\.{z}}', +u'\u017d': '{\\v{Z}}', +u'\u017e': '{\\v{z}}', +u'\u0192': '$f$', +u'\u0195': '{\\texthvlig}', +u'\u019e': '{\\textnrleg}', +u'\u01aa': '$\\eth$', +u'\u01ba': '{{\\fontencoding{LELA}\\selectfont\\char195}}', +u'\u01c2': '{\\textdoublepipe}', +u'\u01f5': "{\\'{g}}", +u'\u0250': '$\\Elztrna$', +u'\u0252': '$\\Elztrnsa$', +u'\u0254': '$\\Elzopeno$', +u'\u0256': '$\\Elzrtld$', +u'\u0258': '{{\\fontencoding{LEIP}\\selectfont\\char61}}', +u'\u0259': '$\\Elzschwa$', +u'\u025b': '$\\varepsilon$', +u'\u0261': '{g}', +u'\u0263': '$\\Elzpgamma$', +u'\u0264': '$\\Elzpbgam$', +u'\u0265': '$\\Elztrnh$', +u'\u026c': '$\\Elzbtdl$', +u'\u026d': '$\\Elzrtll$', +u'\u026f': '$\\Elztrnm$', +u'\u0270': '$\\Elztrnmlr$', +u'\u0271': '$\\Elzltlmr$', +u'\u0272': '{\\Elzltln}', +u'\u0273': '$\\Elzrtln$', +u'\u0277': '$\\Elzclomeg$', +u'\u0278': '{\\textphi}', +u'\u0279': '$\\Elztrnr$', +u'\u027a': '$\\Elztrnrl$', +u'\u027b': '$\\Elzrttrnr$', +u'\u027c': '$\\Elzrl$', +u'\u027d': '$\\Elzrtlr$', +u'\u027e': '$\\Elzfhr$', +u'\u027f': '{{\\fontencoding{LEIP}\\selectfont\\char202}}', +u'\u0282': '$\\Elzrtls$', +u'\u0283': '$\\Elzesh$', +u'\u0287': '$\\Elztrnt$', +u'\u0288': '$\\Elzrtlt$', +u'\u028a': '$\\Elzpupsil$', +u'\u028b': '$\\Elzpscrv$', +u'\u028c': '$\\Elzinvv$', +u'\u028d': '$\\Elzinvw$', +u'\u028e': '$\\Elztrny$', +u'\u0290': '$\\Elzrtlz$', +u'\u0292': '$\\Elzyogh$', +u'\u0294': '$\\Elzglst$', +u'\u0295': '$\\Elzreglst$', +u'\u0296': '$\\Elzinglst$', +u'\u029e': '{\\textturnk}', +u'\u02a4': '$\\Elzdyogh$', +u'\u02a7': '$\\Elztesh$', +u'\u02bc': "{'}", +u'\u02c7': '{\\textasciicaron}', +u'\u02c8': '$\\Elzverts$', +u'\u02cc': '$\\Elzverti$', +u'\u02d0': '$\\Elzlmrk$', +u'\u02d1': '$\\Elzhlmrk$', +u'\u02d2': '$\\Elzsbrhr$', +u'\u02d3': '$\\Elzsblhr$', +u'\u02d4': '$\\Elzrais$', +u'\u02d5': '$\\Elzlow$', +u'\u02d8': '{\\textasciibreve}', +u'\u02d9': '{\\textperiodcentered}', +u'\u02da': '{\\r{}}', +u'\u02db': '{\\k{}}', +u'\u02dc': '{\\texttildelow}', +u'\u02dd': '{\\H{}}', +u'\u02e5': '{\\tone{55}}', +u'\u02e6': '{\\tone{44}}', +u'\u02e7': '{\\tone{33}}', +u'\u02e8': '{\\tone{22}}', +u'\u02e9': '{\\tone{11}}', +u'\u0300': '{\\`}', +u'\u0301': "{\\'}", +u'\u0302': '{\\^}', +u'\u0303': '{\\~}', +u'\u0304': '{\\=}', +u'\u0306': '{\\u}', +u'\u0307': '{\\.}', +u'\u0308': '{\\"}', +u'\u030a': '{\\r}', +u'\u030b': '{\\H}', +u'\u030c': '{\\v}', +u'\u030f': '{\\cyrchar\\C}', +u'\u0311': '{{\\fontencoding{LECO}\\selectfont\\char177}}', +u'\u0318': '{{\\fontencoding{LECO}\\selectfont\\char184}}', +u'\u0319': '{{\\fontencoding{LECO}\\selectfont\\char185}}', +u'\u0321': '$\\Elzpalh$', +u'\u0322': '{\\Elzrh}', +u'\u0327': '{\\c}', +u'\u0328': '{\\k}', +u'\u032a': '$\\Elzsbbrg$', +u'\u032b': '{{\\fontencoding{LECO}\\selectfont\\char203}}', +u'\u032f': '{{\\fontencoding{LECO}\\selectfont\\char207}}', +u'\u0335': '{\\Elzxl}', +u'\u0336': '{\\Elzbar}', +u'\u0337': '{{\\fontencoding{LECO}\\selectfont\\char215}}', +u'\u0338': '{{\\fontencoding{LECO}\\selectfont\\char216}}', +u'\u033a': '{{\\fontencoding{LECO}\\selectfont\\char218}}', +u'\u033b': '{{\\fontencoding{LECO}\\selectfont\\char219}}', +u'\u033c': '{{\\fontencoding{LECO}\\selectfont\\char220}}', +u'\u033d': '{{\\fontencoding{LECO}\\selectfont\\char221}}', +u'\u0361': '{{\\fontencoding{LECO}\\selectfont\\char225}}', +u'\u0386': "{\\'{A}}", +u'\u0388': "{\\'{E}}", +u'\u0389': "{\\'{H}}", +u'\u038a': "{\\'{}{I}}", +u'\u038c': "{\\'{}O}", +u'\u038e': "$\\mathrm{'Y}$", +u'\u038f': "$\\mathrm{'\\Omega}$", +u'\u0390': '$\\acute{\\ddot{\\iota}}$', +u'\u0391': '$\\Alpha$', +u'\u0392': '$\\Beta$', +u'\u0393': '$\\Gamma$', +u'\u0394': '$\\Delta$', +u'\u0395': '$\\Epsilon$', +u'\u0396': '$\\Zeta$', +u'\u0397': '$\\Eta$', +u'\u0398': '$\\Theta$', +u'\u0399': '$\\Iota$', +u'\u039a': '$\\Kappa$', +u'\u039b': '$\\Lambda$', +u'\u039c': '$M$', +u'\u039d': '$N$', +u'\u039e': '$\\Xi$', +u'\u039f': '$O$', +u'\u03a0': '$\\Pi$', +u'\u03a1': '$\\Rho$', +u'\u03a3': '$\\Sigma$', +u'\u03a4': '$\\Tau$', +u'\u03a5': '$\\Upsilon$', +u'\u03a6': '$\\Phi$', +u'\u03a7': '$\\Chi$', +u'\u03a8': '$\\Psi$', +u'\u03a9': '$\\Omega$', +u'\u03aa': '$\\mathrm{\\ddot{I}}$', +u'\u03ab': '$\\mathrm{\\ddot{Y}}$', +u'\u03ac': "{\\'{$\\alpha$}}", +u'\u03ad': '$\\acute{\\epsilon}$', +u'\u03ae': '$\\acute{\\eta}$', +u'\u03af': '$\\acute{\\iota}$', +u'\u03b0': '$\\acute{\\ddot{\\upsilon}}$', +u'\u03b1': '$\\alpha$', +u'\u03b2': '$\\beta$', +u'\u03b3': '$\\gamma$', +u'\u03b4': '$\\delta$', +u'\u03b5': '$\\epsilon$', +u'\u03b6': '$\\zeta$', +u'\u03b7': '$\\eta$', +u'\u03b8': '{\\texttheta}', +u'\u03b9': '$\\iota$', +u'\u03ba': '$\\kappa$', +u'\u03bb': '$\\lambda$', +u'\u03bc': '$\\mu$', +u'\u03bd': '$\\nu$', +u'\u03be': '$\\xi$', +u'\u03bf': '$o$', +u'\u03c0': '$\\pi$', +u'\u03c1': '$\\rho$', +u'\u03c2': '$\\varsigma$', +u'\u03c3': '$\\sigma$', +u'\u03c4': '$\\tau$', +u'\u03c5': '$\\upsilon$', +u'\u03c6': '$\\varphi$', +u'\u03c7': '$\\chi$', +u'\u03c8': '$\\psi$', +u'\u03c9': '$\\omega$', +u'\u03ca': '$\\ddot{\\iota}$', +u'\u03cb': '$\\ddot{\\upsilon}$', +u'\u03cc': "{\\'{o}}", +u'\u03cd': '$\\acute{\\upsilon}$', +u'\u03ce': '$\\acute{\\omega}$', +u'\u03d0': '{\\Pisymbol{ppi022}{87}}', +u'\u03d1': '{\\textvartheta}', +u'\u03d2': '$\\Upsilon$', +u'\u03d5': '$\\phi$', +u'\u03d6': '$\\varpi$', +u'\u03da': '$\\Stigma$', +u'\u03dc': '$\\Digamma$', +u'\u03dd': '$\\digamma$', +u'\u03de': '$\\Koppa$', +u'\u03e0': '$\\Sampi$', +u'\u03f0': '$\\varkappa$', +u'\u03f1': '$\\varrho$', +u'\u03f4': '{\\textTheta}', +u'\u03f6': '$\\backepsilon$', +u'\u0401': '{\\cyrchar\\CYRYO}', +u'\u0402': '{\\cyrchar\\CYRDJE}', +u'\u0403': "{\\cyrchar{\\'\\CYRG}}", +u'\u0404': '{\\cyrchar\\CYRIE}', +u'\u0405': '{\\cyrchar\\CYRDZE}', +u'\u0406': '{\\cyrchar\\CYRII}', +u'\u0407': '{\\cyrchar\\CYRYI}', +u'\u0408': '{\\cyrchar\\CYRJE}', +u'\u0409': '{\\cyrchar\\CYRLJE}', +u'\u040a': '{\\cyrchar\\CYRNJE}', +u'\u040b': '{\\cyrchar\\CYRTSHE}', +u'\u040c': "{\\cyrchar{\\'\\CYRK}}", +u'\u040e': '{\\cyrchar\\CYRUSHRT}', +u'\u040f': '{\\cyrchar\\CYRDZHE}', +u'\u0410': '{\\cyrchar\\CYRA}', +u'\u0411': '{\\cyrchar\\CYRB}', +u'\u0412': '{\\cyrchar\\CYRV}', +u'\u0413': '{\\cyrchar\\CYRG}', +u'\u0414': '{\\cyrchar\\CYRD}', +u'\u0415': '{\\cyrchar\\CYRE}', +u'\u0416': '{\\cyrchar\\CYRZH}', +u'\u0417': '{\\cyrchar\\CYRZ}', +u'\u0418': '{\\cyrchar\\CYRI}', +u'\u0419': '{\\cyrchar\\CYRISHRT}', +u'\u041a': '{\\cyrchar\\CYRK}', +u'\u041b': '{\\cyrchar\\CYRL}', +u'\u041c': '{\\cyrchar\\CYRM}', +u'\u041d': '{\\cyrchar\\CYRN}', +u'\u041e': '{\\cyrchar\\CYRO}', +u'\u041f': '{\\cyrchar\\CYRP}', +u'\u0420': '{\\cyrchar\\CYRR}', +u'\u0421': '{\\cyrchar\\CYRS}', +u'\u0422': '{\\cyrchar\\CYRT}', +u'\u0423': '{\\cyrchar\\CYRU}', +u'\u0424': '{\\cyrchar\\CYRF}', +u'\u0425': '{\\cyrchar\\CYRH}', +u'\u0426': '{\\cyrchar\\CYRC}', +u'\u0427': '{\\cyrchar\\CYRCH}', +u'\u0428': '{\\cyrchar\\CYRSH}', +u'\u0429': '{\\cyrchar\\CYRSHCH}', +u'\u042a': '{\\cyrchar\\CYRHRDSN}', +u'\u042b': '{\\cyrchar\\CYRERY}', +u'\u042c': '{\\cyrchar\\CYRSFTSN}', +u'\u042d': '{\\cyrchar\\CYREREV}', +u'\u042e': '{\\cyrchar\\CYRYU}', +u'\u042f': '{\\cyrchar\\CYRYA}', +u'\u0430': '{\\cyrchar\\cyra}', +u'\u0431': '{\\cyrchar\\cyrb}', +u'\u0432': '{\\cyrchar\\cyrv}', +u'\u0433': '{\\cyrchar\\cyrg}', +u'\u0434': '{\\cyrchar\\cyrd}', +u'\u0435': '{\\cyrchar\\cyre}', +u'\u0436': '{\\cyrchar\\cyrzh}', +u'\u0437': '{\\cyrchar\\cyrz}', +u'\u0438': '{\\cyrchar\\cyri}', +u'\u0439': '{\\cyrchar\\cyrishrt}', +u'\u043a': '{\\cyrchar\\cyrk}', +u'\u043b': '{\\cyrchar\\cyrl}', +u'\u043c': '{\\cyrchar\\cyrm}', +u'\u043d': '{\\cyrchar\\cyrn}', +u'\u043e': '{\\cyrchar\\cyro}', +u'\u043f': '{\\cyrchar\\cyrp}', +u'\u0440': '{\\cyrchar\\cyrr}', +u'\u0441': '{\\cyrchar\\cyrs}', +u'\u0442': '{\\cyrchar\\cyrt}', +u'\u0443': '{\\cyrchar\\cyru}', +u'\u0444': '{\\cyrchar\\cyrf}', +u'\u0445': '{\\cyrchar\\cyrh}', +u'\u0446': '{\\cyrchar\\cyrc}', +u'\u0447': '{\\cyrchar\\cyrch}', +u'\u0448': '{\\cyrchar\\cyrsh}', +u'\u0449': '{\\cyrchar\\cyrshch}', +u'\u044a': '{\\cyrchar\\cyrhrdsn}', +u'\u044b': '{\\cyrchar\\cyrery}', +u'\u044c': '{\\cyrchar\\cyrsftsn}', +u'\u044d': '{\\cyrchar\\cyrerev}', +u'\u044e': '{\\cyrchar\\cyryu}', +u'\u044f': '{\\cyrchar\\cyrya}', +u'\u0451': '{\\cyrchar\\cyryo}', +u'\u0452': '{\\cyrchar\\cyrdje}', +u'\u0453': "{\\cyrchar{\\'\\cyrg}}", +u'\u0454': '{\\cyrchar\\cyrie}', +u'\u0455': '{\\cyrchar\\cyrdze}', +u'\u0456': '{\\cyrchar\\cyrii}', +u'\u0457': '{\\cyrchar\\cyryi}', +u'\u0458': '{\\cyrchar\\cyrje}', +u'\u0459': '{\\cyrchar\\cyrlje}', +u'\u045a': '{\\cyrchar\\cyrnje}', +u'\u045b': '{\\cyrchar\\cyrtshe}', +u'\u045c': "{\\cyrchar{\\'\\cyrk}}", +u'\u045e': '{\\cyrchar\\cyrushrt}', +u'\u045f': '{\\cyrchar\\cyrdzhe}', +u'\u0460': '{\\cyrchar\\CYROMEGA}', +u'\u0461': '{\\cyrchar\\cyromega}', +u'\u0462': '{\\cyrchar\\CYRYAT}', +u'\u0464': '{\\cyrchar\\CYRIOTE}', +u'\u0465': '{\\cyrchar\\cyriote}', +u'\u0466': '{\\cyrchar\\CYRLYUS}', +u'\u0467': '{\\cyrchar\\cyrlyus}', +u'\u0468': '{\\cyrchar\\CYRIOTLYUS}', +u'\u0469': '{\\cyrchar\\cyriotlyus}', +u'\u046a': '{\\cyrchar\\CYRBYUS}', +u'\u046c': '{\\cyrchar\\CYRIOTBYUS}', +u'\u046d': '{\\cyrchar\\cyriotbyus}', +u'\u046e': '{\\cyrchar\\CYRKSI}', +u'\u046f': '{\\cyrchar\\cyrksi}', +u'\u0470': '{\\cyrchar\\CYRPSI}', +u'\u0471': '{\\cyrchar\\cyrpsi}', +u'\u0472': '{\\cyrchar\\CYRFITA}', +u'\u0474': '{\\cyrchar\\CYRIZH}', +u'\u0478': '{\\cyrchar\\CYRUK}', +u'\u0479': '{\\cyrchar\\cyruk}', +u'\u047a': '{\\cyrchar\\CYROMEGARND}', +u'\u047b': '{\\cyrchar\\cyromegarnd}', +u'\u047c': '{\\cyrchar\\CYROMEGATITLO}', +u'\u047d': '{\\cyrchar\\cyromegatitlo}', +u'\u047e': '{\\cyrchar\\CYROT}', +u'\u047f': '{\\cyrchar\\cyrot}', +u'\u0480': '{\\cyrchar\\CYRKOPPA}', +u'\u0481': '{\\cyrchar\\cyrkoppa}', +u'\u0482': '{\\cyrchar\\cyrthousands}', +u'\u0488': '{\\cyrchar\\cyrhundredthousands}', +u'\u0489': '{\\cyrchar\\cyrmillions}', +u'\u048c': '{\\cyrchar\\CYRSEMISFTSN}', +u'\u048d': '{\\cyrchar\\cyrsemisftsn}', +u'\u048e': '{\\cyrchar\\CYRRTICK}', +u'\u048f': '{\\cyrchar\\cyrrtick}', +u'\u0490': '{\\cyrchar\\CYRGUP}', +u'\u0491': '{\\cyrchar\\cyrgup}', +u'\u0492': '{\\cyrchar\\CYRGHCRS}', +u'\u0493': '{\\cyrchar\\cyrghcrs}', +u'\u0494': '{\\cyrchar\\CYRGHK}', +u'\u0495': '{\\cyrchar\\cyrghk}', +u'\u0496': '{\\cyrchar\\CYRZHDSC}', +u'\u0497': '{\\cyrchar\\cyrzhdsc}', +u'\u0498': '{\\cyrchar\\CYRZDSC}', +u'\u0499': '{\\cyrchar\\cyrzdsc}', +u'\u049a': '{\\cyrchar\\CYRKDSC}', +u'\u049b': '{\\cyrchar\\cyrkdsc}', +u'\u049c': '{\\cyrchar\\CYRKVCRS}', +u'\u049d': '{\\cyrchar\\cyrkvcrs}', +u'\u049e': '{\\cyrchar\\CYRKHCRS}', +u'\u049f': '{\\cyrchar\\cyrkhcrs}', +u'\u04a0': '{\\cyrchar\\CYRKBEAK}', +u'\u04a1': '{\\cyrchar\\cyrkbeak}', +u'\u04a2': '{\\cyrchar\\CYRNDSC}', +u'\u04a3': '{\\cyrchar\\cyrndsc}', +u'\u04a4': '{\\cyrchar\\CYRNG}', +u'\u04a5': '{\\cyrchar\\cyrng}', +u'\u04a6': '{\\cyrchar\\CYRPHK}', +u'\u04a7': '{\\cyrchar\\cyrphk}', +u'\u04a8': '{\\cyrchar\\CYRABHHA}', +u'\u04a9': '{\\cyrchar\\cyrabhha}', +u'\u04aa': '{\\cyrchar\\CYRSDSC}', +u'\u04ab': '{\\cyrchar\\cyrsdsc}', +u'\u04ac': '{\\cyrchar\\CYRTDSC}', +u'\u04ad': '{\\cyrchar\\cyrtdsc}', +u'\u04ae': '{\\cyrchar\\CYRY}', +u'\u04af': '{\\cyrchar\\cyry}', +u'\u04b0': '{\\cyrchar\\CYRYHCRS}', +u'\u04b1': '{\\cyrchar\\cyryhcrs}', +u'\u04b2': '{\\cyrchar\\CYRHDSC}', +u'\u04b3': '{\\cyrchar\\cyrhdsc}', +u'\u04b4': '{\\cyrchar\\CYRTETSE}', +u'\u04b5': '{\\cyrchar\\cyrtetse}', +u'\u04b6': '{\\cyrchar\\CYRCHRDSC}', +u'\u04b7': '{\\cyrchar\\cyrchrdsc}', +u'\u04b8': '{\\cyrchar\\CYRCHVCRS}', +u'\u04b9': '{\\cyrchar\\cyrchvcrs}', +u'\u04ba': '{\\cyrchar\\CYRSHHA}', +u'\u04bb': '{\\cyrchar\\cyrshha}', +u'\u04bc': '{\\cyrchar\\CYRABHCH}', +u'\u04bd': '{\\cyrchar\\cyrabhch}', +u'\u04be': '{\\cyrchar\\CYRABHCHDSC}', +u'\u04bf': '{\\cyrchar\\cyrabhchdsc}', +u'\u04c0': '{\\cyrchar\\CYRpalochka}', +u'\u04c3': '{\\cyrchar\\CYRKHK}', +u'\u04c4': '{\\cyrchar\\cyrkhk}', +u'\u04c7': '{\\cyrchar\\CYRNHK}', +u'\u04c8': '{\\cyrchar\\cyrnhk}', +u'\u04cb': '{\\cyrchar\\CYRCHLDSC}', +u'\u04cc': '{\\cyrchar\\cyrchldsc}', +u'\u04d4': '{\\cyrchar\\CYRAE}', +u'\u04d5': '{\\cyrchar\\cyrae}', +u'\u04d8': '{\\cyrchar\\CYRSCHWA}', +u'\u04d9': '{\\cyrchar\\cyrschwa}', +u'\u04e0': '{\\cyrchar\\CYRABHDZE}', +u'\u04e1': '{\\cyrchar\\cyrabhdze}', +u'\u04e8': '{\\cyrchar\\CYROTLD}', +u'\u04e9': '{\\cyrchar\\cyrotld}', +u'\u2002': '{\\hspace{0.6em}}', +u'\u2003': '{\\hspace{1em}}', +u'\u2004': '{\\hspace{0.33em}}', +u'\u2005': '{\\hspace{0.25em}}', +u'\u2006': '{\\hspace{0.166em}}', +u'\u2007': '{\\hphantom{0}}', +u'\u2008': '{\\hphantom{,}}', +u'\u2009': '{\\hspace{0.167em}}', +u'\u200a': '$\\mkern1mu$', +u'\u2010': '{-}', +u'\u2013': '{\\textendash}', +u'\u2014': '{\\textemdash}', +u'\u2015': '{\\rule{1em}{1pt}}', +u'\u2016': '$\\Vert$', +u'\u2018': '{`}', +u'\u2019': "{'}", +u'\u201a': '{,}', +u'\u201b': '$\\Elzreapos$', +u'\u201c': '{\\textquotedblleft}', +u'\u201d': '{\\textquotedblright}', +u'\u201e': '{,,}', +u'\u2020': '{\\textdagger}', +u'\u2021': '{\\textdaggerdbl}', +u'\u2022': '{\\textbullet}', +u'\u2024': '{.}', +u'\u2025': '{..}', +u'\u2026': '{\\ldots}', +u'\u2030': '{\\textperthousand}', +u'\u2031': '{\\textpertenthousand}', +u'\u2032': "${'}$", +u'\u2033': "${''}$", +u'\u2034': "${'''}$", +u'\u2035': '$\\backprime$', +u'\u2039': '{\\guilsinglleft}', +u'\u203a': '{\\guilsinglright}', +u'\u2057': "$''''$", +u'\u205f': '{\\mkern4mu}', +u'\u2060': '{\\nolinebreak}', +u'\u20a7': '{\\ensuremath{\\Elzpes}}', +u'\u20ac': '{\\mbox{\\texteuro}}', +u'\u20db': '$\\dddot$', +u'\u20dc': '$\\ddddot$', +u'\u2102': '$\\mathbb{C}$', +u'\u210a': '{\\mathscr{g}}', +u'\u210b': '$\\mathscr{H}$', +u'\u210c': '$\\mathfrak{H}$', +u'\u210d': '$\\mathbb{H}$', +u'\u210f': '$\\hslash$', +u'\u2110': '$\\mathscr{I}$', +u'\u2111': '$\\mathfrak{I}$', +u'\u2112': '$\\mathscr{L}$', +u'\u2113': '$\\mathscr{l}$', +u'\u2115': '$\\mathbb{N}$', +u'\u2116': '{\\cyrchar\\textnumero}', +u'\u2118': '$\\wp$', +u'\u2119': '$\\mathbb{P}$', +u'\u211a': '$\\mathbb{Q}$', +u'\u211b': '$\\mathscr{R}$', +u'\u211c': '$\\mathfrak{R}$', +u'\u211d': '$\\mathbb{R}$', +u'\u211e': '$\\Elzxrat$', +u'\u2122': '{\\texttrademark}', +u'\u2124': '$\\mathbb{Z}$', +u'\u2126': '$\\Omega$', +u'\u2127': '$\\mho$', +u'\u2128': '$\\mathfrak{Z}$', +u'\u2129': '$\\ElsevierGlyph{2129}$', +u'\u212b': '{\\AA}', +u'\u212c': '$\\mathscr{B}$', +u'\u212d': '$\\mathfrak{C}$', +u'\u212f': '$\\mathscr{e}$', +u'\u2130': '$\\mathscr{E}$', +u'\u2131': '$\\mathscr{F}$', +u'\u2133': '$\\mathscr{M}$', +u'\u2134': '$\\mathscr{o}$', +u'\u2135': '$\\aleph$', +u'\u2136': '$\\beth$', +u'\u2137': '$\\gimel$', +u'\u2138': '$\\daleth$', +u'\u2153': '$\\textfrac{1}{3}$', +u'\u2154': '$\\textfrac{2}{3}$', +u'\u2155': '$\\textfrac{1}{5}$', +u'\u2156': '$\\textfrac{2}{5}$', +u'\u2157': '$\\textfrac{3}{5}$', +u'\u2158': '$\\textfrac{4}{5}$', +u'\u2159': '$\\textfrac{1}{6}$', +u'\u215a': '$\\textfrac{5}{6}$', +u'\u215b': '$\\textfrac{1}{8}$', +u'\u215c': '$\\textfrac{3}{8}$', +u'\u215d': '$\\textfrac{5}{8}$', +u'\u215e': '$\\textfrac{7}{8}$', +u'\u2190': '$\\leftarrow$', +u'\u2191': '$\\uparrow$', +u'\u2192': '$\\rightarrow$', +u'\u2193': '$\\downarrow$', +u'\u2194': '$\\leftrightarrow$', +u'\u2195': '$\\updownarrow$', +u'\u2196': '$\\nwarrow$', +u'\u2197': '$\\nearrow$', +u'\u2198': '$\\searrow$', +u'\u2199': '$\\swarrow$', +u'\u219a': '$\\nleftarrow$', +u'\u219b': '$\\nrightarrow$', +u'\u219c': '$\\arrowwaveright$', +u'\u219d': '$\\arrowwaveright$', +u'\u219e': '$\\twoheadleftarrow$', +u'\u21a0': '$\\twoheadrightarrow$', +u'\u21a2': '$\\leftarrowtail$', +u'\u21a3': '$\\rightarrowtail$', +u'\u21a6': '$\\mapsto$', +u'\u21a9': '$\\hookleftarrow$', +u'\u21aa': '$\\hookrightarrow$', +u'\u21ab': '$\\looparrowleft$', +u'\u21ac': '$\\looparrowright$', +u'\u21ad': '$\\leftrightsquigarrow$', +u'\u21ae': '$\\nleftrightarrow$', +u'\u21b0': '$\\Lsh$', +u'\u21b1': '$\\Rsh$', +u'\u21b3': '$\\ElsevierGlyph{21B3}$', +u'\u21b6': '$\\curvearrowleft$', +u'\u21b7': '$\\curvearrowright$', +u'\u21ba': '$\\circlearrowleft$', +u'\u21bb': '$\\circlearrowright$', +u'\u21bc': '$\\leftharpoonup$', +u'\u21bd': '$\\leftharpoondown$', +u'\u21be': '$\\upharpoonright$', +u'\u21bf': '$\\upharpoonleft$', +u'\u21c0': '$\\rightharpoonup$', +u'\u21c1': '$\\rightharpoondown$', +u'\u21c2': '$\\downharpoonright$', +u'\u21c3': '$\\downharpoonleft$', +u'\u21c4': '$\\rightleftarrows$', +u'\u21c5': '$\\dblarrowupdown$', +u'\u21c6': '$\\leftrightarrows$', +u'\u21c7': '$\\leftleftarrows$', +u'\u21c8': '$\\upuparrows$', +u'\u21c9': '$\\rightrightarrows$', +u'\u21ca': '$\\downdownarrows$', +u'\u21cb': '$\\leftrightharpoons$', +u'\u21cc': '$\\rightleftharpoons$', +u'\u21cd': '$\\nLeftarrow$', +u'\u21ce': '$\\nLeftrightarrow$', +u'\u21cf': '$\\nRightarrow$', +u'\u21d0': '$\\Leftarrow$', +u'\u21d1': '$\\Uparrow$', +u'\u21d2': '$\\Rightarrow$', +u'\u21d3': '$\\Downarrow$', +u'\u21d4': '$\\Leftrightarrow$', +u'\u21d5': '$\\Updownarrow$', +u'\u21da': '$\\Lleftarrow$', +u'\u21db': '$\\Rrightarrow$', +u'\u21dd': '$\\rightsquigarrow$', +u'\u21f5': '$\\DownArrowUpArrow$', +u'\u2200': '$\\forall$', +u'\u2201': '$\\complement$', +u'\u2202': '$\\partial$', +u'\u2203': '$\\exists$', +u'\u2204': '$\\nexists$', +u'\u2205': '$\\varnothing$', +u'\u2207': '$\\nabla$', +u'\u2208': '$\\in$', +u'\u2209': '$\\not\\in$', +u'\u220b': '$\\ni$', +u'\u220c': '$\\not\\ni$', +u'\u220f': '$\\prod$', +u'\u2210': '$\\coprod$', +u'\u2211': '$\\sum$', +u'\u2212': '{-}', +u'\u2213': '$\\mp$', +u'\u2214': '$\\dotplus$', +u'\u2216': '$\\setminus$', +u'\u2217': '${_\\ast}$', +u'\u2218': '$\\circ$', +u'\u2219': '$\\bullet$', +u'\u221a': '$\\surd$', +u'\u221d': '$\\propto$', +u'\u221e': '$\\infty$', +u'\u221f': '$\\rightangle$', +u'\u2220': '$\\angle$', +u'\u2221': '$\\measuredangle$', +u'\u2222': '$\\sphericalangle$', +u'\u2223': '$\\mid$', +u'\u2224': '$\\nmid$', +u'\u2225': '$\\parallel$', +u'\u2226': '$\\nparallel$', +u'\u2227': '$\\wedge$', +u'\u2228': '$\\vee$', +u'\u2229': '$\\cap$', +u'\u222a': '$\\cup$', +u'\u222b': '$\\int$', +u'\u222c': '$\\int\\!\\int$', +u'\u222d': '$\\int\\!\\int\\!\\int$', +u'\u222e': '$\\oint$', +u'\u222f': '$\\surfintegral$', +u'\u2230': '$\\volintegral$', +u'\u2231': '$\\clwintegral$', +u'\u2232': '$\\ElsevierGlyph{2232}$', +u'\u2233': '$\\ElsevierGlyph{2233}$', +u'\u2234': '$\\therefore$', +u'\u2235': '$\\because$', +u'\u2237': '$\\Colon$', +u'\u2238': '$\\ElsevierGlyph{2238}$', +u'\u223a': '$\\mathbin{{:}\\!\\!{-}\\!\\!{:}}$', +u'\u223b': '$\\homothetic$', +u'\u223c': '$\\sim$', +u'\u223d': '$\\backsim$', +u'\u223e': '$\\lazysinv$', +u'\u2240': '$\\wr$', +u'\u2241': '$\\not\\sim$', +u'\u2242': '$\\ElsevierGlyph{2242}$', +u'\u2243': '$\\simeq$', +u'\u2244': '$\\not\\simeq$', +u'\u2245': '$\\cong$', +u'\u2246': '$\\approxnotequal$', +u'\u2247': '$\\not\\cong$', +u'\u2248': '$\\approx$', +u'\u2249': '$\\not\\approx$', +u'\u224a': '$\\approxeq$', +u'\u224b': '$\\tildetrpl$', +u'\u224c': '$\\allequal$', +u'\u224d': '$\\asymp$', +u'\u224e': '$\\Bumpeq$', +u'\u224f': '$\\bumpeq$', +u'\u2250': '$\\doteq$', +u'\u2251': '$\\doteqdot$', +u'\u2252': '$\\fallingdotseq$', +u'\u2253': '$\\risingdotseq$', +u'\u2254': '{:=}', +u'\u2255': '$=:$', +u'\u2256': '$\\eqcirc$', +u'\u2257': '$\\circeq$', +u'\u2259': '$\\estimates$', +u'\u225a': '$\\ElsevierGlyph{225A}$', +u'\u225b': '$\\starequal$', +u'\u225c': '$\\triangleq$', +u'\u225f': '$\\ElsevierGlyph{225F}$', +u'\u2260': '$\\not =$', +u'\u2261': '$\\equiv$', +u'\u2262': '$\\not\\equiv$', +u'\u2264': '$\\leq$', +u'\u2265': '$\\geq$', +u'\u2266': '$\\leqq$', +u'\u2267': '$\\geqq$', +u'\u2268': '$\\lneqq$', +u'\u2269': '$\\gneqq$', +u'\u226a': '$\\ll$', +u'\u226b': '$\\gg$', +u'\u226c': '$\\between$', +u'\u226d': '$\\not\\kern-0.3em\\times$', +u'\u226e': '$\\not<$', +u'\u226f': '$\\not>$', +u'\u2270': '$\\not\\leq$', +u'\u2271': '$\\not\\geq$', +u'\u2272': '$\\lessequivlnt$', +u'\u2273': '$\\greaterequivlnt$', +u'\u2274': '$\\ElsevierGlyph{2274}$', +u'\u2275': '$\\ElsevierGlyph{2275}$', +u'\u2276': '$\\lessgtr$', +u'\u2277': '$\\gtrless$', +u'\u2278': '$\\notlessgreater$', +u'\u2279': '$\\notgreaterless$', +u'\u227a': '$\\prec$', +u'\u227b': '$\\succ$', +u'\u227c': '$\\preccurlyeq$', +u'\u227d': '$\\succcurlyeq$', +u'\u227e': '$\\precapprox$', +u'\u227f': '$\\succapprox$', +u'\u2280': '$\\not\\prec$', +u'\u2281': '$\\not\\succ$', +u'\u2282': '$\\subset$', +u'\u2283': '$\\supset$', +u'\u2284': '$\\not\\subset$', +u'\u2285': '$\\not\\supset$', +u'\u2286': '$\\subseteq$', +u'\u2287': '$\\supseteq$', +u'\u2288': '$\\not\\subseteq$', +u'\u2289': '$\\not\\supseteq$', +u'\u228a': '$\\subsetneq$', +u'\u228b': '$\\supsetneq$', +u'\u228e': '$\\uplus$', +u'\u228f': '$\\sqsubset$', +u'\u2290': '$\\sqsupset$', +u'\u2291': '$\\sqsubseteq$', +u'\u2292': '$\\sqsupseteq$', +u'\u2293': '$\\sqcap$', +u'\u2294': '$\\sqcup$', +u'\u2295': '$\\oplus$', +u'\u2296': '$\\ominus$', +u'\u2297': '$\\otimes$', +u'\u2298': '$\\oslash$', +u'\u2299': '$\\odot$', +u'\u229a': '$\\circledcirc$', +u'\u229b': '$\\circledast$', +u'\u229d': '$\\circleddash$', +u'\u229e': '$\\boxplus$', +u'\u229f': '$\\boxminus$', +u'\u22a0': '$\\boxtimes$', +u'\u22a1': '$\\boxdot$', +u'\u22a2': '$\\vdash$', +u'\u22a3': '$\\dashv$', +u'\u22a4': '$\\top$', +u'\u22a5': '$\\perp$', +u'\u22a7': '$\\truestate$', +u'\u22a8': '$\\forcesextra$', +u'\u22a9': '$\\Vdash$', +u'\u22aa': '$\\Vvdash$', +u'\u22ab': '$\\VDash$', +u'\u22ac': '$\\nvdash$', +u'\u22ad': '$\\nvDash$', +u'\u22ae': '$\\nVdash$', +u'\u22af': '$\\nVDash$', +u'\u22b2': '$\\vartriangleleft$', +u'\u22b3': '$\\vartriangleright$', +u'\u22b4': '$\\trianglelefteq$', +u'\u22b5': '$\\trianglerighteq$', +u'\u22b6': '$\\original$', +u'\u22b7': '$\\image$', +u'\u22b8': '$\\multimap$', +u'\u22b9': '$\\hermitconjmatrix$', +u'\u22ba': '$\\intercal$', +u'\u22bb': '$\\veebar$', +u'\u22be': '$\\rightanglearc$', +u'\u22c0': '$\\ElsevierGlyph{22C0}$', +u'\u22c1': '$\\ElsevierGlyph{22C1}$', +u'\u22c2': '$\\bigcap$', +u'\u22c3': '$\\bigcup$', +u'\u22c4': '$\\diamond$', +u'\u22c5': '$\\cdot$', +u'\u22c6': '$\\star$', +u'\u22c7': '$\\divideontimes$', +u'\u22c8': '$\\bowtie$', +u'\u22c9': '$\\ltimes$', +u'\u22ca': '$\\rtimes$', +u'\u22cb': '$\\leftthreetimes$', +u'\u22cc': '$\\rightthreetimes$', +u'\u22cd': '$\\backsimeq$', +u'\u22ce': '$\\curlyvee$', +u'\u22cf': '$\\curlywedge$', +u'\u22d0': '$\\Subset$', +u'\u22d1': '$\\Supset$', +u'\u22d2': '$\\Cap$', +u'\u22d3': '$\\Cup$', +u'\u22d4': '$\\pitchfork$', +u'\u22d6': '$\\lessdot$', +u'\u22d7': '$\\gtrdot$', +u'\u22d8': '$\\verymuchless$', +u'\u22d9': '$\\verymuchgreater$', +u'\u22da': '$\\lesseqgtr$', +u'\u22db': '$\\gtreqless$', +u'\u22de': '$\\curlyeqprec$', +u'\u22df': '$\\curlyeqsucc$', +u'\u22e2': '$\\not\\sqsubseteq$', +u'\u22e3': '$\\not\\sqsupseteq$', +u'\u22e5': '$\\Elzsqspne$', +u'\u22e6': '$\\lnsim$', +u'\u22e7': '$\\gnsim$', +u'\u22e8': '$\\precedesnotsimilar$', +u'\u22e9': '$\\succnsim$', +u'\u22ea': '$\\ntriangleleft$', +u'\u22eb': '$\\ntriangleright$', +u'\u22ec': '$\\ntrianglelefteq$', +u'\u22ed': '$\\ntrianglerighteq$', +u'\u22ee': '$\\vdots$', +u'\u22ef': '$\\cdots$', +u'\u22f0': '$\\upslopeellipsis$', +u'\u22f1': '$\\downslopeellipsis$', +u'\u2305': '{\\barwedge}', +u'\u2306': '$\\perspcorrespond$', +u'\u2308': '$\\lceil$', +u'\u2309': '$\\rceil$', +u'\u230a': '$\\lfloor$', +u'\u230b': '$\\rfloor$', +u'\u2315': '$\\recorder$', +u'\u2316': '$\\mathchar"2208$', +u'\u231c': '$\\ulcorner$', +u'\u231d': '$\\urcorner$', +u'\u231e': '$\\llcorner$', +u'\u231f': '$\\lrcorner$', +u'\u2322': '$\\frown$', +u'\u2323': '$\\smile$', +u'\u2329': '$\\langle$', +u'\u232a': '$\\rangle$', +u'\u233d': '$\\ElsevierGlyph{E838}$', +u'\u23a3': '$\\Elzdlcorn$', +u'\u23b0': '$\\lmoustache$', +u'\u23b1': '$\\rmoustache$', +u'\u2423': '{\\textvisiblespace}', +u'\u2460': '{\\ding{172}}', +u'\u2461': '{\\ding{173}}', +u'\u2462': '{\\ding{174}}', +u'\u2463': '{\\ding{175}}', +u'\u2464': '{\\ding{176}}', +u'\u2465': '{\\ding{177}}', +u'\u2466': '{\\ding{178}}', +u'\u2467': '{\\ding{179}}', +u'\u2468': '{\\ding{180}}', +u'\u2469': '{\\ding{181}}', +u'\u24c8': '$\\circledS$', +u'\u2506': '$\\Elzdshfnc$', +u'\u2519': '$\\Elzsqfnw$', +u'\u2571': '$\\diagup$', +u'\u25a0': '{\\ding{110}}', +u'\u25a1': '$\\square$', +u'\u25aa': '$\\blacksquare$', +u'\u25ad': '$\\fbox{~~}$', +u'\u25af': '$\\Elzvrecto$', +u'\u25b1': '$\\ElsevierGlyph{E381}$', +u'\u25b2': '{\\ding{115}}', +u'\u25b3': '$\\bigtriangleup$', +u'\u25b4': '$\\blacktriangle$', +u'\u25b5': '$\\vartriangle$', +u'\u25b8': '$\\blacktriangleright$', +u'\u25b9': '$\\triangleright$', +u'\u25bc': '{\\ding{116}}', +u'\u25bd': '$\\bigtriangledown$', +u'\u25be': '$\\blacktriangledown$', +u'\u25bf': '$\\triangledown$', +u'\u25c2': '$\\blacktriangleleft$', +u'\u25c3': '$\\triangleleft$', +u'\u25c6': '{\\ding{117}}', +u'\u25ca': '$\\lozenge$', +u'\u25cb': '$\\bigcirc$', +u'\u25cf': '{\\ding{108}}', +u'\u25d0': '$\\Elzcirfl$', +u'\u25d1': '$\\Elzcirfr$', +u'\u25d2': '$\\Elzcirfb$', +u'\u25d7': '{\\ding{119}}', +u'\u25d8': '$\\Elzrvbull$', +u'\u25e7': '$\\Elzsqfl$', +u'\u25e8': '$\\Elzsqfr$', +u'\u25ea': '$\\Elzsqfse$', +u'\u25ef': '$\\bigcirc$', +u'\u2605': '{\\ding{72}}', +u'\u2606': '{\\ding{73}}', +u'\u260e': '{\\ding{37}}', +u'\u261b': '{\\ding{42}}', +u'\u261e': '{\\ding{43}}', +u'\u263e': '{\\rightmoon}', +u'\u263f': '{\\mercury}', +u'\u2640': '{\\venus}', +u'\u2642': '{\\male}', +u'\u2643': '{\\jupiter}', +u'\u2644': '{\\saturn}', +u'\u2645': '{\\uranus}', +u'\u2646': '{\\neptune}', +u'\u2647': '{\\pluto}', +u'\u2648': '{\\aries}', +u'\u2649': '{\\taurus}', +u'\u264a': '{\\gemini}', +u'\u264b': '{\\cancer}', +u'\u264c': '{\\leo}', +u'\u264d': '{\\virgo}', +u'\u264e': '{\\libra}', +u'\u264f': '{\\scorpio}', +u'\u2650': '{\\sagittarius}', +u'\u2651': '{\\capricornus}', +u'\u2652': '{\\aquarius}', +u'\u2653': '{\\pisces}', +u'\u2660': '{\\ding{171}}', +u'\u2662': '$\\diamond$', +u'\u2663': '{\\ding{168}}', +u'\u2665': '{\\ding{170}}', +u'\u2666': '{\\ding{169}}', +u'\u2669': '{\\quarternote}', +u'\u266a': '{\\eighthnote}', +u'\u266d': '$\\flat$', +u'\u266e': '$\\natural$', +u'\u266f': '$\\sharp$', +u'\u2701': '{\\ding{33}}', +u'\u2702': '{\\ding{34}}', +u'\u2703': '{\\ding{35}}', +u'\u2704': '{\\ding{36}}', +u'\u2706': '{\\ding{38}}', +u'\u2707': '{\\ding{39}}', +u'\u2708': '{\\ding{40}}', +u'\u2709': '{\\ding{41}}', +u'\u270c': '{\\ding{44}}', +u'\u270d': '{\\ding{45}}', +u'\u270e': '{\\ding{46}}', +u'\u270f': '{\\ding{47}}', +u'\u2710': '{\\ding{48}}', +u'\u2711': '{\\ding{49}}', +u'\u2712': '{\\ding{50}}', +u'\u2713': '{\\ding{51}}', +u'\u2714': '{\\ding{52}}', +u'\u2715': '{\\ding{53}}', +u'\u2716': '{\\ding{54}}', +u'\u2717': '{\\ding{55}}', +u'\u2718': '{\\ding{56}}', +u'\u2719': '{\\ding{57}}', +u'\u271a': '{\\ding{58}}', +u'\u271b': '{\\ding{59}}', +u'\u271c': '{\\ding{60}}', +u'\u271d': '{\\ding{61}}', +u'\u271e': '{\\ding{62}}', +u'\u271f': '{\\ding{63}}', +u'\u2720': '{\\ding{64}}', +u'\u2721': '{\\ding{65}}', +u'\u2722': '{\\ding{66}}', +u'\u2723': '{\\ding{67}}', +u'\u2724': '{\\ding{68}}', +u'\u2725': '{\\ding{69}}', +u'\u2726': '{\\ding{70}}', +u'\u2727': '{\\ding{71}}', +u'\u2729': '{\\ding{73}}', +u'\u272a': '{\\ding{74}}', +u'\u272b': '{\\ding{75}}', +u'\u272c': '{\\ding{76}}', +u'\u272d': '{\\ding{77}}', +u'\u272e': '{\\ding{78}}', +u'\u272f': '{\\ding{79}}', +u'\u2730': '{\\ding{80}}', +u'\u2731': '{\\ding{81}}', +u'\u2732': '{\\ding{82}}', +u'\u2733': '{\\ding{83}}', +u'\u2734': '{\\ding{84}}', +u'\u2735': '{\\ding{85}}', +u'\u2736': '{\\ding{86}}', +u'\u2737': '{\\ding{87}}', +u'\u2738': '{\\ding{88}}', +u'\u2739': '{\\ding{89}}', +u'\u273a': '{\\ding{90}}', +u'\u273b': '{\\ding{91}}', +u'\u273c': '{\\ding{92}}', +u'\u273d': '{\\ding{93}}', +u'\u273e': '{\\ding{94}}', +u'\u273f': '{\\ding{95}}', +u'\u2740': '{\\ding{96}}', +u'\u2741': '{\\ding{97}}', +u'\u2742': '{\\ding{98}}', +u'\u2743': '{\\ding{99}}', +u'\u2744': '{\\ding{100}}', +u'\u2745': '{\\ding{101}}', +u'\u2746': '{\\ding{102}}', +u'\u2747': '{\\ding{103}}', +u'\u2748': '{\\ding{104}}', +u'\u2749': '{\\ding{105}}', +u'\u274a': '{\\ding{106}}', +u'\u274b': '{\\ding{107}}', +u'\u274d': '{\\ding{109}}', +u'\u274f': '{\\ding{111}}', +u'\u2750': '{\\ding{112}}', +u'\u2751': '{\\ding{113}}', +u'\u2752': '{\\ding{114}}', +u'\u2756': '{\\ding{118}}', +u'\u2758': '{\\ding{120}}', +u'\u2759': '{\\ding{121}}', +u'\u275a': '{\\ding{122}}', +u'\u275b': '{\\ding{123}}', +u'\u275c': '{\\ding{124}}', +u'\u275d': '{\\ding{125}}', +u'\u275e': '{\\ding{126}}', +u'\u2761': '{\\ding{161}}', +u'\u2762': '{\\ding{162}}', +u'\u2763': '{\\ding{163}}', +u'\u2764': '{\\ding{164}}', +u'\u2765': '{\\ding{165}}', +u'\u2766': '{\\ding{166}}', +u'\u2767': '{\\ding{167}}', +u'\u2776': '{\\ding{182}}', +u'\u2777': '{\\ding{183}}', +u'\u2778': '{\\ding{184}}', +u'\u2779': '{\\ding{185}}', +u'\u277a': '{\\ding{186}}', +u'\u277b': '{\\ding{187}}', +u'\u277c': '{\\ding{188}}', +u'\u277d': '{\\ding{189}}', +u'\u277e': '{\\ding{190}}', +u'\u277f': '{\\ding{191}}', +u'\u2780': '{\\ding{192}}', +u'\u2781': '{\\ding{193}}', +u'\u2782': '{\\ding{194}}', +u'\u2783': '{\\ding{195}}', +u'\u2784': '{\\ding{196}}', +u'\u2785': '{\\ding{197}}', +u'\u2786': '{\\ding{198}}', +u'\u2787': '{\\ding{199}}', +u'\u2788': '{\\ding{200}}', +u'\u2789': '{\\ding{201}}', +u'\u278a': '{\\ding{202}}', +u'\u278b': '{\\ding{203}}', +u'\u278c': '{\\ding{204}}', +u'\u278d': '{\\ding{205}}', +u'\u278e': '{\\ding{206}}', +u'\u278f': '{\\ding{207}}', +u'\u2790': '{\\ding{208}}', +u'\u2791': '{\\ding{209}}', +u'\u2792': '{\\ding{210}}', +u'\u2793': '{\\ding{211}}', +u'\u2794': '{\\ding{212}}', +u'\u2798': '{\\ding{216}}', +u'\u2799': '{\\ding{217}}', +u'\u279a': '{\\ding{218}}', +u'\u279b': '{\\ding{219}}', +u'\u279c': '{\\ding{220}}', +u'\u279d': '{\\ding{221}}', +u'\u279e': '{\\ding{222}}', +u'\u279f': '{\\ding{223}}', +u'\u27a0': '{\\ding{224}}', +u'\u27a1': '{\\ding{225}}', +u'\u27a2': '{\\ding{226}}', +u'\u27a3': '{\\ding{227}}', +u'\u27a4': '{\\ding{228}}', +u'\u27a5': '{\\ding{229}}', +u'\u27a6': '{\\ding{230}}', +u'\u27a7': '{\\ding{231}}', +u'\u27a8': '{\\ding{232}}', +u'\u27a9': '{\\ding{233}}', +u'\u27aa': '{\\ding{234}}', +u'\u27ab': '{\\ding{235}}', +u'\u27ac': '{\\ding{236}}', +u'\u27ad': '{\\ding{237}}', +u'\u27ae': '{\\ding{238}}', +u'\u27af': '{\\ding{239}}', +u'\u27b1': '{\\ding{241}}', +u'\u27b2': '{\\ding{242}}', +u'\u27b3': '{\\ding{243}}', +u'\u27b4': '{\\ding{244}}', +u'\u27b5': '{\\ding{245}}', +u'\u27b6': '{\\ding{246}}', +u'\u27b7': '{\\ding{247}}', +u'\u27b8': '{\\ding{248}}', +u'\u27b9': '{\\ding{249}}', +u'\u27ba': '{\\ding{250}}', +u'\u27bb': '{\\ding{251}}', +u'\u27bc': '{\\ding{252}}', +u'\u27bd': '{\\ding{253}}', +u'\u27be': '{\\ding{254}}', +u'\u27f5': '$\\longleftarrow$', +u'\u27f6': '$\\longrightarrow$', +u'\u27f7': '$\\longleftrightarrow$', +u'\u27f8': '$\\Longleftarrow$', +u'\u27f9': '$\\Longrightarrow$', +u'\u27fa': '$\\Longleftrightarrow$', +u'\u27fc': '$\\longmapsto$', +u'\u27ff': '$\\sim\\joinrel\\leadsto$', +u'\u2905': '$\\ElsevierGlyph{E212}$', +u'\u2912': '$\\UpArrowBar$', +u'\u2913': '$\\DownArrowBar$', +u'\u2923': '$\\ElsevierGlyph{E20C}$', +u'\u2924': '$\\ElsevierGlyph{E20D}$', +u'\u2925': '$\\ElsevierGlyph{E20B}$', +u'\u2926': '$\\ElsevierGlyph{E20A}$', +u'\u2927': '$\\ElsevierGlyph{E211}$', +u'\u2928': '$\\ElsevierGlyph{E20E}$', +u'\u2929': '$\\ElsevierGlyph{E20F}$', +u'\u292a': '$\\ElsevierGlyph{E210}$', +u'\u2933': '$\\ElsevierGlyph{E21C}$', +u'\u2936': '$\\ElsevierGlyph{E21A}$', +u'\u2937': '$\\ElsevierGlyph{E219}$', +u'\u2940': '$\\Elolarr$', +u'\u2941': '$\\Elorarr$', +u'\u2942': '$\\ElzRlarr$', +u'\u2944': '$\\ElzrLarr$', +u'\u2947': '$\\Elzrarrx$', +u'\u294e': '$\\LeftRightVector$', +u'\u294f': '$\\RightUpDownVector$', +u'\u2950': '$\\DownLeftRightVector$', +u'\u2951': '$\\LeftUpDownVector$', +u'\u2952': '$\\LeftVectorBar$', +u'\u2953': '$\\RightVectorBar$', +u'\u2954': '$\\RightUpVectorBar$', +u'\u2955': '$\\RightDownVectorBar$', +u'\u2956': '$\\DownLeftVectorBar$', +u'\u2957': '$\\DownRightVectorBar$', +u'\u2958': '$\\LeftUpVectorBar$', +u'\u2959': '$\\LeftDownVectorBar$', +u'\u295a': '$\\LeftTeeVector$', +u'\u295b': '$\\RightTeeVector$', +u'\u295c': '$\\RightUpTeeVector$', +u'\u295d': '$\\RightDownTeeVector$', +u'\u295e': '$\\DownLeftTeeVector$', +u'\u295f': '$\\DownRightTeeVector$', +u'\u2960': '$\\LeftUpTeeVector$', +u'\u2961': '$\\LeftDownTeeVector$', +u'\u296e': '$\\UpEquilibrium$', +u'\u296f': '$\\ReverseUpEquilibrium$', +u'\u2970': '$\\RoundImplies$', +u'\u297c': '$\\ElsevierGlyph{E214}$', +u'\u297d': '$\\ElsevierGlyph{E215}$', +u'\u2980': '$\\Elztfnc$', +u'\u2985': '$\\ElsevierGlyph{3018}$', +u'\u2986': '$\\Elroang$', +u'\u2993': '$<\\kern-0.58em($', +u'\u2994': '$\\ElsevierGlyph{E291}$', +u'\u2999': '$\\Elzddfnc$', +u'\u299c': '$\\Angle$', +u'\u29a0': '$\\Elzlpargt$', +u'\u29b5': '$\\ElsevierGlyph{E260}$', +u'\u29b6': '$\\ElsevierGlyph{E61B}$', +u'\u29ca': '$\\ElzLap$', +u'\u29cb': '$\\Elzdefas$', +u'\u29cf': '$\\LeftTriangleBar$', +u'\u29d0': '$\\RightTriangleBar$', +u'\u29dc': '$\\ElsevierGlyph{E372}$', +u'\u29eb': '$\\blacklozenge$', +u'\u29f4': '$\\RuleDelayed$', +u'\u2a04': '$\\Elxuplus$', +u'\u2a05': '$\\ElzThr$', +u'\u2a06': '$\\Elxsqcup$', +u'\u2a07': '$\\ElzInf$', +u'\u2a08': '$\\ElzSup$', +u'\u2a0d': '$\\ElzCint$', +u'\u2a0f': '$\\clockoint$', +u'\u2a10': '$\\ElsevierGlyph{E395}$', +u'\u2a16': '$\\sqrint$', +u'\u2a25': '$\\ElsevierGlyph{E25A}$', +u'\u2a2a': '$\\ElsevierGlyph{E25B}$', +u'\u2a2d': '$\\ElsevierGlyph{E25C}$', +u'\u2a2e': '$\\ElsevierGlyph{E25D}$', +u'\u2a2f': '$\\ElzTimes$', +u'\u2a34': '$\\ElsevierGlyph{E25E}$', +u'\u2a35': '$\\ElsevierGlyph{E25E}$', +u'\u2a3c': '$\\ElsevierGlyph{E259}$', +u'\u2a3f': '$\\amalg$', +u'\u2a53': '$\\ElzAnd$', +u'\u2a54': '$\\ElzOr$', +u'\u2a55': '$\\ElsevierGlyph{E36E}$', +u'\u2a56': '$\\ElOr$', +u'\u2a5e': '$\\perspcorrespond$', +u'\u2a5f': '$\\Elzminhat$', +u'\u2a63': '$\\ElsevierGlyph{225A}$', +u'\u2a6e': '$\\stackrel{*}{=}$', +u'\u2a75': '$\\Equal$', +u'\u2a7d': '$\\leqslant$', +u'\u2a7e': '$\\geqslant$', +u'\u2a85': '$\\lessapprox$', +u'\u2a86': '$\\gtrapprox$', +u'\u2a87': '$\\lneq$', +u'\u2a88': '$\\gneq$', +u'\u2a89': '$\\lnapprox$', +u'\u2a8a': '$\\gnapprox$', +u'\u2a8b': '$\\lesseqqgtr$', +u'\u2a8c': '$\\gtreqqless$', +u'\u2a95': '$\\eqslantless$', +u'\u2a96': '$\\eqslantgtr$', +u'\u2a9d': '$\\Pisymbol{ppi020}{117}$', +u'\u2a9e': '$\\Pisymbol{ppi020}{105}$', +u'\u2aa1': '$\\NestedLessLess$', +u'\u2aa2': '$\\NestedGreaterGreater$', +u'\u2aaf': '$\\preceq$', +u'\u2ab0': '$\\succeq$', +u'\u2ab5': '$\\precneqq$', +u'\u2ab6': '$\\succneqq$', +u'\u2ab7': '$\\precapprox$', +u'\u2ab8': '$\\succapprox$', +u'\u2ab9': '$\\precnapprox$', +u'\u2aba': '$\\succnapprox$', +u'\u2ac5': '$\\subseteqq$', +u'\u2ac6': '$\\supseteqq$', +u'\u2acb': '$\\subsetneqq$', +u'\u2acc': '$\\supsetneqq$', +u'\u2aeb': '$\\ElsevierGlyph{E30D}$', +u'\u2af6': '$\\Elztdcol$', +u'\u2afd': '${{/}\\!\\!{/}}$', +u'\u300a': '$\\ElsevierGlyph{300A}$', +u'\u300b': '$\\ElsevierGlyph{300B}$', +u'\u3018': '$\\ElsevierGlyph{3018}$', +u'\u3019': '$\\ElsevierGlyph{3019}$', +u'\u301a': '$\\openbracketleft$', +u'\u301b': '$\\openbracketright$', +u'\ufb00': '{ff}', +u'\ufb01': '{fi}', +u'\ufb02': '{fl}', +u'\ufb03': '{ffi}', +u'\ufb04': '{ffl}', +u'\U0001d400': '$\\mathbf{A}$', +u'\U0001d401': '$\\mathbf{B}$', +u'\U0001d402': '$\\mathbf{C}$', +u'\U0001d403': '$\\mathbf{D}$', +u'\U0001d404': '$\\mathbf{E}$', +u'\U0001d405': '$\\mathbf{F}$', +u'\U0001d406': '$\\mathbf{G}$', +u'\U0001d407': '$\\mathbf{H}$', +u'\U0001d408': '$\\mathbf{I}$', +u'\U0001d409': '$\\mathbf{J}$', +u'\U0001d40a': '$\\mathbf{K}$', +u'\U0001d40b': '$\\mathbf{L}$', +u'\U0001d40c': '$\\mathbf{M}$', +u'\U0001d40d': '$\\mathbf{N}$', +u'\U0001d40e': '$\\mathbf{O}$', +u'\U0001d40f': '$\\mathbf{P}$', +u'\U0001d410': '$\\mathbf{Q}$', +u'\U0001d411': '$\\mathbf{R}$', +u'\U0001d412': '$\\mathbf{S}$', +u'\U0001d413': '$\\mathbf{T}$', +u'\U0001d414': '$\\mathbf{U}$', +u'\U0001d415': '$\\mathbf{V}$', +u'\U0001d416': '$\\mathbf{W}$', +u'\U0001d417': '$\\mathbf{X}$', +u'\U0001d418': '$\\mathbf{Y}$', +u'\U0001d419': '$\\mathbf{Z}$', +u'\U0001d41a': '$\\mathbf{a}$', +u'\U0001d41b': '$\\mathbf{b}$', +u'\U0001d41c': '$\\mathbf{c}$', +u'\U0001d41d': '$\\mathbf{d}$', +u'\U0001d41e': '$\\mathbf{e}$', +u'\U0001d41f': '$\\mathbf{f}$', +u'\U0001d420': '$\\mathbf{g}$', +u'\U0001d421': '$\\mathbf{h}$', +u'\U0001d422': '$\\mathbf{i}$', +u'\U0001d423': '$\\mathbf{j}$', +u'\U0001d424': '$\\mathbf{k}$', +u'\U0001d425': '$\\mathbf{l}$', +u'\U0001d426': '$\\mathbf{m}$', +u'\U0001d427': '$\\mathbf{n}$', +u'\U0001d428': '$\\mathbf{o}$', +u'\U0001d429': '$\\mathbf{p}$', +u'\U0001d42a': '$\\mathbf{q}$', +u'\U0001d42b': '$\\mathbf{r}$', +u'\U0001d42c': '$\\mathbf{s}$', +u'\U0001d42d': '$\\mathbf{t}$', +u'\U0001d42e': '$\\mathbf{u}$', +u'\U0001d42f': '$\\mathbf{v}$', +u'\U0001d430': '$\\mathbf{w}$', +u'\U0001d431': '$\\mathbf{x}$', +u'\U0001d432': '$\\mathbf{y}$', +u'\U0001d433': '$\\mathbf{z}$', +u'\U0001d434': '$\\mathsl{A}$', +u'\U0001d435': '$\\mathsl{B}$', +u'\U0001d436': '$\\mathsl{C}$', +u'\U0001d437': '$\\mathsl{D}$', +u'\U0001d438': '$\\mathsl{E}$', +u'\U0001d439': '$\\mathsl{F}$', +u'\U0001d43a': '$\\mathsl{G}$', +u'\U0001d43b': '$\\mathsl{H}$', +u'\U0001d43c': '$\\mathsl{I}$', +u'\U0001d43d': '$\\mathsl{J}$', +u'\U0001d43e': '$\\mathsl{K}$', +u'\U0001d43f': '$\\mathsl{L}$', +u'\U0001d440': '$\\mathsl{M}$', +u'\U0001d441': '$\\mathsl{N}$', +u'\U0001d442': '$\\mathsl{O}$', +u'\U0001d443': '$\\mathsl{P}$', +u'\U0001d444': '$\\mathsl{Q}$', +u'\U0001d445': '$\\mathsl{R}$', +u'\U0001d446': '$\\mathsl{S}$', +u'\U0001d447': '$\\mathsl{T}$', +u'\U0001d448': '$\\mathsl{U}$', +u'\U0001d449': '$\\mathsl{V}$', +u'\U0001d44a': '$\\mathsl{W}$', +u'\U0001d44b': '$\\mathsl{X}$', +u'\U0001d44c': '$\\mathsl{Y}$', +u'\U0001d44d': '$\\mathsl{Z}$', +u'\U0001d44e': '$\\mathsl{a}$', +u'\U0001d44f': '$\\mathsl{b}$', +u'\U0001d450': '$\\mathsl{c}$', +u'\U0001d451': '$\\mathsl{d}$', +u'\U0001d452': '$\\mathsl{e}$', +u'\U0001d453': '$\\mathsl{f}$', +u'\U0001d454': '$\\mathsl{g}$', +u'\U0001d456': '$\\mathsl{i}$', +u'\U0001d457': '$\\mathsl{j}$', +u'\U0001d458': '$\\mathsl{k}$', +u'\U0001d459': '$\\mathsl{l}$', +u'\U0001d45a': '$\\mathsl{m}$', +u'\U0001d45b': '$\\mathsl{n}$', +u'\U0001d45c': '$\\mathsl{o}$', +u'\U0001d45d': '$\\mathsl{p}$', +u'\U0001d45e': '$\\mathsl{q}$', +u'\U0001d45f': '$\\mathsl{r}$', +u'\U0001d460': '$\\mathsl{s}$', +u'\U0001d461': '$\\mathsl{t}$', +u'\U0001d462': '$\\mathsl{u}$', +u'\U0001d463': '$\\mathsl{v}$', +u'\U0001d464': '$\\mathsl{w}$', +u'\U0001d465': '$\\mathsl{x}$', +u'\U0001d466': '$\\mathsl{y}$', +u'\U0001d467': '$\\mathsl{z}$', +u'\U0001d468': '$\\mathbit{A}$', +u'\U0001d469': '$\\mathbit{B}$', +u'\U0001d46a': '$\\mathbit{C}$', +u'\U0001d46b': '$\\mathbit{D}$', +u'\U0001d46c': '$\\mathbit{E}$', +u'\U0001d46d': '$\\mathbit{F}$', +u'\U0001d46e': '$\\mathbit{G}$', +u'\U0001d46f': '$\\mathbit{H}$', +u'\U0001d470': '$\\mathbit{I}$', +u'\U0001d471': '$\\mathbit{J}$', +u'\U0001d472': '$\\mathbit{K}$', +u'\U0001d473': '$\\mathbit{L}$', +u'\U0001d474': '$\\mathbit{M}$', +u'\U0001d475': '$\\mathbit{N}$', +u'\U0001d476': '$\\mathbit{O}$', +u'\U0001d477': '$\\mathbit{P}$', +u'\U0001d478': '$\\mathbit{Q}$', +u'\U0001d479': '$\\mathbit{R}$', +u'\U0001d47a': '$\\mathbit{S}$', +u'\U0001d47b': '$\\mathbit{T}$', +u'\U0001d47c': '$\\mathbit{U}$', +u'\U0001d47d': '$\\mathbit{V}$', +u'\U0001d47e': '$\\mathbit{W}$', +u'\U0001d47f': '$\\mathbit{X}$', +u'\U0001d480': '$\\mathbit{Y}$', +u'\U0001d481': '$\\mathbit{Z}$', +u'\U0001d482': '$\\mathbit{a}$', +u'\U0001d483': '$\\mathbit{b}$', +u'\U0001d484': '$\\mathbit{c}$', +u'\U0001d485': '$\\mathbit{d}$', +u'\U0001d486': '$\\mathbit{e}$', +u'\U0001d487': '$\\mathbit{f}$', +u'\U0001d488': '$\\mathbit{g}$', +u'\U0001d489': '$\\mathbit{h}$', +u'\U0001d48a': '$\\mathbit{i}$', +u'\U0001d48b': '$\\mathbit{j}$', +u'\U0001d48c': '$\\mathbit{k}$', +u'\U0001d48d': '$\\mathbit{l}$', +u'\U0001d48e': '$\\mathbit{m}$', +u'\U0001d48f': '$\\mathbit{n}$', +u'\U0001d490': '$\\mathbit{o}$', +u'\U0001d491': '$\\mathbit{p}$', +u'\U0001d492': '$\\mathbit{q}$', +u'\U0001d493': '$\\mathbit{r}$', +u'\U0001d494': '$\\mathbit{s}$', +u'\U0001d495': '$\\mathbit{t}$', +u'\U0001d496': '$\\mathbit{u}$', +u'\U0001d497': '$\\mathbit{v}$', +u'\U0001d498': '$\\mathbit{w}$', +u'\U0001d499': '$\\mathbit{x}$', +u'\U0001d49a': '$\\mathbit{y}$', +u'\U0001d49b': '$\\mathbit{z}$', +u'\U0001d49c': '$\\mathscr{A}$', +u'\U0001d49e': '$\\mathscr{C}$', +u'\U0001d49f': '$\\mathscr{D}$', +u'\U0001d4a2': '$\\mathscr{G}$', +u'\U0001d4a5': '$\\mathscr{J}$', +u'\U0001d4a6': '$\\mathscr{K}$', +u'\U0001d4a9': '$\\mathscr{N}$', +u'\U0001d4aa': '$\\mathscr{O}$', +u'\U0001d4ab': '$\\mathscr{P}$', +u'\U0001d4ac': '$\\mathscr{Q}$', +u'\U0001d4ae': '$\\mathscr{S}$', +u'\U0001d4af': '$\\mathscr{T}$', +u'\U0001d4b0': '$\\mathscr{U}$', +u'\U0001d4b1': '$\\mathscr{V}$', +u'\U0001d4b2': '$\\mathscr{W}$', +u'\U0001d4b3': '$\\mathscr{X}$', +u'\U0001d4b4': '$\\mathscr{Y}$', +u'\U0001d4b5': '$\\mathscr{Z}$', +u'\U0001d4b6': '$\\mathscr{a}$', +u'\U0001d4b7': '$\\mathscr{b}$', +u'\U0001d4b8': '$\\mathscr{c}$', +u'\U0001d4b9': '$\\mathscr{d}$', +u'\U0001d4bb': '$\\mathscr{f}$', +u'\U0001d4bd': '$\\mathscr{h}$', +u'\U0001d4be': '$\\mathscr{i}$', +u'\U0001d4bf': '$\\mathscr{j}$', +u'\U0001d4c0': '$\\mathscr{k}$', +u'\U0001d4c1': '$\\mathscr{l}$', +u'\U0001d4c2': '$\\mathscr{m}$', +u'\U0001d4c3': '$\\mathscr{n}$', +u'\U0001d4c5': '$\\mathscr{p}$', +u'\U0001d4c6': '$\\mathscr{q}$', +u'\U0001d4c7': '$\\mathscr{r}$', +u'\U0001d4c8': '$\\mathscr{s}$', +u'\U0001d4c9': '$\\mathscr{t}$', +u'\U0001d4ca': '$\\mathscr{u}$', +u'\U0001d4cb': '$\\mathscr{v}$', +u'\U0001d4cc': '$\\mathscr{w}$', +u'\U0001d4cd': '$\\mathscr{x}$', +u'\U0001d4ce': '$\\mathscr{y}$', +u'\U0001d4cf': '$\\mathscr{z}$', +u'\U0001d4d0': '$\\mathmit{A}$', +u'\U0001d4d1': '$\\mathmit{B}$', +u'\U0001d4d2': '$\\mathmit{C}$', +u'\U0001d4d3': '$\\mathmit{D}$', +u'\U0001d4d4': '$\\mathmit{E}$', +u'\U0001d4d5': '$\\mathmit{F}$', +u'\U0001d4d6': '$\\mathmit{G}$', +u'\U0001d4d7': '$\\mathmit{H}$', +u'\U0001d4d8': '$\\mathmit{I}$', +u'\U0001d4d9': '$\\mathmit{J}$', +u'\U0001d4da': '$\\mathmit{K}$', +u'\U0001d4db': '$\\mathmit{L}$', +u'\U0001d4dc': '$\\mathmit{M}$', +u'\U0001d4dd': '$\\mathmit{N}$', +u'\U0001d4de': '$\\mathmit{O}$', +u'\U0001d4df': '$\\mathmit{P}$', +u'\U0001d4e0': '$\\mathmit{Q}$', +u'\U0001d4e1': '$\\mathmit{R}$', +u'\U0001d4e2': '$\\mathmit{S}$', +u'\U0001d4e3': '$\\mathmit{T}$', +u'\U0001d4e4': '$\\mathmit{U}$', +u'\U0001d4e5': '$\\mathmit{V}$', +u'\U0001d4e6': '$\\mathmit{W}$', +u'\U0001d4e7': '$\\mathmit{X}$', +u'\U0001d4e8': '$\\mathmit{Y}$', +u'\U0001d4e9': '$\\mathmit{Z}$', +u'\U0001d4ea': '$\\mathmit{a}$', +u'\U0001d4eb': '$\\mathmit{b}$', +u'\U0001d4ec': '$\\mathmit{c}$', +u'\U0001d4ed': '$\\mathmit{d}$', +u'\U0001d4ee': '$\\mathmit{e}$', +u'\U0001d4ef': '$\\mathmit{f}$', +u'\U0001d4f0': '$\\mathmit{g}$', +u'\U0001d4f1': '$\\mathmit{h}$', +u'\U0001d4f2': '$\\mathmit{i}$', +u'\U0001d4f3': '$\\mathmit{j}$', +u'\U0001d4f4': '$\\mathmit{k}$', +u'\U0001d4f5': '$\\mathmit{l}$', +u'\U0001d4f6': '$\\mathmit{m}$', +u'\U0001d4f7': '$\\mathmit{n}$', +u'\U0001d4f8': '$\\mathmit{o}$', +u'\U0001d4f9': '$\\mathmit{p}$', +u'\U0001d4fa': '$\\mathmit{q}$', +u'\U0001d4fb': '$\\mathmit{r}$', +u'\U0001d4fc': '$\\mathmit{s}$', +u'\U0001d4fd': '$\\mathmit{t}$', +u'\U0001d4fe': '$\\mathmit{u}$', +u'\U0001d4ff': '$\\mathmit{v}$', +u'\U0001d500': '$\\mathmit{w}$', +u'\U0001d501': '$\\mathmit{x}$', +u'\U0001d502': '$\\mathmit{y}$', +u'\U0001d503': '$\\mathmit{z}$', +u'\U0001d504': '$\\mathfrak{A}$', +u'\U0001d505': '$\\mathfrak{B}$', +u'\U0001d507': '$\\mathfrak{D}$', +u'\U0001d508': '$\\mathfrak{E}$', +u'\U0001d509': '$\\mathfrak{F}$', +u'\U0001d50a': '$\\mathfrak{G}$', +u'\U0001d50d': '$\\mathfrak{J}$', +u'\U0001d50e': '$\\mathfrak{K}$', +u'\U0001d50f': '$\\mathfrak{L}$', +u'\U0001d510': '$\\mathfrak{M}$', +u'\U0001d511': '$\\mathfrak{N}$', +u'\U0001d512': '$\\mathfrak{O}$', +u'\U0001d513': '$\\mathfrak{P}$', +u'\U0001d514': '$\\mathfrak{Q}$', +u'\U0001d516': '$\\mathfrak{S}$', +u'\U0001d517': '$\\mathfrak{T}$', +u'\U0001d518': '$\\mathfrak{U}$', +u'\U0001d519': '$\\mathfrak{V}$', +u'\U0001d51a': '$\\mathfrak{W}$', +u'\U0001d51b': '$\\mathfrak{X}$', +u'\U0001d51c': '$\\mathfrak{Y}$', +u'\U0001d51e': '$\\mathfrak{a}$', +u'\U0001d51f': '$\\mathfrak{b}$', +u'\U0001d520': '$\\mathfrak{c}$', +u'\U0001d521': '$\\mathfrak{d}$', +u'\U0001d522': '$\\mathfrak{e}$', +u'\U0001d523': '$\\mathfrak{f}$', +u'\U0001d524': '$\\mathfrak{g}$', +u'\U0001d525': '$\\mathfrak{h}$', +u'\U0001d526': '$\\mathfrak{i}$', +u'\U0001d527': '$\\mathfrak{j}$', +u'\U0001d528': '$\\mathfrak{k}$', +u'\U0001d529': '$\\mathfrak{l}$', +u'\U0001d52a': '$\\mathfrak{m}$', +u'\U0001d52b': '$\\mathfrak{n}$', +u'\U0001d52c': '$\\mathfrak{o}$', +u'\U0001d52d': '$\\mathfrak{p}$', +u'\U0001d52e': '$\\mathfrak{q}$', +u'\U0001d52f': '$\\mathfrak{r}$', +u'\U0001d530': '$\\mathfrak{s}$', +u'\U0001d531': '$\\mathfrak{t}$', +u'\U0001d532': '$\\mathfrak{u}$', +u'\U0001d533': '$\\mathfrak{v}$', +u'\U0001d534': '$\\mathfrak{w}$', +u'\U0001d535': '$\\mathfrak{x}$', +u'\U0001d536': '$\\mathfrak{y}$', +u'\U0001d537': '$\\mathfrak{z}$', +u'\U0001d538': '$\\mathbb{A}$', +u'\U0001d539': '$\\mathbb{B}$', +u'\U0001d53b': '$\\mathbb{D}$', +u'\U0001d53c': '$\\mathbb{E}$', +u'\U0001d53d': '$\\mathbb{F}$', +u'\U0001d53e': '$\\mathbb{G}$', +u'\U0001d540': '$\\mathbb{I}$', +u'\U0001d541': '$\\mathbb{J}$', +u'\U0001d542': '$\\mathbb{K}$', +u'\U0001d543': '$\\mathbb{L}$', +u'\U0001d544': '$\\mathbb{M}$', +u'\U0001d546': '$\\mathbb{O}$', +u'\U0001d54a': '$\\mathbb{S}$', +u'\U0001d54b': '$\\mathbb{T}$', +u'\U0001d54c': '$\\mathbb{U}$', +u'\U0001d54d': '$\\mathbb{V}$', +u'\U0001d54e': '$\\mathbb{W}$', +u'\U0001d54f': '$\\mathbb{X}$', +u'\U0001d550': '$\\mathbb{Y}$', +u'\U0001d552': '$\\mathbb{a}$', +u'\U0001d553': '$\\mathbb{b}$', +u'\U0001d554': '$\\mathbb{c}$', +u'\U0001d555': '$\\mathbb{d}$', +u'\U0001d556': '$\\mathbb{e}$', +u'\U0001d557': '$\\mathbb{f}$', +u'\U0001d558': '$\\mathbb{g}$', +u'\U0001d559': '$\\mathbb{h}$', +u'\U0001d55a': '$\\mathbb{i}$', +u'\U0001d55b': '$\\mathbb{j}$', +u'\U0001d55c': '$\\mathbb{k}$', +u'\U0001d55d': '$\\mathbb{l}$', +u'\U0001d55e': '$\\mathbb{m}$', +u'\U0001d55f': '$\\mathbb{n}$', +u'\U0001d560': '$\\mathbb{o}$', +u'\U0001d561': '$\\mathbb{p}$', +u'\U0001d562': '$\\mathbb{q}$', +u'\U0001d563': '$\\mathbb{r}$', +u'\U0001d564': '$\\mathbb{s}$', +u'\U0001d565': '$\\mathbb{t}$', +u'\U0001d566': '$\\mathbb{u}$', +u'\U0001d567': '$\\mathbb{v}$', +u'\U0001d568': '$\\mathbb{w}$', +u'\U0001d569': '$\\mathbb{x}$', +u'\U0001d56a': '$\\mathbb{y}$', +u'\U0001d56b': '$\\mathbb{z}$', +u'\U0001d56c': '$\\mathslbb{A}$', +u'\U0001d56d': '$\\mathslbb{B}$', +u'\U0001d56e': '$\\mathslbb{C}$', +u'\U0001d56f': '$\\mathslbb{D}$', +u'\U0001d570': '$\\mathslbb{E}$', +u'\U0001d571': '$\\mathslbb{F}$', +u'\U0001d572': '$\\mathslbb{G}$', +u'\U0001d573': '$\\mathslbb{H}$', +u'\U0001d574': '$\\mathslbb{I}$', +u'\U0001d575': '$\\mathslbb{J}$', +u'\U0001d576': '$\\mathslbb{K}$', +u'\U0001d577': '$\\mathslbb{L}$', +u'\U0001d578': '$\\mathslbb{M}$', +u'\U0001d579': '$\\mathslbb{N}$', +u'\U0001d57a': '$\\mathslbb{O}$', +u'\U0001d57b': '$\\mathslbb{P}$', +u'\U0001d57c': '$\\mathslbb{Q}$', +u'\U0001d57d': '$\\mathslbb{R}$', +u'\U0001d57e': '$\\mathslbb{S}$', +u'\U0001d57f': '$\\mathslbb{T}$', +u'\U0001d580': '$\\mathslbb{U}$', +u'\U0001d581': '$\\mathslbb{V}$', +u'\U0001d582': '$\\mathslbb{W}$', +u'\U0001d583': '$\\mathslbb{X}$', +u'\U0001d584': '$\\mathslbb{Y}$', +u'\U0001d585': '$\\mathslbb{Z}$', +u'\U0001d586': '$\\mathslbb{a}$', +u'\U0001d587': '$\\mathslbb{b}$', +u'\U0001d588': '$\\mathslbb{c}$', +u'\U0001d589': '$\\mathslbb{d}$', +u'\U0001d58a': '$\\mathslbb{e}$', +u'\U0001d58b': '$\\mathslbb{f}$', +u'\U0001d58c': '$\\mathslbb{g}$', +u'\U0001d58d': '$\\mathslbb{h}$', +u'\U0001d58e': '$\\mathslbb{i}$', +u'\U0001d58f': '$\\mathslbb{j}$', +u'\U0001d590': '$\\mathslbb{k}$', +u'\U0001d591': '$\\mathslbb{l}$', +u'\U0001d592': '$\\mathslbb{m}$', +u'\U0001d593': '$\\mathslbb{n}$', +u'\U0001d594': '$\\mathslbb{o}$', +u'\U0001d595': '$\\mathslbb{p}$', +u'\U0001d596': '$\\mathslbb{q}$', +u'\U0001d597': '$\\mathslbb{r}$', +u'\U0001d598': '$\\mathslbb{s}$', +u'\U0001d599': '$\\mathslbb{t}$', +u'\U0001d59a': '$\\mathslbb{u}$', +u'\U0001d59b': '$\\mathslbb{v}$', +u'\U0001d59c': '$\\mathslbb{w}$', +u'\U0001d59d': '$\\mathslbb{x}$', +u'\U0001d59e': '$\\mathslbb{y}$', +u'\U0001d59f': '$\\mathslbb{z}$', +u'\U0001d5a0': '$\\mathsf{A}$', +u'\U0001d5a1': '$\\mathsf{B}$', +u'\U0001d5a2': '$\\mathsf{C}$', +u'\U0001d5a3': '$\\mathsf{D}$', +u'\U0001d5a4': '$\\mathsf{E}$', +u'\U0001d5a5': '$\\mathsf{F}$', +u'\U0001d5a6': '$\\mathsf{G}$', +u'\U0001d5a7': '$\\mathsf{H}$', +u'\U0001d5a8': '$\\mathsf{I}$', +u'\U0001d5a9': '$\\mathsf{J}$', +u'\U0001d5aa': '$\\mathsf{K}$', +u'\U0001d5ab': '$\\mathsf{L}$', +u'\U0001d5ac': '$\\mathsf{M}$', +u'\U0001d5ad': '$\\mathsf{N}$', +u'\U0001d5ae': '$\\mathsf{O}$', +u'\U0001d5af': '$\\mathsf{P}$', +u'\U0001d5b0': '$\\mathsf{Q}$', +u'\U0001d5b1': '$\\mathsf{R}$', +u'\U0001d5b2': '$\\mathsf{S}$', +u'\U0001d5b3': '$\\mathsf{T}$', +u'\U0001d5b4': '$\\mathsf{U}$', +u'\U0001d5b5': '$\\mathsf{V}$', +u'\U0001d5b6': '$\\mathsf{W}$', +u'\U0001d5b7': '$\\mathsf{X}$', +u'\U0001d5b8': '$\\mathsf{Y}$', +u'\U0001d5b9': '$\\mathsf{Z}$', +u'\U0001d5ba': '$\\mathsf{a}$', +u'\U0001d5bb': '$\\mathsf{b}$', +u'\U0001d5bc': '$\\mathsf{c}$', +u'\U0001d5bd': '$\\mathsf{d}$', +u'\U0001d5be': '$\\mathsf{e}$', +u'\U0001d5bf': '$\\mathsf{f}$', +u'\U0001d5c0': '$\\mathsf{g}$', +u'\U0001d5c1': '$\\mathsf{h}$', +u'\U0001d5c2': '$\\mathsf{i}$', +u'\U0001d5c3': '$\\mathsf{j}$', +u'\U0001d5c4': '$\\mathsf{k}$', +u'\U0001d5c5': '$\\mathsf{l}$', +u'\U0001d5c6': '$\\mathsf{m}$', +u'\U0001d5c7': '$\\mathsf{n}$', +u'\U0001d5c8': '$\\mathsf{o}$', +u'\U0001d5c9': '$\\mathsf{p}$', +u'\U0001d5ca': '$\\mathsf{q}$', +u'\U0001d5cb': '$\\mathsf{r}$', +u'\U0001d5cc': '$\\mathsf{s}$', +u'\U0001d5cd': '$\\mathsf{t}$', +u'\U0001d5ce': '$\\mathsf{u}$', +u'\U0001d5cf': '$\\mathsf{v}$', +u'\U0001d5d0': '$\\mathsf{w}$', +u'\U0001d5d1': '$\\mathsf{x}$', +u'\U0001d5d2': '$\\mathsf{y}$', +u'\U0001d5d3': '$\\mathsf{z}$', +u'\U0001d5d4': '$\\mathsfbf{A}$', +u'\U0001d5d5': '$\\mathsfbf{B}$', +u'\U0001d5d6': '$\\mathsfbf{C}$', +u'\U0001d5d7': '$\\mathsfbf{D}$', +u'\U0001d5d8': '$\\mathsfbf{E}$', +u'\U0001d5d9': '$\\mathsfbf{F}$', +u'\U0001d5da': '$\\mathsfbf{G}$', +u'\U0001d5db': '$\\mathsfbf{H}$', +u'\U0001d5dc': '$\\mathsfbf{I}$', +u'\U0001d5dd': '$\\mathsfbf{J}$', +u'\U0001d5de': '$\\mathsfbf{K}$', +u'\U0001d5df': '$\\mathsfbf{L}$', +u'\U0001d5e0': '$\\mathsfbf{M}$', +u'\U0001d5e1': '$\\mathsfbf{N}$', +u'\U0001d5e2': '$\\mathsfbf{O}$', +u'\U0001d5e3': '$\\mathsfbf{P}$', +u'\U0001d5e4': '$\\mathsfbf{Q}$', +u'\U0001d5e5': '$\\mathsfbf{R}$', +u'\U0001d5e6': '$\\mathsfbf{S}$', +u'\U0001d5e7': '$\\mathsfbf{T}$', +u'\U0001d5e8': '$\\mathsfbf{U}$', +u'\U0001d5e9': '$\\mathsfbf{V}$', +u'\U0001d5ea': '$\\mathsfbf{W}$', +u'\U0001d5eb': '$\\mathsfbf{X}$', +u'\U0001d5ec': '$\\mathsfbf{Y}$', +u'\U0001d5ed': '$\\mathsfbf{Z}$', +u'\U0001d5ee': '$\\mathsfbf{a}$', +u'\U0001d5ef': '$\\mathsfbf{b}$', +u'\U0001d5f0': '$\\mathsfbf{c}$', +u'\U0001d5f1': '$\\mathsfbf{d}$', +u'\U0001d5f2': '$\\mathsfbf{e}$', +u'\U0001d5f3': '$\\mathsfbf{f}$', +u'\U0001d5f4': '$\\mathsfbf{g}$', +u'\U0001d5f5': '$\\mathsfbf{h}$', +u'\U0001d5f6': '$\\mathsfbf{i}$', +u'\U0001d5f7': '$\\mathsfbf{j}$', +u'\U0001d5f8': '$\\mathsfbf{k}$', +u'\U0001d5f9': '$\\mathsfbf{l}$', +u'\U0001d5fa': '$\\mathsfbf{m}$', +u'\U0001d5fb': '$\\mathsfbf{n}$', +u'\U0001d5fc': '$\\mathsfbf{o}$', +u'\U0001d5fd': '$\\mathsfbf{p}$', +u'\U0001d5fe': '$\\mathsfbf{q}$', +u'\U0001d5ff': '$\\mathsfbf{r}$', +u'\U0001d600': '$\\mathsfbf{s}$', +u'\U0001d601': '$\\mathsfbf{t}$', +u'\U0001d602': '$\\mathsfbf{u}$', +u'\U0001d603': '$\\mathsfbf{v}$', +u'\U0001d604': '$\\mathsfbf{w}$', +u'\U0001d605': '$\\mathsfbf{x}$', +u'\U0001d606': '$\\mathsfbf{y}$', +u'\U0001d607': '$\\mathsfbf{z}$', +u'\U0001d608': '$\\mathsfsl{A}$', +u'\U0001d609': '$\\mathsfsl{B}$', +u'\U0001d60a': '$\\mathsfsl{C}$', +u'\U0001d60b': '$\\mathsfsl{D}$', +u'\U0001d60c': '$\\mathsfsl{E}$', +u'\U0001d60d': '$\\mathsfsl{F}$', +u'\U0001d60e': '$\\mathsfsl{G}$', +u'\U0001d60f': '$\\mathsfsl{H}$', +u'\U0001d610': '$\\mathsfsl{I}$', +u'\U0001d611': '$\\mathsfsl{J}$', +u'\U0001d612': '$\\mathsfsl{K}$', +u'\U0001d613': '$\\mathsfsl{L}$', +u'\U0001d614': '$\\mathsfsl{M}$', +u'\U0001d615': '$\\mathsfsl{N}$', +u'\U0001d616': '$\\mathsfsl{O}$', +u'\U0001d617': '$\\mathsfsl{P}$', +u'\U0001d618': '$\\mathsfsl{Q}$', +u'\U0001d619': '$\\mathsfsl{R}$', +u'\U0001d61a': '$\\mathsfsl{S}$', +u'\U0001d61b': '$\\mathsfsl{T}$', +u'\U0001d61c': '$\\mathsfsl{U}$', +u'\U0001d61d': '$\\mathsfsl{V}$', +u'\U0001d61e': '$\\mathsfsl{W}$', +u'\U0001d61f': '$\\mathsfsl{X}$', +u'\U0001d620': '$\\mathsfsl{Y}$', +u'\U0001d621': '$\\mathsfsl{Z}$', +u'\U0001d622': '$\\mathsfsl{a}$', +u'\U0001d623': '$\\mathsfsl{b}$', +u'\U0001d624': '$\\mathsfsl{c}$', +u'\U0001d625': '$\\mathsfsl{d}$', +u'\U0001d626': '$\\mathsfsl{e}$', +u'\U0001d627': '$\\mathsfsl{f}$', +u'\U0001d628': '$\\mathsfsl{g}$', +u'\U0001d629': '$\\mathsfsl{h}$', +u'\U0001d62a': '$\\mathsfsl{i}$', +u'\U0001d62b': '$\\mathsfsl{j}$', +u'\U0001d62c': '$\\mathsfsl{k}$', +u'\U0001d62d': '$\\mathsfsl{l}$', +u'\U0001d62e': '$\\mathsfsl{m}$', +u'\U0001d62f': '$\\mathsfsl{n}$', +u'\U0001d630': '$\\mathsfsl{o}$', +u'\U0001d631': '$\\mathsfsl{p}$', +u'\U0001d632': '$\\mathsfsl{q}$', +u'\U0001d633': '$\\mathsfsl{r}$', +u'\U0001d634': '$\\mathsfsl{s}$', +u'\U0001d635': '$\\mathsfsl{t}$', +u'\U0001d636': '$\\mathsfsl{u}$', +u'\U0001d637': '$\\mathsfsl{v}$', +u'\U0001d638': '$\\mathsfsl{w}$', +u'\U0001d639': '$\\mathsfsl{x}$', +u'\U0001d63a': '$\\mathsfsl{y}$', +u'\U0001d63b': '$\\mathsfsl{z}$', +u'\U0001d63c': '$\\mathsfbfsl{A}$', +u'\U0001d63d': '$\\mathsfbfsl{B}$', +u'\U0001d63e': '$\\mathsfbfsl{C}$', +u'\U0001d63f': '$\\mathsfbfsl{D}$', +u'\U0001d640': '$\\mathsfbfsl{E}$', +u'\U0001d641': '$\\mathsfbfsl{F}$', +u'\U0001d642': '$\\mathsfbfsl{G}$', +u'\U0001d643': '$\\mathsfbfsl{H}$', +u'\U0001d644': '$\\mathsfbfsl{I}$', +u'\U0001d645': '$\\mathsfbfsl{J}$', +u'\U0001d646': '$\\mathsfbfsl{K}$', +u'\U0001d647': '$\\mathsfbfsl{L}$', +u'\U0001d648': '$\\mathsfbfsl{M}$', +u'\U0001d649': '$\\mathsfbfsl{N}$', +u'\U0001d64a': '$\\mathsfbfsl{O}$', +u'\U0001d64b': '$\\mathsfbfsl{P}$', +u'\U0001d64c': '$\\mathsfbfsl{Q}$', +u'\U0001d64d': '$\\mathsfbfsl{R}$', +u'\U0001d64e': '$\\mathsfbfsl{S}$', +u'\U0001d64f': '$\\mathsfbfsl{T}$', +u'\U0001d650': '$\\mathsfbfsl{U}$', +u'\U0001d651': '$\\mathsfbfsl{V}$', +u'\U0001d652': '$\\mathsfbfsl{W}$', +u'\U0001d653': '$\\mathsfbfsl{X}$', +u'\U0001d654': '$\\mathsfbfsl{Y}$', +u'\U0001d655': '$\\mathsfbfsl{Z}$', +u'\U0001d656': '$\\mathsfbfsl{a}$', +u'\U0001d657': '$\\mathsfbfsl{b}$', +u'\U0001d658': '$\\mathsfbfsl{c}$', +u'\U0001d659': '$\\mathsfbfsl{d}$', +u'\U0001d65a': '$\\mathsfbfsl{e}$', +u'\U0001d65b': '$\\mathsfbfsl{f}$', +u'\U0001d65c': '$\\mathsfbfsl{g}$', +u'\U0001d65d': '$\\mathsfbfsl{h}$', +u'\U0001d65e': '$\\mathsfbfsl{i}$', +u'\U0001d65f': '$\\mathsfbfsl{j}$', +u'\U0001d660': '$\\mathsfbfsl{k}$', +u'\U0001d661': '$\\mathsfbfsl{l}$', +u'\U0001d662': '$\\mathsfbfsl{m}$', +u'\U0001d663': '$\\mathsfbfsl{n}$', +u'\U0001d664': '$\\mathsfbfsl{o}$', +u'\U0001d665': '$\\mathsfbfsl{p}$', +u'\U0001d666': '$\\mathsfbfsl{q}$', +u'\U0001d667': '$\\mathsfbfsl{r}$', +u'\U0001d668': '$\\mathsfbfsl{s}$', +u'\U0001d669': '$\\mathsfbfsl{t}$', +u'\U0001d66a': '$\\mathsfbfsl{u}$', +u'\U0001d66b': '$\\mathsfbfsl{v}$', +u'\U0001d66c': '$\\mathsfbfsl{w}$', +u'\U0001d66d': '$\\mathsfbfsl{x}$', +u'\U0001d66e': '$\\mathsfbfsl{y}$', +u'\U0001d66f': '$\\mathsfbfsl{z}$', +u'\U0001d670': '$\\mathtt{A}$', +u'\U0001d671': '$\\mathtt{B}$', +u'\U0001d672': '$\\mathtt{C}$', +u'\U0001d673': '$\\mathtt{D}$', +u'\U0001d674': '$\\mathtt{E}$', +u'\U0001d675': '$\\mathtt{F}$', +u'\U0001d676': '$\\mathtt{G}$', +u'\U0001d677': '$\\mathtt{H}$', +u'\U0001d678': '$\\mathtt{I}$', +u'\U0001d679': '$\\mathtt{J}$', +u'\U0001d67a': '$\\mathtt{K}$', +u'\U0001d67b': '$\\mathtt{L}$', +u'\U0001d67c': '$\\mathtt{M}$', +u'\U0001d67d': '$\\mathtt{N}$', +u'\U0001d67e': '$\\mathtt{O}$', +u'\U0001d67f': '$\\mathtt{P}$', +u'\U0001d680': '$\\mathtt{Q}$', +u'\U0001d681': '$\\mathtt{R}$', +u'\U0001d682': '$\\mathtt{S}$', +u'\U0001d683': '$\\mathtt{T}$', +u'\U0001d684': '$\\mathtt{U}$', +u'\U0001d685': '$\\mathtt{V}$', +u'\U0001d686': '$\\mathtt{W}$', +u'\U0001d687': '$\\mathtt{X}$', +u'\U0001d688': '$\\mathtt{Y}$', +u'\U0001d689': '$\\mathtt{Z}$', +u'\U0001d68a': '$\\mathtt{a}$', +u'\U0001d68b': '$\\mathtt{b}$', +u'\U0001d68c': '$\\mathtt{c}$', +u'\U0001d68d': '$\\mathtt{d}$', +u'\U0001d68e': '$\\mathtt{e}$', +u'\U0001d68f': '$\\mathtt{f}$', +u'\U0001d690': '$\\mathtt{g}$', +u'\U0001d691': '$\\mathtt{h}$', +u'\U0001d692': '$\\mathtt{i}$', +u'\U0001d693': '$\\mathtt{j}$', +u'\U0001d694': '$\\mathtt{k}$', +u'\U0001d695': '$\\mathtt{l}$', +u'\U0001d696': '$\\mathtt{m}$', +u'\U0001d697': '$\\mathtt{n}$', +u'\U0001d698': '$\\mathtt{o}$', +u'\U0001d699': '$\\mathtt{p}$', +u'\U0001d69a': '$\\mathtt{q}$', +u'\U0001d69b': '$\\mathtt{r}$', +u'\U0001d69c': '$\\mathtt{s}$', +u'\U0001d69d': '$\\mathtt{t}$', +u'\U0001d69e': '$\\mathtt{u}$', +u'\U0001d69f': '$\\mathtt{v}$', +u'\U0001d6a0': '$\\mathtt{w}$', +u'\U0001d6a1': '$\\mathtt{x}$', +u'\U0001d6a2': '$\\mathtt{y}$', +u'\U0001d6a3': '$\\mathtt{z}$', +u'\U0001d6a8': '$\\mathbf{\\Alpha}$', +u'\U0001d6a9': '$\\mathbf{\\Beta}$', +u'\U0001d6aa': '$\\mathbf{\\Gamma}$', +u'\U0001d6ab': '$\\mathbf{\\Delta}$', +u'\U0001d6ac': '$\\mathbf{\\Epsilon}$', +u'\U0001d6ad': '$\\mathbf{\\Zeta}$', +u'\U0001d6ae': '$\\mathbf{\\Eta}$', +u'\U0001d6af': '$\\mathbf{\\Theta}$', +u'\U0001d6b0': '$\\mathbf{\\Iota}$', +u'\U0001d6b1': '$\\mathbf{\\Kappa}$', +u'\U0001d6b2': '$\\mathbf{\\Lambda}$', +u'\U0001d6b3': '$M$', +u'\U0001d6b4': '$N$', +u'\U0001d6b5': '$\\mathbf{\\Xi}$', +u'\U0001d6b6': '$O$', +u'\U0001d6b7': '$\\mathbf{\\Pi}$', +u'\U0001d6b8': '$\\mathbf{\\Rho}$', +u'\U0001d6b9': '{\\mathbf{\\vartheta}}', +u'\U0001d6ba': '$\\mathbf{\\Sigma}$', +u'\U0001d6bb': '$\\mathbf{\\Tau}$', +u'\U0001d6bc': '$\\mathbf{\\Upsilon}$', +u'\U0001d6bd': '$\\mathbf{\\Phi}$', +u'\U0001d6be': '$\\mathbf{\\Chi}$', +u'\U0001d6bf': '$\\mathbf{\\Psi}$', +u'\U0001d6c0': '$\\mathbf{\\Omega}$', +u'\U0001d6c1': '$\\mathbf{\\nabla}$', +u'\U0001d6c2': '$\\mathbf{\\Alpha}$', +u'\U0001d6c3': '$\\mathbf{\\Beta}$', +u'\U0001d6c4': '$\\mathbf{\\Gamma}$', +u'\U0001d6c5': '$\\mathbf{\\Delta}$', +u'\U0001d6c6': '$\\mathbf{\\Epsilon}$', +u'\U0001d6c7': '$\\mathbf{\\Zeta}$', +u'\U0001d6c8': '$\\mathbf{\\Eta}$', +u'\U0001d6c9': '$\\mathbf{\\theta}$', +u'\U0001d6ca': '$\\mathbf{\\Iota}$', +u'\U0001d6cb': '$\\mathbf{\\Kappa}$', +u'\U0001d6cc': '$\\mathbf{\\Lambda}$', +u'\U0001d6cd': '$M$', +u'\U0001d6ce': '$N$', +u'\U0001d6cf': '$\\mathbf{\\Xi}$', +u'\U0001d6d0': '$O$', +u'\U0001d6d1': '$\\mathbf{\\Pi}$', +u'\U0001d6d2': '$\\mathbf{\\Rho}$', +u'\U0001d6d3': '$\\mathbf{\\varsigma}$', +u'\U0001d6d4': '$\\mathbf{\\Sigma}$', +u'\U0001d6d5': '$\\mathbf{\\Tau}$', +u'\U0001d6d6': '$\\mathbf{\\Upsilon}$', +u'\U0001d6d7': '$\\mathbf{\\Phi}$', +u'\U0001d6d8': '$\\mathbf{\\Chi}$', +u'\U0001d6d9': '$\\mathbf{\\Psi}$', +u'\U0001d6da': '$\\mathbf{\\Omega}$', +u'\U0001d6db': '$\\partial$', +u'\U0001d6dc': '$\\in$', +u'\U0001d6dd': '{\\mathbf{\\vartheta}}', +u'\U0001d6de': '{\\mathbf{\\varkappa}}', +u'\U0001d6df': '{\\mathbf{\\phi}}', +u'\U0001d6e0': '{\\mathbf{\\varrho}}', +u'\U0001d6e1': '{\\mathbf{\\varpi}}', +u'\U0001d6e2': '$\\mathsl{\\Alpha}$', +u'\U0001d6e3': '$\\mathsl{\\Beta}$', +u'\U0001d6e4': '$\\mathsl{\\Gamma}$', +u'\U0001d6e5': '$\\mathsl{\\Delta}$', +u'\U0001d6e6': '$\\mathsl{\\Epsilon}$', +u'\U0001d6e7': '$\\mathsl{\\Zeta}$', +u'\U0001d6e8': '$\\mathsl{\\Eta}$', +u'\U0001d6e9': '$\\mathsl{\\Theta}$', +u'\U0001d6ea': '$\\mathsl{\\Iota}$', +u'\U0001d6eb': '$\\mathsl{\\Kappa}$', +u'\U0001d6ec': '$\\mathsl{\\Lambda}$', +u'\U0001d6ed': '$M$', +u'\U0001d6ee': '$N$', +u'\U0001d6ef': '$\\mathsl{\\Xi}$', +u'\U0001d6f0': '$O$', +u'\U0001d6f1': '$\\mathsl{\\Pi}$', +u'\U0001d6f2': '$\\mathsl{\\Rho}$', +u'\U0001d6f3': '{\\mathsl{\\vartheta}}', +u'\U0001d6f4': '$\\mathsl{\\Sigma}$', +u'\U0001d6f5': '$\\mathsl{\\Tau}$', +u'\U0001d6f6': '$\\mathsl{\\Upsilon}$', +u'\U0001d6f7': '$\\mathsl{\\Phi}$', +u'\U0001d6f8': '$\\mathsl{\\Chi}$', +u'\U0001d6f9': '$\\mathsl{\\Psi}$', +u'\U0001d6fa': '$\\mathsl{\\Omega}$', +u'\U0001d6fb': '$\\mathsl{\\nabla}$', +u'\U0001d6fc': '$\\mathsl{\\Alpha}$', +u'\U0001d6fd': '$\\mathsl{\\Beta}$', +u'\U0001d6fe': '$\\mathsl{\\Gamma}$', +u'\U0001d6ff': '$\\mathsl{\\Delta}$', +u'\U0001d700': '$\\mathsl{\\Epsilon}$', +u'\U0001d701': '$\\mathsl{\\Zeta}$', +u'\U0001d702': '$\\mathsl{\\Eta}$', +u'\U0001d703': '$\\mathsl{\\Theta}$', +u'\U0001d704': '$\\mathsl{\\Iota}$', +u'\U0001d705': '$\\mathsl{\\Kappa}$', +u'\U0001d706': '$\\mathsl{\\Lambda}$', +u'\U0001d707': '$M$', +u'\U0001d708': '$N$', +u'\U0001d709': '$\\mathsl{\\Xi}$', +u'\U0001d70a': '$O$', +u'\U0001d70b': '$\\mathsl{\\Pi}$', +u'\U0001d70c': '$\\mathsl{\\Rho}$', +u'\U0001d70d': '$\\mathsl{\\varsigma}$', +u'\U0001d70e': '$\\mathsl{\\Sigma}$', +u'\U0001d70f': '$\\mathsl{\\Tau}$', +u'\U0001d710': '$\\mathsl{\\Upsilon}$', +u'\U0001d711': '$\\mathsl{\\Phi}$', +u'\U0001d712': '$\\mathsl{\\Chi}$', +u'\U0001d713': '$\\mathsl{\\Psi}$', +u'\U0001d714': '$\\mathsl{\\Omega}$', +u'\U0001d715': '$\\partial$', +u'\U0001d716': '$\\in$', +u'\U0001d717': '{\\mathsl{\\vartheta}}', +u'\U0001d718': '{\\mathsl{\\varkappa}}', +u'\U0001d719': '{\\mathsl{\\phi}}', +u'\U0001d71a': '{\\mathsl{\\varrho}}', +u'\U0001d71b': '{\\mathsl{\\varpi}}', +u'\U0001d71c': '$\\mathbit{\\Alpha}$', +u'\U0001d71d': '$\\mathbit{\\Beta}$', +u'\U0001d71e': '$\\mathbit{\\Gamma}$', +u'\U0001d71f': '$\\mathbit{\\Delta}$', +u'\U0001d720': '$\\mathbit{\\Epsilon}$', +u'\U0001d721': '$\\mathbit{\\Zeta}$', +u'\U0001d722': '$\\mathbit{\\Eta}$', +u'\U0001d723': '$\\mathbit{\\Theta}$', +u'\U0001d724': '$\\mathbit{\\Iota}$', +u'\U0001d725': '$\\mathbit{\\Kappa}$', +u'\U0001d726': '$\\mathbit{\\Lambda}$', +u'\U0001d727': '$M$', +u'\U0001d728': '$N$', +u'\U0001d729': '$\\mathbit{\\Xi}$', +u'\U0001d72a': '$O$', +u'\U0001d72b': '$\\mathbit{\\Pi}$', +u'\U0001d72c': '$\\mathbit{\\Rho}$', +u'\U0001d72d': '{\\mathbit{O}}', +u'\U0001d72e': '$\\mathbit{\\Sigma}$', +u'\U0001d72f': '$\\mathbit{\\Tau}$', +u'\U0001d730': '$\\mathbit{\\Upsilon}$', +u'\U0001d731': '$\\mathbit{\\Phi}$', +u'\U0001d732': '$\\mathbit{\\Chi}$', +u'\U0001d733': '$\\mathbit{\\Psi}$', +u'\U0001d734': '$\\mathbit{\\Omega}$', +u'\U0001d735': '$\\mathbit{\\nabla}$', +u'\U0001d736': '$\\mathbit{\\Alpha}$', +u'\U0001d737': '$\\mathbit{\\Beta}$', +u'\U0001d738': '$\\mathbit{\\Gamma}$', +u'\U0001d739': '$\\mathbit{\\Delta}$', +u'\U0001d73a': '$\\mathbit{\\Epsilon}$', +u'\U0001d73b': '$\\mathbit{\\Zeta}$', +u'\U0001d73c': '$\\mathbit{\\Eta}$', +u'\U0001d73d': '$\\mathbit{\\Theta}$', +u'\U0001d73e': '$\\mathbit{\\Iota}$', +u'\U0001d73f': '$\\mathbit{\\Kappa}$', +u'\U0001d740': '$\\mathbit{\\Lambda}$', +u'\U0001d741': '$M$', +u'\U0001d742': '$N$', +u'\U0001d743': '$\\mathbit{\\Xi}$', +u'\U0001d744': '$O$', +u'\U0001d745': '$\\mathbit{\\Pi}$', +u'\U0001d746': '$\\mathbit{\\Rho}$', +u'\U0001d747': '$\\mathbit{\\varsigma}$', +u'\U0001d748': '$\\mathbit{\\Sigma}$', +u'\U0001d749': '$\\mathbit{\\Tau}$', +u'\U0001d74a': '$\\mathbit{\\Upsilon}$', +u'\U0001d74b': '$\\mathbit{\\Phi}$', +u'\U0001d74c': '$\\mathbit{\\Chi}$', +u'\U0001d74d': '$\\mathbit{\\Psi}$', +u'\U0001d74e': '$\\mathbit{\\Omega}$', +u'\U0001d74f': '$\\partial$', +u'\U0001d750': '$\\in$', +u'\U0001d751': '{\\mathbit{\\vartheta}}', +u'\U0001d752': '{\\mathbit{\\varkappa}}', +u'\U0001d753': '{\\mathbit{\\phi}}', +u'\U0001d754': '{\\mathbit{\\varrho}}', +u'\U0001d755': '{\\mathbit{\\varpi}}', +u'\U0001d756': '$\\mathsfbf{\\Alpha}$', +u'\U0001d757': '$\\mathsfbf{\\Beta}$', +u'\U0001d758': '$\\mathsfbf{\\Gamma}$', +u'\U0001d759': '$\\mathsfbf{\\Delta}$', +u'\U0001d75a': '$\\mathsfbf{\\Epsilon}$', +u'\U0001d75b': '$\\mathsfbf{\\Zeta}$', +u'\U0001d75c': '$\\mathsfbf{\\Eta}$', +u'\U0001d75d': '$\\mathsfbf{\\Theta}$', +u'\U0001d75e': '$\\mathsfbf{\\Iota}$', +u'\U0001d75f': '$\\mathsfbf{\\Kappa}$', +u'\U0001d760': '$\\mathsfbf{\\Lambda}$', +u'\U0001d761': '$M$', +u'\U0001d762': '$N$', +u'\U0001d763': '$\\mathsfbf{\\Xi}$', +u'\U0001d764': '$O$', +u'\U0001d765': '$\\mathsfbf{\\Pi}$', +u'\U0001d766': '$\\mathsfbf{\\Rho}$', +u'\U0001d767': '{\\mathsfbf{\\vartheta}}', +u'\U0001d768': '$\\mathsfbf{\\Sigma}$', +u'\U0001d769': '$\\mathsfbf{\\Tau}$', +u'\U0001d76a': '$\\mathsfbf{\\Upsilon}$', +u'\U0001d76b': '$\\mathsfbf{\\Phi}$', +u'\U0001d76c': '$\\mathsfbf{\\Chi}$', +u'\U0001d76d': '$\\mathsfbf{\\Psi}$', +u'\U0001d76e': '$\\mathsfbf{\\Omega}$', +u'\U0001d76f': '$\\mathsfbf{\\nabla}$', +u'\U0001d770': '$\\mathsfbf{\\Alpha}$', +u'\U0001d771': '$\\mathsfbf{\\Beta}$', +u'\U0001d772': '$\\mathsfbf{\\Gamma}$', +u'\U0001d773': '$\\mathsfbf{\\Delta}$', +u'\U0001d774': '$\\mathsfbf{\\Epsilon}$', +u'\U0001d775': '$\\mathsfbf{\\Zeta}$', +u'\U0001d776': '$\\mathsfbf{\\Eta}$', +u'\U0001d777': '$\\mathsfbf{\\Theta}$', +u'\U0001d778': '$\\mathsfbf{\\Iota}$', +u'\U0001d779': '$\\mathsfbf{\\Kappa}$', +u'\U0001d77a': '$\\mathsfbf{\\Lambda}$', +u'\U0001d77b': '$M$', +u'\U0001d77c': '$N$', +u'\U0001d77d': '$\\mathsfbf{\\Xi}$', +u'\U0001d77e': '$O$', +u'\U0001d77f': '$\\mathsfbf{\\Pi}$', +u'\U0001d780': '$\\mathsfbf{\\Rho}$', +u'\U0001d781': '$\\mathsfbf{\\varsigma}$', +u'\U0001d782': '$\\mathsfbf{\\Sigma}$', +u'\U0001d783': '$\\mathsfbf{\\Tau}$', +u'\U0001d784': '$\\mathsfbf{\\Upsilon}$', +u'\U0001d785': '$\\mathsfbf{\\Phi}$', +u'\U0001d786': '$\\mathsfbf{\\Chi}$', +u'\U0001d787': '$\\mathsfbf{\\Psi}$', +u'\U0001d788': '$\\mathsfbf{\\Omega}$', +u'\U0001d789': '$\\partial$', +u'\U0001d78a': '$\\in$', +u'\U0001d78b': '{\\mathsfbf{\\vartheta}}', +u'\U0001d78c': '{\\mathsfbf{\\varkappa}}', +u'\U0001d78d': '{\\mathsfbf{\\phi}}', +u'\U0001d78e': '{\\mathsfbf{\\varrho}}', +u'\U0001d78f': '{\\mathsfbf{\\varpi}}', +u'\U0001d790': '$\\mathsfbfsl{\\Alpha}$', +u'\U0001d791': '$\\mathsfbfsl{\\Beta}$', +u'\U0001d792': '$\\mathsfbfsl{\\Gamma}$', +u'\U0001d793': '$\\mathsfbfsl{\\Delta}$', +u'\U0001d794': '$\\mathsfbfsl{\\Epsilon}$', +u'\U0001d795': '$\\mathsfbfsl{\\Zeta}$', +u'\U0001d796': '$\\mathsfbfsl{\\Eta}$', +u'\U0001d797': '$\\mathsfbfsl{\\vartheta}$', +u'\U0001d798': '$\\mathsfbfsl{\\Iota}$', +u'\U0001d799': '$\\mathsfbfsl{\\Kappa}$', +u'\U0001d79a': '$\\mathsfbfsl{\\Lambda}$', +u'\U0001d79b': '$M$', +u'\U0001d79c': '$N$', +u'\U0001d79d': '$\\mathsfbfsl{\\Xi}$', +u'\U0001d79e': '$O$', +u'\U0001d79f': '$\\mathsfbfsl{\\Pi}$', +u'\U0001d7a0': '$\\mathsfbfsl{\\Rho}$', +u'\U0001d7a1': '{\\mathsfbfsl{\\vartheta}}', +u'\U0001d7a2': '$\\mathsfbfsl{\\Sigma}$', +u'\U0001d7a3': '$\\mathsfbfsl{\\Tau}$', +u'\U0001d7a4': '$\\mathsfbfsl{\\Upsilon}$', +u'\U0001d7a5': '$\\mathsfbfsl{\\Phi}$', +u'\U0001d7a6': '$\\mathsfbfsl{\\Chi}$', +u'\U0001d7a7': '$\\mathsfbfsl{\\Psi}$', +u'\U0001d7a8': '$\\mathsfbfsl{\\Omega}$', +u'\U0001d7a9': '$\\mathsfbfsl{\\nabla}$', +u'\U0001d7aa': '$\\mathsfbfsl{\\Alpha}$', +u'\U0001d7ab': '$\\mathsfbfsl{\\Beta}$', +u'\U0001d7ac': '$\\mathsfbfsl{\\Gamma}$', +u'\U0001d7ad': '$\\mathsfbfsl{\\Delta}$', +u'\U0001d7ae': '$\\mathsfbfsl{\\Epsilon}$', +u'\U0001d7af': '$\\mathsfbfsl{\\Zeta}$', +u'\U0001d7b0': '$\\mathsfbfsl{\\Eta}$', +u'\U0001d7b1': '$\\mathsfbfsl{\\vartheta}$', +u'\U0001d7b2': '$\\mathsfbfsl{\\Iota}$', +u'\U0001d7b3': '$\\mathsfbfsl{\\Kappa}$', +u'\U0001d7b4': '$\\mathsfbfsl{\\Lambda}$', +u'\U0001d7b5': '$M$', +u'\U0001d7b6': '$N$', +u'\U0001d7b7': '$\\mathsfbfsl{\\Xi}$', +u'\U0001d7b8': '$O$', +u'\U0001d7b9': '$\\mathsfbfsl{\\Pi}$', +u'\U0001d7ba': '$\\mathsfbfsl{\\Rho}$', +u'\U0001d7bb': '$\\mathsfbfsl{\\varsigma}$', +u'\U0001d7bc': '$\\mathsfbfsl{\\Sigma}$', +u'\U0001d7bd': '$\\mathsfbfsl{\\Tau}$', +u'\U0001d7be': '$\\mathsfbfsl{\\Upsilon}$', +u'\U0001d7bf': '$\\mathsfbfsl{\\Phi}$', +u'\U0001d7c0': '$\\mathsfbfsl{\\Chi}$', +u'\U0001d7c1': '$\\mathsfbfsl{\\Psi}$', +u'\U0001d7c2': '$\\mathsfbfsl{\\Omega}$', +u'\U0001d7c3': '$\\partial$', +u'\U0001d7c4': '$\\in$', +u'\U0001d7c5': '{\\mathsfbfsl{\\vartheta}}', +u'\U0001d7c6': '{\\mathsfbfsl{\\varkappa}}', +u'\U0001d7c7': '{\\mathsfbfsl{\\phi}}', +u'\U0001d7c8': '{\\mathsfbfsl{\\varrho}}', +u'\U0001d7c9': '{\\mathsfbfsl{\\varpi}}', +u'\U0001d7ce': '$\\mathbf{0}$', +u'\U0001d7cf': '$\\mathbf{1}$', +u'\U0001d7d0': '$\\mathbf{2}$', +u'\U0001d7d1': '$\\mathbf{3}$', +u'\U0001d7d2': '$\\mathbf{4}$', +u'\U0001d7d3': '$\\mathbf{5}$', +u'\U0001d7d4': '$\\mathbf{6}$', +u'\U0001d7d5': '$\\mathbf{7}$', +u'\U0001d7d6': '$\\mathbf{8}$', +u'\U0001d7d7': '$\\mathbf{9}$', +u'\U0001d7d8': '$\\mathbb{0}$', +u'\U0001d7d9': '$\\mathbb{1}$', +u'\U0001d7da': '$\\mathbb{2}$', +u'\U0001d7db': '$\\mathbb{3}$', +u'\U0001d7dc': '$\\mathbb{4}$', +u'\U0001d7dd': '$\\mathbb{5}$', +u'\U0001d7de': '$\\mathbb{6}$', +u'\U0001d7df': '$\\mathbb{7}$', +u'\U0001d7e0': '$\\mathbb{8}$', +u'\U0001d7e1': '$\\mathbb{9}$', +u'\U0001d7e2': '$\\mathsf{0}$', +u'\U0001d7e3': '$\\mathsf{1}$', +u'\U0001d7e4': '$\\mathsf{2}$', +u'\U0001d7e5': '$\\mathsf{3}$', +u'\U0001d7e6': '$\\mathsf{4}$', +u'\U0001d7e7': '$\\mathsf{5}$', +u'\U0001d7e8': '$\\mathsf{6}$', +u'\U0001d7e9': '$\\mathsf{7}$', +u'\U0001d7ea': '$\\mathsf{8}$', +u'\U0001d7eb': '$\\mathsf{9}$', +u'\U0001d7ec': '$\\mathsfbf{0}$', +u'\U0001d7ed': '$\\mathsfbf{1}$', +u'\U0001d7ee': '$\\mathsfbf{2}$', +u'\U0001d7ef': '$\\mathsfbf{3}$', +u'\U0001d7f0': '$\\mathsfbf{4}$', +u'\U0001d7f1': '$\\mathsfbf{5}$', +u'\U0001d7f2': '$\\mathsfbf{6}$', +u'\U0001d7f3': '$\\mathsfbf{7}$', +u'\U0001d7f4': '$\\mathsfbf{8}$', +u'\U0001d7f5': '$\\mathsfbf{9}$', +u'\U0001d7f6': '$\\mathtt{0}$', +u'\U0001d7f7': '$\\mathtt{1}$', +u'\U0001d7f8': '$\\mathtt{2}$', +u'\U0001d7f9': '$\\mathtt{3}$', +u'\U0001d7fa': '$\\mathtt{4}$', +u'\U0001d7fb': '$\\mathtt{5}$', +u'\U0001d7fc': '$\\mathtt{6}$', +u'\U0001d7fd': '$\\mathtt{7}$', +u'\U0001d7fe': '$\\mathtt{8}$', +u'\U0001d7ff': '$\\mathtt{9}$'} diff -Nru zope3-3.4.0/src/docutils/writers/null.py zope3-3.5~bzr18/src/docutils/writers/null.py --- zope3-3.4.0/src/docutils/writers/null.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/null.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,21 @@ +# $Id: null.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A do-nothing Writer. +""" + +from docutils import writers + + +class Writer(writers.UnfilteredWriter): + + supported = ('null',) + """Formats this writer supports.""" + + config_section = 'null writer' + config_section_dependencies = ('writers',) + + def translate(self): + pass diff -Nru zope3-3.4.0/src/docutils/writers/odf_odt/__init__.py zope3-3.5~bzr18/src/docutils/writers/odf_odt/__init__.py --- zope3-3.4.0/src/docutils/writers/odf_odt/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/odf_odt/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2703 @@ +# $Id: __init__.py 6034 2009-07-20 18:46:51Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +Open Document Format (ODF) Writer. + +""" + +VERSION = '1.0a' + +__docformat__ = 'reStructuredText' + + +import sys +import os +import os.path +import tempfile +import zipfile +from xml.dom import minidom +import time +import re +import StringIO +import inspect +import imp +import copy +import docutils +from docutils import frontend, nodes, utils, writers, languages +from docutils.parsers import rst +from docutils.readers import standalone +from docutils.transforms import references + + +WhichElementTree = '' +try: + # 1. Try to use lxml. + #from lxml import etree + #WhichElementTree = 'lxml' + raise ImportError('Ignoring lxml') +except ImportError, e: + try: + # 2. Try to use ElementTree from the Python standard library. + from xml.etree import ElementTree as etree + WhichElementTree = 'elementtree' + except ImportError, e: + try: + # 3. Try to use a version of ElementTree installed as a separate + # product. + from elementtree import ElementTree as etree + WhichElementTree = 'elementtree' + except ImportError, e: + s1 = 'Must install either a version of Python containing ' \ + 'ElementTree (Python version >=2.5) or install ElementTree.' + raise ImportError(s1) + +# +# Import pygments and odtwriter pygments formatters if possible. +try: + import pygments + import pygments.lexers + from pygmentsformatter import OdtPygmentsProgFormatter, \ + OdtPygmentsLaTeXFormatter +except ImportError, exp: + pygments = None + +# +# Is the PIL imaging library installed? +try: + import Image +except ImportError, exp: + Image = None + +## import warnings +## warnings.warn('importing IPShellEmbed', UserWarning) +## from IPython.Shell import IPShellEmbed +## args = ['-pdb', '-pi1', 'In <\\#>: ', '-pi2', ' .\\D.: ', +## '-po', 'Out<\\#>: ', '-nosep'] +## ipshell = IPShellEmbed(args, +## banner = 'Entering IPython. Press Ctrl-D to exit.', +## exit_msg = 'Leaving Interpreter, back to program.') + + +# +# ElementTree does not support getparent method (lxml does). +# This wrapper class and the following support functions provide +# that support for the ability to get the parent of an element. +# +if WhichElementTree == 'elementtree': + class _ElementInterfaceWrapper(etree._ElementInterface): + def __init__(self, tag, attrib=None): + etree._ElementInterface.__init__(self, tag, attrib) + if attrib is None: + attrib = {} + self.parent = None + def setparent(self, parent): + self.parent = parent + def getparent(self): + return self.parent + + +# +# Constants and globals + +SPACES_PATTERN = re.compile(r'( +)') +TABS_PATTERN = re.compile(r'(\t+)') +FILL_PAT1 = re.compile(r'^ +') +FILL_PAT2 = re.compile(r' {2,}') + +TableStylePrefix = 'Table' + +GENERATOR_DESC = 'Docutils.org/odf_odt' + +NAME_SPACE_1 = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0' + +CONTENT_NAMESPACE_DICT = CNSD = { +# 'office:version': '1.0', + 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'dom': 'http://www.w3.org/2001/xml-events', + 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'math': 'http://www.w3.org/1998/Math/MathML', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'office': NAME_SPACE_1, + 'ooo': 'http://openoffice.org/2004/office', + 'oooc': 'http://openoffice.org/2004/calc', + 'ooow': 'http://openoffice.org/2004/writer', + 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + + 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xforms': 'http://www.w3.org/2002/xforms', + 'xlink': 'http://www.w3.org/1999/xlink', + 'xsd': 'http://www.w3.org/2001/XMLSchema', + 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', + } + +STYLES_NAMESPACE_DICT = SNSD = { +# 'office:version': '1.0', + 'chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'dom': 'http://www.w3.org/2001/xml-events', + 'dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'math': 'http://www.w3.org/1998/Math/MathML', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'office': NAME_SPACE_1, + 'presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'ooo': 'http://openoffice.org/2004/office', + 'oooc': 'http://openoffice.org/2004/calc', + 'ooow': 'http://openoffice.org/2004/writer', + 'script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xlink': 'http://www.w3.org/1999/xlink', + } + +MANIFEST_NAMESPACE_DICT = MANNSD = { + 'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0', +} + +META_NAMESPACE_DICT = METNSD = { +# 'office:version': '1.0', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'office': NAME_SPACE_1, + 'ooo': 'http://openoffice.org/2004/office', + 'xlink': 'http://www.w3.org/1999/xlink', +} + +# +# Attribute dictionaries for use with ElementTree (not lxml), which +# does not support use of nsmap parameter on Element() and SubElement(). + +CONTENT_NAMESPACE_ATTRIB = { + 'office:version': '1.0', + 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:dom': 'http://www.w3.org/2001/xml-events', + 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'xmlns:math': 'http://www.w3.org/1998/Math/MathML', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:oooc': 'http://openoffice.org/2004/calc', + 'xmlns:ooow': 'http://openoffice.org/2004/writer', + 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xmlns:xforms': 'http://www.w3.org/2002/xforms', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + 'xmlns:xsd': 'http://www.w3.org/2001/XMLSchema', + 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', + } + +STYLES_NAMESPACE_ATTRIB = { + 'office:version': '1.0', + 'xmlns:chart': 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:dom': 'http://www.w3.org/2001/xml-events', + 'xmlns:dr3d': 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0', + 'xmlns:draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0', + 'xmlns:fo': 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0', + 'xmlns:form': 'urn:oasis:names:tc:opendocument:xmlns:form:1.0', + 'xmlns:math': 'http://www.w3.org/1998/Math/MathML', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:number': 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:presentation': 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0', + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:oooc': 'http://openoffice.org/2004/calc', + 'xmlns:ooow': 'http://openoffice.org/2004/writer', + 'xmlns:script': 'urn:oasis:names:tc:opendocument:xmlns:script:1.0', + 'xmlns:style': 'urn:oasis:names:tc:opendocument:xmlns:style:1.0', + 'xmlns:svg': 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0', + 'xmlns:table': 'urn:oasis:names:tc:opendocument:xmlns:table:1.0', + 'xmlns:text': 'urn:oasis:names:tc:opendocument:xmlns:text:1.0', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + } + +MANIFEST_NAMESPACE_ATTRIB = { + 'xmlns:manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0', +} + +META_NAMESPACE_ATTRIB = { + 'office:version': '1.0', + 'xmlns:dc': 'http://purl.org/dc/elements/1.1/', + 'xmlns:meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0', + 'xmlns:office': NAME_SPACE_1, + 'xmlns:ooo': 'http://openoffice.org/2004/office', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', +} + + +# +# Functions +# + +# +# ElementTree support functions. +# In order to be able to get the parent of elements, must use these +# instead of the functions with same name provided by ElementTree. +# +def Element(tag, attrib=None, nsmap=None, nsdict=CNSD): + if attrib is None: + attrib = {} + tag, attrib = fix_ns(tag, attrib, nsdict) + if WhichElementTree == 'lxml': + el = etree.Element(tag, attrib, nsmap=nsmap) + else: + el = _ElementInterfaceWrapper(tag, attrib) + return el + +def SubElement(parent, tag, attrib=None, nsmap=None, nsdict=CNSD): + if attrib is None: + attrib = {} + tag, attrib = fix_ns(tag, attrib, nsdict) + if WhichElementTree == 'lxml': + el = etree.SubElement(parent, tag, attrib, nsmap=nsmap) + else: + el = _ElementInterfaceWrapper(tag, attrib) + parent.append(el) + el.setparent(parent) + return el + +def fix_ns(tag, attrib, nsdict): + nstag = add_ns(tag, nsdict) + nsattrib = {} + for key, val in attrib.iteritems(): + nskey = add_ns(key, nsdict) + nsattrib[nskey] = val + return nstag, nsattrib + +def add_ns(tag, nsdict=CNSD): + if WhichElementTree == 'lxml': + nstag, name = tag.split(':') + ns = nsdict.get(nstag) + if ns is None: + raise RuntimeError, 'Invalid namespace prefix: %s' % nstag + tag = '{%s}%s' % (ns, name,) + return tag + +def ToString(et): + outstream = StringIO.StringIO() + et.write(outstream) + s1 = outstream.getvalue() + outstream.close() + return s1 + + +def escape_cdata(text): + text = text.replace("&", "&") + text = text.replace("<", "<") + text = text.replace(">", ">") + ascii = '' + for char in text: + if ord(char) >= ord("\x7f"): + ascii += "&#x%X;" % ( ord(char), ) + else: + ascii += char + return ascii + + +# +# Classes +# + + + +WORD_SPLIT_PAT1 = re.compile(r'\b(\w*)\b\W*') + +def split_words(line): + # We need whitespace at the end of the string for our regexpr. + line += ' ' + words = [] + pos1 = 0 + mo = WORD_SPLIT_PAT1.search(line, pos1) + while mo is not None: + word = mo.groups()[0] + words.append(word) + pos1 = mo.end() + mo = WORD_SPLIT_PAT1.search(line, pos1) + return words + + +# +# Information about the indentation level for lists nested inside +# other contexts, e.g. dictionary lists. +class ListLevel(object): + def __init__(self, level, sibling_level=True, nested_level=True): + self.level = level + self.sibling_level = sibling_level + self.nested_level = nested_level + def set_sibling(self, sibling_level): self.sibling_level = sibling_level + def get_sibling(self): return self.sibling_level + def set_nested(self, nested_level): self.nested_level = nested_level + def get_nested(self): return self.nested_level + def set_level(self, level): self.level = level + def get_level(self): return self.level + + +class Writer(writers.Writer): + + MIME_TYPE = 'application/vnd.oasis.opendocument.text' + EXTENSION = '.odt' + + supported = ('html', 'html4css1', 'xhtml') + """Formats this writer supports.""" + + default_stylesheet = 'styles' + EXTENSION + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + default_template = 'template.txt' + + default_template_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_template)) + + settings_spec = ( + 'ODF-Specific Options', + None, + ( + ('Specify a stylesheet. ' + 'Default: "%s"' % default_stylesheet_path, + ['--stylesheet'], + { + 'default': default_stylesheet_path, + 'dest': 'stylesheet' + }), + ('Specify a configuration/mapping file relative to the ' + 'current working ' + 'directory for additional ODF options. ' + 'In particular, this file may contain a section named ' + '"Formats" that maps default style names to ' + 'names to be used in the resulting output file allowing for ' + 'adhering to external standards. ' + 'For more info and the format of the configuration/mapping file, ' + 'see the odtwriter doc.', + ['--odf-config-file'], + {'metavar': ''}), + ('Obfuscate email addresses to confuse harvesters while still ' + 'keeping email links usable with standards-compliant browsers.', + ['--cloak-email-addresses'], + {'default': False, + 'action': 'store_true', + 'dest': 'cloak_email_addresses', + 'validator': frontend.validate_boolean}), + ('Do not obfuscate email addresses.', + ['--no-cloak-email-addresses'], + {'default': False, + 'action': 'store_false', + 'dest': 'cloak_email_addresses', + 'validator': frontend.validate_boolean}), + ('Specify the thickness of table borders in thousands of a cm. ' + 'Default is 35.', + ['--table-border-thickness'], + {'default': 35, + 'validator': frontend.validate_nonnegative_int}), + ('Add syntax highlighting in literal code blocks.', + ['--add-syntax-highlighting'], + {'default': False, + 'action': 'store_true', + 'dest': 'add_syntax_highlighting', + 'validator': frontend.validate_boolean}), + ('Do not add syntax highlighting in literal code blocks. (default)', + ['--no-syntax-highlighting'], + {'default': False, + 'action': 'store_false', + 'dest': 'add_syntax_highlighting', + 'validator': frontend.validate_boolean}), + ('Create sections for headers. (default)', + ['--create-sections'], + {'default': True, + 'action': 'store_true', + 'dest': 'create_sections', + 'validator': frontend.validate_boolean}), + ('Do not create sections for headers.', + ['--no-sections'], + {'default': True, + 'action': 'store_false', + 'dest': 'create_sections', + 'validator': frontend.validate_boolean}), + ('Create links.', + ['--create-links'], + {'default': False, + 'action': 'store_true', + 'dest': 'create_links', + 'validator': frontend.validate_boolean}), + ('Do not create links. (default)', + ['--no-links'], + {'default': False, + 'action': 'store_false', + 'dest': 'create_links', + 'validator': frontend.validate_boolean}), + ('Generate endnotes at end of document, not footnotes ' + 'at bottom of page.', + ['--endnotes-end-doc'], + {'default': False, + 'action': 'store_true', + 'dest': 'endnotes_end_doc', + 'validator': frontend.validate_boolean}), + ('Generate footnotes at bottom of page, not endnotes ' + 'at end of document. (default)', + ['--no-endnotes-end-doc'], + {'default': False, + 'action': 'store_false', + 'dest': 'endnotes_end_doc', + 'validator': frontend.validate_boolean}), + ('Generate a bullet list table of contents, not ' + 'an ODF/oowriter table of contents.', + ['--generate-list-toc'], + {'default': True, + 'action': 'store_false', + 'dest': 'generate_oowriter_toc', + 'validator': frontend.validate_boolean}), + ('Generate an ODF/oowriter table of contents, not ' + 'a bullet list. (default)', + ['--generate-oowriter-toc'], + {'default': True, + 'action': 'store_true', + 'dest': 'generate_oowriter_toc', + 'validator': frontend.validate_boolean}), + ) + ) + + settings_defaults = { + 'output_encoding_error_handler': 'xmlcharrefreplace', + } + + relative_path_settings = ( + 'stylesheet_path', + ) + + config_section = 'opendocument odf writer' + config_section_dependencies = ( + 'writers', + ) + + def __init__(self): + writers.Writer.__init__(self) + self.translator_class = ODFTranslator + + def translate(self): + self.settings = self.document.settings + self.visitor = self.translator_class(self.document) + self.document.walkabout(self.visitor) + self.visitor.add_doc_title() + self.assemble_my_parts() + self.output = self.parts['whole'] + + def assemble_my_parts(self): + """Assemble the `self.parts` dictionary. Extend in subclasses. + """ + writers.Writer.assemble_parts(self) + f = tempfile.NamedTemporaryFile() + zfile = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) + content = self.visitor.content_astext() + self.write_zip_str(zfile, 'content.xml', content) + self.write_zip_str(zfile, 'mimetype', self.MIME_TYPE) + s1 = self.create_manifest() + self.write_zip_str(zfile, 'META-INF/manifest.xml', s1) + s1 = self.create_meta() + self.write_zip_str(zfile, 'meta.xml', s1) + s1 = self.get_stylesheet() + self.write_zip_str(zfile, 'styles.xml', s1) + s1 = self.get_settings() + self.write_zip_str(zfile, 'settings.xml', s1) + self.store_embedded_files(zfile) + zfile.close() + f.seek(0) + whole = f.read() + f.close() + self.parts['whole'] = whole + self.parts['encoding'] = self.document.settings.output_encoding + self.parts['version'] = docutils.__version__ + + def write_zip_str(self, zfile, name, bytes): + localtime = time.localtime(time.time()) + zinfo = zipfile.ZipInfo(name, localtime) + # Add some standard UNIX file access permissions (-rw-r--r--). + zinfo.external_attr = (0x81a4 & 0xFFFF) << 16L + zinfo.compress_type = zipfile.ZIP_DEFLATED + zfile.writestr(zinfo, bytes) + + def store_embedded_files(self, zfile): + embedded_files = self.visitor.get_embedded_file_list() + for source, destination in embedded_files: + if source is None: + continue + try: + # encode/decode + destination1 = destination.decode('latin-1').encode('utf-8') + zfile.write(source, destination1, zipfile.ZIP_STORED) + except OSError, e: + self.document.reporter.warning( + "Can't open file %s." % (source, )) + + def get_settings(self): + """ + modeled after get_stylesheet + """ + stylespath = self.settings.stylesheet + zfile = zipfile.ZipFile(stylespath, 'r') + s1 = zfile.read('settings.xml') + zfile.close() + return s1 + + def get_stylesheet(self): + """Retrieve the stylesheet from either a .xml file or from + a .odt (zip) file. Return the content as a string. + """ + stylespath = self.settings.stylesheet + ext = os.path.splitext(stylespath)[1] + if ext == '.xml': + stylesfile = open(stylespath, 'r') + s1 = stylesfile.read() + stylesfile.close() + elif ext == self.EXTENSION: + zfile = zipfile.ZipFile(stylespath, 'r') + s1 = zfile.read('styles.xml') + zfile.close() + else: + raise RuntimeError, 'stylesheet path (%s) must be %s or .xml file' %(stylespath, self.EXTENSION) + s1 = self.visitor.setup_page(s1) + return s1 + + def assemble_parts(self): + pass + + def create_manifest(self): + if WhichElementTree == 'lxml': + root = Element('manifest:manifest', + nsmap=MANIFEST_NAMESPACE_DICT, + nsdict=MANIFEST_NAMESPACE_DICT, + ) + else: + root = Element('manifest:manifest', + attrib=MANIFEST_NAMESPACE_ATTRIB, + nsdict=MANIFEST_NAMESPACE_DICT, + ) + doc = etree.ElementTree(root) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': self.MIME_TYPE, + 'manifest:full-path': '/', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'content.xml', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'styles.xml', + }, nsdict=MANNSD) + SubElement(root, 'manifest:file-entry', attrib={ + 'manifest:media-type': 'text/xml', + 'manifest:full-path': 'meta.xml', + }, nsdict=MANNSD) + s1 = ToString(doc) + doc = minidom.parseString(s1) + s1 = doc.toprettyxml(' ') + return s1 + + def create_meta(self): + if WhichElementTree == 'lxml': + root = Element('office:document-meta', + nsmap=META_NAMESPACE_DICT, + nsdict=META_NAMESPACE_DICT, + ) + else: + root = Element('office:document-meta', + attrib=META_NAMESPACE_ATTRIB, + nsdict=META_NAMESPACE_DICT, + ) + doc = etree.ElementTree(root) + root = SubElement(root, 'office:meta', nsdict=METNSD) + el1 = SubElement(root, 'meta:generator', nsdict=METNSD) + el1.text = 'Docutils/rst2odf.py/%s' % (VERSION, ) + s1 = os.environ.get('USER', '') + el1 = SubElement(root, 'meta:initial-creator', nsdict=METNSD) + el1.text = s1 + s2 = time.strftime('%Y-%m-%dT%H:%M:%S', time.localtime()) + el1 = SubElement(root, 'meta:creation-date', nsdict=METNSD) + el1.text = s2 + el1 = SubElement(root, 'dc:creator', nsdict=METNSD) + el1.text = s1 + el1 = SubElement(root, 'dc:date', nsdict=METNSD) + el1.text = s2 + el1 = SubElement(root, 'dc:language', nsdict=METNSD) + el1.text = 'en-US' + el1 = SubElement(root, 'meta:editing-cycles', nsdict=METNSD) + el1.text = '1' + el1 = SubElement(root, 'meta:editing-duration', nsdict=METNSD) + el1.text = 'PT00M01S' + title = self.visitor.get_title() + el1 = SubElement(root, 'dc:title', nsdict=METNSD) + if title: + el1.text = title + else: + el1.text = '[no title]' + meta_dict = self.visitor.get_meta_dict() + keywordstr = meta_dict.get('keywords') + if keywordstr is not None: + keywords = split_words(keywordstr) + for keyword in keywords: + el1 = SubElement(root, 'meta:keyword', nsdict=METNSD) + el1.text = keyword + description = meta_dict.get('description') + if description is not None: + el1 = SubElement(root, 'dc:description', nsdict=METNSD) + el1.text = description + s1 = ToString(doc) + #doc = minidom.parseString(s1) + #s1 = doc.toprettyxml(' ') + return s1 + +# class ODFTranslator(nodes.SparseNodeVisitor): + +class ODFTranslator(nodes.GenericNodeVisitor): + + used_styles = ( + 'attribution', 'blockindent', 'blockquote', 'blockquote-bulletitem', + 'blockquote-bulletlist', 'blockquote-enumitem', 'blockquote-enumlist', + 'bulletitem', 'bulletlist', 'caption', 'centeredtextbody', 'codeblock', + 'codeblock-classname', 'codeblock-comment', 'codeblock-functionname', + 'codeblock-keyword', 'codeblock-name', 'codeblock-number', + 'codeblock-operator', 'codeblock-string', 'emphasis', 'enumitem', + 'enumlist', 'epigraph', 'epigraph-bulletitem', 'epigraph-bulletlist', + 'epigraph-enumitem', 'epigraph-enumlist', 'footer', + 'footnote', 'citation', + 'header', 'highlights', 'highlights-bulletitem', + 'highlights-bulletlist', 'highlights-enumitem', 'highlights-enumlist', + 'horizontalline', 'inlineliteral', 'quotation', 'rubric', + 'strong', 'table-title', 'textbody', 'tocbulletlist', 'tocenumlist', + 'title', + 'subtitle', + 'heading1', + 'heading2', + 'heading3', + 'heading4', + 'heading5', + 'heading6', + 'heading7', + 'admon-attention-hdr', + 'admon-attention-body', + 'admon-caution-hdr', + 'admon-caution-body', + 'admon-danger-hdr', + 'admon-danger-body', + 'admon-error-hdr', + 'admon-error-body', + 'admon-generic-hdr', + 'admon-generic-body', + 'admon-hint-hdr', + 'admon-hint-body', + 'admon-important-hdr', + 'admon-important-body', + 'admon-note-hdr', + 'admon-note-body', + 'admon-tip-hdr', + 'admon-tip-body', + 'admon-warning-hdr', + 'admon-warning-body', + 'tableoption', + 'tableoption.%c', 'tableoption.%c%d', 'Table%d', 'Table%d.%c', + 'Table%d.%c%d', + 'lineblock1', + 'lineblock2', + 'lineblock3', + 'lineblock4', + 'lineblock5', + 'lineblock6', + ) + + def __init__(self, document): + #nodes.SparseNodeVisitor.__init__(self, document) + nodes.GenericNodeVisitor.__init__(self, document) + self.settings = document.settings + self.format_map = { } + if self.settings.odf_config_file: + from ConfigParser import ConfigParser + + parser = ConfigParser() + parser.read(self.settings.odf_config_file) + for rststyle, format in parser.items("Formats"): + if rststyle not in self.used_styles: + self.document.reporter.warning( + 'Style "%s" is not a style used by odtwriter.' % ( + rststyle, )) + self.format_map[rststyle] = format + self.section_level = 0 + self.section_count = 0 + # Create ElementTree content and styles documents. + if WhichElementTree == 'lxml': + root = Element( + 'office:document-content', + nsmap=CONTENT_NAMESPACE_DICT, + ) + else: + root = Element( + 'office:document-content', + attrib=CONTENT_NAMESPACE_ATTRIB, + ) + self.content_tree = etree.ElementTree(element=root) + self.current_element = root + SubElement(root, 'office:scripts') + SubElement(root, 'office:font-face-decls') + el = SubElement(root, 'office:automatic-styles') + self.automatic_styles = el + el = SubElement(root, 'office:body') + el = self.generate_content_element(el) + self.current_element = el + self.body_text_element = el + self.paragraph_style_stack = [self.rststyle('textbody'), ] + self.list_style_stack = [] + self.table_count = 0 + self.column_count = ord('A') - 1 + self.trace_level = -1 + self.optiontablestyles_generated = False + self.field_name = None + self.field_element = None + self.title = None + self.image_count = 0 + self.image_style_count = 0 + self.image_dict = {} + self.embedded_file_list = [] + self.syntaxhighlighting = 1 + self.syntaxhighlight_lexer = 'python' + self.header_content = [] + self.footer_content = [] + self.in_header = False + self.in_footer = False + self.blockstyle = '' + self.in_table_of_contents = False + self.table_of_content_index_body = None + self.list_level = 0 + self.footnote_ref_dict = {} + self.footnote_list = [] + self.footnote_chars_idx = 0 + self.footnote_level = 0 + self.pending_ids = [ ] + self.in_paragraph = False + self.found_doc_title = False + self.bumped_list_level_stack = [] + self.meta_dict = {} + self.line_block_level = 0 + self.line_indent_level = 0 + self.citation_id = None + + def add_doc_title(self): + text = self.settings.title + if text: + self.title = text + if not self.found_doc_title: + el = Element('text:p', attrib = { + 'text:style-name': self.rststyle('title'), + }) + el.text = text + self.body_text_element.insert(0, el) + + def rststyle(self, name, parameters=( )): + """ + Returns the style name to use for the given style. + + If `parameters` is given `name` must contain a matching number of ``%`` and + is used as a format expression with `parameters` as the value. + """ + name1 = name % parameters + stylename = self.format_map.get(name1, 'rststyle-%s' % name1) + return stylename + + def generate_content_element(self, root): + return SubElement(root, 'office:text') + + def setup_page(self, content): + root_el = etree.fromstring(content) + self.setup_paper(root_el) + if len(self.header_content) > 0 or len(self.footer_content) > 0: + self.add_header_footer(root_el) + new_content = etree.tostring(root_el) + return new_content + + def setup_paper(self, root_el): + try: + fin = os.popen("paperconf -s 2> /dev/null") + w, h = map(float, fin.read().split()) + fin.close() + except: + w, h = 612, 792 # default to Letter + def walk(el): + if el.tag == "{%s}page-layout-properties" % SNSD["style"] and \ + not el.attrib.has_key("{%s}page-width" % SNSD["fo"]): + el.attrib["{%s}page-width" % SNSD["fo"]] = "%.3fpt" % w + el.attrib["{%s}page-height" % SNSD["fo"]] = "%.3fpt" % h + el.attrib["{%s}margin-left" % SNSD["fo"]] = \ + el.attrib["{%s}margin-right" % SNSD["fo"]] = \ + "%.3fpt" % (.1 * w) + el.attrib["{%s}margin-top" % SNSD["fo"]] = \ + el.attrib["{%s}margin-bottom" % SNSD["fo"]] = \ + "%.3fpt" % (.1 * h) + else: + for subel in el.getchildren(): walk(subel) + walk(root_el) + + def add_header_footer(self, root_el): + path = '{%s}master-styles' % (NAME_SPACE_1, ) + master_el = root_el.find(path) + if master_el is None: + return + path = '{%s}master-page' % (SNSD['style'], ) + master_el = master_el.find(path) + if master_el is None: + return + el1 = master_el + if len(self.header_content) > 0: + if WhichElementTree == 'lxml': + el2 = SubElement(el1, 'style:header', nsdict=SNSD) + else: + el2 = SubElement(el1, 'style:header', + attrib=STYLES_NAMESPACE_ATTRIB, + nsdict=STYLES_NAMESPACE_DICT, + ) + for el in self.header_content: + attrkey = add_ns('text:style-name', nsdict=SNSD) + el.attrib[attrkey] = self.rststyle('header') + el2.append(el) + if len(self.footer_content) > 0: + if WhichElementTree == 'lxml': + el2 = SubElement(el1, 'style:footer', nsdict=SNSD) + else: + el2 = SubElement(el1, 'style:footer', + attrib=STYLES_NAMESPACE_ATTRIB, + nsdict=STYLES_NAMESPACE_DICT, + ) + for el in self.footer_content: + attrkey = add_ns('text:style-name', nsdict=SNSD) + el.attrib[attrkey] = self.rststyle('footer') + el2.append(el) + + def astext(self): + root = self.content_tree.getroot() + et = etree.ElementTree(root) + s1 = ToString(et) + return s1 + + def content_astext(self): + return self.astext() + + def set_title(self, title): self.title = title + def get_title(self): return self.title + def set_embedded_file_list(self, embedded_file_list): + self.embedded_file_list = embedded_file_list + def get_embedded_file_list(self): return self.embedded_file_list + def get_meta_dict(self): return self.meta_dict + + def process_footnotes(self): + for node, el1 in self.footnote_list: + backrefs = node.attributes.get('backrefs', []) + first = True + for ref in backrefs: + el2 = self.footnote_ref_dict.get(ref) + if el2 is not None: + if first: + first = False + el3 = copy.deepcopy(el1) + el2.append(el3) + else: + children = el2.getchildren() + if len(children) > 0: # and 'id' in el2.attrib: + child = children[0] + ref1 = child.text + attribkey = add_ns('text:id', nsdict=SNSD) + id1 = el2.get(attribkey, 'footnote-error') + if id1 is None: + id1 = '' + tag = add_ns('text:note-ref', nsdict=SNSD) + el2.tag = tag + if self.settings.endnotes_end_doc: + note_class = 'endnote' + else: + note_class = 'footnote' + el2.attrib.clear() + attribkey = add_ns('text:note-class', nsdict=SNSD) + el2.attrib[attribkey] = note_class + attribkey = add_ns('text:ref-name', nsdict=SNSD) + el2.attrib[attribkey] = id1 + attribkey = add_ns('text:reference-format', nsdict=SNSD) + el2.attrib[attribkey] = 'page' + el2.text = ref1 + + # + # Utility methods + + def append_child(self, tag, attrib=None, parent=None): + if parent is None: + parent = self.current_element + if attrib is None: + el = SubElement(parent, tag) + else: + el = SubElement(parent, tag, attrib) + return el + + def append_p(self, style, text=None): + result = self.append_child('text:p', attrib={ + 'text:style-name': self.rststyle(style)}) + self.append_pending_ids(result) + if text is not None: + result.text = text + return result + + def append_pending_ids(self, el): + if self.settings.create_links: + for id in self.pending_ids: + SubElement(el, 'text:reference-mark', attrib={ + 'text:name': id}) + self.pending_ids = [ ] + + def set_current_element(self, el): + self.current_element = el + + def set_to_parent(self): + self.current_element = self.current_element.getparent() + + def generate_labeled_block(self, node, label): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + el = self.append_p('blockindent') + return el + + def generate_labeled_line(self, node, label): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + el1.tail = node.astext() + return el + + def encode(self, text): + text = text.replace(u'\u00a0', " ") + return text + + # + # Visitor functions + # + # In alphabetic order, more or less. + # See docutils.docutils.nodes.node_class_names. + # + + def dispatch_visit(self, node): + """Override to catch basic attributes which many nodes have.""" + self.handle_basic_atts(node) + nodes.GenericNodeVisitor.dispatch_visit(self, node) + + def handle_basic_atts(self, node): + if isinstance(node, nodes.Element) and node['ids']: + self.pending_ids += node['ids'] + + def default_visit(self, node): + self.document.reporter.warning('missing visit_%s' % (node.tagname, )) + + def default_departure(self, node): + self.document.reporter.warning('missing depart_%s' % (node.tagname, )) + + def visit_Text(self, node): + # Skip nodes whose text has been processed in parent nodes. + if isinstance(node.parent, docutils.nodes.literal_block): + return + text = node.astext() + # Are we in mixed content? If so, add the text to the + # etree tail of the previous sibling element. + if len(self.current_element.getchildren()) > 0: + if self.current_element.getchildren()[-1].tail: + self.current_element.getchildren()[-1].tail += text + else: + self.current_element.getchildren()[-1].tail = text + else: + if self.current_element.text: + self.current_element.text += text + else: + self.current_element.text = text + + def depart_Text(self, node): + pass + + # + # Pre-defined fields + # + + def visit_address(self, node): + el = self.generate_labeled_block(node, 'Address: ') + self.set_current_element(el) + + def depart_address(self, node): + self.set_to_parent() + + def visit_author(self, node): + if isinstance(node.parent, nodes.authors): + el = self.append_p('blockindent') + else: + el = self.generate_labeled_block(node, 'Author: ') + self.set_current_element(el) + + def depart_author(self, node): + self.set_to_parent() + + def visit_authors(self, node): + label = 'Authors:' + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = label + + def depart_authors(self, node): + pass + + def visit_contact(self, node): + el = self.generate_labeled_block(node, 'Contact: ') + self.set_current_element(el) + + def depart_contact(self, node): + self.set_to_parent() + + def visit_copyright(self, node): + el = self.generate_labeled_block(node, 'Copyright: ') + self.set_current_element(el) + + def depart_copyright(self, node): + self.set_to_parent() + + def visit_date(self, node): + self.generate_labeled_line(node, 'Date: ') + + def depart_date(self, node): + pass + + def visit_organization(self, node): + el = self.generate_labeled_block(node, 'Organization: ') + self.set_current_element(el) + + def depart_organization(self, node): + self.set_to_parent() + + def visit_status(self, node): + el = self.generate_labeled_block(node, 'Status: ') + self.set_current_element(el) + + def depart_status(self, node): + self.set_to_parent() + + def visit_revision(self, node): + self.generate_labeled_line(node, 'Revision: ') + + def depart_revision(self, node): + pass + + def visit_version(self, node): + el = self.generate_labeled_line(node, 'Version: ') + #self.set_current_element(el) + + def depart_version(self, node): + #self.set_to_parent() + pass + + def visit_attribution(self, node): + el = self.append_p('attribution', node.astext()) + + def depart_attribution(self, node): + pass + + def visit_block_quote(self, node): + if 'epigraph' in node.attributes['classes']: + self.paragraph_style_stack.append(self.rststyle('epigraph')) + self.blockstyle = self.rststyle('epigraph') + elif 'highlights' in node.attributes['classes']: + self.paragraph_style_stack.append(self.rststyle('highlights')) + self.blockstyle = self.rststyle('highlights') + else: + self.paragraph_style_stack.append(self.rststyle('blockquote')) + self.blockstyle = self.rststyle('blockquote') + self.line_indent_level += 1 + + def depart_block_quote(self, node): + self.paragraph_style_stack.pop() + self.blockstyle = '' + self.line_indent_level -= 1 + + def visit_bullet_list(self, node): + self.list_level +=1 + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + pass + else: + if node.has_key('classes') and \ + 'auto-toc' in node.attributes['classes']: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('tocenumlist'), + }) + self.list_style_stack.append(self.rststyle('enumitem')) + else: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('tocbulletlist'), + }) + self.list_style_stack.append(self.rststyle('bulletitem')) + self.set_current_element(el) + else: + if self.blockstyle == self.rststyle('blockquote'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('blockquote-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('blockquote-bulletitem')) + elif self.blockstyle == self.rststyle('highlights'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('highlights-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('highlights-bulletitem')) + elif self.blockstyle == self.rststyle('epigraph'): + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('epigraph-bulletlist'), + }) + self.list_style_stack.append( + self.rststyle('epigraph-bulletitem')) + else: + el = SubElement(self.current_element, 'text:list', attrib={ + 'text:style-name': self.rststyle('bulletlist'), + }) + self.list_style_stack.append(self.rststyle('bulletitem')) + self.set_current_element(el) + + def depart_bullet_list(self, node): + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + pass + else: + self.set_to_parent() + self.list_style_stack.pop() + else: + self.set_to_parent() + self.list_style_stack.pop() + self.list_level -=1 + + def visit_caption(self, node): + raise nodes.SkipChildren() + pass + + def depart_caption(self, node): + pass + + def visit_comment(self, node): + el = self.append_p('textbody') + el1 = SubElement(el, 'office:annotation', attrib={}) + el2 = SubElement(el1, 'text:p', attrib={}) + el2.text = node.astext() + + def depart_comment(self, node): + pass + + def visit_compound(self, node): + # The compound directive currently receives no special treatment. + pass + + def depart_compound(self, node): + pass + + def visit_container(self, node): + styles = node.attributes.get('classes', ()) + if len(styles) > 0: + self.paragraph_style_stack.append(self.rststyle(styles[0])) + + def depart_container(self, node): + styles = node.attributes.get('classes', ()) + if len(styles) > 0: + self.paragraph_style_stack.pop() + + def visit_decoration(self, node): + pass + + def depart_decoration(self, node): + pass + + def visit_definition(self, node): + self.paragraph_style_stack.append(self.rststyle('blockindent')) + self.bumped_list_level_stack.append(ListLevel(1)) + + def depart_definition(self, node): + self.paragraph_style_stack.pop() + self.bumped_list_level_stack.pop() + + def visit_definition_list(self, node): + pass + + def depart_definition_list(self, node): + pass + + def visit_definition_list_item(self, node): + pass + + def depart_definition_list_item(self, node): + pass + + def visit_term(self, node): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + #el1.text = node.astext() + self.set_current_element(el1) + + def depart_term(self, node): + self.set_to_parent() + self.set_to_parent() + + def visit_classifier(self, node): + els = self.current_element.getchildren() + if len(els) > 0: + el = els[-1] + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('emphasis') + }) + el1.text = ' (%s)' % (node.astext(), ) + + def depart_classifier(self, node): + pass + + def visit_document(self, node): + pass + + def depart_document(self, node): + self.process_footnotes() + + def visit_docinfo(self, node): + self.section_level += 1 + self.section_count += 1 + if self.settings.create_sections: + el = self.append_child('text:section', attrib={ + 'text:name': 'Section%d' % self.section_count, + 'text:style-name': 'Sect%d' % self.section_level, + }) + self.set_current_element(el) + + def depart_docinfo(self, node): + self.section_level -= 1 + if self.settings.create_sections: + self.set_to_parent() + + def visit_emphasis(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('emphasis')}) + self.set_current_element(el) + + def depart_emphasis(self, node): + self.set_to_parent() + + def visit_enumerated_list(self, node): + el1 = self.current_element + if self.blockstyle == self.rststyle('blockquote'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('blockquote-enumlist'), + }) + self.list_style_stack.append(self.rststyle('blockquote-enumitem')) + elif self.blockstyle == self.rststyle('highlights'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('highlights-enumlist'), + }) + self.list_style_stack.append(self.rststyle('highlights-enumitem')) + elif self.blockstyle == self.rststyle('epigraph'): + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle('epigraph-enumlist'), + }) + self.list_style_stack.append(self.rststyle('epigraph-enumitem')) + else: + liststylename = 'enumlist-%s' % (node.get('enumtype', 'arabic'), ) + el2 = SubElement(el1, 'text:list', attrib={ + 'text:style-name': self.rststyle(liststylename), + }) + self.list_style_stack.append(self.rststyle('enumitem')) + self.set_current_element(el2) + + def depart_enumerated_list(self, node): + self.set_to_parent() + self.list_style_stack.pop() + + def visit_list_item(self, node): + # If we are in a "bumped" list level, then wrap this + # list in an outer lists in order to increase the + # indentation level. + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + self.paragraph_style_stack.append( + self.rststyle('contents-%d' % (self.list_level, ))) + else: + el1 = self.append_child('text:list-item') + self.set_current_element(el1) + else: + el1 = self.append_child('text:list-item') + el3 = el1 + if len(self.bumped_list_level_stack) > 0: + level_obj = self.bumped_list_level_stack[-1] + if level_obj.get_sibling(): + level_obj.set_nested(False) + for level_obj1 in self.bumped_list_level_stack: + for idx in range(level_obj1.get_level()): + el2 = self.append_child('text:list', parent=el3) + el3 = self.append_child( + 'text:list-item', parent=el2) + self.paragraph_style_stack.append(self.list_style_stack[-1]) + self.set_current_element(el3) + + def depart_list_item(self, node): + if self.in_table_of_contents: + if self.settings.generate_oowriter_toc: + self.paragraph_style_stack.pop() + else: + self.set_to_parent() + else: + if len(self.bumped_list_level_stack) > 0: + level_obj = self.bumped_list_level_stack[-1] + if level_obj.get_sibling(): + level_obj.set_nested(True) + for level_obj1 in self.bumped_list_level_stack: + for idx in range(level_obj1.get_level()): + self.set_to_parent() + self.set_to_parent() + self.paragraph_style_stack.pop() + self.set_to_parent() + + def visit_header(self, node): + self.in_header = True + + def depart_header(self, node): + self.in_header = False + + def visit_footer(self, node): + self.in_footer = True + + def depart_footer(self, node): + self.in_footer = False + + def visit_field(self, node): + pass + + def depart_field(self, node): + pass + + def visit_field_list(self, node): + pass + + def depart_field_list(self, node): + pass + + def visit_field_name(self, node): + el = self.append_p('textbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = node.astext() + + def depart_field_name(self, node): + pass + + def visit_field_body(self, node): + self.paragraph_style_stack.append(self.rststyle('blockindent')) + + def depart_field_body(self, node): + self.paragraph_style_stack.pop() + + def visit_figure(self, node): + pass + + def depart_figure(self, node): + pass + + def visit_footnote(self, node): + self.footnote_level += 1 + self.save_footnote_current = self.current_element + el1 = Element('text:note-body') + self.current_element = el1 + self.footnote_list.append((node, el1)) + if isinstance(node, docutils.nodes.citation): + self.paragraph_style_stack.append(self.rststyle('citation')) + else: + self.paragraph_style_stack.append(self.rststyle('footnote')) + + def depart_footnote(self, node): + self.paragraph_style_stack.pop() + self.current_element = self.save_footnote_current + self.footnote_level -= 1 + + footnote_chars = [ + '*', '**', '***', + '++', '+++', + '##', '###', + '@@', '@@@', + ] + + def visit_footnote_reference(self, node): + if self.footnote_level <= 0: + id = node.attributes['ids'][0] + refid = node.attributes.get('refid') + if refid is None: + refid = '' + if self.settings.endnotes_end_doc: + note_class = 'endnote' + else: + note_class = 'footnote' + el1 = self.append_child('text:note', attrib={ + 'text:id': '%s' % (refid, ), + 'text:note-class': note_class, + }) + note_auto = str(node.attributes.get('auto', 1)) + if isinstance(node, docutils.nodes.citation_reference): + citation = '[%s]' % node.astext() + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': citation, + }) + el2.text = citation + elif note_auto == '1': + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': node.astext(), + }) + el2.text = node.astext() + elif note_auto == '*': + if self.footnote_chars_idx >= len( + ODFTranslator.footnote_chars): + self.footnote_chars_idx = 0 + footnote_char = ODFTranslator.footnote_chars[ + self.footnote_chars_idx] + self.footnote_chars_idx += 1 + el2 = SubElement(el1, 'text:note-citation', attrib={ + 'text:label': footnote_char, + }) + el2.text = footnote_char + self.footnote_ref_dict[id] = el1 + raise nodes.SkipChildren() + + def depart_footnote_reference(self, node): + pass + + def visit_citation(self, node): + for id in node.attributes['ids']: + self.citation_id = id + break + self.paragraph_style_stack.append(self.rststyle('blockindent')) + self.bumped_list_level_stack.append(ListLevel(1)) + + def depart_citation(self, node): + self.citation_id = None + self.paragraph_style_stack.pop() + self.bumped_list_level_stack.pop() + + def visit_citation_reference(self, node): + if self.settings.create_links: + id = node.attributes['refid'] + el = self.append_child('text:reference-ref', attrib={ + 'text:ref-name': '%s' % (id, ), + 'text:reference-format': 'text', + }) + el.text = '[' + self.set_current_element(el) + elif self.current_element.text is None: + self.current_element.text = '[' + else: + self.current_element.text += '[' + + def depart_citation_reference(self, node): + self.current_element.text += ']' + if self.settings.create_links: + self.set_to_parent() + + def visit_label(self, node): + if isinstance(node.parent, docutils.nodes.footnote): + raise nodes.SkipChildren() + elif self.citation_id is not None: + el = self.append_p('textbody') + self.set_current_element(el) + el.text = '[' + if self.settings.create_links: + el1 = self.append_child('text:reference-mark-start', attrib={ + 'text:name': '%s' % (self.citation_id, ), + }) + + def depart_label(self, node): + if isinstance(node.parent, docutils.nodes.footnote): + pass + elif self.citation_id is not None: + self.current_element.text += ']' + if self.settings.create_links: + el = self.append_child('text:reference-mark-end', attrib={ + 'text:name': '%s' % (self.citation_id, ), + }) + self.set_to_parent() + + def visit_generated(self, node): + pass + + def depart_generated(self, node): + pass + + def check_file_exists(self, path): + if os.path.exists(path): + return 1 + else: + return 0 + + def visit_image(self, node): + # Capture the image file. + if 'uri' in node.attributes: + source = node.attributes['uri'] + if not self.check_file_exists(source): + self.document.reporter.warning( + 'Cannot find image file %s.' % (source, )) + return + else: + return + if source in self.image_dict: + filename, destination = self.image_dict[source] + else: + self.image_count += 1 + filename = os.path.split(source)[1] + destination = 'Pictures/1%08x%s' % (self.image_count, filename, ) + spec = (os.path.abspath(source), destination,) + + self.embedded_file_list.append(spec) + self.image_dict[source] = (source, destination,) + # Is this a figure (containing an image) or just a plain image? + if self.in_paragraph: + el1 = self.current_element + else: + el1 = SubElement(self.current_element, 'text:p', + attrib={'text:style-name': self.rststyle('textbody')}) + el2 = el1 + if isinstance(node.parent, docutils.nodes.figure): + el3, el4, caption = self.generate_figure(node, source, + destination, el2) + attrib = { + 'draw:blue': '0%', + 'draw:color-inversion': 'false', + 'draw:color-mode': 'standard', + 'draw:contrast': '0%', + 'draw:gamma': '100%', + 'draw:green': '0%', + 'draw:image-opacity': '100%', + 'draw:luminance': '0%', + 'draw:red': '0%', + 'fo:border': 'none', + 'fo:clip': 'rect(0in 0in 0in 0in)', + 'fo:margin-bottom': '0in', + 'fo:margin-left': '0in', + 'fo:margin-right': '0in', + 'fo:margin-top': '0in', + 'fo:padding': '0in', + 'style:horizontal-pos': 'from-left', + 'style:horizontal-rel': 'paragraph-content', + 'style:mirror': 'none', + 'style:run-through': 'foreground', + 'style:shadow': 'none', + 'style:vertical-pos': 'from-top', + 'style:vertical-rel': 'paragraph-content', + 'style:wrap': 'none', + } + el5, width = self.generate_image(node, source, destination, + el4, attrib) + if caption is not None: + el5.tail = caption + else: #if isinstance(node.parent, docutils.nodes.image): + el3 = self.generate_image(node, source, destination, el2) + + def depart_image(self, node): + pass + + def get_image_width_height(self, node, attr): + size = None + if attr in node.attributes: + size = node.attributes[attr] + unit = size[-2:] + if unit.isalpha(): + size = size[:-2] + else: + unit = 'px' + try: + size = float(size) + except ValueError, e: + self.document.reporter.warning( + 'Invalid %s for image: "%s"' % ( + attr, node.attributes[attr])) + size = [size, unit] + return size + + def get_image_scale(self, node): + if 'scale' in node.attributes: + try: + scale = int(node.attributes['scale']) + if scale < 1: # or scale > 100: + self.document.reporter.warning( + 'scale out of range (%s), using 1.' % (scale, )) + scale = 1 + scale = scale * 0.01 + except ValueError, e: + self.document.reporter.warning( + 'Invalid scale for image: "%s"' % ( + node.attributes['scale'], )) + else: + scale = 1.0 + return scale + + def get_image_scaled_width_height(self, node, source): + scale = self.get_image_scale(node) + width = self.get_image_width_height(node, 'width') + height = self.get_image_width_height(node, 'height') + + dpi = (72, 72) + if Image is not None and source in self.image_dict: + filename, destination = self.image_dict[source] + imageobj = Image.open(filename, 'r') + dpi = imageobj.info.get('dpi', dpi) + # dpi information can be (xdpi, ydpi) or xydpi + try: iter(dpi) + except: dpi = (dpi, dpi) + else: + imageobj = None + + if width is None or height is None: + if imageobj is None: + raise RuntimeError( + 'image size not fully specified and PIL not installed') + if width is None: width = [imageobj.size[0], 'px'] + if height is None: height = [imageobj.size[1], 'px'] + + width[0] *= scale + height[0] *= scale + if width[1] == 'px': width = [width[0] / dpi[0], 'in'] + if height[1] == 'px': height = [height[0] / dpi[1], 'in'] + + width[0] = str(width[0]) + height[0] = str(height[0]) + return ''.join(width), ''.join(height) + + def generate_figure(self, node, source, destination, current_element): + caption = None + width, height = self.get_image_scaled_width_height(node, source) + for node1 in node.parent.children: + if node1.tagname == 'caption': + caption = node1.astext() + self.image_style_count += 1 + # + # Add the style for the caption. + if caption is not None: + attrib = { + 'style:class': 'extra', + 'style:family': 'paragraph', + 'style:name': 'Caption', + 'style:parent-style-name': 'Standard', + } + el1 = SubElement(self.automatic_styles, 'style:style', + attrib=attrib, nsdict=SNSD) + attrib = { + 'fo:margin-bottom': '0.0835in', + 'fo:margin-top': '0.0835in', + 'text:line-number': '0', + 'text:number-lines': 'false', + } + el2 = SubElement(el1, 'style:paragraph-properties', + attrib=attrib, nsdict=SNSD) + attrib = { + 'fo:font-size': '12pt', + 'fo:font-style': 'italic', + 'style:font-name': 'Times', + 'style:font-name-complex': 'Lucidasans1', + 'style:font-size-asian': '12pt', + 'style:font-size-complex': '12pt', + 'style:font-style-asian': 'italic', + 'style:font-style-complex': 'italic', + } + el2 = SubElement(el1, 'style:text-properties', + attrib=attrib, nsdict=SNSD) + style_name = 'rstframestyle%d' % self.image_style_count + # Add the styles + attrib = { + 'style:name': style_name, + 'style:family': 'graphic', + 'style:parent-style-name': 'Frame', + } + el1 = SubElement(self.automatic_styles, + 'style:style', attrib=attrib, nsdict=SNSD) + halign = 'center' + valign = 'top' + if 'align' in node.attributes: + align = node.attributes['align'].split() + for val in align: + if val in ('left', 'center', 'right'): + halign = val + elif val in ('top', 'middle', 'bottom'): + valign = val + attrib = { + 'fo:margin-left': '0cm', + 'fo:margin-right': '0cm', + 'fo:margin-top': '0cm', + 'fo:margin-bottom': '0cm', +# 'style:wrap': 'dynamic', #vds + 'style:number-wrapped-paragraphs': 'no-limit', + 'style:vertical-pos': valign, + 'style:vertical-rel': 'paragraph', + 'style:horizontal-pos': halign, + 'style:horizontal-rel': 'paragraph', + 'fo:padding': '0cm', + 'fo:border': 'none', + } + wrap = False + classes = node.parent.attributes.get('classes') + if classes and 'wrap' in classes: + wrap = True + if wrap: + attrib['style:wrap'] = 'dynamic' + else: + attrib['style:wrap'] = 'none' + el2 = SubElement(el1, + 'style:graphic-properties', attrib=attrib, nsdict=SNSD) + attrib = { + 'draw:style-name': style_name, + 'draw:name': 'Frame1', + 'text:anchor-type': 'paragraph', + 'draw:z-index': '1', + } + attrib['svg:width'] = width + # dbg + #attrib['svg:height'] = height + el3 = SubElement(current_element, 'draw:frame', attrib=attrib) + attrib = {} + el4 = SubElement(el3, 'draw:text-box', attrib=attrib) + attrib = { + 'text:style-name': self.rststyle('caption'), + } + el5 = SubElement(el4, 'text:p', attrib=attrib) + return el3, el5, caption + + def generate_image(self, node, source, destination, current_element, + frame_attrs=None): + width, height = self.get_image_scaled_width_height(node, source) + self.image_style_count += 1 + style_name = 'rstframestyle%d' % self.image_style_count + # Add the style. + attrib = { + 'style:name': style_name, + 'style:family': 'graphic', + 'style:parent-style-name': 'Graphics', + } + el1 = SubElement(self.automatic_styles, + 'style:style', attrib=attrib, nsdict=SNSD) + halign = None + valign = None + if 'align' in node.attributes: + align = node.attributes['align'].split() + for val in align: + if val in ('left', 'center', 'right'): + halign = val + elif val in ('top', 'middle', 'bottom'): + valign = val + if frame_attrs is None: + attrib = { + 'style:vertical-pos': 'top', + 'style:vertical-rel': 'paragraph', + 'style:horizontal-rel': 'paragraph', + 'style:mirror': 'none', + 'fo:clip': 'rect(0cm 0cm 0cm 0cm)', + 'draw:luminance': '0%', + 'draw:contrast': '0%', + 'draw:red': '0%', + 'draw:green': '0%', + 'draw:blue': '0%', + 'draw:gamma': '100%', + 'draw:color-inversion': 'false', + 'draw:image-opacity': '100%', + 'draw:color-mode': 'standard', + } + else: + attrib = frame_attrs + if halign is not None: + attrib['style:horizontal-pos'] = halign + if valign is not None: + attrib['style:vertical-pos'] = valign + # If there is a classes/wrap directive or we are + # inside a table, add a no-wrap style. + wrap = False + classes = node.attributes.get('classes') + if classes and 'wrap' in classes: + wrap = True + if wrap: + attrib['style:wrap'] = 'dynamic' + else: + attrib['style:wrap'] = 'none' + # If we are inside a table, add a no-wrap style. + if self.is_in_table(node): + attrib['style:wrap'] = 'none' + el2 = SubElement(el1, + 'style:graphic-properties', attrib=attrib, nsdict=SNSD) + # Add the content. + #el = SubElement(current_element, 'text:p', + # attrib={'text:style-name': self.rststyle('textbody')}) + attrib={ + 'draw:style-name': style_name, + 'draw:name': 'graphics2', + 'draw:z-index': '1', + } + if isinstance(node.parent, nodes.TextElement): + attrib['text:anchor-type'] = 'as-char' #vds + else: + attrib['text:anchor-type'] = 'paragraph' + attrib['svg:width'] = width + attrib['svg:height'] = height + el1 = SubElement(current_element, 'draw:frame', attrib=attrib) + el2 = SubElement(el1, 'draw:image', attrib={ + 'xlink:href': '%s' % (destination, ), + 'xlink:type': 'simple', + 'xlink:show': 'embed', + 'xlink:actuate': 'onLoad', + }) + return el1, width + + def is_in_table(self, node): + node1 = node.parent + while node1: + if isinstance(node1, docutils.nodes.entry): + return True + node1 = node1.parent + return False + + def visit_legend(self, node): + # Currently, the legend receives *no* special treatment. + pass + + def depart_legend(self, node): + pass + + def visit_line_block(self, node): + self.line_indent_level += 1 + self.line_block_level += 1 + + def depart_line_block(self, node): + if self.line_block_level <= 1: + el1 = SubElement(self.current_element, 'text:p', attrib={ + 'text:style-name': self.rststyle('lineblock1'), + }) + self.line_indent_level -= 1 + self.line_block_level -= 1 + + def visit_line(self, node): + style = 'lineblock%d' % self.line_indent_level + el1 = SubElement(self.current_element, 'text:p', attrib={ + 'text:style-name': self.rststyle(style), + }) + self.current_element = el1 + + def depart_line(self, node): + self.set_to_parent() + + def visit_literal(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('inlineliteral')}) + self.set_current_element(el) + + def depart_literal(self, node): + self.set_to_parent() + + def _calculate_code_block_padding(self, line): + count = 0 + matchobj = SPACES_PATTERN.match(line) + if matchobj: + pad = matchobj.group() + count = len(pad) + else: + matchobj = TABS_PATTERN.match(line) + if matchobj: + pad = matchobj.group() + count = len(pad) * 8 + return count + + def _add_syntax_highlighting(self, insource, language): + lexer = pygments.lexers.get_lexer_by_name(language, stripall=True) + if language in ('latex', 'tex'): + fmtr = OdtPygmentsLaTeXFormatter(lambda name, parameters=(): + self.rststyle(name, parameters), + escape_function=escape_cdata) + else: + fmtr = OdtPygmentsProgFormatter(lambda name, parameters=(): + self.rststyle(name, parameters), + escape_function=escape_cdata) + outsource = pygments.highlight(insource, lexer, fmtr) + return outsource + + def fill_line(self, line): + line = FILL_PAT1.sub(self.fill_func1, line) + line = FILL_PAT2.sub(self.fill_func2, line) + return line + + def fill_func1(self, matchobj): + spaces = matchobj.group(0) + repl = '' % (len(spaces), ) + return repl + + def fill_func2(self, matchobj): + spaces = matchobj.group(0) + repl = ' ' % (len(spaces) - 1, ) + return repl + + def visit_literal_block(self, node): + wrapper1 = '%%s' % ( + self.rststyle('codeblock'), ) + source = node.astext() + if (pygments and + self.settings.add_syntax_highlighting + #and + #node.get('hilight', False) + ): + language = node.get('language', 'python') + source = self._add_syntax_highlighting(source, language) + else: + source = escape_cdata(source) + lines = source.split('\n') + lines1 = [''] + + my_lines = [] + for my_line in lines: + my_line = self.fill_line(my_line) + my_line = my_line.replace(" ", "\n") + my_lines.append(my_line) + my_lines_str = ''.join(my_lines) + my_lines_str2 = wrapper1 % (my_lines_str, ) + lines1.append(my_lines_str2) + lines1.append('') + s1 = ''.join(lines1) + if WhichElementTree != "lxml": + s1 = s1.encode("utf-8") + el1 = etree.fromstring(s1) + children = el1.getchildren() + for child in children: + self.current_element.append(child) + + def depart_literal_block(self, node): + pass + + visit_doctest_block = visit_literal_block + depart_doctest_block = depart_literal_block + + def visit_meta(self, node): + name = node.attributes.get('name') + content = node.attributes.get('content') + if name is not None and content is not None: + self.meta_dict[name] = content + + def depart_meta(self, node): + pass + + def visit_option_list(self, node): + table_name = 'tableoption' + # + # Generate automatic styles + if not self.optiontablestyles_generated: + self.optiontablestyles_generated = True + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle(table_name), + 'style:family': 'table'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-properties', attrib={ + 'style:width': '17.59cm', + 'table:align': 'left', + 'style:shadow': 'none'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle('%s.%%c' % table_name, ( 'A', )), + 'style:family': 'table-column'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-column-properties', attrib={ + 'style:column-width': '4.999cm'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle('%s.%%c' % table_name, ( 'B', )), + 'style:family': 'table-column'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-column-properties', attrib={ + 'style:column-width': '12.587cm'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 1, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:background-color': 'transparent', + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': 'none', + 'fo:border-top': '0.035cm solid #000000', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + el2 = SubElement(el1, 'style:background-image', nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 1, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border': '0.035cm solid #000000'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 2, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': 'none', + 'fo:border-top': 'none', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + el = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 2, )), + 'style:family': 'table-cell'}, nsdict=SNSD) + el1 = SubElement(el, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.097cm', + 'fo:border-left': '0.035cm solid #000000', + 'fo:border-right': '0.035cm solid #000000', + 'fo:border-top': 'none', + 'fo:border-bottom': '0.035cm solid #000000'}, nsdict=SNSD) + # + # Generate table data + el = self.append_child('table:table', attrib={ + 'table:name': self.rststyle(table_name), + 'table:style-name': self.rststyle(table_name), + }) + el1 = SubElement(el, 'table:table-column', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c' % table_name, ( 'A', ))}) + el1 = SubElement(el, 'table:table-column', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c' % table_name, ( 'B', ))}) + el1 = SubElement(el, 'table:table-header-rows') + el2 = SubElement(el1, 'table:table-row') + el3 = SubElement(el2, 'table:table-cell', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'A', 1, )), + 'office:value-type': 'string'}) + el4 = SubElement(el3, 'text:p', attrib={ + 'text:style-name': 'Table_20_Heading'}) + el4.text= 'Option' + el3 = SubElement(el2, 'table:table-cell', attrib={ + 'table:style-name': self.rststyle( + '%s.%%c%%d' % table_name, ( 'B', 1, )), + 'office:value-type': 'string'}) + el4 = SubElement(el3, 'text:p', attrib={ + 'text:style-name': 'Table_20_Heading'}) + el4.text= 'Description' + self.set_current_element(el) + + def depart_option_list(self, node): + self.set_to_parent() + + def visit_option_list_item(self, node): + el = self.append_child('table:table-row') + self.set_current_element(el) + + def depart_option_list_item(self, node): + self.set_to_parent() + + def visit_option_group(self, node): + el = self.append_child('table:table-cell', attrib={ + 'table:style-name': 'Table%d.A2' % self.table_count, + 'office:value-type': 'string', + }) + self.set_current_element(el) + + def depart_option_group(self, node): + self.set_to_parent() + + def visit_option(self, node): + el = self.append_child('text:p', attrib={ + 'text:style-name': 'Table_20_Contents'}) + el.text = node.astext() + + def depart_option(self, node): + pass + + def visit_option_string(self, node): + pass + + def depart_option_string(self, node): + pass + + def visit_option_argument(self, node): + pass + + def depart_option_argument(self, node): + pass + + def visit_description(self, node): + el = self.append_child('table:table-cell', attrib={ + 'table:style-name': 'Table%d.B2' % self.table_count, + 'office:value-type': 'string', + }) + el1 = SubElement(el, 'text:p', attrib={ + 'text:style-name': 'Table_20_Contents'}) + el1.text = node.astext() + raise nodes.SkipChildren() + + def depart_description(self, node): + pass + + def visit_paragraph(self, node): + self.in_paragraph = True + if self.in_header: + el = self.append_p('header') + elif self.in_footer: + el = self.append_p('footer') + else: + style_name = self.paragraph_style_stack[-1] + el = self.append_child('text:p', + attrib={'text:style-name': style_name}) + self.append_pending_ids(el) + self.set_current_element(el) + + def depart_paragraph(self, node): + self.in_paragraph = False + self.set_to_parent() + if self.in_header: + self.header_content.append( + self.current_element.getchildren()[-1]) + self.current_element.remove( + self.current_element.getchildren()[-1]) + elif self.in_footer: + self.footer_content.append( + self.current_element.getchildren()[-1]) + self.current_element.remove( + self.current_element.getchildren()[-1]) + + def visit_problematic(self, node): + pass + + def depart_problematic(self, node): + pass + + def visit_raw(self, node): + if 'format' in node.attributes: + formats = node.attributes['format'] + formatlist = formats.split() + if 'odt' in formatlist: + rawstr = node.astext() + attrstr = ' '.join(['%s="%s"' % (k, v, ) + for k,v in CONTENT_NAMESPACE_ATTRIB.items()]) + contentstr = '%s' % (attrstr, rawstr, ) + if WhichElementTree != "lxml": + contentstr = contentstr.encode("utf-8") + content = etree.fromstring(contentstr) + elements = content.getchildren() + if len(elements) > 0: + el1 = elements[0] + if self.in_header: + pass + elif self.in_footer: + pass + else: + self.current_element.append(el1) + raise nodes.SkipChildren() + + def depart_raw(self, node): + if self.in_header: + pass + elif self.in_footer: + pass + else: + pass + + def visit_reference(self, node): + text = node.astext() + if self.settings.create_links: + if node.has_key('refuri'): + href = node['refuri'] + if ( self.settings.cloak_email_addresses + and href.startswith('mailto:')): + href = self.cloak_mailto(href) + el = self.append_child('text:a', attrib={ + 'xlink:href': '%s' % href, + 'xlink:type': 'simple', + }) + self.set_current_element(el) + elif node.has_key('refid'): + if self.settings.create_links: + href = node['refid'] + el = self.append_child('text:reference-ref', attrib={ + 'text:ref-name': '%s' % href, + 'text:reference-format': 'text', + }) + else: + self.document.reporter.warning( + 'References must have "refuri" or "refid" attribute.') + if (self.in_table_of_contents and + len(node.children) >= 1 and + isinstance(node.children[0], docutils.nodes.generated)): + node.remove(node.children[0]) + + def depart_reference(self, node): + if self.settings.create_links: + if node.has_key('refuri'): + self.set_to_parent() + + def visit_rubric(self, node): + style_name = self.rststyle('rubric') + classes = node.get('classes') + if classes: + class1 = classes[0] + if class1: + style_name = class1 + el = SubElement(self.current_element, 'text:h', attrib = { + #'text:outline-level': '%d' % section_level, + #'text:style-name': 'Heading_20_%d' % section_level, + 'text:style-name': style_name, + }) + text = node.astext() + el.text = self.encode(text) + + def depart_rubric(self, node): + pass + + def visit_section(self, node, move_ids=1): + self.section_level += 1 + self.section_count += 1 + if self.settings.create_sections: + el = self.append_child('text:section', attrib={ + 'text:name': 'Section%d' % self.section_count, + 'text:style-name': 'Sect%d' % self.section_level, + }) + self.set_current_element(el) + + def depart_section(self, node): + self.section_level -= 1 + if self.settings.create_sections: + self.set_to_parent() + + def visit_strong(self, node): + el = SubElement(self.current_element, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + self.set_current_element(el) + + def depart_strong(self, node): + self.set_to_parent() + + def visit_substitution_definition(self, node): + raise nodes.SkipChildren() + + def depart_substitution_definition(self, node): + pass + + def visit_system_message(self, node): + pass + + def depart_system_message(self, node): + pass + + def visit_table(self, node): + self.table_count += 1 + table_name = '%s%%d' % TableStylePrefix + el1 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + 'style:family': 'table', + }, nsdict=SNSD) + el1_1 = SubElement(el1, 'style:table-properties', attrib={ + #'style:width': '17.59cm', + 'table:align': 'margins', + 'fo:margin-top': '0in', + 'fo:margin-bottom': '0.10in', + }, nsdict=SNSD) + # We use a single cell style for all cells in this table. + # That's probably not correct, but seems to work. + el2 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': self.rststyle( + '%s.%%c%%d' % table_name, ( self.table_count, 'A', 1, )), + 'style:family': 'table-cell', + }, nsdict=SNSD) + line_style1 = '0.%03dcm solid #000000' % ( + self.settings.table_border_thickness, ) + el2_1 = SubElement(el2, 'style:table-cell-properties', attrib={ + 'fo:padding': '0.049cm', + 'fo:border-left': line_style1, + 'fo:border-right': line_style1, + 'fo:border-top': line_style1, + 'fo:border-bottom': line_style1, + }, nsdict=SNSD) + title = None + for child in node.children: + if child.tagname == 'title': + title = child.astext() + break + if title is not None: + el3 = self.append_p('table-title', title) + else: + pass + el4 = SubElement(self.current_element, 'table:table', attrib={ + 'table:name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + 'table:style-name': self.rststyle( + '%s' % table_name, ( self.table_count, )), + }) + self.set_current_element(el4) + self.current_table_style = el1 + self.table_width = 0 + + def depart_table(self, node): + attribkey = add_ns('style:width', nsdict=SNSD) + attribval = '%dcm' % self.table_width + self.current_table_style.attrib[attribkey] = attribval + self.set_to_parent() + + def visit_tgroup(self, node): + self.column_count = ord('A') - 1 + + def depart_tgroup(self, node): + pass + + def visit_colspec(self, node): + self.column_count += 1 + colspec_name = self.rststyle( + '%s%%d.%%s' % TableStylePrefix, + (self.table_count, chr(self.column_count), ) + ) + colwidth = node['colwidth'] + el1 = SubElement(self.automatic_styles, 'style:style', attrib={ + 'style:name': colspec_name, + 'style:family': 'table-column', + }, nsdict=SNSD) + el1_1 = SubElement(el1, 'style:table-column-properties', attrib={ + 'style:column-width': '%dcm' % colwidth }, nsdict=SNSD) + el2 = self.append_child('table:table-column', attrib={ + 'table:style-name': colspec_name, + }) + self.table_width += colwidth + + def depart_colspec(self, node): + pass + + def visit_thead(self, node): + el = self.append_child('table:table-header-rows') + self.set_current_element(el) + self.in_thead = True + self.paragraph_style_stack.append('Table_20_Heading') + + def depart_thead(self, node): + self.set_to_parent() + self.in_thead = False + self.paragraph_style_stack.pop() + + def visit_row(self, node): + self.column_count = ord('A') - 1 + el = self.append_child('table:table-row') + self.set_current_element(el) + + def depart_row(self, node): + self.set_to_parent() + + def visit_entry(self, node): + self.column_count += 1 + cellspec_name = self.rststyle( + '%s%%d.%%c%%d' % TableStylePrefix, + (self.table_count, 'A', 1, ) + ) + attrib={ + 'table:style-name': cellspec_name, + 'office:value-type': 'string', + } + morecols = node.get('morecols', 0) + if morecols > 0: + attrib['table:number-columns-spanned'] = '%d' % (morecols + 1,) + self.column_count += morecols + morerows = node.get('morerows', 0) + if morerows > 0: + attrib['table:number-rows-spanned'] = '%d' % (morerows + 1,) + el1 = self.append_child('table:table-cell', attrib=attrib) + self.set_current_element(el1) + + def depart_entry(self, node): + self.set_to_parent() + + def visit_tbody(self, node): + pass + + def depart_tbody(self, node): + pass + + def visit_target(self, node): + # + # I don't know how to implement targets in ODF. + # How do we create a target in oowriter? A cross-reference? + if not (node.has_key('refuri') or node.has_key('refid') + or node.has_key('refname')): + pass + else: + pass + + def depart_target(self, node): + pass + + def visit_title(self, node, move_ids=1, title_type='title'): + if isinstance(node.parent, docutils.nodes.section): + section_level = self.section_level + if section_level > 7: + self.document.reporter.warning( + 'Heading/section levels greater than 7 not supported.') + self.document.reporter.warning( + ' Reducing to heading level 7 for heading: "%s"' % ( + node.astext(), )) + section_level = 7 + el1 = self.append_child('text:h', attrib = { + 'text:outline-level': '%d' % section_level, + #'text:style-name': 'Heading_20_%d' % section_level, + 'text:style-name': self.rststyle( + 'heading%d', (section_level, )), + }) + self.append_pending_ids(el1) + self.set_current_element(el1) + elif isinstance(node.parent, docutils.nodes.document): + # text = self.settings.title + #else: + # text = node.astext() + el1 = SubElement(self.current_element, 'text:p', attrib = { + 'text:style-name': self.rststyle(title_type), + }) + self.append_pending_ids(el1) + text = node.astext() + self.title = text + self.found_doc_title = True + self.set_current_element(el1) + + def depart_title(self, node): + if (isinstance(node.parent, docutils.nodes.section) or + isinstance(node.parent, docutils.nodes.document)): + self.set_to_parent() + + def visit_subtitle(self, node, move_ids=1): + self.visit_title(node, move_ids, title_type='subtitle') + + def depart_subtitle(self, node): + self.depart_title(node) + + def visit_title_reference(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': self.rststyle('quotation')}) + el.text = self.encode(node.astext()) + raise nodes.SkipChildren() + + def depart_title_reference(self, node): + pass + + def generate_table_of_content_entry_template(self, el1): + for idx in range(1, 11): + el2 = SubElement(el1, + 'text:table-of-content-entry-template', + attrib={ + 'text:outline-level': "%d" % (idx, ), + 'text:style-name': self.rststyle('contents-%d' % (idx, )), + }) + el3 = SubElement(el2, 'text:index-entry-chapter') + el3 = SubElement(el2, 'text:index-entry-text') + el3 = SubElement(el2, 'text:index-entry-tab-stop', attrib={ + 'style:leader-char': ".", + 'style:type': "right", + }) + el3 = SubElement(el2, 'text:index-entry-page-number') + + def visit_topic(self, node): + if 'classes' in node.attributes: + if 'contents' in node.attributes['classes']: + if self.settings.generate_oowriter_toc: + el1 = self.append_child('text:table-of-content', attrib={ + 'text:name': 'Table of Contents1', + 'text:protected': 'true', + 'text:style-name': 'Sect1', + }) + el2 = SubElement(el1, + 'text:table-of-content-source', + attrib={ + 'text:outline-level': '10', + }) + el3 =SubElement(el2, 'text:index-title-template', attrib={ + 'text:style-name': 'Contents_20_Heading', + }) + el3.text = 'Table of Contents' + self.generate_table_of_content_entry_template(el2) + el4 = SubElement(el1, 'text:index-body') + el5 = SubElement(el4, 'text:index-title') + el6 = SubElement(el5, 'text:p', attrib={ + 'text:style-name': self.rststyle('contents-heading'), + }) + el6.text = 'Table of Contents' + self.save_current_element = self.current_element + self.table_of_content_index_body = el4 + self.set_current_element(el4) + else: + el = self.append_p('horizontalline') + el = self.append_p('centeredtextbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = 'Contents' + self.in_table_of_contents = True + elif 'abstract' in node.attributes['classes']: + el = self.append_p('horizontalline') + el = self.append_p('centeredtextbody') + el1 = SubElement(el, 'text:span', + attrib={'text:style-name': self.rststyle('strong')}) + el1.text = 'Abstract' + + def depart_topic(self, node): + if 'classes' in node.attributes: + if 'contents' in node.attributes['classes']: + if self.settings.generate_oowriter_toc: + self.update_toc_page_numbers( + self.table_of_content_index_body) + self.set_current_element(self.save_current_element) + else: + el = self.append_p('horizontalline') + self.in_table_of_contents = False + + def update_toc_page_numbers(self, el): + collection = [] + self.update_toc_collect(el, 0, collection) + self.update_toc_add_numbers(collection) + + def update_toc_collect(self, el, level, collection): + collection.append((level, el)) + level += 1 + for child_el in el.getchildren(): + if child_el.tag != 'text:index-body': + self.update_toc_collect(child_el, level, collection) + + def update_toc_add_numbers(self, collection): + for level, el1 in collection: + if (el1.tag == 'text:p' and + el1.text != 'Table of Contents'): + el2 = SubElement(el1, 'text:tab') + el2.tail = '9999' + + + def visit_transition(self, node): + el = self.append_p('horizontalline') + + def depart_transition(self, node): + pass + + # + # Admonitions + # + def visit_warning(self, node): + self.generate_admonition(node, 'warning') + + def depart_warning(self, node): + self.paragraph_style_stack.pop() + + def visit_attention(self, node): + self.generate_admonition(node, 'attention') + + depart_attention = depart_warning + + def visit_caution(self, node): + self.generate_admonition(node, 'caution') + + depart_caution = depart_warning + + def visit_danger(self, node): + self.generate_admonition(node, 'danger') + + depart_danger = depart_warning + + def visit_error(self, node): + self.generate_admonition(node, 'error') + + depart_error = depart_warning + + def visit_hint(self, node): + self.generate_admonition(node, 'hint') + + depart_hint = depart_warning + + def visit_important(self, node): + self.generate_admonition(node, 'important') + + depart_important = depart_warning + + def visit_note(self, node): + self.generate_admonition(node, 'note') + + depart_note = depart_warning + + def visit_tip(self, node): + self.generate_admonition(node, 'tip') + + depart_tip = depart_warning + + def visit_admonition(self, node): + #import pdb; pdb.set_trace() + title = None + for child in node.children: + if child.tagname == 'title': + title = child.astext() + if title is None: + classes1 = node.get('classes') + if classes1: + title = classes1[0] + self.generate_admonition(node, 'generic', title) + + depart_admonition = depart_warning + + def generate_admonition(self, node, label, title=None): + el1 = SubElement(self.current_element, 'text:p', attrib = { + 'text:style-name': self.rststyle('admon-%s-hdr', ( label, )), + }) + if title: + el1.text = title + else: + el1.text = '%s!' % (label.capitalize(), ) + s1 = self.rststyle('admon-%s-body', ( label, )) + self.paragraph_style_stack.append(s1) + + # + # Roles (e.g. subscript, superscript, strong, ... + # + def visit_subscript(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': 'rststyle-subscript', + }) + self.set_current_element(el) + + def depart_subscript(self, node): + self.set_to_parent() + + def visit_superscript(self, node): + el = self.append_child('text:span', attrib={ + 'text:style-name': 'rststyle-superscript', + }) + self.set_current_element(el) + + def depart_superscript(self, node): + self.set_to_parent() + + +# Use an own reader to modify transformations done. +class Reader(standalone.Reader): + + def get_transforms(self): + default = standalone.Reader.get_transforms(self) + if self.settings.create_links: + return default + return [ i + for i in default + if i is not references.DanglingReferences ] diff -Nru zope3-3.4.0/src/docutils/writers/odf_odt/pygmentsformatter.py zope3-3.5~bzr18/src/docutils/writers/odf_odt/pygmentsformatter.py --- zope3-3.4.0/src/docutils/writers/odf_odt/pygmentsformatter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/odf_odt/pygmentsformatter.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,109 @@ +# $Id: pygmentsformatter.py 5853 2009-01-19 21:02:02Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" + +Additional support for Pygments formatter. + +""" + + +import pygments +import pygments.formatter + + +class OdtPygmentsFormatter(pygments.formatter.Formatter): + def __init__(self, rststyle_function, escape_function): + pygments.formatter.Formatter.__init__(self) + self.rststyle_function = rststyle_function + self.escape_function = escape_function + + def rststyle(self, name, parameters=( )): + return self.rststyle_function(name, parameters) + + +class OdtPygmentsProgFormatter(OdtPygmentsFormatter): + def format(self, tokensource, outfile): + tokenclass = pygments.token.Token + for ttype, value in tokensource: + value = self.escape_function(value) + if ttype == tokenclass.Keyword: + s2 = self.rststyle('codeblock-keyword') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Literal.String: + s2 = self.rststyle('codeblock-string') + s1 = '%s' % \ + (s2, value, ) + elif ttype in ( + tokenclass.Literal.Number.Integer, + tokenclass.Literal.Number.Integer.Long, + tokenclass.Literal.Number.Float, + tokenclass.Literal.Number.Hex, + tokenclass.Literal.Number.Oct, + tokenclass.Literal.Number, + ): + s2 = self.rststyle('codeblock-number') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Operator: + s2 = self.rststyle('codeblock-operator') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Comment: + s2 = self.rststyle('codeblock-comment') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Class: + s2 = self.rststyle('codeblock-classname') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Function: + s2 = self.rststyle('codeblock-functionname') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name: + s2 = self.rststyle('codeblock-name') + s1 = '%s' % \ + (s2, value, ) + else: + s1 = value + outfile.write(s1) + + +class OdtPygmentsLaTeXFormatter(OdtPygmentsFormatter): + def format(self, tokensource, outfile): + tokenclass = pygments.token.Token + for ttype, value in tokensource: + value = self.escape_function(value) + if ttype == tokenclass.Keyword: + s2 = self.rststyle('codeblock-keyword') + s1 = '%s' % \ + (s2, value, ) + elif ttype in (tokenclass.Literal.String, + tokenclass.Literal.String.Backtick, + ): + s2 = self.rststyle('codeblock-string') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Attribute: + s2 = self.rststyle('codeblock-operator') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Comment: + if value[-1] == '\n': + s2 = self.rststyle('codeblock-comment') + s1 = '%s\n' % \ + (s2, value[:-1], ) + else: + s2 = self.rststyle('codeblock-comment') + s1 = '%s' % \ + (s2, value, ) + elif ttype == tokenclass.Name.Builtin: + s2 = self.rststyle('codeblock-name') + s1 = '%s' % \ + (s2, value, ) + else: + s1 = value + outfile.write(s1) Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/docutils/writers/odf_odt/styles.odt and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/docutils/writers/odf_odt/styles.odt differ diff -Nru zope3-3.4.0/src/docutils/writers/pep_html/__init__.py zope3-3.5~bzr18/src/docutils/writers/pep_html/__init__.py --- zope3-3.4.0/src/docutils/writers/pep_html/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/pep_html/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,105 @@ +# $Id: __init__.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +PEP HTML Writer. +""" + +__docformat__ = 'reStructuredText' + + +import sys +import os +import os.path +import codecs +import docutils +from docutils import frontend, nodes, utils, writers +from docutils.writers import html4css1 + + +class Writer(html4css1.Writer): + + default_stylesheet = 'pep.css' + + default_stylesheet_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_stylesheet)) + + default_template = 'template.txt' + + default_template_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), default_template)) + + settings_spec = html4css1.Writer.settings_spec + ( + 'PEP/HTML-Specific Options', + 'For the PEP/HTML writer, the default value for the --stylesheet-path ' + 'option is "%s", and the default value for --template is "%s". ' + 'See HTML-Specific Options above.' + % (default_stylesheet_path, default_template_path), + (('Python\'s home URL. Default is "http://www.python.org".', + ['--python-home'], + {'default': 'http://www.python.org', 'metavar': ''}), + ('Home URL prefix for PEPs. Default is "." (current directory).', + ['--pep-home'], + {'default': '.', 'metavar': ''}), + # For testing. + (frontend.SUPPRESS_HELP, + ['--no-random'], + {'action': 'store_true', 'validator': frontend.validate_boolean}),)) + + settings_default_overrides = {'stylesheet_path': default_stylesheet_path, + 'template': default_template_path,} + + relative_path_settings = (html4css1.Writer.relative_path_settings + + ('template',)) + + config_section = 'pep_html writer' + config_section_dependencies = ('writers', 'html4css1 writer') + + def __init__(self): + html4css1.Writer.__init__(self) + self.translator_class = HTMLTranslator + + def interpolation_dict(self): + subs = html4css1.Writer.interpolation_dict(self) + settings = self.document.settings + pyhome = settings.python_home + subs['pyhome'] = pyhome + subs['pephome'] = settings.pep_home + if pyhome == '..': + subs['pepindex'] = '.' + else: + subs['pepindex'] = pyhome + '/dev/peps' + index = self.document.first_child_matching_class(nodes.field_list) + header = self.document[index] + self.pepnum = header[0][1].astext() + subs['pep'] = self.pepnum + if settings.no_random: + subs['banner'] = 0 + else: + import random + subs['banner'] = random.randrange(64) + try: + subs['pepnum'] = '%04i' % int(self.pepnum) + except ValueError: + subs['pepnum'] = pepnum + self.title = header[1][1].astext() + subs['title'] = self.title + subs['body'] = ''.join( + self.body_pre_docinfo + self.docinfo + self.body) + return subs + + def assemble_parts(self): + html4css1.Writer.assemble_parts(self) + self.parts['title'] = [self.title] + self.parts['pepnum'] = self.pepnum + + +class HTMLTranslator(html4css1.HTMLTranslator): + + def depart_field_list(self, node): + html4css1.HTMLTranslator.depart_field_list(self, node) + if 'rfc2822' in node['classes']: + self.body.append('
\n') diff -Nru zope3-3.4.0/src/docutils/writers/pep_html/pep.css zope3-3.5~bzr18/src/docutils/writers/pep_html/pep.css --- zope3-3.4.0/src/docutils/writers/pep_html/pep.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/pep_html/pep.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,344 @@ +/* +:Author: David Goodger +:Contact: goodger@python.org +:date: $Date: 2006-05-21 22:44:42 +0200 (Son, 21 Mai 2006) $ +:version: $Revision: 4564 $ +:copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the PEP HTML output of Docutils. +*/ + +/* "! important" is used here to override other ``margin-top`` and + ``margin-bottom`` styles that are later in the stylesheet or + more specific. See http://www.w3.org/TR/CSS1#the-cascade */ +.first { + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +.navigation { + width: 100% ; + background: #99ccff ; + margin-top: 0px ; + margin-bottom: 0px } + +.navigation .navicon { + width: 150px ; + height: 35px } + +.navigation .textlinks { + padding-left: 1em ; + text-align: left } + +.navigation td, .navigation th { + padding-left: 0em ; + padding-right: 0em ; + vertical-align: middle } + +.rfc2822 { + margin-top: 0.5em ; + margin-left: 0.5em ; + margin-right: 0.5em ; + margin-bottom: 0em } + +.rfc2822 td { + text-align: left } + +.rfc2822 th.field-name { + text-align: right ; + font-family: sans-serif ; + padding-right: 0.5em ; + font-weight: bold ; + margin-bottom: 0em } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +body { + margin: 0px ; + margin-bottom: 1em ; + padding: 0px } + +dl.docutils dd { + margin-bottom: 0.5em } + +div.section { + margin-left: 1em ; + margin-right: 1em ; + margin-bottom: 1.5em } + +div.section div.section { + margin-left: 0em ; + margin-right: 0em ; + margin-top: 1.5em } + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.footer { + margin-left: 1em ; + margin-right: 1em } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin-left: 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1 { + font-family: sans-serif ; + font-size: large } + +h2 { + font-family: sans-serif ; + font-size: medium } + +h3 { + font-family: sans-serif ; + font-size: small } + +h4 { + font-family: sans-serif ; + font-style: italic ; + font-size: small } + +h5 { + font-family: sans-serif; + font-size: x-small } + +h6 { + font-family: sans-serif; + font-style: italic ; + font-size: x-small } + +hr.docutils { + width: 75% } + +img.align-left { + clear: left } + +img.align-right { + clear: right } + +img.borderless { + border: 0 } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-family: sans-serif ; + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.option-argument { + font-style: italic } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +td.num { + text-align: right } + +th.field-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } diff -Nru zope3-3.4.0/src/docutils/writers/pep_html/template.txt zope3-3.5~bzr18/src/docutils/writers/pep_html/template.txt --- zope3-3.4.0/src/docutils/writers/pep_html/template.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/pep_html/template.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ + + + + + + + + PEP %(pep)s -- %(title)s + %(stylesheet)s + + + + + +
+%(body)s +%(body_suffix)s diff -Nru zope3-3.4.0/src/docutils/writers/pseudoxml.py zope3-3.5~bzr18/src/docutils/writers/pseudoxml.py --- zope3-3.4.0/src/docutils/writers/pseudoxml.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/pseudoxml.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,31 @@ +# $Id: pseudoxml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +Simple internal document tree Writer, writes indented pseudo-XML. +""" + +__docformat__ = 'reStructuredText' + + +from docutils import writers + + +class Writer(writers.Writer): + + supported = ('pprint', 'pformat', 'pseudoxml') + """Formats this writer supports.""" + + config_section = 'pseudoxml writer' + config_section_dependencies = ('writers',) + + output = None + """Final translated form of `document`.""" + + def translate(self): + self.output = self.document.pformat() + + def supports(self, format): + """This writer supports all format-specific elements.""" + return 1 diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/__init__.py zope3-3.5~bzr18/src/docutils/writers/s5_html/__init__.py --- zope3-3.4.0/src/docutils/writers/s5_html/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,340 @@ +# $Id: __init__.py 5889 2009-04-01 20:00:21Z gbrandl $ +# Authors: Chris Liechti ; +# David Goodger +# Copyright: This module has been placed in the public domain. + +""" +S5/HTML Slideshow Writer. +""" + +__docformat__ = 'reStructuredText' + + +import sys +import os +import re +import docutils +from docutils import frontend, nodes, utils +from docutils.writers import html4css1 +from docutils.parsers.rst import directives +from docutils._compat import b + +themes_dir_path = utils.relative_path( + os.path.join(os.getcwd(), 'dummy'), + os.path.join(os.path.dirname(__file__), 'themes')) + +def find_theme(name): + # Where else to look for a theme? + # Check working dir? Destination dir? Config dir? Plugins dir? + path = os.path.join(themes_dir_path, name) + if not os.path.isdir(path): + raise docutils.ApplicationError( + 'Theme directory not found: %r (path: %r)' % (name, path)) + return path + + +class Writer(html4css1.Writer): + + settings_spec = html4css1.Writer.settings_spec + ( + 'S5 Slideshow Specific Options', + 'For the S5/HTML writer, the --no-toc-backlinks option ' + '(defined in General Docutils Options above) is the default, ' + 'and should not be changed.', + (('Specify an installed S5 theme by name. Overrides --theme-url. ' + 'The default theme name is "default". The theme files will be ' + 'copied into a "ui/" directory, in the same directory as the ' + 'destination file (output HTML). Note that existing theme files ' + 'will not be overwritten (unless --overwrite-theme-files is used).', + ['--theme'], + {'default': 'default', 'metavar': '', + 'overrides': 'theme_url'}), + ('Specify an S5 theme URL. The destination file (output HTML) will ' + 'link to this theme; nothing will be copied. Overrides --theme.', + ['--theme-url'], + {'metavar': '', 'overrides': 'theme'}), + ('Allow existing theme files in the ``ui/`` directory to be ' + 'overwritten. The default is not to overwrite theme files.', + ['--overwrite-theme-files'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Keep existing theme files in the ``ui/`` directory; do not ' + 'overwrite any. This is the default.', + ['--keep-theme-files'], + {'dest': 'overwrite_theme_files', 'action': 'store_false'}), + ('Set the initial view mode to "slideshow" [default] or "outline".', + ['--view-mode'], + {'choices': ['slideshow', 'outline'], 'default': 'slideshow', + 'metavar': ''}), + ('Normally hide the presentation controls in slideshow mode. ' + 'This is the default.', + ['--hidden-controls'], + {'action': 'store_true', 'default': True, + 'validator': frontend.validate_boolean}), + ('Always show the presentation controls in slideshow mode. ' + 'The default is to hide the controls.', + ['--visible-controls'], + {'dest': 'hidden_controls', 'action': 'store_false'}), + ('Enable the current slide indicator ("1 / 15"). ' + 'The default is to disable it.', + ['--current-slide'], + {'action': 'store_true', 'validator': frontend.validate_boolean}), + ('Disable the current slide indicator. This is the default.', + ['--no-current-slide'], + {'dest': 'current_slide', 'action': 'store_false'}),)) + + settings_default_overrides = {'toc_backlinks': 0} + + config_section = 's5_html writer' + config_section_dependencies = ('writers', 'html4css1 writer') + + def __init__(self): + html4css1.Writer.__init__(self) + self.translator_class = S5HTMLTranslator + + +class S5HTMLTranslator(html4css1.HTMLTranslator): + + s5_stylesheet_template = """\ + + + + + + + + +\n""" + # The script element must go in front of the link elements to + # avoid a flash of unstyled content (FOUC), reproducible with + # Firefox. + + disable_current_slide = """ +\n""" + + layout_template = """\ +
+
+
+ + +
\n""" +#
+#
+#
+#
+ + default_theme = 'default' + """Name of the default theme.""" + + base_theme_file = '__base__' + """Name of the file containing the name of the base theme.""" + + direct_theme_files = ( + 'slides.css', 'outline.css', 'print.css', 'opera.css', 'slides.js') + """Names of theme files directly linked to in the output HTML""" + + indirect_theme_files = ( + 's5-core.css', 'framing.css', 'pretty.css', 'blank.gif', 'iepngfix.htc') + """Names of files used indirectly; imported or used by files in + `direct_theme_files`.""" + + required_theme_files = indirect_theme_files + direct_theme_files + """Names of mandatory theme files.""" + + def __init__(self, *args): + html4css1.HTMLTranslator.__init__(self, *args) + #insert S5-specific stylesheet and script stuff: + self.theme_file_path = None + self.setup_theme() + view_mode = self.document.settings.view_mode + control_visibility = ('visible', 'hidden')[self.document.settings + .hidden_controls] + self.stylesheet.append(self.s5_stylesheet_template + % {'path': self.theme_file_path, + 'view_mode': view_mode, + 'control_visibility': control_visibility}) + if not self.document.settings.current_slide: + self.stylesheet.append(self.disable_current_slide) + self.add_meta('\n') + self.s5_footer = [] + self.s5_header = [] + self.section_count = 0 + self.theme_files_copied = None + + def setup_theme(self): + if self.document.settings.theme: + self.copy_theme() + elif self.document.settings.theme_url: + self.theme_file_path = self.document.settings.theme_url + else: + raise docutils.ApplicationError( + 'No theme specified for S5/HTML writer.') + + def copy_theme(self): + """ + Locate & copy theme files. + + A theme may be explicitly based on another theme via a '__base__' + file. The default base theme is 'default'. Files are accumulated + from the specified theme, any base themes, and 'default'. + """ + settings = self.document.settings + path = find_theme(settings.theme) + theme_paths = [path] + self.theme_files_copied = {} + required_files_copied = {} + # This is a link (URL) in HTML, so we use "/", not os.sep: + self.theme_file_path = '%s/%s' % ('ui', settings.theme) + if settings._destination: + dest = os.path.join( + os.path.dirname(settings._destination), 'ui', settings.theme) + if not os.path.isdir(dest): + os.makedirs(dest) + else: + # no destination, so we can't copy the theme + return + default = 0 + while path: + for f in os.listdir(path): # copy all files from each theme + if f == self.base_theme_file: + continue # ... except the "__base__" file + if ( self.copy_file(f, path, dest) + and f in self.required_theme_files): + required_files_copied[f] = 1 + if default: + break # "default" theme has no base theme + # Find the "__base__" file in theme directory: + base_theme_file = os.path.join(path, self.base_theme_file) + # If it exists, read it and record the theme path: + if os.path.isfile(base_theme_file): + lines = open(base_theme_file).readlines() + for line in lines: + line = line.strip() + if line and not line.startswith('#'): + path = find_theme(line) + if path in theme_paths: # check for duplicates (cycles) + path = None # if found, use default base + else: + theme_paths.append(path) + break + else: # no theme name found + path = None # use default base + else: # no base theme file found + path = None # use default base + if not path: + path = find_theme(self.default_theme) + theme_paths.append(path) + default = 1 + if len(required_files_copied) != len(self.required_theme_files): + # Some required files weren't found & couldn't be copied. + required = list(self.required_theme_files) + for f in required_files_copied.keys(): + required.remove(f) + raise docutils.ApplicationError( + 'Theme files not found: %s' + % ', '.join(['%r' % f for f in required])) + + files_to_skip_pattern = re.compile(r'~$|\.bak$|#$|\.cvsignore$') + + def copy_file(self, name, source_dir, dest_dir): + """ + Copy file `name` from `source_dir` to `dest_dir`. + Return 1 if the file exists in either `source_dir` or `dest_dir`. + """ + source = os.path.join(source_dir, name) + dest = os.path.join(dest_dir, name) + if dest in self.theme_files_copied: + return 1 + else: + self.theme_files_copied[dest] = 1 + if os.path.isfile(source): + if self.files_to_skip_pattern.search(source): + return None + settings = self.document.settings + if os.path.exists(dest) and not settings.overwrite_theme_files: + settings.record_dependencies.add(dest) + else: + src_file = open(source, 'rb') + src_data = src_file.read() + src_file.close() + dest_file = open(dest, 'wb') + dest_dir = dest_dir.replace(os.sep, '/') + dest_file.write(src_data.replace( + b('ui/default'), + dest_dir[dest_dir.rfind('ui/'):].encode( + sys.getfilesystemencoding()))) + dest_file.close() + settings.record_dependencies.add(source) + return 1 + if os.path.isfile(dest): + return 1 + + def depart_document(self, node): + header = ''.join(self.s5_header) + footer = ''.join(self.s5_footer) + title = ''.join(self.html_title).replace('

', '

') + layout = self.layout_template % {'header': header, + 'title': title, + 'footer': footer} + self.fragment.extend(self.body) + self.body_prefix.extend(layout) + self.body_prefix.append('
\n') + self.body_prefix.append( + self.starttag({'classes': ['slide'], 'ids': ['slide0']}, 'div')) + if not self.section_count: + self.body.append('
\n') + self.body_suffix.insert(0, '

\n') + # skip content-type meta tag with interpolated charset value: + self.html_head.extend(self.head[1:]) + self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo + + self.docinfo + self.body + + self.body_suffix[:-1]) + + def depart_footer(self, node): + start = self.context.pop() + self.s5_footer.append('

') + self.s5_footer.extend(self.body[start:]) + self.s5_footer.append('

') + del self.body[start:] + + def depart_header(self, node): + start = self.context.pop() + header = ['\n') + del self.body[start:] + self.s5_header.extend(header) + + def visit_section(self, node): + if not self.section_count: + self.body.append('\n\n') + self.section_count += 1 + self.section_level += 1 + if self.section_level > 1: + # dummy for matching div's + self.body.append(self.starttag(node, 'div', CLASS='section')) + else: + self.body.append(self.starttag(node, 'div', CLASS='slide')) + + def visit_subtitle(self, node): + if isinstance(node.parent, nodes.section): + level = self.section_level + self.initial_header_level - 1 + if level == 1: + level = 2 + tag = 'h%s' % level + self.body.append(self.starttag(node, tag, '')) + self.context.append('\n' % tag) + else: + html4css1.HTMLTranslator.visit_subtitle(self, node) + + def visit_title(self, node): + html4css1.HTMLTranslator.visit_title(self, node) diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/big-black/__base__ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-black/__base__ --- zope3-3.4.0/src/docutils/writers/s5_html/themes/big-black/__base__ 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-black/__base__ 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# base theme of this theme: +big-white diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/big-black/framing.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-black/framing.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/big-black/framing.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-black/framing.css 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#header {top: 0; z-index: 1;} +div#footer {display:none;} +.slide {top: 0; width: 92%; padding: 0.1em 4% 4%; z-index: 2;} +/* list-style: none;} */ +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/big-black/pretty.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-black/pretty.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/big-black/pretty.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-black/pretty.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,109 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: black; color: white;} +:link, :visited {text-decoration: none; color: cyan;} +#controls :active {color: #888 !important;} +#controls :focus {outline: 1px dotted #CCC;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +.slide {font-size: 3em; font-family: sans-serif; font-weight: bold;} +.slide h1 {padding-top: 0; z-index: 1; margin: 0; font-size: 120%;} +.slide h2 {font-size: 110%;} +.slide h3 {font-size: 105%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + border: none; color: #888; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: black; color: #CCC;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #AAA; + font-family: sans-serif; font-weight: bold;} + +#slide0 h1 {position: static; margin: 0 0 0.5em; padding-top: 0.3em; top: 0; + font-size: 150%; white-space: normal; background: transparent;} +#slide0 h2 {font: 110%; font-style: italic; color: gray;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #FCC;} + +.incremental, .incremental *, .incremental *:after { + color: black; visibility: visible; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: lime;} + +.slide-display {display: inline ! important;} + +.huge {font-size: 150%;} +.big {font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 0.5em 0 0.5em 1em;} + +div.sidebar {background-color: black;} + +pre.literal-block, pre.doctest-block {background-color: black;} + +tt.docutils {background-color: black;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/big-white/framing.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-white/framing.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/big-white/framing.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-white/framing.css 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,24 @@ +/* This file has been placed in the public domain. */ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#footer {display:none;} +.slide {top: 0; width: 92%; padding: 0.25em 4% 4%; z-index: 2;} +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/big-white/pretty.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-white/pretty.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/big-white/pretty.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/big-white/pretty.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,107 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: white; color: black;} +:link, :visited {text-decoration: none; color: #00C;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +.slide {font-size: 3em; font-family: sans-serif; font-weight: bold;} +.slide h1 {padding-top: 0; z-index: 1; margin: 0; font-size: 120%;} +.slide h2 {font-size: 110%;} +.slide h3 {font-size: 105%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + border: none; color: #005; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: #DDD; color: #227;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #444; + font-family: sans-serif; font-weight: bold;} + +#slide0 h1 {position: static; margin: 0 0 0.5em; padding-top: 0.3em; top: 0; + font-size: 150%; white-space: normal; background: transparent;} +#slide0 h2 {font: 110%; font-style: italic; color: gray;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #77B;} + +.incremental, .incremental *, .incremental *:after { + color: white; visibility: visible; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: green;} + +.slide-display {display: inline ! important;} + +.huge {font-size: 150%;} +.big {font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 0.5em 0 0.5em 1em;} + +pre.literal-block, pre.doctest-block {background-color: white;} + +tt.docutils {background-color: white;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/docutils/writers/s5_html/themes/default/blank.gif and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/blank.gif differ diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/framing.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/framing.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/framing.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/framing.css 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ +/* This file has been placed in the public domain. */ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#header {position: fixed; top: 0; height: 3em; z-index: 1;} +div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} +.slide {top: 0; width: 92%; padding: 2.5em 4% 4%; z-index: 2;} +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; + z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/iepngfix.htc zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/iepngfix.htc --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/iepngfix.htc 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/iepngfix.htc 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,42 @@ + + + + + \ No newline at end of file diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/opera.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/opera.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/opera.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/opera.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ +/* This file has been placed in the public domain. */ +/* DO NOT CHANGE THESE unless you really want to break Opera Show */ +.slide { + visibility: visible !important; + position: static !important; + page-break-before: always; +} +#slide0 {page-break-before: avoid;} diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/outline.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/outline.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/outline.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/outline.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,16 @@ +/* This file has been placed in the public domain. */ +/* Don't change this unless you want the layout stuff to show up in the + outline view! */ + +.layout div, #footer *, #controlForm * {display: none;} +#footer, #controls, #controlForm, #navLinks, #toggle { + display: block; visibility: visible; margin: 0; padding: 0;} +#toggle {float: right; padding: 0.5em;} +html>body #toggle {position: fixed; top: 0; right: 0;} + +/* making the outline look pretty-ish */ + +#slide0 h1, #slide0 h2, #slide0 h3, #slide0 h4 {border: none; margin: 0;} +#toggle {border: 1px solid; border-width: 0 0 1px 1px; background: #FFF;} + +.outline {display: inline ! important;} diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/pretty.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/pretty.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/pretty.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/pretty.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,120 @@ +/* This file has been placed in the public domain. */ +/* Following are the presentation styles -- edit away! */ + +html, body {margin: 0; padding: 0;} +body {background: white; color: black;} +/* Replace the background style above with the style below (and again for + div#header) for a graphic: */ +/* background: white url(bodybg.gif) -16px 0 no-repeat; */ +:link, :visited {text-decoration: none; color: #00C;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} +h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;} + +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} +blockquote p {margin: 0;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} +.slide ul ul li {list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} +.slide tt {font-size: 90%;} + +div#header, div#footer {background: #005; color: #AAB; font-family: sans-serif;} +/* background: #005 url(bodybg.gif) -16px 0 no-repeat; */ +div#footer {font-size: 0.5em; font-weight: bold; padding: 1em 0;} +#footer h1 {display: block; padding: 0 1em;} +#footer h2 {display: block; padding: 0.8em 1em 0;} + +.slide {font-size: 1.2em;} +.slide h1 {position: absolute; top: 0.45em; z-index: 1; + margin: 0; padding-left: 0.7em; white-space: nowrap; + font: bold 150% sans-serif; color: #DDE; background: #005;} +.slide h2 {font: bold 120%/1em sans-serif; padding-top: 0.5em;} +.slide h3 {font: bold 100% sans-serif; padding-top: 0.5em;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; text-align: right; font: bold 0.9em sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + background: #005; border: none; color: #779; cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; + background: #DDD; color: #227;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #449; + font-family: sans-serif; font-weight: bold;} + +#slide0 {padding-top: 1.5em} +#slide0 h1 {position: static; margin: 1em 0 0; padding: 0; color: #000; + font: bold 2em sans-serif; white-space: normal; background: transparent;} +#slide0 h2 {font: bold italic 1em sans-serif; margin: 0.25em;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #77B;} + +.incremental, .incremental *, .incremental *:after {visibility: visible; + color: white; border: 0;} +img.incremental {visibility: hidden;} +.slide .current {color: green;} + +.slide-display {display: inline ! important;} + +.huge {font-family: sans-serif; font-weight: bold; font-size: 150%;} +.big {font-family: sans-serif; font-weight: bold; font-size: 120%;} +.small {font-size: 75%;} +.tiny {font-size: 50%;} +.huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} +.huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} + +.maroon {color: maroon;} +.red {color: red;} +.magenta {color: magenta;} +.fuchsia {color: fuchsia;} +.pink {color: #FAA;} +.orange {color: orange;} +.yellow {color: yellow;} +.lime {color: lime;} +.green {color: green;} +.olive {color: olive;} +.teal {color: teal;} +.cyan {color: cyan;} +.aqua {color: aqua;} +.blue {color: blue;} +.navy {color: navy;} +.purple {color: purple;} +.black {color: black;} +.gray {color: gray;} +.silver {color: silver;} +.white {color: white;} + +.left {text-align: left ! important;} +.center {text-align: center ! important;} +.right {text-align: right ! important;} + +.animation {position: relative; margin: 1em 0; padding: 0;} +.animation img {position: absolute;} + +/* Docutils-specific overrides */ + +.slide table.docinfo {margin: 1em 0 0.5em 2em;} + +pre.literal-block, pre.doctest-block {background-color: white;} + +tt.docutils {background-color: white;} + +/* diagnostics */ +/* +li:after {content: " [" attr(class) "]"; color: #F88;} +div:before {content: "[" attr(class) "]"; color: #F88;} +*/ diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/print.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/print.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/print.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/print.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,24 @@ +/* This file has been placed in the public domain. */ +/* The following rule is necessary to have all slides appear in print! + DO NOT REMOVE IT! */ +.slide, ul {page-break-inside: avoid; visibility: visible !important;} +h1 {page-break-after: avoid;} + +body {font-size: 12pt; background: white;} +* {color: black;} + +#slide0 h1 {font-size: 200%; border: none; margin: 0.5em 0 0.25em;} +#slide0 h3 {margin: 0; padding: 0;} +#slide0 h4 {margin: 0 0 0.5em; padding: 0;} +#slide0 {margin-bottom: 3em;} + +#header {display: none;} +#footer h1 {margin: 0; border-bottom: 1px solid; color: gray; + font-style: italic;} +#footer h2, #controls {display: none;} + +.print {display: inline ! important;} + +/* The following rule keeps the layout stuff out of print. + Remove at your own risk! */ +.layout, .layout * {display: none !important;} diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/s5-core.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/s5-core.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/s5-core.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/s5-core.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ +/* This file has been placed in the public domain. */ +/* Do not edit or override these styles! + The system will likely break if you do. */ + +div#header, div#footer, div#controls, .slide {position: absolute;} +html>body div#header, html>body div#footer, + html>body div#controls, html>body .slide {position: fixed;} +.handout {display: none;} +.layout {display: block;} +.slide, .hideme, .incremental {visibility: hidden;} +#slide0 {visibility: visible;} diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/slides.css zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/slides.css --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/slides.css 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/slides.css 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ +/* This file has been placed in the public domain. */ + +/* required to make the slide show run at all */ +@import url(s5-core.css); + +/* sets basic placement and size of slide components */ +@import url(framing.css); + +/* styles that make the slides look good */ +@import url(pretty.css); diff -Nru zope3-3.4.0/src/docutils/writers/s5_html/themes/default/slides.js zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/slides.js --- zope3-3.4.0/src/docutils/writers/s5_html/themes/default/slides.js 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/docutils/writers/s5_html/themes/default/slides.js 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,558 @@ +// S5 v1.1 slides.js -- released into the Public Domain +// Modified for Docutils (http://docutils.sf.net) by David Goodger +// +// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for +// information about all the wonderful and talented contributors to this code! + +var undef; +var slideCSS = ''; +var snum = 0; +var smax = 1; +var slideIDs = new Array(); +var incpos = 0; +var number = undef; +var s5mode = true; +var defaultView = 'slideshow'; +var controlVis = 'visible'; + +var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0; +var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0; +var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0; + +function hasClass(object, className) { + if (!object.className) return false; + return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1); +} + +function hasValue(object, value) { + if (!object) return false; + return (object.search('(^|\\s)' + value + '(\\s|$)') != -1); +} + +function removeClass(object,className) { + if (!object) return; + object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2); +} + +function addClass(object,className) { + if (!object || hasClass(object, className)) return; + if (object.className) { + object.className += ' '+className; + } else { + object.className = className; + } +} + +function GetElementsWithClassName(elementName,className) { + var allElements = document.getElementsByTagName(elementName); + var elemColl = new Array(); + for (var i = 0; i< allElements.length; i++) { + if (hasClass(allElements[i], className)) { + elemColl[elemColl.length] = allElements[i]; + } + } + return elemColl; +} + +function isParentOrSelf(element, id) { + if (element == null || element.nodeName=='BODY') return false; + else if (element.id == id) return true; + else return isParentOrSelf(element.parentNode, id); +} + +function nodeValue(node) { + var result = ""; + if (node.nodeType == 1) { + var children = node.childNodes; + for (var i = 0; i < children.length; ++i) { + result += nodeValue(children[i]); + } + } + else if (node.nodeType == 3) { + result = node.nodeValue; + } + return(result); +} + +function slideLabel() { + var slideColl = GetElementsWithClassName('*','slide'); + var list = document.getElementById('jumplist'); + smax = slideColl.length; + for (var n = 0; n < smax; n++) { + var obj = slideColl[n]; + + var did = 'slide' + n.toString(); + if (obj.getAttribute('id')) { + slideIDs[n] = obj.getAttribute('id'); + } + else { + obj.setAttribute('id',did); + slideIDs[n] = did; + } + if (isOp) continue; + + var otext = ''; + var menu = obj.firstChild; + if (!menu) continue; // to cope with empty slides + while (menu && menu.nodeType == 3) { + menu = menu.nextSibling; + } + if (!menu) continue; // to cope with slides with only text nodes + + var menunodes = menu.childNodes; + for (var o = 0; o < menunodes.length; o++) { + otext += nodeValue(menunodes[o]); + } + list.options[list.length] = new Option(n + ' : ' + otext, n); + } +} + +function currentSlide() { + var cs; + var footer_nodes; + var vis = 'visible'; + if (document.getElementById) { + cs = document.getElementById('currentSlide'); + footer_nodes = document.getElementById('footer').childNodes; + } else { + cs = document.currentSlide; + footer = document.footer.childNodes; + } + cs.innerHTML = '' + snum + '<\/span> ' + + '\/<\/span> ' + + '' + (smax-1) + '<\/span>'; + if (snum == 0) { + vis = 'hidden'; + } + cs.style.visibility = vis; + for (var i = 0; i < footer_nodes.length; i++) { + if (footer_nodes[i].nodeType == 1) { + footer_nodes[i].style.visibility = vis; + } + } +} + +function go(step) { + if (document.getElementById('slideProj').disabled || step == 0) return; + var jl = document.getElementById('jumplist'); + var cid = slideIDs[snum]; + var ce = document.getElementById(cid); + if (incrementals[snum].length > 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + removeClass(incrementals[snum][i], 'current'); + removeClass(incrementals[snum][i], 'incremental'); + } + } + if (step != 'j') { + snum += step; + lmax = smax - 1; + if (snum > lmax) snum = lmax; + if (snum < 0) snum = 0; + } else + snum = parseInt(jl.value); + var nid = slideIDs[snum]; + var ne = document.getElementById(nid); + if (!ne) { + ne = document.getElementById(slideIDs[0]); + snum = 0; + } + if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;} + if (incrementals[snum].length > 0 && incpos == 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + if (hasClass(incrementals[snum][i], 'current')) + incpos = i + 1; + else + addClass(incrementals[snum][i], 'incremental'); + } + } + if (incrementals[snum].length > 0 && incpos > 0) + addClass(incrementals[snum][incpos - 1], 'current'); + ce.style.visibility = 'hidden'; + ne.style.visibility = 'visible'; + jl.selectedIndex = snum; + currentSlide(); + number = 0; +} + +function goTo(target) { + if (target >= smax || target == snum) return; + go(target - snum); +} + +function subgo(step) { + if (step > 0) { + removeClass(incrementals[snum][incpos - 1],'current'); + removeClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos],'current'); + incpos++; + } else { + incpos--; + removeClass(incrementals[snum][incpos],'current'); + addClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos - 1],'current'); + } +} + +function toggle() { + var slideColl = GetElementsWithClassName('*','slide'); + var slides = document.getElementById('slideProj'); + var outline = document.getElementById('outlineStyle'); + if (!slides.disabled) { + slides.disabled = true; + outline.disabled = false; + s5mode = false; + fontSize('1em'); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'visible'; + } + } else { + slides.disabled = false; + outline.disabled = true; + s5mode = true; + fontScale(); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'hidden'; + } + slideColl[snum].style.visibility = 'visible'; + } +} + +function showHide(action) { + var obj = GetElementsWithClassName('*','hideme')[0]; + switch (action) { + case 's': obj.style.visibility = 'visible'; break; + case 'h': obj.style.visibility = 'hidden'; break; + case 'k': + if (obj.style.visibility != 'visible') { + obj.style.visibility = 'visible'; + } else { + obj.style.visibility = 'hidden'; + } + break; + } +} + +// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/) +function keys(key) { + if (!key) { + key = event; + key.which = key.keyCode; + } + if (key.which == 84) { + toggle(); + return; + } + if (s5mode) { + switch (key.which) { + case 10: // return + case 13: // enter + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + if(number != undef) { + goTo(number); + break; + } + case 32: // spacebar + case 34: // page down + case 39: // rightkey + case 40: // downkey + if(number != undef) { + go(number); + } else if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + break; + case 33: // page up + case 37: // leftkey + case 38: // upkey + if(number != undef) { + go(-1 * number); + } else if (!incrementals[snum] || incpos <= 0) { + go(-1); + } else { + subgo(-1); + } + break; + case 36: // home + goTo(0); + break; + case 35: // end + goTo(smax-1); + break; + case 67: // c + showHide('k'); + break; + } + if (key.which < 48 || key.which > 57) { + number = undef; + } else { + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + number = (((number != undef) ? number : 0) * 10) + (key.which - 48); + } + } + return false; +} + +function clicker(e) { + number = undef; + var target; + if (window.event) { + target = window.event.srcElement; + e = window.event; + } else target = e.target; + if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true; + if (!e.which || e.which == 1) { + if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + } +} + +function findSlide(hash) { + var target = document.getElementById(hash); + if (target) { + for (var i = 0; i < slideIDs.length; i++) { + if (target.id == slideIDs[i]) return i; + } + } + return null; +} + +function slideJump() { + if (window.location.hash == null || window.location.hash == '') { + currentSlide(); + return; + } + if (window.location.hash == null) return; + var dest = null; + dest = findSlide(window.location.hash.slice(1)); + if (dest == null) { + dest = 0; + } + go(dest - snum); +} + +function fixLinks() { + var thisUri = window.location.href; + thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length); + var aelements = document.getElementsByTagName('A'); + for (var i = 0; i < aelements.length; i++) { + var a = aelements[i].href; + var slideID = a.match('\#.+'); + if ((slideID) && (slideID[0].slice(0,1) == '#')) { + var dest = findSlide(slideID[0].slice(1)); + if (dest != null) { + if (aelements[i].addEventListener) { + aelements[i].addEventListener("click", new Function("e", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "if (e.preventDefault) e.preventDefault();"), true); + } else if (aelements[i].attachEvent) { + aelements[i].attachEvent("onclick", new Function("", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "event.returnValue = false;")); + } + } + } + } +} + +function externalLinks() { + if (!document.getElementsByTagName) return; + var anchors = document.getElementsByTagName('a'); + for (var i=0; i' + + ' + + diff -Nru zope3-3.4.0/src/zope/app/container/browser/contents.py zope3-3.5~bzr18/src/zope/app/container/browser/contents.py --- zope3-3.4.0/src/zope/app/container/browser/contents.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/contents.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,464 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""View Class for the Container's Contents view. + +$Id: contents.py 85553 2008-04-21 17:35:27Z lgs $ +""" +__docformat__ = 'restructuredtext' + +import urllib + +from zope.component import queryMultiAdapter +from zope.event import notify +from zope.exceptions.interfaces import UserError +from zope.security.interfaces import Unauthorized +from zope.security import canWrite +from zope.size.interfaces import ISized +from zope.traversing.interfaces import TraversalError +from zope.publisher.browser import BrowserView +from zope.dublincore.interfaces import IZopeDublinCore +from zope.dublincore.interfaces import IDCDescriptiveProperties +from zope.copypastemove.interfaces import IPrincipalClipboard +from zope.copypastemove.interfaces import IObjectCopier, IObjectMover +from zope.copypastemove.interfaces import IContainerItemRenamer +from zope.annotation.interfaces import IAnnotations +from zope.lifecycleevent import ObjectModifiedEvent, Attributes +from zope.traversing.api import getName, getPath, joinPath, traverse + +from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile +from zope.app.container.i18n import ZopeMessageFactory as _ + +from zope.app.container.browser.adding import Adding +from zope.app.container.interfaces import IContainer, DuplicateIDError +from zope.app.container.interfaces import IContainerNamesContainer + +class Contents(BrowserView): + + __used_for__ = IContainer + + error = '' + message = '' + normalButtons = False + specialButtons = False + supportsRename = False + + def listContentInfo(self): + request = self.request + + if "container_cancel_button" in request: + if "type_name" in request: + del request.form['type_name'] + if "rename_ids" in request and "new_value" in request: + del request.form['rename_ids'] + if "retitle_id" in request and "new_value" in request: + del request.form['retitle_id'] + + return self._normalListContentsInfo() + + elif "container_rename_button" in request and not request.get("ids"): + self.error = _("You didn't specify any ids to rename.") + elif "container_add_button" in request: + if "single_type_name" in request \ + and "single_new_value" in request: + request.form['type_name'] = request['single_type_name'] + request.form['new_value'] = request['single_new_value'] + self.addObject() + elif 'single_type_name' in request \ + and 'single_new_value' not in request: + request.form['type_name'] = request['single_type_name'] + request.form['new_value'] = "" + self.addObject() + elif "type_name" in request and "new_value" in request: + self.addObject() + elif "rename_ids" in request and "new_value" in request: + self.renameObjects() + elif "retitle_id" in request and "new_value" in request: + self.changeTitle() + elif "container_cut_button" in request: + self.cutObjects() + elif "container_copy_button" in request: + self.copyObjects() + elif "container_paste_button" in request: + self.pasteObjects() + elif "container_delete_button" in request: + self.removeObjects() + else: + return self._normalListContentsInfo() + + if self.error: + return self._normalListContentsInfo() + + status = request.response.getStatus() + if status not in (302, 303): + # Only redirect if nothing else has + request.response.redirect(request.URL) + return () + + def normalListContentInfo(self): + return self._normalListContentsInfo() + + def _normalListContentsInfo(self): + request = self.request + + self.specialButtons = ( + 'type_name' in request or + 'rename_ids' in request or + ('container_rename_button' in request + and request.get("ids")) or + 'retitle_id' in request + ) + self.normalButtons = not self.specialButtons + + info = map(self._extractContentInfo, self.context.items()) + + self.supportsCut = info + self.supportsCopy = info + self.supportsDelete = info + self.supportsPaste = self.pasteable() + self.supportsRename = ( + self.supportsCut and + not IContainerNamesContainer.providedBy(self.context) + ) + + return info + + + def _extractContentInfo(self, item): + request = self.request + + + rename_ids = {} + if "container_rename_button" in request: + for rename_id in request.get('ids', ()): + rename_ids[rename_id] = rename_id + elif "rename_ids" in request: + for rename_id in request.get('rename_ids', ()): + rename_ids[rename_id] = rename_id + + + retitle_id = request.get('retitle_id') + + id, obj = item + info = {} + info['id'] = info['cb_id'] = id + info['object'] = obj + + info['url'] = urllib.quote(id.encode('utf-8')) + info['rename'] = rename_ids.get(id) + info['retitle'] = id == retitle_id + + + zmi_icon = queryMultiAdapter((obj, self.request), name='zmi_icon') + if zmi_icon is None: + info['icon'] = None + else: + info['icon'] = zmi_icon() + + dc = IZopeDublinCore(obj, None) + if dc is not None: + info['retitleable'] = canWrite(dc, 'title') + info['plaintitle'] = not info['retitleable'] + + title = self.safe_getattr(dc, 'title', None) + if title: + info['title'] = title + + formatter = self.request.locale.dates.getFormatter( + 'dateTime', 'short') + + created = self.safe_getattr(dc, 'created', None) + if created is not None: + info['created'] = formatter.format(created) + + modified = self.safe_getattr(dc, 'modified', None) + if modified is not None: + info['modified'] = formatter.format(modified) + else: + info['retitleable'] = 0 + info['plaintitle'] = 1 + + + sized_adapter = ISized(obj, None) + if sized_adapter is not None: + info['size'] = sized_adapter + return info + + def safe_getattr(self, obj, attr, default): + """Attempts to read the attr, returning default if Unauthorized.""" + try: + return getattr(obj, attr, default) + except Unauthorized: + return default + + def renameObjects(self): + """Given a sequence of tuples of old, new ids we rename""" + request = self.request + ids = request.get("rename_ids") + newids = request.get("new_value") + + renamer = IContainerItemRenamer(self.context) + for oldid, newid in zip(ids, newids): + if newid != oldid: + renamer.renameItem(oldid, newid) + + def changeTitle(self): + """Given a sequence of tuples of old, new ids we rename""" + request = self.request + id = request.get("retitle_id") + new = request.get("new_value") + + item = self.context[id] + dc = IDCDescriptiveProperties(item) + dc.title = new + notify(ObjectModifiedEvent(item, Attributes(IZopeDublinCore, 'title'))) + + def hasAdding(self): + """Returns true if an adding view is available.""" + adding = queryMultiAdapter((self.context, self.request), name="+") + return (adding is not None) + + def addObject(self): + request = self.request + if IContainerNamesContainer.providedBy(self.context): + new = "" + else: + new = request["new_value"] + + adding = queryMultiAdapter((self.context, self.request), name="+") + if adding is None: + adding = Adding(self.context, request) + else: + # Set up context so that the adding can build a url + # if the type name names a view. + # Note that we can't so this for the "adding is None" case + # above, because there is no "+" view. + adding.__parent__ = self.context + adding.__name__ = '+' + + adding.action(request['type_name'], new) + + def removeObjects(self): + """Remove objects specified in a list of object ids""" + request = self.request + ids = request.get('ids') + if not ids: + self.error = _("You didn't specify any ids to remove.") + return + + container = self.context + for id in ids: + del container[id] + + def copyObjects(self): + """Copy objects specified in a list of object ids""" + request = self.request + ids = request.get('ids') + if not ids: + self.error = _("You didn't specify any ids to copy.") + return + + container_path = getPath(self.context) + + # For each item, check that it can be copied; if so, save the + # path of the object for later copying when a destination has + # been selected; if not copyable, provide an error message + # explaining that the object can't be copied. + items = [] + for id in ids: + ob = self.context[id] + copier = IObjectCopier(ob) + if not copier.copyable(): + m = {"name": id} + title = getDCTitle(ob) + if title: + m["title"] = title + self.error = _( + "Object '${name}' (${title}) cannot be copied", + mapping=m) + else: + self.error = _("Object '${name}' cannot be copied", + mapping=m) + return + items.append(joinPath(container_path, id)) + + # store the requested operation in the principal annotations: + clipboard = getPrincipalClipboard(self.request) + clipboard.clearContents() + clipboard.addItems('copy', items) + + def cutObjects(self): + """move objects specified in a list of object ids""" + request = self.request + ids = request.get('ids') + if not ids: + self.error = _("You didn't specify any ids to cut.") + return + + container_path = getPath(self.context) + + # For each item, check that it can be moved; if so, save the + # path of the object for later moving when a destination has + # been selected; if not movable, provide an error message + # explaining that the object can't be moved. + items = [] + for id in ids: + ob = self.context[id] + mover = IObjectMover(ob) + if not mover.moveable(): + m = {"name": id} + title = getDCTitle(ob) + if title: + m["title"] = title + self.error = _( + "Object '${name}' (${title}) cannot be moved", + mapping=m) + else: + self.error = _("Object '${name}' cannot be moved", + mapping=m) + return + items.append(joinPath(container_path, id)) + + # store the requested operation in the principal annotations: + clipboard = getPrincipalClipboard(self.request) + clipboard.clearContents() + clipboard.addItems('cut', items) + + + def pasteable(self): + """Decide if there is anything to paste + """ + target = self.context + clipboard = getPrincipalClipboard(self.request) + items = clipboard.getContents() + for item in items: + try: + obj = traverse(target, item['target']) + except TraversalError: + pass + else: + if item['action'] == 'cut': + mover = IObjectMover(obj) + moveableTo = self.safe_getattr(mover, 'moveableTo', None) + if moveableTo is None or not moveableTo(target): + return False + elif item['action'] == 'copy': + copier = IObjectCopier(obj) + copyableTo = self.safe_getattr(copier, 'copyableTo', None) + if copyableTo is None or not copyableTo(target): + return False + else: + raise + + return True + + + def pasteObjects(self): + """Paste ojects in the user clipboard to the container + """ + target = self.context + clipboard = getPrincipalClipboard(self.request) + items = clipboard.getContents() + moved = False + not_pasteable_ids = [] + for item in items: + duplicated_id = False + try: + obj = traverse(target, item['target']) + except TraversalError: + pass + else: + if item['action'] == 'cut': + mover = IObjectMover(obj) + try: + mover.moveTo(target) + moved = True + except DuplicateIDError: + duplicated_id = True + elif item['action'] == 'copy': + copier = IObjectCopier(obj) + try: + copier.copyTo(target) + except DuplicateIDError: + duplicated_id = True + else: + raise + + if duplicated_id: + not_pasteable_ids.append(getName(obj)) + + if moved: + # Clear the clipboard if we do a move, but not if we only do a copy + clipboard.clearContents() + + if not_pasteable_ids != []: + # Show the ids of objects that can't be pasted because + # their ids are already taken. + # TODO Can't we add a 'copy_of' or something as a prefix + # instead of raising an exception ? + raise UserError( + _("The given name(s) %s is / are already being used" %( + str(not_pasteable_ids)))) + + def hasClipboardContents(self): + """Interogate the ``PrinicipalAnnotation`` to see if clipboard + contents exist.""" + + if not self.supportsPaste: + return False + + # touch at least one item to in clipboard confirm contents + clipboard = getPrincipalClipboard(self.request) + items = clipboard.getContents() + for item in items: + try: + traverse(self.context, item['target']) + except TraversalError: + pass + else: + return True + + return False + + contents = ViewPageTemplateFile('contents.pt') + contentsMacros = contents + + _index = ViewPageTemplateFile('index.pt') + + def index(self): + if 'index.html' in self.context: + self.request.response.redirect('index.html') + return '' + + return self._index() + +class JustContents(Contents): + """Like Contents, but does't delegate to item named index.html""" + + def index(self): + return self._index() + + +def getDCTitle(ob): + dc = IDCDescriptiveProperties(ob, None) + if dc is None: + return None + else: + return dc.title + + +def getPrincipalClipboard(request): + """Return the clipboard based on the request.""" + user = request.principal + annotations = IAnnotations(user) + return IPrincipalClipboard(annotations) diff -Nru zope3-3.4.0/src/zope/app/container/browser/find.pt zope3-3.5~bzr18/src/zope/app/container/browser/find.pt --- zope3-3.4.0/src/zope/app/container/browser/find.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/find.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ + + +
+ +
+
+ +
+ + + + + +
+ id +
+ +
+ + diff -Nru zope3-3.4.0/src/zope/app/container/browser/find.py zope3-3.5~bzr18/src/zope/app/container/browser/find.py --- zope3-3.4.0/src/zope/app/container/browser/find.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/find.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,42 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Find View Class + +$Id: find.py 67630 2006-04-27 00:54:03Z jim $ +""" +__docformat__ = 'restructuredtext' + +from zope.traversing.api import getName +from zope.traversing.browser.absoluteurl import absoluteURL +from zope.publisher.browser import BrowserView + +from zope.app.container.find import SimpleIdFindFilter +from zope.app.container.interfaces import IFind + +# Very simple implementation right now +class Find(BrowserView): + + def findByIds(self, ids): + """Do a find for the `ids` listed in `ids`, which is a string.""" + finder = IFind(self.context) + ids = ids.split() + # if we don't have any ids listed, don't search at all + if not ids: + return [] + request = self.request + result = [] + for object in finder.find([SimpleIdFindFilter(ids)]): + url = absoluteURL(object, request) + result.append({ 'id': getName(object), 'url': url}) + return result diff -Nru zope3-3.4.0/src/zope/app/container/browser/index.pt zope3-3.5~bzr18/src/zope/app/container/browser/index.pt --- zope3-3.4.0/src/zope/app/container/browser/index.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/index.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,66 @@ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
 NameTitleCreatedModified
+ + + ID here +    
+ +
+ + diff -Nru zope3-3.4.0/src/zope/app/container/browser/__init__.py zope3-3.5~bzr18/src/zope/app/container/browser/__init__.py --- zope3-3.4.0/src/zope/app/container/browser/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/container/browser/metaconfigure.py zope3-3.5~bzr18/src/zope/app/container/browser/metaconfigure.py --- zope3-3.4.0/src/zope/app/container/browser/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,102 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Container-specific browser ZCML namespace directive handlers + +$Id: metaconfigure.py 97197 2009-02-24 07:50:58Z nadako $ +""" + +__docformat__ = 'restructuredtext' + +from zope.interface import Interface +from zope.component import queryMultiAdapter +from zope.configuration.fields import GlobalObject, GlobalInterface +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.schema import Id +from zope.security.zcml import Permission +from zope.app.publisher.browser.viewmeta import page, view +from zope.app.publisher.browser.menumeta import menuItemDirective +from zope.app.container.browser.contents import Contents +from zope.app.container.browser.adding import Adding +from zope.app.container.i18n import ZopeMessageFactory as _ + + +class IContainerViews(Interface): + """Define several container views for an `IContainer` implementation.""" + + for_ = GlobalObject( + title=u"The declaration this containerViews are for.", + description=u""" + The containerViews will be available for all objects that + provide this declaration. + """, + required=True) + + contents = Permission( + title=u"The permission needed for content page.", + required=False) + + index = Permission( + title=u"The permission needed for index page.", + required=False) + + add = Permission( + title=u"The permission needed for add page.", + required=False) + + layer = GlobalInterface( + title=_("The layer the view is in."), + description=_("""A skin is composed of layers. It is common to put + skin specific views in a layer named after the skin. If the 'layer' + attribute is not supplied, it defaults to 'default'."""), + required=False + ) + + +def containerViews(_context, for_, contents=None, add=None, index=None, + layer=IDefaultBrowserLayer): + """Set up container views for a given content type.""" + + if for_ is None: + raise ValueError("A for interface must be specified.") + + if contents is not None: + from zope.app.menus import zmi_views + page(_context, name='contents.html', permission=contents, + for_=for_, layer=layer, class_=Contents, attribute='contents', + menu=zmi_views, title=_('Contents')) + + if index is not None: + page(_context, name='index.html', permission=index, for_=for_, + layer=layer, class_=Contents, attribute='index') + + if add is not None: + from zope.app.menus import zmi_actions + viewObj = view(_context, name='+', layer=layer, for_=for_, + permission=add, class_=Adding) + menuItemDirective( + _context, zmi_actions, for_, '+', _('Add'), permission=add, layer=layer, + filter='python:modules["zope.app.container.browser.metaconfigure"].menuFilter(context, request)') + viewObj.page(_context, name='index.html', attribute='index') + viewObj.page(_context, name='action.html', attribute='action') + viewObj() + +def menuFilter(context, request): + '''This is a function that filters the "Add" menu item''' + adding = queryMultiAdapter((context, request), name="+") + if adding is None: + adding = Adding(context, request) + adding.__parent__ = context + adding.__name__ = '+' + info = adding.addingInfo() + return bool(info) diff -Nru zope3-3.4.0/src/zope/app/container/browser/meta.zcml zope3-3.5~bzr18/src/zope/app/container/browser/meta.zcml --- zope3-3.4.0/src/zope/app/container/browser/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/app/container/browser/tests/configure.zcml zope3-3.5~bzr18/src/zope/app/container/browser/tests/configure.zcml --- zope3-3.4.0/src/zope/app/container/browser/tests/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/tests/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,16 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/container/browser/tests/index.txt zope3-3.5~bzr18/src/zope/app/container/browser/tests/index.txt --- zope3-3.4.0/src/zope/app/container/browser/tests/index.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/tests/index.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,18 @@ +The containerViews directive lets us associate some standard forms +for containers with an interface. There's an "index.html" view that +provides a listing of the contained objects without provinding any way +to manage them (though it allows us to visit them by clicking on +links). + +We can get this view from the root folder easily:: + + >>> response = http(r""" + ... GET / HTTP/1.1 + ... """) + +And we can check that there isn't a form (where the management +operations would have buttons):: + + >>> body = response.getBody().lower() + >>> ">> registerAddMenu() + + >>> class TestMenu(zope.interface.Interface): + ... pass + >>> zope.interface.directlyProvides(TestMenu, IMenuItemType) + + >>> ztapi.provideUtility(IMenuItemType, TestMenu, 'TestMenu') + >>> ztapi.provideUtility(IBrowserMenu, BrowserMenu('TestMenu', u'', u''), + ... 'TestMenu') + + >>> defineMenuItem(TestMenu, IAdding, '', 'item1') + >>> defineMenuItem(TestMenu, IAdding, '', 'Item2') + + >>> defineMenuItem(AddMenu, IAdding, '', 'item3', extra={'factory': 'f1'}) + >>> defineMenuItem(AddMenu, IAdding, '', 'item4', extra={'factory': 'f2'}) + + >>> class F1(object): + ... pass + + >>> class F2(object): + ... pass + + >>> def pre(container, name, object): + ... if not isinstance(object, F1): + ... raise zope.interface.Invalid() + >>> def prefactory(container, name, factory): + ... if factory._callable is not F1: + ... raise zope.interface.Invalid() + >>> pre.factory = prefactory + + + >>> class IContainer(zope.interface.Interface): + ... def __setitem__(name, object): + ... pass + ... __setitem__.precondition = pre + + + >>> class Container(object): + ... zope.interface.implements(IContainer) + + >>> from zope.component.factory import Factory + >>> ztapi.provideUtility(IFactory, Factory(F1), 'f1') + >>> ztapi.provideUtility(IFactory, Factory(F2), 'f2') + + >>> from zope.app.container.browser.adding import Adding + >>> adding = Adding(Container(), TestRequest()) + >>> items = adding.addingInfo() + >>> len(items) + 1 + >>> items[0]['title'] + u'item3' + + >>> adding.menu_id = 'TestMenu' + >>> items = adding.addingInfo() + >>> len(items) + 3 + >>> items[0]['title'] + u'item1' + >>> items[1]['title'] # the collator ordered this one correctly! + u'Item2' + >>> items[2]['title'] + u'item3' + """ + +def test_constraint_driven_add(): + """ + >>> from zope.app.container.sample import SampleContainer + >>> from zope.app.container.browser.adding import Adding + + >>> class F1(object): + ... pass + + >>> class F2(object): + ... pass + + >>> def pre(container, name, object): + ... "a mock item constraint " + ... if not isinstance(object, F1): + ... raise zope.interface.Invalid('not a valid child') + + >>> class ITestContainer(zope.interface.Interface): + ... def __setitem__(name, object): + ... pass + ... __setitem__.precondition = pre + + >>> class Container(SampleContainer): + ... zope.interface.implements(ITestContainer) + + >>> adding = Adding(Container(), TestRequest()) + >>> c = adding.add(F1()) + + This test should fail, because the container only + accepts instances of F1 + + >>> adding.add(F2()) + Traceback (most recent call last): + ... + Invalid: not a valid child + + >>> class ValidContainer(SampleContainer): + ... zope.interface.implements(ITestContainer) + + >>> def constr(container): + ... "a mock container constraint" + ... if not isinstance(container, ValidContainer): + ... raise zope.interface.Invalid('not a valid container') + ... return True + + >>> class I2(zope.interface.Interface): + ... __parent__ = zope.schema.Field(constraint = constr) + + >>> zope.interface.classImplements(F1, I2) + + This adding now fails, because the Container is not a valid + parent for F1 + + >>> c = adding.add(F1()) + Traceback (most recent call last): + ... + Invalid: not a valid container + + >>> adding = Adding(ValidContainer(), TestRequest()) + >>> c = adding.add(F1()) + + """ + + +def test_nameAllowed(): + """ + Test for nameAllowed in adding.py + + >>> from zope.app.container.browser.adding import Adding + >>> from zope.app.container.interfaces import IContainerNamesContainer + + Class implements IContainerNamesContainer + + >>> class FakeContainer(object): + ... zope.interface.implements(IContainerNamesContainer) + + nameAllowed returns False if the class imlements + IContainerNamesContainer + + >>> adding = Adding(FakeContainer(),TestRequest()) + >>> adding.nameAllowed() + False + + Fake class without IContainerNamesContainer + + >>> class Fake(object): + ... pass + + nameAllowed returns True if the class + doesn't imlement IContainerNamesContainer + + >>> adding = Adding(Fake(),TestRequest()) + >>> adding.nameAllowed() + True + + """ + + + +def test_chooseName(): + """If user don't enter name, pick one + + >>> class MyContainer(object): + ... args = {} + ... zope.interface.implements(INameChooser, IContainer) + ... def chooseName(self, name, object): + ... self.args["choose"] = name, object + ... return 'pickone' + ... def checkName(self, name, object): + ... self.args["check"] = name, object + ... def __setitem__(self, name, object): + ... setattr(self, name, object) + ... self.name = name + ... def __getitem__(self, key): + ... return getattr(self, key) + + >>> request = TestRequest() + >>> mycontainer = MyContainer() + >>> adding = Adding(mycontainer, request) + >>> o = object() + >>> add_obj = adding.add(o) + >>> mycontainer.name + 'pickone' + >>> add_obj is o + True + + Make sure right arguments passed to INameChooser adapter: + + >>> name, obj = mycontainer.args["choose"] + >>> name + '' + >>> obj is o + True + >>> name, obj = mycontainer.args["check"] + >>> name + 'pickone' + >>> obj is o + True + """ + + + +def test_SingleMenuItem_and_CustomAddView_NonICNC(): + """ + This tests the condition if the content has Custom Add views and + the container contains only a single content object + + >>> registerAddMenu() + >>> defineMenuItem(AddMenu, IAdding, '', 'item3', extra={'factory': 'f1'}) + + >>> class F1(object): + ... pass + + >>> class F2(object): + ... pass + + >>> def pre(container, name, object): + ... if not isinstance(object, F1): + ... raise zope.interface.Invalid() + >>> def prefactory(container, name, factory): + ... if factory._callable is not F1: + ... raise zope.interface.Invalid() + >>> pre.factory = prefactory + + + >>> class IContainer(zope.interface.Interface): + ... def __setitem__(name, object): + ... pass + ... __setitem__.precondition = pre + + + >>> class Container(object): + ... zope.interface.implements(IContainer) + + >>> from zope.component.factory import Factory + >>> ztapi.provideUtility(IFactory, Factory(F1), 'f1') + >>> ztapi.provideUtility(IFactory, Factory(F2), 'f2') + + >>> from zope.app.container.browser.adding import Adding + >>> adding = Adding(Container(), TestRequest()) + >>> items = adding.addingInfo() + >>> len(items) + 1 + + isSingleMenuItem returns True if there is only one content class + inside the Container + + >>> adding.isSingleMenuItem() + True + + hasCustomAddView will return False as the content does not have + a custom Add View + + >>> adding.hasCustomAddView() + True + + """ + +def test_SingleMenuItem_and_NoCustomAddView_NonICNC(): + """ + + This function checks the case where there is a single content object + and there is non custom add view . Also the container does not + implement IContainerNamesContainer + + >>> registerAddMenu() + >>> defineMenuItem(AddMenu, None, '', 'item3', extra={'factory': ''}) + >>> class F1(object): + ... pass + + >>> class F2(object): + ... pass + + >>> def pre(container, name, object): + ... if not isinstance(object, F1): + ... raise zope.interface.Invalid() + >>> def prefactory(container, name, factory): + ... if factory._callable is not F1: + ... raise zope.interface.Invalid() + >>> pre.factory = prefactory + + + >>> class IContainer(zope.interface.Interface): + ... def __setitem__(name, object): + ... pass + ... __setitem__.precondition = pre + + + >>> class Container(object): + ... zope.interface.implements(IContainer) + + >>> from zope.component.factory import Factory + >>> ztapi.provideUtility(IFactory, Factory(F1), 'f1') + >>> ztapi.provideUtility(IFactory, Factory(F2), 'f2') + + >>> from zope.app.container.browser.adding import Adding + >>> adding = Adding(Container(), TestRequest()) + >>> items = adding.addingInfo() + >>> len(items) + 1 + + The isSingleMenuItem will return True if there is one single content + that can be added inside the Container + + >>> adding.isSingleMenuItem() + True + + hasCustomAddView will return False as the content does not have + a custom Add View + + >>> adding.hasCustomAddView() + False + + """ + +def test_isSingleMenuItem_with_ICNC(): + """ + This test checks for whether there is a single content that can be added + and the container uses IContainerNamesContaienr + + >>> registerAddMenu() + >>> defineMenuItem(AddMenu, None, '', 'item3', extra={'factory': ''}) + + >>> class F1(object): + ... pass + + >>> class F2(object): + ... pass + + >>> def pre(container, name, object): + ... if not isinstance(object, F1): + ... raise zope.interface.Invalid() + >>> def prefactory(container, name, factory): + ... if factory._callable is not F1: + ... raise zope.interface.Invalid() + >>> pre.factory = prefactory + + + >>> class IContainer(zope.interface.Interface): + ... def __setitem__(name, object): + ... pass + ... __setitem__.precondition = pre + + + >>> class Container(object): + ... zope.interface.implements(IContainer, IContainerNamesContainer) + + >>> from zope.app.container.browser.adding import Adding + >>> adding = Adding(Container(), TestRequest()) + >>> items = adding.addingInfo() + >>> len(items) + 1 + >>> adding.isSingleMenuItem() + True + >>> adding.hasCustomAddView() + False + + """ + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test), + doctest.DocTestSuite(setUp=setUp, tearDown=tearDown), + )) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/container/browser/tests/test_contents_functional.py zope3-3.5~bzr18/src/zope/app/container/browser/tests/test_contents_functional.py --- zope3-3.4.0/src/zope/app/container/browser/tests/test_contents_functional.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/tests/test_contents_functional.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,375 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Functional tests for the Container's 'Contents' view + +$Id: test_contents_functional.py 87310 2008-06-11 09:06:34Z ccomb $ +""" + +import unittest + +from persistent import Persistent +import transaction +from zope import copypastemove +from zope.interface import implements, Interface +from zope.annotation.interfaces import IAttributeAnnotatable +from zope.dublincore.interfaces import IZopeDublinCore + +from zope.app.container.interfaces import IReadContainer, IContained +from zope.app.testing import ztapi +from zope.app.testing.functional import BrowserTestCase +from zope.app.testing.functional import FunctionalDocFileSuite +from zope.app.container.testing import AppContainerLayer + + +class IImmovable(Interface): + """Marker interface for immovable objects.""" + +class IUncopyable(Interface): + """Marker interface for uncopyable objects.""" + +class File(Persistent): + implements(IAttributeAnnotatable) + +class ImmovableFile(File): + implements(IImmovable) + +class UncopyableFile(File): + implements(IUncopyable) + +class ObjectNonCopier(copypastemove.ObjectCopier): + + def copyable(self): + return False + +class ObjectNonMover(copypastemove.ObjectMover): + + def moveable(self): + return False + +class ReadOnlyContainer(Persistent): + implements(IReadContainer, IContained) + __parent__ = __name__ = None + + def __init__(self): self.data = {} + def keys(self): return self.data.keys() + def __getitem__(self, key): return self.data[key] + def get(self, key, default=None): return self.data.get(key, default) + def __iter__(self): return iter(self.data) + def values(self): return self.data.values() + def __len__(self): return len(self.data) + def items(self): return self.data.items() + def __contains__(self, key): return key in self.data + def has_key(self, key): return self.data.has_key(key) + + +class Test(BrowserTestCase): + + def test_inplace_add(self): + root = self.getRootFolder() + self.assert_('foo' not in root) + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'type_name': u'zope.app.content.File'}) + body = ' '.join(response.getBody().split()) + self.assert_(body.find('type="hidden" name="type_name"') >= 0) + self.assert_(body.find('input name="new_value"') >= 0) + self.assert_(body.find('type="submit" name="container_cancel_button"') + >= 0) + self.assert_(body.find('type="submit" name="container_rename_button"') + < 0) + + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'type_name': u'zope.app.content.File', + 'new_value': 'foo'}) + self.assertEqual(response.getStatus(), 302) + self.assertEqual(response.getHeader('Location'), + 'http://localhost/@@contents.html') + + root._p_jar.sync() + self.assert_('foo' in root) + + def test_inplace_rename_multiple(self): + root = self.getRootFolder() + root['foo'] = File() + self.assert_('foo' in root) + transaction.commit() + + # Check that we don't change mode if there are no items selected + + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'container_rename_button': u''}) + body = ' '.join(response.getBody().split()) + self.assert_(body.find('input name="new_value:list"') < 0) + self.assert_(body.find('type="submit" name="container_cancel_button"') + < 0) + self.assert_(body.find('type="submit" name="container_rename_button"') + >= 0) + self.assert_(body.find('div class="page_error"') + >= 0) + + + # Check normal multiple select + + + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'container_rename_button': u'', + 'ids': ['foo']}) + body = ' '.join(response.getBody().split()) + self.assert_(body.find('input name="new_value:list"') >= 0) + self.assert_(body.find('type="submit" name="container_cancel_button"') + >= 0) + self.assert_(body.find('type="submit" name="container_rename_button"') + < 0) + + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'rename_ids': ['foo'], + 'new_value': ['bar']}) + self.assertEqual(response.getStatus(), 302) + self.assertEqual(response.getHeader('Location'), + 'http://localhost/@@contents.html') + + root._p_jar.sync() + self.assert_('foo' not in root) + self.assert_('bar' in root) + + + def test_inplace_rename_single(self): + root = self.getRootFolder() + root['foo'] = File() + self.assert_('foo' in root) + transaction.commit() + + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'rename_ids': ['foo']}) + body = ' '.join(response.getBody().split()) + self.assert_(body.find('input name="new_value:list"') >= 0) + self.assert_(body.find('type="submit" name="container_cancel_button"') + >= 0) + self.assert_(body.find('type="submit" name="container_rename_button"') + < 0) + + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'rename_ids': ['foo'], + 'new_value': ['bar']}) + self.assertEqual(response.getStatus(), 302) + self.assertEqual(response.getHeader('Location'), + 'http://localhost/@@contents.html') + + root._p_jar.sync() + self.assert_('foo' not in root) + self.assert_('bar' in root) + + def test_inplace_change_title(self): + root = self.getRootFolder() + root['foo'] = File() + transaction.commit() + self.assert_('foo' in root) + dc = IZopeDublinCore(root['foo']) + self.assert_(dc.title == '') + + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'retitle_id': u'foo'}) + body = ' '.join(response.getBody().split()) + self.assert_(body.find('type="hidden" name="retitle_id"') >= 0) + self.assert_(body.find('input name="new_value"') >= 0) + self.assert_(body.find('type="submit" name="container_cancel_button"') + >= 0) + self.assert_(body.find('type="submit" name="container_rename_button"') + < 0) + + response = self.publish('/@@contents.html', + basic='mgr:mgrpw', + form={'retitle_id': u'foo', + 'new_value': u'test title'}) + self.assertEqual(response.getStatus(), 302) + self.assertEqual(response.getHeader('Location'), + 'http://localhost/@@contents.html') + + root._p_jar.sync() + self.assert_('foo' in root) + dc = IZopeDublinCore(root['foo']) + self.assert_(dc.title == 'test title') + + + def test_pasteable_for_deleted_clipboard_item(self): + """Tests Paste button visibility when copied item is deleted.""" + + root = self.getRootFolder() + root['foo'] = File() # item to be copied/deleted + root['bar'] = File() # ensures that there's always an item in + # the collection view + transaction.commit() + + # confirm foo in contents, Copy button visible, Paste not visible + response = self.publish('/@@contents.html', basic='mgr:mgrpw') + self.assertEqual(response.getStatus(), 200) + self.assert_(response.getBody().find( + 'foo') != -1) + self.assert_(response.getBody().find( + ' nothing valid to paste -> Paste should not be visible + del root['foo'] + transaction.commit() + response = self.publish('/@@contents.html', basic='mgr:mgrpw') + self.assertEqual(response.getStatus(), 200) + self.assert_(response.getBody().find( + 'foo') != -1) + self.assert_(response.getBody().find( + 'bar') != -1) + self.assert_(response.getBody().find( + ' bar still available -> Paste should be visible + del root['foo'] + transaction.commit() + response = self.publish('/@@contents.html', basic='mgr:mgrpw') + self.assertEqual(response.getStatus(), 200) + self.assert_(response.getBody().find( + '" % __name__ + + +class TestCutCopyPaste(PlacefulSetup, TestCase): + + def setUp(self): + PlacefulSetup.setUp(self) + PlacefulSetup.buildFolders(self) + ztapi.provideAdapter(IContained, IObjectCopier, ObjectCopier) + ztapi.provideAdapter(IContained, IObjectMover, ObjectMover) + ztapi.provideAdapter(IContainer, IContainerItemRenamer, + ContainerItemRenamer) + + ztapi.provideAdapter(IAnnotations, IPrincipalClipboard, + PrincipalClipboard) + ztapi.provideAdapter(Principal, IAnnotations, + PrincipalAnnotations) + + def testRename(self): + container = traverse(self.rootFolder, 'folder1') + fc = self._TestView__newView(container) + ids=['document1', 'document2'] + for id in ids: + document = Document() + container[id] = document + fc.request.form.update({'rename_ids': ids, + 'new_value': ['document1_1', 'document2_2'] + }) + fc.renameObjects() + self.failIf('document1_1' not in container) + self.failIf('document1' in container) + + def testCopyPaste(self): + container = traverse(self.rootFolder, 'folder1') + fc = self._TestView__newView(container) + ids=['document1', 'document2'] + for id in ids: + document = Document() + container[id] = document + + fc.request.form['ids'] = ids + fc.copyObjects() + fc.pasteObjects() + self.failIf('document1' not in container) + self.failIf('document2' not in container) + self.failIf('document1-2' not in container) + self.failIf('document2-2' not in container) + + def testCopyFolder(self): + container = traverse(self.rootFolder, 'folder1') + fc = self._TestView__newView(container) + ids = ['folder1_1'] + fc.request.form['ids'] = ids + fc.copyObjects() + fc.pasteObjects() + self.failIf('folder1_1' not in container) + self.failIf('folder1_1-2' not in container) + + def testCopyFolder2(self): + container = traverse(self.rootFolder, '/folder1/folder1_1') + fc = self._TestView__newView(container) + ids = ['folder1_1_1'] + fc.request.form['ids'] = ids + fc.copyObjects() + fc.pasteObjects() + self.failIf('folder1_1_1' not in container) + self.failIf('folder1_1_1-2' not in container) + + def testCopyFolder3(self): + container = traverse(self.rootFolder, '/folder1/folder1_1') + target = traverse(self.rootFolder, '/folder2/folder2_1') + fc = self._TestView__newView(container) + tg = self._TestView__newView(target) + ids = ['folder1_1_1'] + fc.request.form['ids'] = ids + fc.copyObjects() + tg.pasteObjects() + self.failIf('folder1_1_1' not in container) + self.failIf('folder1_1_1' not in target) + + def testCutPaste(self): + container = traverse(self.rootFolder, 'folder1') + fc = self._TestView__newView(container) + ids=['document1', 'document2'] + for id in ids: + document = Document() + container[id] = document + fc.request.form['ids'] = ids + fc.cutObjects() + fc.pasteObjects() + self.failIf('document1' not in container) + self.failIf('document2' not in container) + + def testCutFolder(self): + container = traverse(self.rootFolder, 'folder1') + fc = self._TestView__newView(container) + ids = ['folder1_1'] + fc.request.form['ids'] = ids + fc.cutObjects() + fc.pasteObjects() + self.failIf('folder1_1' not in container) + + def testCutFolder2(self): + container = traverse(self.rootFolder, '/folder1/folder1_1') + fc = self._TestView__newView(container) + ids = ['folder1_1_1'] + fc.request.form['ids'] = ids + fc.cutObjects() + fc.pasteObjects() + self.failIf('folder1_1_1' not in container) + + def testCutFolder3(self): + container = traverse(self.rootFolder, '/folder1/folder1_1') + target = traverse(self.rootFolder, '/folder2/folder2_1') + fc = self._TestView__newView(container) + tg = self._TestView__newView(target) + ids = ['folder1_1_1'] + fc.request.form['ids'] = ids + fc.cutObjects() + tg.pasteObjects() + self.failIf('folder1_1_1' in container) + self.failIf('folder1_1_1' not in target) + + def _TestView__newView(self, container): + from zope.app.container.browser.contents import Contents + from zope.publisher.browser import TestRequest + request = TestRequest() + request.setPrincipal(Principal()) + return Contents(container, request) + +class Test(BaseTestContentsBrowserView, TestCase): + + def _TestView__newContext(self): + from zope.app.container.sample import SampleContainer + from zope.app.folder import rootFolder + root = rootFolder() + container = SampleContainer() + return contained(container, root, 'sample') + + def _TestView__newView(self, container): + from zope.app.container.browser.contents import Contents + from zope.publisher.browser import TestRequest + request = TestRequest() + request.setPrincipal(Principal()) + return Contents(container, request) + +def test_suite(): + return TestSuite(( + makeSuite(Test), + makeSuite(TestCutCopyPaste), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/container/browser/tests/test_directive.py zope3-3.5~bzr18/src/zope/app/container/browser/tests/test_directive.py --- zope3-3.4.0/src/zope/app/container/browser/tests/test_directive.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/tests/test_directive.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,289 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""'containerView' directive test + +$Id: test_directive.py 107090 2009-12-26 15:09:15Z icemac $ +""" +import cStringIO +import doctest +import pprint +import re +import unittest + +from zope.interface import Interface +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.app.container.browser.metaconfigure import containerViews + +atre = re.compile(' at [0-9a-fA-Fx]+') + +class Context(object): + actions = () + info = '' + + def action(self, discriminator, callable, args): + self.actions += ((discriminator, callable, args), ) + self.info = 'info' + + def __repr__(self): + stream = cStringIO.StringIO() + pprinter = pprint.PrettyPrinter(stream=stream, width=60) + pprinter.pprint(self.actions) + r = stream.getvalue() + return (''.join(atre.split(r))).strip() + +class I(Interface): + pass + + +class ITestLayer(IBrowserRequest): + pass + + +def test_containerViews(): + """ + >>> from zope.browsermenu.metaconfigure import menus + >>> from zope.interface.interface import InterfaceClass + >>> zmi_views = InterfaceClass('zmi_views', __module__='zope.app.menus') + >>> menus.zmi_views = zmi_views + >>> zmi_actions = InterfaceClass('zmi_actions', __module__='zope.app.menus') + >>> menus.zmi_actions = zmi_actions + + >>> context = Context() + >>> containerViews(context, for_=I, contents='zope.ManageContent', + ... add='zope.ManageContent', index='zope.View') + >>> context + ((('adapter', + (, + ), + , + u'Contents'), + , + ('registerAdapter', + , + (, + ), + , + u'Contents', + '')), + (None, + , + ('', )), + (None, + , + ('', + )), + (None, + , + ('', + )), + (None, + , + ('', + )), + (('view', + , + 'contents.html', + , + ), + , + ('registerAdapter', + , + (, + ), + , + 'contents.html', + 'info')), + (None, + , + ('', + )), + (('view', + , + 'index.html', + , + ), + , + ('registerAdapter', + , + (, + ), + , + 'index.html', + 'info')), + (('adapter', + (, + ), + , + u'Add'), + , + ('registerAdapter', + , + (, + ), + , + u'Add', + 'info')), + (None, + , + ('', )), + (None, + , + ('', + )), + (None, + , + ('', + )), + (None, + , + ('', + )), + (None, + , + ('', )), + (('view', + (, + ), + '+', + ), + , + ('registerAdapter', + , + (, + ), + , + '+', + 'info'))) + """ + +def test_containerViews_layer(): + """ + >>> from zope.browsermenu.metaconfigure import menus + >>> from zope.interface.interface import InterfaceClass + >>> zmi_views = InterfaceClass('zmi_views', __module__='zope.app.menus') + >>> menus.zmi_views = zmi_views + >>> zmi_actions = InterfaceClass('zmi_actions', __module__='zope.app.menus') + >>> menus.zmi_actions = zmi_actions + + >>> context = Context() + >>> containerViews(context, for_=I, contents='zope.ManageContent', + ... add='zope.ManageContent', index='zope.View', layer=ITestLayer) + >>> context + ((('adapter', + (, + ), + , + u'Contents'), + , + ('registerAdapter', + , + (, + ), + , + u'Contents', + '')), + (None, + , + ('', )), + (None, + , + ('', + )), + (None, + , + ('', + )), + (None, + , + ('', + )), + (('view', + , + 'contents.html', + , + ), + , + ('registerAdapter', + , + (, + ), + , + 'contents.html', + 'info')), + (None, + , + ('', + )), + (('view', + , + 'index.html', + , + ), + , + ('registerAdapter', + , + (, + ), + , + 'index.html', + 'info')), + (('adapter', + (, + ), + , + u'Add'), + , + ('registerAdapter', + , + (, + ), + , + u'Add', + 'info')), + (None, + , + ('', )), + (None, + , + ('', + )), + (None, + , + ('', + )), + (None, + , + ('', + )), + (None, + , + ('', )), + (('view', + (, + ), + '+', + ), + , + ('registerAdapter', + , + (, + ), + , + '+', + 'info'))) + """ + + +def test_suite(): + return doctest.DocTestSuite() diff -Nru zope3-3.4.0/src/zope/app/container/browser/tests/test_view_permissions.py zope3-3.5~bzr18/src/zope/app/container/browser/tests/test_view_permissions.py --- zope3-3.4.0/src/zope/app/container/browser/tests/test_view_permissions.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/browser/tests/test_view_permissions.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,103 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Container View Permissions Tests + +$Id: test_view_permissions.py 106029 2009-11-26 14:31:39Z yusei $ +""" +import unittest +import transaction + +from zope.security.interfaces import Unauthorized + +from zope.app.testing.functional import BrowserTestCase +from zope.app.file import File +from zope.dublincore.interfaces import IZopeDublinCore +from zope.securitypolicy.interfaces import IRolePermissionManager +from zope.app.container.testing import AppContainerLayer + +class Tests(BrowserTestCase): + + def test_default_view_permissions(self): + """Tests the default view permissions. + + See zope/app/securitypolicy/configure.zcml for the grants of + zope.View and zope.app.dublincore.view to zope.Anonymous. These + ensure that, by default, anonymous users can view container contents. + """ + # add an item that can be viewed from the root folder + file = File() + self.getRootFolder()['file'] = file + IZopeDublinCore(file).title = u'My File' + transaction.commit() + + response = self.publish('/') + self.assertEquals(response.getStatus(), 200) + body = response.getBody() + + # confirm we can see the file name + self.assert_(body.find('file') != -1) + + # confirm we can see the metadata title + self.assert_(body.find('My File') != -1) + + def test_deny_view(self): + """Tests the denial of view permissions to anonymous. + + This test uses the ZMI interface to deny anonymous zope.View permission + to the root folder. + """ + # deny zope.View to zope.Anonymous + prm = IRolePermissionManager(self.getRootFolder()) + prm.denyPermissionToRole('zope.View', 'zope.Anonymous') + transaction.commit() + + # confirm Unauthorized when viewing root folder + self.assertRaises(Unauthorized, self.publish, '/') + + def test_deny_dublincore_view(self): + """Tests the denial of dublincore view permissions to anonymous. + + Users who can view a folder contents page but cannot view dublin core + should still be able to see the folder items' names, but not their + title, modified, and created info. + """ + # add an item that can be viewed from the root folder + file = File() + self.getRootFolder()['file'] = file + IZopeDublinCore(file).title = u'My File' + + # deny zope.app.dublincore.view to zope.Anonymous + prm = IRolePermissionManager(self.getRootFolder()) + prm.denyPermissionToRole('zope.app.dublincore.view', 'zope.Anonymous') + transaction.commit() + + response = self.publish('/') + self.assertEquals(response.getStatus(), 200) + body = response.getBody() + + # confirm we can see the file name + self.assert_(body.find('file') != -1) + + # confirm we *cannot* see the metadata title + self.assert_(body.find('My File') == -1) + + +def test_suite(): + suite = unittest.TestSuite() + Tests.layer = AppContainerLayer + suite.addTest(unittest.makeSuite(Tests)) + return suite + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/container/btree.py zope3-3.5~bzr18/src/zope/app/container/btree.py --- zope3-3.4.0/src/zope/app/container/btree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/btree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""BBB this module moved to zope.container +""" + +from zope.container.btree import BTreeContainer # BBB diff -Nru zope3-3.4.0/src/zope/app/container/configure.zcml zope3-3.5~bzr18/src/zope/app/container/configure.zcml --- zope3-3.4.0/src/zope/app/container/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,9 @@ + + + + diff -Nru zope3-3.4.0/src/zope/app/container/constraints.py zope3-3.5~bzr18/src/zope/app/container/constraints.py --- zope3-3.4.0/src/zope/app/container/constraints.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/constraints.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,38 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""BBB this module moved to zope.container +""" + +# BBB +from zope.container.constraints import ( + checkObject, + checkFactory, + IItemTypePrecondition, + _TypesBased, + ItemTypePrecondition, + contains, + IContainerTypesConstraint, + ContainerTypesConstraint, + containers +) + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/container/contained.py zope3-3.5~bzr18/src/zope/app/container/contained.py --- zope3-3.4.0/src/zope/app/container/contained.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/contained.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,37 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""BBB this module moved to zope.container +""" + +# BBB +from zope.container.contained import ( + Contained, + ObjectMovedEvent, + ObjectAddedEvent, + ObjectRemovedEvent, + ContainerModifiedEvent, + dispatchToSublocations, + ContainerSublocations, + containedEvent, + contained, + notifyContainerModified, + setitem, + fixing_up, + uncontained, + NameChooser, + DecoratorSpecificationDescriptor, + DecoratedSecurityCheckerDescriptor, + ContainedProxyClassProvides, + ContainedProxy, +) diff -Nru zope3-3.4.0/src/zope/app/container/dependency.py zope3-3.5~bzr18/src/zope/app/container/dependency.py --- zope3-3.4.0/src/zope/app/container/dependency.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/dependency.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## + +"""BBB: this module moved to zope.container +""" + +# BBB +from zope.container.dependency import ( + exception_msg, + CheckDependency +) diff -Nru zope3-3.4.0/src/zope/app/container/directory.py zope3-3.5~bzr18/src/zope/app/container/directory.py --- zope3-3.4.0/src/zope/app/container/directory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/directory.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,19 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""BBB: this module moved to zope.container +""" + +# BBB +from zope.container.directory import ( + noop, + Cloner +) diff -Nru zope3-3.4.0/src/zope/app/container/find.py zope3-3.5~bzr18/src/zope/app/container/find.py --- zope3-3.4.0/src/zope/app/container/find.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/find.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""BBB: this module moved to zope.container +""" + +# BBB +from zope.container.find import ( + FindAdapter, + _find_helper, + SimpleIdFindFilter, + SimpleInterfacesFindFilter +) diff -Nru zope3-3.4.0/src/zope/app/container/ftesting.zcml zope3-3.5~bzr18/src/zope/app/container/ftesting.zcml --- zope3-3.4.0/src/zope/app/container/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/container/i18n.py zope3-3.5~bzr18/src/zope/app/container/i18n.py --- zope3-3.4.0/src/zope/app/container/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/i18n.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Customization of zope.i18n for the Zope application server + +$Id: i18n.py 73547 2007-03-25 09:03:04Z dobe $ +""" +__docformat__ = 'restructuredtext' + +# import this as _ to create i18n messages in the zope domain +from zope.i18nmessageid import MessageFactory +ZopeMessageFactory = MessageFactory('zope') diff -Nru zope3-3.4.0/src/zope/app/container/__init__.py zope3-3.5~bzr18/src/zope/app/container/__init__.py --- zope3-3.4.0/src/zope/app/container/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/container/interfaces.py zope3-3.5~bzr18/src/zope/app/container/interfaces.py --- zope3-3.4.0/src/zope/app/container/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,49 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Container-related interfaces + +$Id: interfaces.py 99932 2009-05-13 21:33:55Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +# BBB +from zope.browser.interfaces import IAdding + +# BBB +from zope.container.interfaces import ( + DuplicateIDError, + ContainerError, + InvalidContainerType, + InvalidItemType, + InvalidType, + IContained, + IItemContainer, + ISimpleReadContainer, + IReadContainer, + IWriteContainer, + IItemWriteContainer, + IContainer, + IBTreeContainer, + IOrderedContainer, + IContainerNamesContainer, + IObjectMovedEvent, + UnaddableError, + IObjectAddedEvent, + INameChooser, + IObjectRemovedEvent, + IContainerModifiedEvent, + IFind, + IObjectFindFilter, + IIdFindFilter +) diff -Nru zope3-3.4.0/src/zope/app/container/ordered.py zope3-3.5~bzr18/src/zope/app/container/ordered.py --- zope3-3.4.0/src/zope/app/container/ordered.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/ordered.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,18 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""BBB: this module moved to zope.container +""" + +# BBB +from zope.container.ordered import OrderedContainer diff -Nru zope3-3.4.0/src/zope/app/container/sample.py zope3-3.5~bzr18/src/zope/app/container/sample.py --- zope3-3.4.0/src/zope/app/container/sample.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/sample.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,18 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""BBB: this module moved to zope.container +""" + +# BBB +from zope.container.sample import SampleContainer diff -Nru zope3-3.4.0/src/zope/app/container/SETUP.cfg zope3-3.5~bzr18/src/zope/app/container/SETUP.cfg --- zope3-3.4.0/src/zope/app/container/SETUP.cfg 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/SETUP.cfg 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ +# Extension information for zpkg: + + + source _zope_app_container_contained.c + diff -Nru zope3-3.4.0/src/zope/app/container/size.py zope3-3.5~bzr18/src/zope/app/container/size.py --- zope3-3.4.0/src/zope/app/container/size.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/size.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,19 @@ + +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""BBB: this module moved to zope.container +""" + +# BBB +from zope.container.size import ContainerSized diff -Nru zope3-3.4.0/src/zope/app/container/testing.py zope3-3.5~bzr18/src/zope/app/container/testing.py --- zope3-3.4.0/src/zope/app/container/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ +import os +from zope.app.testing.functional import ZCMLLayer + +AppContainerLayer = ZCMLLayer( + os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'), + __name__, 'AppContainerLayer', allow_teardown=True) + diff -Nru zope3-3.4.0/src/zope/app/container/tests/__init__.py zope3-3.5~bzr18/src/zope/app/container/tests/__init__.py --- zope3-3.4.0/src/zope/app/container/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/container/tests/placelesssetup.py zope3-3.5~bzr18/src/zope/app/container/tests/placelesssetup.py --- zope3-3.4.0/src/zope/app/container/tests/placelesssetup.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/tests/placelesssetup.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Unit test logic for setting up and tearing down basic infrastructure + +$Id: placelesssetup.py 29143 2005-02-14 22:43:16Z srichter $ +""" +from zope.app.testing import ztapi +from zope.app.container.interfaces import IWriteContainer, INameChooser +from zope.app.container.contained import NameChooser + +class PlacelessSetup(object): + + def setUp(self): + ztapi.provideAdapter(IWriteContainer, INameChooser, NameChooser) diff -Nru zope3-3.4.0/src/zope/app/container/traversal.py zope3-3.5~bzr18/src/zope/app/container/traversal.py --- zope3-3.4.0/src/zope/app/container/traversal.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/container/traversal.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""BBB: this module moved to zope.container +""" + +# BBB +from zope.container.traversal import ( + ContainerTraverser, + ItemTraverser, + _marker, + ContainerTraversable +) diff -Nru zope3-3.4.0/src/zope/app/debug/debug.py zope3-3.5~bzr18/src/zope/app/debug/debug.py --- zope3-3.4.0/src/zope/app/debug/debug.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/debug/debug.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,174 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Code to initialize the application server + +$Id: debug.py 83383 2008-02-01 15:20:41Z adamg $ +""" +__docformat__ = 'restructuredtext' + +import base64, time +import urllib +from StringIO import StringIO +from zope.publisher.publish import publish as _publish, debug_call +from zope.publisher.browser import TestRequest, setDefaultSkin +from zope.app.publication.browser import BrowserPublication +from zope.app.appsetup import config, database + +class Debugger(object): + + def __init__(self, db=None, config_file=None): + if db is None and config_file is None: + db = 'Data.fs' + config_file = 'site.zcml' + + if config_file is not None: + config(config_file) + self.db = database(db) + + def fromDatabase(cls, db): + inst = cls.__new__(cls) + inst.db = db + return inst + fromDatabase = classmethod(fromDatabase) + + def root(self): + """Get the top-level application object + + The object returned is connected to an open database connection. + """ + + from zope.app.publication.zopepublication import ZopePublication + return self.db.open().root()[ZopePublication.root_name] + + def _request(self, + path='/', stdin='', basic=None, + environment = None, form=None, + request=None, publication=BrowserPublication): + """Create a request + """ + + env = {} + + if type(stdin) is str: + stdin = StringIO(stdin) + + p=path.split('?') + if len(p)==1: + env['PATH_INFO'] = p[0] + elif len(p)==2: + env['PATH_INFO'], env['QUERY_STRING'] = p + else: + raise ValueError("Too many ?s in path", path) + + env['PATH_INFO'] = urllib.unquote(env['PATH_INFO']) + + if environment is not None: + env.update(environment) + + if basic: + env['HTTP_AUTHORIZATION']="Basic %s" % base64.encodestring(basic) + + + pub = publication(self.db) + + if request is not None: + request = request(stdin, env) + else: + request = TestRequest(stdin, env) + setDefaultSkin(request) + request.setPublication(pub) + if form: + # This requires that request class has an attribute 'form' + # (BrowserRequest has, TestRequest hasn't) + request.form.update(form) + + return request + + def publish(self, path='/', stdin='', *args, **kw): + t, c = time.time(), time.clock() + + request = self._request(path, stdin, *args, **kw) + + # agroszer: 2008.feb.1.: if a retry occurs in the publisher, + # the response will be LOST, so we must accept the returned request + request = _publish(request) + getStatus = getattr(request.response, 'getStatus', lambda: None) + + headers = request.response.getHeaders() + headers.sort() + print 'Status %s\r\n%s\r\n\r\n%s' % ( + request.response.getStatusString(), + '\r\n'.join([("%s: %s" % h) for h in headers]), + request.response.consumeBody(), + ) + return time.time()-t, time.clock()-c, getStatus() + + def run(self, *args, **kw): + t, c = time.time(), time.clock() + request = self._request(*args, **kw) + # agroszer: 2008.feb.1.: if a retry occurs in the publisher, + # the response will be LOST, so we must accept the returned request + request = _publish(request, handle_errors=False) + getStatus = getattr(request.response, 'getStatus', lambda: None) + + return time.time()-t, time.clock()-c, getStatus() + + def debug(self, *args, **kw): + + import pdb + + class Pdb(pdb.Pdb): + def do_pub(self,arg): + if hasattr(self,'done_pub'): + print 'pub already done.' + else: + self.do_s('') + self.do_s('') + self.do_c('') + self.done_pub=1 + def do_ob(self,arg): + if hasattr(self,'done_ob'): + print 'ob already done.' + else: + self.do_pub('') + self.do_c('') + self.done_ob=1 + + db=Pdb() + + request = self._request(*args, **kw) + fbreak(db, _publish) + fbreak(db, debug_call) + + print '* Type c to jump to published object call.' + db.runcall(_publish, request) + + +def fbreak(db, meth): + try: + meth = meth.im_func + except AttributeError: + pass + code = meth.func_code + lineno = getlineno(code) + filename = code.co_filename + db.set_break(filename,lineno) + + + +try: + from codehack import getlineno +except: + def getlineno(code): + return code.co_firstlineno diff -Nru zope3-3.4.0/src/zope/app/debug/__init__.py zope3-3.5~bzr18/src/zope/app/debug/__init__.py --- zope3-3.4.0/src/zope/app/debug/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/debug/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 Application debugger package + +$Id: __init__.py 26725 2004-07-23 21:11:32Z pruggera $ +""" +__docformat__ = 'restructuredtext' + +from zope.app.debug.debug import Debugger diff -Nru zope3-3.4.0/src/zope/app/dependable/configure.zcml zope3-3.5~bzr18/src/zope/app/dependable/configure.zcml --- zope3-3.4.0/src/zope/app/dependable/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/dependable/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,18 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/dependable/dependency.py zope3-3.5~bzr18/src/zope/app/dependable/dependency.py --- zope3-3.4.0/src/zope/app/dependable/dependency.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/dependable/dependency.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## + +"""Subscriber function checking dependencies if a removal is performed +on an object having dependencies. It raises an exception if it's the +case. +""" +__docformat__ = 'restructuredtext' + +from zope.i18nmessageid import Message +from zope.i18nmessageid import MessageFactory +from zope.location.interfaces import ILocationInfo + +from zope.app.dependable.interfaces import IDependable, DependencyError + +_ = MessageFactory('zope') + +exception_msg = _(""" +Removal of object (${object}) which has dependents (${dependents}) +is not possible ! + +You must deactivate this object before trying to remove it. +""") + +def CheckDependency(event): + object = event.object + dependency = IDependable(object, None) + if dependency is not None: + dependents = dependency.dependents() + if dependents: + mapping = { + "object": ILocationInfo(object).getPath(), + "dependents": ", ".join(dependents) + } + raise DependencyError(Message(exception_msg, mapping=mapping)) + diff -Nru zope3-3.4.0/src/zope/app/dependable/__init__.py zope3-3.5~bzr18/src/zope/app/dependable/__init__.py --- zope3-3.4.0/src/zope/app/dependable/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/dependable/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,106 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Dependable Framework. + +$Id: __init__.py 67630 2006-04-27 00:54:03Z jim $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import implements +from zope.traversing.api import getParent, canonicalPath, getPath +from zope.annotation.interfaces import IAnnotations + +from zope.app.dependable.interfaces import IDependable + +class PathSetAnnotation(object): + """Abstract base class for annotations that are sets of paths. + + To make this into a concrete class, a subclass must set the class + attribute `key` to a unique annotation key. A subclass may also + choose to rename the methods. + """ + + def __init__(self, context): + self.context = context + try: + parent = getParent(self.context) + except TypeError: + parent = None + if parent is not None: + try: + pp = getPath(parent) + except TypeError: + pp = "" + if not pp.endswith("/"): + pp += "/" + self.pp = pp # parentpath + else: + self.pp = "" + self.pplen = len(self.pp) + + def addPath(self, path): + path = self._make_relative(path) + annotations = IAnnotations(self.context) + old = annotations.get(self.key, ()) + fixed = map(self._make_relative, old) + if path not in fixed: + fixed.append(path) + new = tuple(fixed) + if new != old: + annotations[self.key] = new + + def removePath(self, path): + path = self._make_relative(path) + annotations = IAnnotations(self.context) + old = annotations.get(self.key, ()) + if old: + fixed = map(self._make_relative, old) + fixed = [loc for loc in fixed if loc != path] + new = tuple(fixed) + if new != old: + if new: + annotations[self.key] = new + else: + del annotations[self.key] + + def getPaths(self): + annotations = IAnnotations(self.context) + locs = annotations.get(self.key, ()) + return tuple(map(self._make_absolute, locs)) + + def _make_relative(self, path): + if path.startswith("/") and self.pp: + path = canonicalPath(path) + if path.startswith(self.pp): + path = path[self.pplen:] + while path.startswith("/"): + path = path[1:] + return path + + def _make_absolute(self, path): + if not path.startswith("/") and self.pp: + path = self.pp + path + return path + + +class Dependable(PathSetAnnotation): + """See `IDependable`.""" + + implements(IDependable) + + key = "zope.app.dependable.Dependents" + + addDependent = PathSetAnnotation.addPath + removeDependent = PathSetAnnotation.removePath + dependents = PathSetAnnotation.getPaths diff -Nru zope3-3.4.0/src/zope/app/dependable/interfaces.py zope3-3.5~bzr18/src/zope/app/dependable/interfaces.py --- zope3-3.4.0/src/zope/app/dependable/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/dependable/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Dependable framework interfaces + +$Id: interfaces.py 67630 2006-04-27 00:54:03Z jim $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface +from zope.exceptions.interfaces import UserError + +class IDependable(Interface): + """Objects that other objects depend on. + + Note that `IDependable` will normally be implemented by an adapter. + """ + + def addDependent(location): + """Add a dependency to a dependent object by location + + The location is the physical path to the dependent object. + """ + def removeDependent(location): + """Remove a dependency with a dependent object by location. + + The location is the physical path to the dependent object. + """ + def dependents(): + """Return a sequence of dependent object locations. + """ + +class DependencyError(UserError): + """ This object is dependable""" diff -Nru zope3-3.4.0/src/zope/app/dependable/tests.py zope3-3.5~bzr18/src/zope/app/dependable/tests.py --- zope3-3.4.0/src/zope/app/dependable/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/dependable/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,103 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Unit tests for Dependable class. + +$Id: tests.py 106578 2009-12-15 21:19:35Z hannosch $ +""" +from unittest import TestCase, TestSuite, main, makeSuite + +from zope.annotation.attribute import AttributeAnnotations +from zope.app.testing.placelesssetup import PlacelessSetup +from zope.interface import implements +from zope.lifecycleevent import ObjectRemovedEvent +from zope.traversing.interfaces import IPhysicallyLocatable + +from zope.app.dependable.dependency import CheckDependency +from zope.app.dependable.interfaces import IDependable, DependencyError + + +class C(object): + pass + + +class DummyObject(object): + + implements(IDependable, IPhysicallyLocatable) + + def dependents(self): + return ['dependency1', 'dependency2'] + + def getPath(self): + return '/dummy-object' + + +class Test(PlacelessSetup, TestCase): + + def factory(self): + from zope.app.dependable import Dependable + return Dependable(AttributeAnnotations(C())) + + def testVerifyInterface(self): + from zope.interface.verify import verifyObject + from zope.app.dependable.interfaces import IDependable + object = self.factory() + verifyObject(IDependable, object) + + def testBasic(self): + dependable = self.factory() + self.failIf(dependable.dependents()) + dependable.addDependent('/a/b') + dependable.addDependent('/c/d') + dependable.addDependent('/c/e') + dependable.addDependent('/c/d') + dependents = list(dependable.dependents()) + dependents.sort() + self.assertEqual(dependents, ['/a/b', '/c/d', '/c/e']) + dependable.removeDependent('/c/d') + dependents = list(dependable.dependents()) + dependents.sort() + self.assertEqual(dependents, ['/a/b', '/c/e']) + dependable.removeDependent('/c/d') + dependents = list(dependable.dependents()) + dependents.sort() + self.assertEqual(dependents, ['/a/b', '/c/e']) + + def testRelativeAbsolute(self): + obj = self.factory() + # Hack the object to have a parent path + obj.pp = "/a/" + obj.pplen = len(obj.pp) + obj.addDependent("foo") + self.assertEqual(obj.dependents(), ("/a/foo",)) + obj.removeDependent("/a/foo") + self.assertEqual(obj.dependents(), ()) + obj.addDependent("/a/bar") + self.assertEqual(obj.dependents(), ("/a/bar",)) + obj.removeDependent("bar") + self.assertEqual(obj.dependents(), ()) + + def testCheckDependency(self): + obj = DummyObject() + parent = object() + event = ObjectRemovedEvent(obj, parent, 'oldName') + self.assertRaises(DependencyError, CheckDependency, event) + + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/exception/browser/configure.zcml zope3-3.5~bzr18/src/zope/app/exception/browser/configure.zcml --- zope3-3.4.0/src/zope/app/exception/browser/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/exception/browser/exception-ftesting.zcml zope3-3.5~bzr18/src/zope/app/exception/browser/exception-ftesting.zcml --- zope3-3.4.0/src/zope/app/exception/browser/exception-ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/exception-ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/app/exception/browser/__init__.py zope3-3.5~bzr18/src/zope/app/exception/browser/__init__.py --- zope3-3.4.0/src/zope/app/exception/browser/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# empty __init__.py file to make this directory into a package diff -Nru zope3-3.4.0/src/zope/app/exception/browser/notfound.pt zope3-3.5~bzr18/src/zope/app/exception/browser/notfound.pt --- zope3-3.4.0/src/zope/app/exception/browser/notfound.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/notfound.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,21 @@ + + + +
+ +

+ The page that you are trying to access is not available +

+
+Please note the following: +
+
    +
  1. You might have misspelled the url
  2. +
  3. + You might be trying to access a non-existing page +
  4. +
+
+ + diff -Nru zope3-3.4.0/src/zope/app/exception/browser/notfound.py zope3-3.5~bzr18/src/zope/app/exception/browser/notfound.py --- zope3-3.4.0/src/zope/app/exception/browser/notfound.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/notfound.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,28 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""NotFound Error View class. + +$Id: notfound.py 70826 2006-10-20 03:41:16Z baijum $ +""" +__docformat__ = 'restructuredtext' + +class NotFound(object): + """`NotFound` Error View + + `NotFound` errors should return 404 instead of 200. + """ + + def __call__(self, *args, **kw): + self.request.response.setStatus(404) + return self.index(*args, **kw) diff -Nru zope3-3.4.0/src/zope/app/exception/browser/systemerror.pt zope3-3.5~bzr18/src/zope/app/exception/browser/systemerror.pt --- zope3-3.4.0/src/zope/app/exception/browser/systemerror.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/systemerror.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ +System Error + + A system error occurred. + + diff -Nru zope3-3.4.0/src/zope/app/exception/browser/systemerror.txt zope3-3.5~bzr18/src/zope/app/exception/browser/systemerror.txt --- zope3-3.4.0/src/zope/app/exception/browser/systemerror.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/systemerror.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,74 @@ +System Errors +============= + +System Errors are errors representing a system failure. At the +application level, they are errors that are uncaught by the +application and that a developer hasn't provided a custom error view +for. + +Zope provides a default system error view that prints an obnoxius +terse message and that sets the response status. + +To see an example of this, we'll create a ZPT page with an intentional +error: + + >>> print http(r""" + ... POST /+/zope.app.zptpage.ZPTPage%3D HTTP/1.1 + ... Authorization: Basic mgr:mgrpw + ... Content-Length: 739 + ... Content-Type: multipart/form-data; boundary=---------------------------125598457818223697821067764270 + ... Referer: http://localhost:8081/+/zope.app.zptpage.ZPTPage= + ... + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="field.source" + ... + ... + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="field.expand.used" + ... + ... + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="field.evaluateInlineCode.used" + ... + ... + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="UPDATE_SUBMIT" + ... + ... Add + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="add_input_name" + ... + ... test.html + ... -----------------------------125598457818223697821067764270-- + ... """) + HTTP/1.1 303 See Other + ... + Location: http://localhost/@@contents.html + ... + +When we visit it, we get a terse error and a 500 status: + +We get a system error, because the problem is in the template, not in +the URL: + + >>> print http(r""" + ... GET /test.html HTTP/1.1 + ... """) + HTTP/1.1 500 Internal Server Error + ... + A system error occurred. + ... + +Another way of getting a system error is the occurrence of a system +error, such as ``ComponentLookupError``. I have registered a simple +view in ``exception-ftesting.zcml`` that will raise a component lookup +error. So if we call ``componentlookuperror.html``, we should get the +error message: + + >>> print http(r""" + ... GET /componentlookuperror.html HTTP/1.1 + ... """) + HTTP/1.1 500 Internal Server Error + ... + A system error occurred. + ... diff -Nru zope3-3.4.0/src/zope/app/exception/browser/tests/__init__.py zope3-3.5~bzr18/src/zope/app/exception/browser/tests/__init__.py --- zope3-3.4.0/src/zope/app/exception/browser/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# empty __init__.py file to make this directory into a package diff -Nru zope3-3.4.0/src/zope/app/exception/browser/tests/test_error.py zope3-3.5~bzr18/src/zope/app/exception/browser/tests/test_error.py --- zope3-3.4.0/src/zope/app/exception/browser/tests/test_error.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/tests/test_error.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,50 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Functional tests for NotFoundError + +$Id: test_error.py 73711 2007-03-27 10:50:23Z dobe $ +""" +import unittest +from zope.app.testing import functional +from zope.component.interfaces import ComponentLookupError +from zope.app.exception.testing import AppExceptionLayer + +class RaiseComponentLookupError(object): + + def __call__(self): + raise ComponentLookupError() + + +class TestComponentLookupError(functional.BrowserTestCase): + + def testComponentLookupError(self): + response = self.publish('/foobar', basic='mgr:mgrpw', + handle_errors=True) + self.assertEqual(response.getStatus(), 404) + body = response.getBody() + self.assert_( + 'The page that you are trying to access is not available' in body) + + +def test_suite(): + TestComponentLookupError.layer = AppExceptionLayer + systemerror = functional.FunctionalDocFileSuite('../systemerror.txt') + systemerror.layer = AppExceptionLayer + return unittest.TestSuite(( + unittest.makeSuite(TestComponentLookupError), + systemerror, + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/exception/browser/tests/test_unauthorized.py zope3-3.5~bzr18/src/zope/app/exception/browser/tests/test_unauthorized.py --- zope3-3.4.0/src/zope/app/exception/browser/tests/test_unauthorized.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/tests/test_unauthorized.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,127 @@ +############################################################################## +# +# Copyright (c) 2001-2009 Zope Corporation 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 Unauthorized Exception Views + +$Id: test_unauthorized.py 107631 2010-01-04 15:17:15Z janwijbrand $ +""" +from unittest import TestCase, main, makeSuite +from zope import component, interface +import zope.browserpage.namedtemplate +from zope.publisher.browser import TestRequest +from zope.authentication.interfaces import IAuthentication +from zope.security.interfaces import IPrincipal +from zope.app.testing import ztapi +from zope.app.exception.browser.unauthorized import Unauthorized +from zope.app.testing.placelesssetup import PlacelessSetup + +class DummyPrincipal(object): + interface.implements(IPrincipal) # this is a lie + + def __init__(self, id): + self.id = id + + def getId(self): + return self.id + +class DummyAuthUtility(object): + interface.implements(IAuthentication) # this is a lie + + status = None + + def unauthorized(self, principal_id, request): + self.principal_id = principal_id + self.request = request + if self.status is not None: + self.request.response.setStatus(self.status) + +class DummyTemplate(object): + + def __init__(self, context): + self.context = context + + component.adapts(Unauthorized) + interface.implements(zope.browserpage.namedtemplate.INamedTemplate) + + def __call__(self): + return 'You are not authorized' + +class Test(PlacelessSetup, TestCase): + + def setUp(self): + super(Test, self).setUp() + self.auth = DummyAuthUtility() + ztapi.provideUtility(IAuthentication, self.auth) + + def tearDown(self): + super(Test, self).tearDown() + + def testUnauthorized(self): + component.provideAdapter(DummyTemplate, name="default") + exception = Exception() + try: + raise exception + except: + pass + request = TestRequest() + request.setPrincipal(DummyPrincipal(23)) + u = Unauthorized(exception, request) + res = u() + + # Make sure that we rendered the expected template + self.assertEqual("You are not authorized", res) + + # Make sure the response status was set + self.assertEqual(request.response.getStatus(), 403) + + # check headers that work around squid "negative_ttl" + self.assertEqual(request.response.getHeader('Expires'), + 'Mon, 26 Jul 1997 05:00:00 GMT') + self.assertEqual(request.response.getHeader('Pragma'), + 'no-cache') + self.assertEqual(request.response.getHeader('Cache-Control'), + 'no-store, no-cache, must-revalidate') + + # Make sure the auth utility was called + self.failUnless(self.auth.request is request) + self.assertEqual(self.auth.principal_id, 23) + + def testRedirect(self): + exception= Exception() + try: + raise exception + except: + pass + request = TestRequest() + request.setPrincipal(DummyPrincipal(23)) + u = Unauthorized(exception, request) + + self.auth.status = 303 + + res = u() + + # Make sure that the template was not rendered + self.assert_(res is None) + + # Make sure the auth's redirect is honored + self.assertEqual(request.response.getStatus(), 303) + + # Make sure the auth utility was called + self.failUnless(self.auth.request is request) + self.assertEqual(self.auth.principal_id, 23) + +def test_suite(): + return makeSuite(Test) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/exception/browser/unauthorized.pt zope3-3.5~bzr18/src/zope/app/exception/browser/unauthorized.pt --- zope3-3.4.0/src/zope/app/exception/browser/unauthorized.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/unauthorized.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,13 @@ + + + +
+ +

Unauthorized

+ +

You are not authorized

+ +
+ + diff -Nru zope3-3.4.0/src/zope/app/exception/browser/unauthorized.py zope3-3.5~bzr18/src/zope/app/exception/browser/unauthorized.py --- zope3-3.4.0/src/zope/app/exception/browser/unauthorized.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/unauthorized.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Unauthorized Exception View Class + +$Id: unauthorized.py 107631 2010-01-04 15:17:15Z janwijbrand $ +""" +__docformat__ = 'restructuredtext' + +from zope.authentication.interfaces import IAuthentication +from zope.publisher.browser import BrowserPage +from zope.browserpage import namedtemplate +from zope.component import getUtility +from zope.browserpage import ViewPageTemplateFile + +class Unauthorized(BrowserPage): + + def __call__(self): + # Set the error status to 403 (Forbidden) in the case when we don't + # challenge the user + self.request.response.setStatus(403) + + # make sure that squid does not keep the response in the cache + self.request.response.setHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT') + self.request.response.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate') + self.request.response.setHeader('Pragma', 'no-cache') + + principal = self.request.principal + auth = getUtility(IAuthentication) + auth.unauthorized(principal.id, self.request) + if self.request.response.getStatus() not in (302, 303): + return self.template() + + template = namedtemplate.NamedTemplate('default') + +default_template = namedtemplate.NamedTemplateImplementation( + ViewPageTemplateFile('unauthorized.pt'), Unauthorized) diff -Nru zope3-3.4.0/src/zope/app/exception/browser/user.pt zope3-3.5~bzr18/src/zope/app/exception/browser/user.pt --- zope3-3.4.0/src/zope/app/exception/browser/user.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/user.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,13 @@ + + +
+ +
    +
  • Error message
  • +
+ +
+ + diff -Nru zope3-3.4.0/src/zope/app/exception/browser/user.py zope3-3.5~bzr18/src/zope/app/exception/browser/user.py --- zope3-3.4.0/src/zope/app/exception/browser/user.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/browser/user.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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 user views + +$Id: user.py 26738 2004-07-23 22:12:52Z pruggera $ +""" +__docformat__ = 'restructuredtext' + +class UserErrorView(object): + + def title(self): + return self.context.__class__.__name__ + + diff -Nru zope3-3.4.0/src/zope/app/exception/ftesting.zcml zope3-3.5~bzr18/src/zope/app/exception/ftesting.zcml --- zope3-3.4.0/src/zope/app/exception/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/exception/__init__.py zope3-3.5~bzr18/src/zope/app/exception/__init__.py --- zope3-3.4.0/src/zope/app/exception/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Import this! diff -Nru zope3-3.4.0/src/zope/app/exception/interfaces.py zope3-3.5~bzr18/src/zope/app/exception/interfaces.py --- zope3-3.4.0/src/zope/app/exception/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,19 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""General exceptions + +$Id: interfaces.py 100078 2009-05-18 17:08:59Z icemac $ +""" +__docformat__ = 'restructuredtext' + +# BBB +from zope.browser.interfaces import ISystemErrorView diff -Nru zope3-3.4.0/src/zope/app/exception/systemerror.py zope3-3.5~bzr18/src/zope/app/exception/systemerror.py --- zope3-3.4.0/src/zope/app/exception/systemerror.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/systemerror.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""System error indicator + +$Id: systemerror.py 100078 2009-05-18 17:08:59Z icemac $ +""" + +import zope.interface +import zope.browser.interfaces + +class SystemErrorView: + zope.interface.implements(zope.browser.interfaces.ISystemErrorView) + + def isSystemError(self): + return True + diff -Nru zope3-3.4.0/src/zope/app/exception/testing.py zope3-3.5~bzr18/src/zope/app/exception/testing.py --- zope3-3.4.0/src/zope/app/exception/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/exception/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,26 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation 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.app.exception common test related classes/functions/objects. + +$Id: testing.py 72477 2007-02-09 03:25:38Z baijum $ +""" + +__docformat__ = "reStructuredText" + +import os +from zope.app.testing.functional import ZCMLLayer + +AppExceptionLayer = ZCMLLayer( + os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'), + __name__, 'AppExceptionLayer', allow_teardown=True) diff -Nru zope3-3.4.0/src/zope/app/form/browser/add.pt zope3-3.5~bzr18/src/zope/app/form/browser/add.pt --- zope3-3.4.0/src/zope/app/form/browser/add.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/add.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,75 @@ + + +
+ +
+ +
+ +
+ +

Edit something

+ +

+ +

+ There are 6 input errors. +

+ +
+
+ +
+
Extra top
+
+
+ +
+ +
+ +
+
Extra bottom
+
+
+ +
+ +
+

+
+

+ + + +   Object Name   + + +
+
+ +
+
+ +
+ +
+ +
+ + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/add.py zope3-3.5~bzr18/src/zope/app/form/browser/add.py --- zope3-3.4.0/src/zope/app/form/browser/add.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/add.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,184 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Add Form View class + +$Id: add.py 107371 2009-12-30 18:36:02Z faassen $ +""" +__docformat__ = 'restructuredtext' + +import sys + +import zope.component +from zope.component.interfaces import IFactory +from zope.event import notify +from zope.interface import Interface +from zope.schema.interfaces import ValidationError +from zope.security.checker import defineChecker, NamesChecker +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent +from zope.lifecycleevent import Attributes + +from zope.app.form.utility import setUpWidgets, getWidgetsData +from zope.formlib.interfaces import IInputWidget, WidgetsError +from zope.app.form.browser.i18n import _ +from zope.browserpage.simpleviewclass import SimpleViewClass +from zope.browserpage import ViewPageTemplateFile +from editview import EditView +from submit import Update + +class AddView(EditView): + """Simple edit-view base class. + + Subclasses should provide a schema attribute defining the schema + to be edited. + """ + + def _setUpWidgets(self): + setUpWidgets(self, self.schema, IInputWidget, names=self.fieldNames) + + def update(self): + + if self.update_status is not None: + # We've been called before. Just return the previous result. + return self.update_status + + if Update in self.request: + + self.update_status = '' + try: + data = getWidgetsData(self, self.schema, names=self.fieldNames) + self.createAndAdd(data) + except WidgetsError, errors: + self.errors = errors + self.update_status = _("An error occurred.") + return self.update_status + + self.request.response.redirect(self.nextURL()) + + return self.update_status + + def create(self, *args, **kw): + """Do the actual instantiation.""" + return self._factory(*args, **kw) + + def createAndAdd(self, data): + """Add the desired object using the data in the data argument. + + The data argument is a dictionary with the data entered in the form. + """ + + args = [] + if self._arguments: + for name in self._arguments: + args.append(data[name]) + + kw = {} + if self._keyword_arguments: + for name in self._keyword_arguments: + if name in data: + kw[str(name)] = data[name] + + content = self.create(*args, **kw) + + errors = [] + + if self._set_before_add: + adapted = self.schema(content) + for name in self._set_before_add: + if name in data: + field = self.schema[name] + try: + field.set(adapted, data[name]) + except ValidationError: + errors.append(sys.exc_info()[1]) + + if errors: + raise WidgetsError(*errors) + + notify(ObjectCreatedEvent(content)) + + content = self.add(content) + + if self._set_after_add: + adapted = self.schema(content) + for name in self._set_after_add: + if name in data: + if data[name] is not None: + field = self.schema[name] + try: + field.set(adapted, data[name]) + except ValidationError: + errors.append(sys.exc_info()[1]) + # We have modified the object, so we need to publish an + # object-modified event: + description = Attributes(self.schema, *self._set_after_add) + notify(ObjectModifiedEvent(content, description)) + + if errors: + raise WidgetsError(*errors) + + return content + + def add(self, content): + return self.context.add(content) + + def nextURL(self): + return self.context.nextURL() + +# helper for factory resp. content_factory handling +def _getFactory(self): + # get factory or factory id + factory = self.__dict__.get('_factory_or_id', self._factory_or_id) + + if type(factory) is str: # factory id + return zope.component.getUtility(IFactory, factory, self.context) + else: + return factory + +def _setFactory(self, value): + self.__dict__['_factory_or_id'] = value + + +def AddViewFactory(name, schema, label, permission, layer, + template, default_template, bases, for_, + fields, content_factory, arguments, + keyword_arguments, set_before_add, set_after_add): + + class_ = SimpleViewClass( + template, used_for=schema, bases=bases, name=name) + + class_.schema = schema + class_.label = label + class_.fieldNames = fields + class_._factory_or_id = content_factory + class_._factory = property(_getFactory, _setFactory) + class_._arguments = arguments + class_._keyword_arguments = keyword_arguments + class_._set_before_add = set_before_add + class_._set_after_add = set_after_add + + class_.generated_form = ViewPageTemplateFile(default_template) + + defineChecker(class_, + NamesChecker( + ("__call__", "__getitem__", + "browserDefault", "publishTraverse"), + permission, + ) + ) + if layer is None: + layer = IDefaultBrowserLayer + + s = zope.component.getGlobalSiteManager() + s.registerAdapter(class_, (for_, layer), Interface, name) diff -Nru zope3-3.4.0/src/zope/app/form/browser/boolwidgets.py zope3-3.5~bzr18/src/zope/app/form/browser/boolwidgets.py --- zope3-3.4.0/src/zope/app/form/browser/boolwidgets.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/boolwidgets.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Browser widgets for items + +$Id: boolwidgets.py 107385 2009-12-30 20:25:24Z faassen $ +""" +# BBB implementation moved to zope.formlib.boolwidgets +from zope.formlib.boolwidgets import ( + CheckBoxWidget, + BooleanRadioWidget, + BooleanSelectWidget, + BooleanDropdownWidget, + BooleanDisplayWidget) + diff -Nru zope3-3.4.0/src/zope/app/form/browser/configure.zcml zope3-3.5~bzr18/src/zope/app/form/browser/configure.zcml --- zope3-3.4.0/src/zope/app/form/browser/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/display.pt zope3-3.5~bzr18/src/zope/app/form/browser/display.pt --- zope3-3.4.0/src/zope/app/form/browser/display.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/display.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,37 @@ + + +
+
+ +
+ +

Display something

+ +
+
+ +
+
Extra top
+
+
+ +
+ +
+
Extra bottom
+
+
+ +
+ +
+
+ + diff -Nru zope3-3.4.0/src/zope/app/form/browser/edit.pt zope3-3.5~bzr18/src/zope/app/form/browser/edit.pt --- zope3-3.4.0/src/zope/app/form/browser/edit.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/edit.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,71 @@ + + + +
+ +
+ +
+ +
+ +

Edit something

+ +

+ +

+ There are 6 input errors. +

+ +
+
+ +
+
Extra top
+
+
+ +
+ +
+ +
+
Extra bottom
+
+
+
+
+ +
+
+ + +
+
+
+
+ +
+ + + +
+ +
+ + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/editview.py zope3-3.5~bzr18/src/zope/app/form/browser/editview.py --- zope3-3.4.0/src/zope/app/form/browser/editview.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/editview.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,147 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Edit View Classes + +$Id: editview.py 107371 2009-12-30 18:36:02Z faassen $ +""" +__docformat__ = 'restructuredtext' + +from datetime import datetime +import transaction + +import zope.component +from zope.interface import Interface +from zope.schema import getFieldNamesInOrder +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.publisher.browser import BrowserView +from zope.security.checker import defineChecker, NamesChecker +from zope.event import notify +from zope.lifecycleevent import ObjectModifiedEvent +from zope.lifecycleevent import Attributes + +from zope.browserpage import ViewPageTemplateFile +from zope.browserpage.simpleviewclass import SimpleViewClass +from zope.formlib.interfaces import WidgetsError +from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges +from zope.app.form.browser.i18n import _ +from zope.app.form.browser.submit import Update + +class EditView(BrowserView): + """Simple edit-view base class + + Subclasses should provide a `schema` attribute defining the schema + to be edited. + + The automatically generated widgets are available by name through + the attributes `*_widget`. + (E.g. ``view.title_widget for the title widget``) + """ + + errors = () + update_status = None + label = '' + + # Fall-back field names computes from schema + fieldNames = property(lambda self: getFieldNamesInOrder(self.schema)) + # Fall-back template + generated_form = ViewPageTemplateFile('edit.pt') + + def __init__(self, context, request): + super(EditView, self).__init__(context, request) + self._setUpWidgets() + + def _setUpWidgets(self): + self.adapted = self.schema(self.context) + setUpEditWidgets(self, self.schema, source=self.adapted, + names=self.fieldNames) + + def setPrefix(self, prefix): + for widget in self.widgets(): + widget.setPrefix(prefix) + + def widgets(self): + return [getattr(self, name+'_widget') + for name in self.fieldNames] + + def changed(self): + # This method is overridden to execute logic *after* changes + # have been made. + pass + + def update(self): + if self.update_status is not None: + # We've been called before. Just return the status we previously + # computed. + return self.update_status + + status = '' + + content = self.adapted + + if Update in self.request: + changed = False + try: + changed = applyWidgetsChanges(self, self.schema, + target=content, names=self.fieldNames) + # We should not generate events when an adapter is used. + # That's the adapter's job. + if changed and self.context is self.adapted: + description = Attributes(self.schema, *self.fieldNames) + notify(ObjectModifiedEvent(content, description)) + except WidgetsError, errors: + self.errors = errors + status = _("An error occurred.") + transaction.doom() + else: + setUpEditWidgets(self, self.schema, source=self.adapted, + ignoreStickyValues=True, + names=self.fieldNames) + if changed: + self.changed() + formatter = self.request.locale.dates.getFormatter( + 'dateTime', 'medium') + status = _("Updated on ${date_time}", + mapping={'date_time': + formatter.format(datetime.utcnow())}) + + self.update_status = status + return status + + +def EditViewFactory(name, schema, label, permission, layer, + template, default_template, bases, for_, fields, + fulledit_path=None, fulledit_label=None): + + class_ = SimpleViewClass(template, used_for=schema, bases=bases, name=name) + class_.schema = schema + class_.label = label + class_.fieldNames = fields + + class_.fulledit_path = fulledit_path + if fulledit_path and (fulledit_label is None): + fulledit_label = "Full edit" + + class_.fulledit_label = fulledit_label + + class_.generated_form = ViewPageTemplateFile(default_template) + + defineChecker(class_, + NamesChecker(("__call__", "__getitem__", + "browserDefault", "publishTraverse"), + permission)) + if layer is None: + layer = IDefaultBrowserLayer + + s = zope.component.getGlobalSiteManager() + s.registerAdapter(class_, (for_, layer), Interface, name) diff -Nru zope3-3.4.0/src/zope/app/form/browser/exception.py zope3-3.5~bzr18/src/zope/app/form/browser/exception.py --- zope3-3.4.0/src/zope/app/form/browser/exception.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/exception.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,19 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Form-related exception views + +$Id: exception.py 107385 2009-12-30 20:25:24Z faassen $ +""" +# BBB implementation moved to zope.formlib.exception +from zope.formlib.exception import WidgetInputErrorView diff -Nru zope3-3.4.0/src/zope/app/form/browser/form.txt zope3-3.5~bzr18/src/zope/app/form/browser/form.txt --- zope3-3.4.0/src/zope/app/form/browser/form.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/form.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,203 @@ +============= +Generic Forms +============= + +The `browser:form` allows the developer to use the form and widget framework +without relying on a particular context object. Instead, it is up to the +developer to implement two simple methods that handle the retrieval and +mutation of the data in form of a dictionary. + +But I am getting ahead of myself. We first need to define a schema that we +would like to use for our form: + + >>> import zope.interface + >>> import zope.schema + >>> class IName(zope.interface.Interface): + ... """The name of a person.""" + ... + ... first = zope.schema.TextLine( + ... title=u"First Name", + ... required=False) + ... + ... last = zope.schema.TextLine( + ... title=u"Last Name", + ... required=True) + +Now we are almost ready to create the form view. But first we have to create a +class that knows how to get and set values for the fields described by the +schema. The system calls for a class (that will be used as a mixin) that +implements a method called `getData()` that returns a dictionary mapping field +names to values and a second method called `setData(data)` that takes a data +dictionary as argument and handles the data in any way fit. In our case, let's +store the data in a global attribute called name: + + >>> name = ['John', 'Doe'] + + >>> class DataHandler(object): + ... + ... def getData(self): + ... global name + ... return {'first': name[0], 'last': name[1]} + ... + ... def setData(self, data): + ... global name + ... name[0] = data['first'] + ... name[1] = data['last'] + ... return u"Saved changes." + + +We now imitate the form-directive's behavior and create the view class: + + >>> from zope.app.form.browser.formview import FormView + >>> View = type('View', (DataHandler, FormView), {'schema': IName}) + +To initialize the view, you still need a context and a request. The context is +useful because it gives you a location, so that you can look up local +components to store the data, such as the principal annotations. In our case +we do not care about the context, so it will be just `None`: + + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + >>> view = View(None, request) + +We can now look at the widgets and see that the data was correctly loaded: + + >>> view.first_widget() + u'' + + >>> view.last_widget() + u'' + +Now when a request is sent in, the data is transmitted in the form + + >>> request.form['field.first'] = u'Stephan' + >>> request.form['field.last'] = u'Richter' + >>> request.form['UPDATE_SUBMIT'] = u'' + +and as soon as the `update()` method is called + + >>> view.update() + u'Saved changes.' + +the global name variable is changed: + + >>> name + [u'Stephan', u'Richter'] + +And that's pretty much all that there is to it. The rest behaves exactly like +an edit form, from which the form view was derived. + +Of course you can also completely overwrite the `update()` method like for the +edit view removing the requirement to implement the `getData()` and +`setData()` methods. + + +Using the `browser:form` directive +================================== + +Let's now see how the form directive works. The first task is to load the +necessary meta-configuration: + + >>> from zope.configuration import xmlconfig + + >>> import zope.component + >>> context = xmlconfig.file('meta.zcml', zope.component) + + >>> import zope.app.form.browser + >>> context = xmlconfig.file('meta.zcml', zope.app.form.browser, context) + +Before we run the directive, make sure that no view exists: + + >>> class IAnything(zope.interface.Interface): + ... pass + + >>> class Anything(object): + ... zope.interface.implements(IAnything) + + >>> from zope.publisher.browser import TestRequest + + >>> from zope.component import queryMultiAdapter + >>> queryMultiAdapter((Anything(), TestRequest()), name='name.html') + +Now that we know that the view did not exist before the registration, let's +execute the form directive: + + >>> import sys + >>> sys.modules['form'] = type( + ... 'Module', (), + ... {'DataHandler': DataHandler, + ... 'IAnything': IAnything, + ... 'IName': IName})() + + >>> context = xmlconfig.string(''' + ... + ... + ... + ... + ... + ... + ... + ... ''', context) + +and try to look up the view again: + + >>> queryMultiAdapter( + ... (Anything(), TestRequest()), name='name.html') #doctest:+ELLIPSIS + + + +Now, if I do not specify my own template, and my class does not overwrite the +`update()` method, then the class *must* implement `getData()` and +`setData(data)`, otherwise a configuration error is raised: + + >>> class NewDataHandler(object): + ... + ... def getData(self): + ... return {} + + >>> sys.modules['form'].NewDataHandler = NewDataHandler + + >>> context = xmlconfig.string(''' + ... + ... + ... + ... + ... + ... ''', context) + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 6.6 + ConfigurationError: You must specify a class that implements + `getData()` and `setData()`, if you do not + overwrite `update()`. + +Now we need to clean up afterwards. + + >>> del sys.modules['form'] diff -Nru zope3-3.4.0/src/zope/app/form/browser/formview.py zope3-3.5~bzr18/src/zope/app/form/browser/formview.py --- zope3-3.4.0/src/zope/app/form/browser/formview.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/formview.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,88 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Form View Classes + +$Id: formview.py 107371 2009-12-30 18:36:02Z faassen $ +""" +__docformat__ = 'restructuredtext' + +import transaction + +from zope.formlib.interfaces import WidgetsError, IInputWidget + +from zope.app.form.utility import setUpWidgets, applyWidgetsChanges +from zope.app.form.browser.editview import EditView +from zope.app.form.browser.i18n import _ +from zope.app.form.browser.submit import Update + + +class Data(dict): + """Dictionary wrapper to make keys available as attributes.""" + + def __getattr__(self, name): + return self[name] + + def __setattr__(self, name, value): + self[name] = value + + +class FormView(EditView): + + def getData(self): + """Get the data for the form. + + This method should return a dictionary mapping field names to values. + """ + NotImplemented, 'Must be implemented by a specific form class' + + def setData(self, data): + """Set the data gotten from a form. + + The data will be a dictionary of fieldnames to values. + + May return a status message. + """ + NotImplemented, 'Must be implemented by a specific form class' + + def _setUpWidgets(self): + self.data = Data(self.getData()) + setUpWidgets( + self, self.schema, IInputWidget, initial=self.data, + names=self.fieldNames) + + def update(self): + if self.update_status is not None: + # We've been called before. Just return the status we previously + # computed. + return self.update_status + + status = '' + + if Update in self.request: + try: + changed = applyWidgetsChanges( + self, self.schema, target=self.data, names=self.fieldNames) + except WidgetsError, errors: + self.errors = errors + status = _("An error occurred.") + transaction.doom() + else: + if changed: + status = self.setData(self.data) + setUpWidgets( + self, self.schema, IInputWidget, initial=self.data, + ignoreStickyValues=True, names=self.fieldNames) + + self.update_status = status + return status diff -Nru zope3-3.4.0/src/zope/app/form/browser/i18n.py zope3-3.5~bzr18/src/zope/app/form/browser/i18n.py --- zope3-3.4.0/src/zope/app/form/browser/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/i18n.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ +"""\ +I18N support for zope.app.form.browser. + +""" +# BBB implementation moved to zope.formlib.i18n +from zope.formlib.i18n import _ diff -Nru zope3-3.4.0/src/zope/app/form/browser/i18n.txt zope3-3.5~bzr18/src/zope/app/form/browser/i18n.txt --- zope3-3.4.0/src/zope/app/form/browser/i18n.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/i18n.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,139 @@ +==================== +Internationalization +==================== + +Forms are fully internationalized. The field names, descriptions, +labels, and hints are all automatically translated if they are made +i18n messages in the schema. + +Let's take this simple add form... + + >>> print http(r""" + ... GET /addfieldcontent.html HTTP/1.1 + ... """, handle_errors=False) + HTTP/1.1 200 OK + ... + +with an error... + + >>> print http(r""" + ... POST /addfieldcontent.html HTTP/1.1 + ... Content-Length: 670 + ... Content-Type: multipart/form-data; boundary=---------------------------19588947601368617292863650127 + ... + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="field.title" + ... + ... + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="field.description" + ... + ... + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="field.somenumber" + ... + ... 0 + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="UPDATE_SUBMIT" + ... + ... Hinzufxgen + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="add_input_name" + ... + ... + ... -----------------------------19588947601368617292863650127-- + ... """, handle_errors=False) + HTTP/1.1 200 OK + ... + There are 1 input errors. + ... + + +Translated +========== + +And now the add form in German: + + >>> print http(r""" + ... GET /addfieldcontent.html HTTP/1.1 + ... Accept-Language: de + ... """, handle_errors=False) + HTTP/1.1 200 OK + ...Felderinhalt hinzuf... + ...Eine kurz...Titel... + ...Eine ausf...Beschreibung... + ...Irgendeine Zahl... + ...Irgendeine Liste... + +The same with an input error: + + >>> print http(r""" + ... POST /addfieldcontent.html HTTP/1.1 + ... Accept-Language: de + ... Content-Length: 670 + ... Content-Type: multipart/form-data; boundary=---------------------------19588947601368617292863650127 + ... + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="field.title" + ... + ... + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="field.description" + ... + ... + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="field.somenumber" + ... + ... 0 + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="UPDATE_SUBMIT" + ... + ... Hinzufxgen + ... -----------------------------19588947601368617292863650127 + ... Content-Disposition: form-data; name="add_input_name" + ... + ... + ... -----------------------------19588947601368617292863650127-- + ... """, handle_errors=False) + HTTP/1.1 200 OK + ...Felderinhalt hinzuf... + ...1... + ...Eine kurz...Titel... + ...Eine ausf...Beschreibung... + ...Irgendeine Zahl... + + +Source widgets +============== + +Titles of terms are translated by the source widgets. Let's create a source +for which the terms create message ids: + + >>> import zc.sourcefactory.basic + >>> from zope.i18nmessageid import MessageFactory + >>> _ = MessageFactory('coffee') + >>> class Coffees(zc.sourcefactory.basic.BasicSourceFactory): + ... def getValues(self): + ... return ['arabica', 'robusta', 'liberica', 'excelsa'] + ... def getTitle(self, value): + ... return _(value, default='Translated %s' % value) + + + >>> import zope.schema + >>> from zope.publisher.browser import TestRequest + >>> coffee = zope.schema.Choice( + ... __name__ = 'coffee', + ... title=u"Kinds of coffee beans", + ... source=Coffees()) + >>> request = TestRequest() + >>> import zope.formlib.source + >>> widget = zope.formlib.source.SourceDisplayWidget( + ... coffee, coffee.source, request) + >>> print widget() + Nothing + >>> from zope.formlib.interfaces import IBrowserWidget + >>> IBrowserWidget.providedBy(widget) + True + >>> widget.setRenderedValue('arabica') + >>> print widget() + Translated arabica diff -Nru zope3-3.4.0/src/zope/app/form/browser/__init__.py zope3-3.5~bzr18/src/zope/app/form/browser/__init__.py --- zope3-3.4.0/src/zope/app/form/browser/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,83 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Browser widgets + +$Id: __init__.py 107377 2009-12-30 19:46:00Z faassen $ +""" +__docformat__ = 'restructuredtext' + +# the implementation of widgets has moved to zope.formlib.widgets +# import directly from there instead. + +from zope.formlib.widget import BrowserWidget, DisplayWidget +from zope.formlib.widget import UnicodeDisplayWidget + +from zope.formlib.widgets import TextWidget, BytesWidget +from zope.formlib.widgets import TextAreaWidget, BytesAreaWidget +from zope.formlib.widgets import PasswordWidget, FileWidget +from zope.formlib.widgets import ASCIIWidget, ASCIIAreaWidget +from zope.formlib.widgets import IntWidget, FloatWidget +from zope.formlib.widgets import DecimalWidget +from zope.formlib.widgets import DatetimeWidget, DateWidget +from zope.formlib.widgets import DatetimeI18nWidget +from zope.formlib.widgets import DateI18nWidget +from zope.formlib.widgets import DatetimeDisplayWidget +from zope.formlib.widgets import DateDisplayWidget +from zope.formlib.widgets import BytesDisplayWidget +from zope.formlib.widgets import ASCIIDisplayWidget +from zope.formlib.widgets import URIDisplayWidget + +# Widgets for boolean fields +from zope.formlib.widgets import CheckBoxWidget +from zope.formlib.widgets import BooleanRadioWidget +from zope.formlib.widgets import BooleanSelectWidget +from zope.formlib.widgets import BooleanDropdownWidget + +# Choice and Sequence Display Widgets +from zope.formlib.widgets import ItemDisplayWidget +from zope.formlib.widgets import ItemsMultiDisplayWidget +from zope.formlib.widgets import SetDisplayWidget +from zope.formlib.widgets import ListDisplayWidget + +# Widgets for fields with vocabularies. +# Note that these are only dispatchers for the widgets below. +from zope.formlib.widgets import ChoiceDisplayWidget +from zope.formlib.widgets import ChoiceInputWidget +from zope.formlib.widgets import CollectionDisplayWidget +from zope.formlib.widgets import CollectionInputWidget +from zope.formlib.widgets import ChoiceCollectionDisplayWidget +from zope.formlib.widgets import ChoiceCollectionInputWidget + +# Widgets that let you choose a single item from a list +# These widgets are multi-views on (field, vocabulary) +from zope.formlib.widgets import SelectWidget +from zope.formlib.widgets import DropdownWidget +from zope.formlib.widgets import RadioWidget + +# Widgets that let you choose several items from a list +# These widgets are multi-views on (field, vocabulary) +from zope.formlib.widgets import MultiSelectWidget +from zope.formlib.widgets import MultiSelectSetWidget +from zope.formlib.widgets import MultiSelectFrozenSetWidget +from zope.formlib.widgets import MultiCheckBoxWidget +from zope.formlib.widgets import OrderedMultiSelectWidget + +# Widgets that let you enter several items in a sequence +# These widgets are multi-views on (sequence type, value type) +from zope.formlib.widgets import SequenceWidget +from zope.formlib.widgets import TupleSequenceWidget +from zope.formlib.widgets import ListSequenceWidget +from zope.formlib.widgets import SequenceDisplayWidget + +from zope.formlib.widgets import ObjectWidget diff -Nru zope3-3.4.0/src/zope/app/form/browser/interfaces.py zope3-3.5~bzr18/src/zope/app/form/browser/interfaces.py --- zope3-3.4.0/src/zope/app/form/browser/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,116 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Form and Widget Interfaces + +$Id: interfaces.py 107398 2009-12-30 22:26:34Z faassen $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface +from zope.schema import TextLine, Bool +from zope.formlib.interfaces import IWidget, IInputWidget + +# BBB: ITerms are also used by z3c.form and possibly other form +# frameworks, so it was moved to zope.browser.interfaces and it's +# preferred to import from there now. +from zope.browser.interfaces import ITerms + +from zope.formlib.interfaces import ( + IBrowserWidget, ISimpleInputWidget, ITextBrowserWidget, + IWidgetInputErrorView, ISourceQueryView) + +class IFormCollaborationView(Interface): + """Views that collaborate to create a single form. + + When a form is applied, the changes in the form need to + be applied to individual views, which update objects as + necessary. + """ + + def __call__(): + """Render the view as a part of a larger form. + + Form input elements should be included, prefixed with the + prefix given to setPrefix. + + `form` and `submit` elements should not be included. They + will be provided for the larger form. + """ + + def setPrefix(prefix): + """Set the `prefix` used for names of input elements + + Element names should begin with the given `prefix`, + followed by a dot. + """ + + def update(): + """Update the form with data from the request.""" + + +class IAddFormCustomization(Interface): + """API for add form customization. + + Classes supplied when defining add forms may need to override some + of these methods. + + In particular, when the context of an add form is not an `IAdding`, + a subclass needs to override `nextURL` and one of `add` or + `createAndAdd`. + + To see how all this fits together, here's pseudo code for the + update() method of the form: + + def update(self): + data = # a dict + self.createAndAdd(data) + self.request.response.redirect(self.nextURL()) + + def createAndAdd(self, data): + content = + content = self.add(content) + + """ + + def createAndAdd(data): + """Create a new object from the given data and the resulting object. + + The data argument is a dictionary with values supplied by the form. + + If any user errors occur, they should be collected into a list + and raised as a ``WidgetsError``. + + (For the default implementation, see pseudo-code in class docs.) + """ + + def add(content): + """Add the given content. + + This method is overridden when the context of the add form is + not an `IAdding`. In this case, the class that customizes the + form must take over adding the object. + + The default implementation returns `self.context.add(content)`, + i.e. it delegates to the `IAdding` view. + """ + + def nextURL(): + """Return the URL to be displayed after the add operation. + + This can be relative to the view's context. + + The default implementation returns `self.context.nextURL()`, + i.e. it delegates to the `IAdding` view. + """ + diff -Nru zope3-3.4.0/src/zope/app/form/browser/itemswidgets.py zope3-3.5~bzr18/src/zope/app/form/browser/itemswidgets.py --- zope3-3.4.0/src/zope/app/form/browser/itemswidgets.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/itemswidgets.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Browser widgets for items + +$Id: itemswidgets.py 107385 2009-12-30 20:25:24Z faassen $ +""" +__docformat__ = 'restructuredtext' + +# BBB the implementation has moved to zope.formlib.itemswidgets +from zope.formlib.itemswidgets import ( + ChoiceDisplayWidget, + ChoiceInputWidget, + CollectionDisplayWidget, + CollectionInputWidget, + ChoiceCollectionDisplayWidget, + ChoiceCollectionInputWidget, + TranslationHook, + ItemsWidgetBase, + SingleDataHelper, + MultiDataHelper, + ItemsWidgetBase, + ItemDisplayWidget, + ItemsMultiDisplayWidget, + ListDisplayWidget, + SetDisplayWidget, + ItemsEditWidgetBase, + EXPLICIT_EMPTY_SELECTION, + SelectWidget, + DropdownWidget, + RadioWidget, + ItemsMultiEditWidgetBase, + MultiSelectWidget, + MultiSelectSetWidget, + MultiSelectFrozenSetWidget, + OrderedMultiSelectWidget, + MultiCheckBoxWidget) diff -Nru zope3-3.4.0/src/zope/app/form/browser/macros.py zope3-3.5~bzr18/src/zope/app/form/browser/macros.py --- zope3-3.4.0/src/zope/app/form/browser/macros.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/macros.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,51 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""API Documentation macros + +$Id: macros.py 106723 2009-12-17 23:16:34Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +from zope.component import getMultiAdapter +from zope.interface import implements +from zope.interface.common.mapping import IItemMapping +from zope.publisher.browser import BrowserView + +class FormMacros(BrowserView): + implements(IItemMapping) + + macro_pages = ( + 'view_macros', + 'widget_macros', + 'addform_macros', + ) + aliases = { + 'view': 'page', + 'dialog': 'page', + 'addingdialog': 'page', + } + + def __getitem__(self, key): + key = self.aliases.get(key, key) + context = self.context + request = self.request + for name in self.macro_pages: + page = getMultiAdapter((context, request), name=name) + try: + v = page[key] + except KeyError: + pass + else: + return v + raise KeyError(key) diff -Nru zope3-3.4.0/src/zope/app/form/browser/metaconfigure.py zope3-3.5~bzr18/src/zope/app/form/browser/metaconfigure.py --- zope3-3.4.0/src/zope/app/form/browser/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,322 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 handlers for forms and widgets + +$Id: metaconfigure.py 107376 2009-12-30 19:21:24Z faassen $ +""" +__docformat__ = 'restructuredtext' + +import os + +import zope.component +from zope.security.checker import CheckerPublic +from zope.interface import implementedBy +from zope.configuration.exceptions import ConfigurationError + +from zope.browser.interfaces import IAdding +from zope.schema import getFieldNamesInOrder +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.browsermenu.metaconfigure import menuItemDirective + +from zope.formlib.widget import CustomWidgetFactory +from zope.app.form.browser.i18n import _ +from zope.formlib.interfaces import IInputWidget, IDisplayWidget +from zope.formlib.interfaces import IWidgetFactory +from add import AddView, AddViewFactory +from editview import EditView, EditViewFactory +from formview import FormView +from schemadisplay import DisplayView, DisplayViewFactory + +class BaseFormDirective(object): + + # to be overriden by the subclasses + view = None + default_template = None + + # default basic information + for_ = None + layer = IDefaultBrowserLayer + permission = CheckerPublic + template = None + class_ = None + + # default form information + title = None + label = None + menu = None + fields = None + + def __init__(self, _context, **kwargs): + self._context = _context + for key, value in kwargs.items(): + if not (value is None and hasattr(self, key)): + setattr(self, key, value) + self._normalize() + self._widgets = {} + + def widget(self, _context, field, **kw): + attrs = kw + class_ = attrs.pop("class_", None) + # Try to do better than accepting the string value by looking through + # the interfaces and trying to find the field, so that we can use + # 'fromUnicode()' + if isinstance(class_, type): + ifaces = implementedBy(class_) + for name, value in kw.items(): + for iface in ifaces: + if name in iface: + attrs[name] = iface[name].fromUnicode(value) + break + if class_ is None: + # The _default_widget_factory is required to allow the + # directive to be given without a "class" + # attribute. This can be used to override some of the + # presentational attributes of the widget implementation. + class_ = self._default_widget_factory + + # don't wrap a factory into a factory + if IWidgetFactory.providedBy(class_): + factory = class_ + else: + factory = CustomWidgetFactory(class_, **attrs) + + self._widgets[field+'_widget'] = factory + + def _processWidgets(self): + if self._widgets: + customWidgetsObject = type('CustomWidgetsMixin', (object,), + self._widgets) + self.bases = self.bases + (customWidgetsObject,) + + def _normalize(self): + if self.for_ is None: + self.for_ = self.schema + + if self.class_ is None: + self.bases = (self.view,) + else: + self.bases = (self.class_, self.view) + + if self.template is not None: + self.template = os.path.abspath(str(self.template)) + if not os.path.isfile(self.template): + raise ConfigurationError("No such file", self.template) + else: + self.template = self.default_template + + self.names = getFieldNamesInOrder(self.schema) + + if self.fields: + for name in self.fields: + if name not in self.names: + raise ValueError("Field name is not in schema", + name, self.schema) + else: + self.fields = self.names + + def _args(self): + permission = self.permission + if permission == 'zope.Public': + # Translate public permission to CheckerPublic + permission = CheckerPublic + return (self.name, self.schema, self.label, permission, + self.layer, self.template, self.default_template, + self.bases, self.for_, self.fields) + + def _discriminator(self): + return ('view', self.for_, self.name, IBrowserRequest, + self.layer) + + +class AddFormDirective(BaseFormDirective): + + view = AddView + default_template = 'add.pt' + for_ = IAdding + + # default add form information + description = None + content_factory_id = None + content_factory = None + arguments = None + keyword_arguments = None + set_before_add = None + set_after_add = None + + def _default_widget_factory(self, field, request): + # `field` is a bound field + return zope.component.getMultiAdapter( + (field, request), IInputWidget) + + def _handle_menu(self): + if self.menu or self.title: + if (not self.menu) or (not self.title): + raise ValueError("If either menu or title are specified, " + "they must both be specified") + # Add forms are really for IAdding components, so do not use + # for=self.schema. + menuItemDirective( + self._context, self.menu, self.for_, '@@' + self.name, + self.title, permission=self.permission, layer=self.layer, + description=self.description) + + def _handle_arguments(self, leftover=None): + schema = self.schema + fields = self.fields + arguments = self.arguments + keyword_arguments = self.keyword_arguments + set_before_add = self.set_before_add + set_after_add = self.set_after_add + + if leftover is None: + leftover = fields + + if arguments: + missing = [n for n in arguments if n not in fields] + if missing: + raise ValueError("Some arguments are not included in the form", + missing) + optional = [n for n in arguments if not schema[n].required] + if optional: + raise ValueError("Some arguments are optional, use" + " keyword_arguments for them", + optional) + leftover = [n for n in leftover if n not in arguments] + + if keyword_arguments: + missing = [n for n in keyword_arguments if n not in fields] + if missing: + raise ValueError( + "Some keyword_arguments are not included in the form", + missing) + leftover = [n for n in leftover if n not in keyword_arguments] + + if set_before_add: + missing = [n for n in set_before_add if n not in fields] + if missing: + raise ValueError( + "Some set_before_add are not included in the form", + missing) + leftover = [n for n in leftover if n not in set_before_add] + + if set_after_add: + missing = [n for n in set_after_add if n not in fields] + if missing: + raise ValueError( + "Some set_after_add are not included in the form", + missing) + leftover = [n for n in leftover if n not in set_after_add] + + self.set_after_add += leftover + + else: + self.set_after_add = leftover + + def _handle_content_factory(self): + if self.content_factory is None: + self.content_factory = self.content_factory_id + + def __call__(self): + self._processWidgets() + self._handle_menu() + self._handle_content_factory() + self._handle_arguments() + + self._context.action( + discriminator=self._discriminator(), + callable=AddViewFactory, + args=self._args()+(self.content_factory, self.arguments, + self.keyword_arguments, + self.set_before_add, self.set_after_add), + ) + +class EditFormDirectiveBase(BaseFormDirective): + + view = EditView + + def _default_widget_factory(self, field, request): + # `field` is a bound field + if field.readonly: + iface = IDisplayWidget + else: + iface = IInputWidget + return zope.component.getMultiAdapter( + (field, request), iface) + +class EditFormDirective(EditFormDirectiveBase): + + default_template = 'edit.pt' + title = _('Edit') + + def _handle_menu(self): + if self.menu: + menuItemDirective( + self._context, self.menu, self.for_ or self.schema, + '@@' + self.name, self.title, permission=self.permission, + layer=self.layer) + + def __call__(self): + self._processWidgets() + self._handle_menu() + self._context.action( + discriminator=self._discriminator(), + callable=EditViewFactory, + args=self._args(), + ) + +class FormDirective(EditFormDirective): + + view = FormView + + def __init__(self, _context, **kwargs): + super(FormDirective, self).__init__(_context, **kwargs) + attrs = self.class_.__dict__.keys() + if 'template' not in kwargs.keys() and 'update' not in attrs and \ + ('getData' not in attrs or 'setData' not in attrs): + raise ConfigurationError( + "You must specify a class that implements `getData()` " + "and `setData()`, if you do not overwrite `update()`.") + + +class SubeditFormDirective(EditFormDirectiveBase): + + default_template = 'subedit.pt' + + # default subedit form directive + fulledit_path = None + fulledit_label = None + + def __call__(self): + self._processWidgets() + self._context.action( + discriminator = self._discriminator(), + callable = EditViewFactory, + args = self._args()+(self.fulledit_path, self.fulledit_label), + ) + + +class SchemaDisplayDirective(EditFormDirective): + + view = DisplayView + default_template = 'display.pt' + + def __call__(self): + self._processWidgets() + self._handle_menu() + self._context.action( + discriminator = self._discriminator(), + callable = DisplayViewFactory, + args = self._args()+(self.menu,) + ) diff -Nru zope3-3.4.0/src/zope/app/form/browser/metadirectives.py zope3-3.5~bzr18/src/zope/app/form/browser/metadirectives.py --- zope3-3.4.0/src/zope/app/form/browser/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,298 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Form and Widget specific 'browser' ZCML namespace interfaces + +$Id: metadirectives.py 104906 2009-10-08 06:06:50Z tlotze $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface +from zope.configuration.fields import GlobalObject, GlobalInterface +from zope.configuration.fields import Tokens, Path, Bool, PythonIdentifier +from zope.configuration.fields import MessageID +from zope.schema import Text, TextLine, Id +from zope.security.zcml import Permission +from zope.browsermenu.field import MenuField + +class ICommonInformation(Interface): + """ + Common information for all successive directives + """ + + name = TextLine( + title=u"Name", + description=u"The name of the generated view.", + required=True + ) + + schema = GlobalInterface( + title=u"Schema", + description=u"The schema from which the form is generated.", + required=True + ) + + for_ = GlobalInterface( + title=u"Interface", + description=u""" + The interface this page (view) applies to. + + The view will be for all objects that implement this + interface. The schema is used if the for attribute is not + specified. + + If the for attribute is specified, then the objects views must + implement or be adaptable to the schema.""", + required=False + ) + + permission = Permission( + title=u"Permission", + description=u"The permission needed to use the view.", + required=True + ) + + layer = GlobalInterface( + title=u"Layer", + description=u"The later the view is in. Default: 'default'", + required=False + ) + + template = Path( + title=u"Template", + description=u"An alternate template to use for the form.", + required=False + ) + + class_ = GlobalObject( + title=u"Class", + description=u""" + A class to provide custom widget definitions or methods to be + used by a custom template. + + This class is used as a mix-in class. As a result, it needn't + subclass any special classes, such as BrowserView.""", + required=False + ) + + +class ICommonFormInformation(ICommonInformation): + """ + Common information for browser forms + """ + + label = MessageID( + title=u"Label", + description=u"A label to be used as the heading for the form.", + required=False + ) + + menu = MenuField( + title=u"The browser menu to include the form in.", + description=u""" + Many views are included in menus. It's convenient to name the + menu in the page directive, rather than having to give a + separate menuItem directive.""", + required=False + ) + + title = MessageID( + title=u"Menu title", + description=u"The browser menu label for the form.", + required=False + ) + + fields = Tokens( + title=u"Fields", + description=u""" + Here you can specify the names of the fields you wish to display. + The order in this list is also the order the fields will + be displayed in. If this attribute is not specified, all schema fields + will be displayed in the order specified in the schema itself.""", + required=False, + value_type=PythonIdentifier() + ) + + +class ICommonAddInformation(Interface): + """ + Common information for add forms + """ + + content_factory = GlobalObject( + title=u"Content factory", + description=u""" + An object to call to create new content objects. + + This attribute isn't used if a class is specified that + implements createAndAdd.""", + required=False + ) + + content_factory_id = Id( + title=u"Content factory id", + description=u"A factory id to create new content objects", + required = False, + ) + + arguments = Tokens( + title=u"Arguments", + description=u""" + A list of field names to supply as positional arguments to the + factory.""", + required=False, + value_type=PythonIdentifier() + ) + + keyword_arguments = Tokens( + title=u"Keyword arguments", + description=u""" + A list of field names to supply as keyword arguments to the + factory.""", + required=False, + value_type=PythonIdentifier() + ) + + set_before_add = Tokens( + title=u"Set before add", + description=u""" + A list of fields to be assigned to the newly created object + before it is added.""", + required=False, + value_type=PythonIdentifier(), + ) + + set_after_add = Tokens( + title=u"Set after add", + description=u""" + A list of fields to be assigned to the newly created object + after it is added.""", + required=False, + value_type=PythonIdentifier() + ) + + +class IFormDirective(ICommonFormInformation): + """ + Define an automatically generated form. + + The form directive does nto require the data to be stored in its context, + but leaves the storing procedure to the to a method. + """ + class_ = GlobalObject( + title=u"Class", + description=u""" + A class to provide the `getData()` and `setData()` methods or + completely custom methods to be used by a custom template. + + This class is used as a mix-in class. As a result, it needn't + subclass any special classes, such as BrowserView.""", + required=True + ) + +class IEditFormDirective(ICommonFormInformation): + """ + Define an automatically generated edit form + + The editform directive creates and registers a view for editing + an object based on a schema. + """ + +class ISubeditFormDirective(ICommonInformation): + """ + Define a subedit form + """ + + label = TextLine( + title=u"Label", + description=u"A label to be used as the heading for the form.", + required=False + ) + + fulledit_path = TextLine( + title=u"Path (relative URL) to the full edit form", + required=False + ) + + fulledit_label = MessageID( + title=u"Label of the full edit form", + required=False + ) + +class IAddFormDirective(ICommonFormInformation, ICommonAddInformation): + """ + Define an automatically generated add form + + The addform directive creates and registers a view for adding an + object based on a schema. + + Adding an object is a bit trickier than editing an object, because + the object the schema applies to isn't available when forms are + being rendered. The addform directive provides a customization + interface to overcome this difficulty. + + See zope.app.form.browser.interfaces.IAddFormCustomization. + """ + + description = MessageID( + title=u"A longer description of the add form.", + description=u""" + A UI may display this with the item or display it when the + user requests more assistance.""", + required=False + ) + +class ISchemaDisplayDirective(ICommonFormInformation): + """ + Define an automatically generated display form. + + The schemadisplay directive creates and registers a view for + displaying an object based on a schema. + """ + + title = MessageID( + title=u"The browser menu label for the edit form", + description=u"This attribute defaults to 'Edit'.", + required=False + ) + + +class IWidgetSubdirective(Interface): + """Register custom widgets for a form. + + This directive allows you to quickly generate custom widget directives for + a form. + + Besides the two required arguments, field and class, you can specify any + amount of keyword arguments, e.g. style='background-color:#fefefe;'. + The keywords will be stored as attributes on the widget instance. To see + which keywords are sensible, you should look at the code of the specified + widget class. + """ + + field = TextLine( + title=u"Field Name", + description=u""" + The name of the field/attribute/property for which this widget will be + used.""", + required=True, + ) + + class_ = GlobalObject( + title=u"Widget Class", + description=u"""The class that will create the widget.""", + required=False, + ) + +# Arbitrary keys and values are allowed to be passed to the CustomWidget. +IWidgetSubdirective.setTaggedValue('keyword_arguments', True) diff -Nru zope3-3.4.0/src/zope/app/form/browser/meta.zcml zope3-3.5~bzr18/src/zope/app/form/browser/meta.zcml --- zope3-3.4.0/src/zope/app/form/browser/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/objectwidget.py zope3-3.5~bzr18/src/zope/app/form/browser/objectwidget.py --- zope3-3.4.0/src/zope/app/form/browser/objectwidget.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/objectwidget.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Browser widgets for text-like data + +$Id: objectwidget.py 107385 2009-12-30 20:25:24Z faassen $ +""" +# implementation moved to zope.formlib.objectwidget +# BBB +from zope.formlib.objectwidget import ( + ObjectWidgetView, + ObjectWidget) diff -Nru zope3-3.4.0/src/zope/app/form/browser/schemadisplay.py zope3-3.5~bzr18/src/zope/app/form/browser/schemadisplay.py --- zope3-3.4.0/src/zope/app/form/browser/schemadisplay.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/schemadisplay.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,84 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 display-only pages based on schema. + +$Id: schemadisplay.py 106909 2009-12-22 19:05:21Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +from zope.interface import Interface +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.publisher.browser import BrowserView +from zope.schema import getFieldNamesInOrder +from zope.security.checker import defineChecker, NamesChecker + +from zope.app.form.utility import setUpDisplayWidgets +from zope.browserpage import ViewPageTemplateFile +from zope.browserpage.simpleviewclass import SimpleViewClass + +class DisplayView(BrowserView): + """Simple display-view base class. + + Subclasses should provide a `schema` attribute defining the schema + to be displayed. + """ + + errors = () + update_status = '' + label = '' + + # Fall-back field names computes from schema + fieldNames = property(lambda self: getFieldNamesInOrder(self.schema)) + + def __init__(self, context, request): + super(DisplayView, self).__init__(context, request) + self._setUpWidgets() + + def _setUpWidgets(self): + self.adapted = self.schema(self.context) + setUpDisplayWidgets(self, self.schema, source=self.adapted, + names=self.fieldNames) + + def setPrefix(self, prefix): + for widget in self.widgets(): + widget.setPrefix(prefix) + + def widgets(self): + return [getattr(self, name+'_widget') + for name in self.fieldNames] + + +def DisplayViewFactory(name, schema, label, permission, layer, + template, default_template, bases, for_, fields, + fulledit_path=None, fulledit_label=None): + class_ = SimpleViewClass(template, used_for=schema, bases=bases, + name=name) + class_.schema = schema + class_.label = label + class_.fieldNames = fields + class_.fulledit_path = fulledit_path + if fulledit_path and (fulledit_label is None): + fulledit_label = "Full display" + class_.fulledit_label = fulledit_label + class_.generated_form = ViewPageTemplateFile(default_template) + defineChecker(class_, + NamesChecker(("__call__", "__getitem__", "browserDefault"), + permission)) + + if layer is None: + layer = IDefaultBrowserLayer + + sm = zope.component.getGlobalSiteManager() + sm.registerAdapter(class_, (for_, layer), Interface, name) diff -Nru zope3-3.4.0/src/zope/app/form/browser/sequencewidget.py zope3-3.5~bzr18/src/zope/app/form/browser/sequencewidget.py --- zope3-3.4.0/src/zope/app/form/browser/sequencewidget.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/sequencewidget.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Browser widgets for sequences + +$Id: sequencewidget.py 107385 2009-12-30 20:25:24Z faassen $ +""" +# BBB implementation moved to zope.formlib.sequencewidget +from zope.formlib.sequencewidget import ( + SequenceWidget, + TupleSequenceWidget, + ListSequenceWidget, + SequenceDisplayWidget) diff -Nru zope3-3.4.0/src/zope/app/form/browser/source.py zope3-3.5~bzr18/src/zope/app/form/browser/source.py --- zope3-3.4.0/src/zope/app/form/browser/source.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/source.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,32 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""Source widgets support + +$Id: source.py 107398 2009-12-30 22:26:34Z faassen $ +""" +# BBB +from zope.formlib.source import ( + SourceDisplayWidget, + SourceSequenceDisplayWidget, + SourceInputWidget, + SourceListInputWidget, + IterableSourceVocabulary, + SourceSelectWidget, + SourceDropdownWidget, + SourceRadioWidget, + SourceMultiSelectWidget, + SourceOrderedMultiSelectWidget, + SourceMultiSelectSetWidget, + SourceMultiSelectFrozenSetWidget, + SourceMultiCheckBoxWidget) diff -Nru zope3-3.4.0/src/zope/app/form/browser/subedit.pt zope3-3.5~bzr18/src/zope/app/form/browser/subedit.pt --- zope3-3.4.0/src/zope/app/form/browser/subedit.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/subedit.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,44 @@ +
+
+ + Edit something + +
+

+ Full edit +

+

+ +

+
    +
  • + + Error Type: + Error text +
  • +
+
+ +
+
+ +
+
Extra top
+
+
+ +
+ +
+
Extra bottom
+
+
+ +
diff -Nru zope3-3.4.0/src/zope/app/form/browser/submit.py zope3-3.5~bzr18/src/zope/app/form/browser/submit.py --- zope3-3.4.0/src/zope/app/form/browser/submit.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/submit.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,24 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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 submit button names + +Update -- Name of the standard update submit button + +$Id: submit.py 26748 2004-07-24 05:51:58Z pruggera $ +""" +__docformat__ = 'restructuredtext' + +Next = "NEXT_SUBMIT" +Previous = "PREVIOUS_SUBMIT" +Update = "UPDATE_SUBMIT" diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/i18n.zcml zope3-3.5~bzr18/src/zope/app/form/browser/tests/i18n.zcml --- zope3-3.4.0/src/zope/app/form/browser/tests/i18n.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/i18n.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,18 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/__init__.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/__init__.py --- zope3-3.4.0/src/zope/app/form/browser/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/app/form/browser/tests/locales/de/LC_MESSAGES/formtest.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/app/form/browser/tests/locales/de/LC_MESSAGES/formtest.mo differ diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/locales/de/LC_MESSAGES/formtest.po zope3-3.5~bzr18/src/zope/app/form/browser/tests/locales/de/LC_MESSAGES/formtest.po --- zope3-3.4.0/src/zope/app/form/browser/tests/locales/de/LC_MESSAGES/formtest.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/locales/de/LC_MESSAGES/formtest.po 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,51 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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. +# +############################################################################## +msgid "" +msgstr "" +"Project-Id-Version: Five form tests\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2005-07-29 11:38+0100\n" +"Last-Translator: \n" +"Language-Team: Five Developers \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Title" +msgstr "Titel" + +msgid "A short description of the event." +msgstr "Eine kurze Beschreibung des Ereignisses." + +msgid "Description" +msgstr "Beschreibung" + +msgid "A long description of the event." +msgstr "Eine ausführliche Beschreibung des Ereignisses." + +msgid "Some number" +msgstr "Irgendeine Zahl" + +msgid "Some List" +msgstr "Irgendeine Liste" + +msgid "Some item" +msgstr "Irgendeine Element" + +msgid "Edit Field Content" +msgstr "Felderinhalt bearbeiten" + +msgid "Add Field Content" +msgstr "Felderinhalt hinzufügen" + diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/locales/formtest.pot zope3-3.5~bzr18/src/zope/app/form/browser/tests/locales/formtest.pot --- zope3-3.4.0/src/zope/app/form/browser/tests/locales/formtest.pot 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/locales/formtest.pot 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,50 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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. +# +############################################################################## +msgid "" +msgstr "" +"Project-Id-Version: Five form tests\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: Five Developers \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Title" +msgstr "" + +msgid "A short description of the event." +msgstr "" + +msgid "Description" +msgstr "" + +msgid "A long description of the event." +msgstr "" + +msgid "Some number" +msgstr "" + +msgid "Some List" +msgstr "" + +msgid "Some item" +msgstr "" + +msgid "Edit Field Content" +msgstr "" + +msgid "Add Field Content" +msgstr "" diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/support.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/support.py --- zope3-3.4.0/src/zope/app/form/browser/tests/support.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/support.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,102 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""General test support. + +$Id: support.py 107413 2009-12-31 00:31:07Z faassen $ +""" +import re +from zope.configuration import xmlconfig +from zope.formlib.tests.support import (VerifyResults, + patternExists) + +def registerEditForm(schema, widgets={}): + """Registers an edit form for the specified schema. + + widgets is a mapping of field name to dict. The dict for each field must + contain a 'class' item, which is the widget class, and any additional + widget attributes (e.g. text field size, rows, cols, etc.) + """ + widgetsXml = [] + for field in widgets: + widgetsXml.append('') + xmlconfig.string(""" + + + + %s + + + """ % (schema.__identifier__, ''.join(widgetsXml))) + + +def defineSecurity(class_, schema): + class_ = '%s.%s' % (class_.__module__, class_.__name__) + schema = schema.__identifier__ + xmlconfig.string(""" + + + + + + + """ % (class_, schema, schema)) + + +def defineWidgetView(field_interface, widget_class, view_type): + field_interface = field_interface.__identifier__ + widget_class = '%s.%s' % (widget_class.__module__, widget_class.__name__) + view_type = '%s.%s' % (view_type.__module__, view_type.__name__) + xmlconfig.string(""" + + + + + """ % (field_interface, widget_class, view_type)) + + +def validationErrorExists(field, error_msg, source): + regex = re.compile(r'%s.*?name="field.(\w+)(?:\.[\w\.]+)?"' % (error_msg,), + re.DOTALL) + # compile it first because Python 2.3 doesn't allow flags in findall + return field in regex.findall(source) + + +def missingInputErrorExists(field, source): + return validationErrorExists(field, 'Required input is missing.', source) + + +def invalidValueErrorExists(field, source): + # assumes this error is displayed for select elements + return patternExists( + 'Invalid value.*name="field.%s".*' % field, + source, re.DOTALL) + + +def updatedMsgExists(source): + return patternExists('

Updated .*

', source) diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/test_add.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_add.py --- zope3-3.4.0/src/zope/app/form/browser/tests/test_add.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_add.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,445 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 add-form + +$Id: test_add.py 107376 2009-12-30 19:21:24Z faassen $ +""" +import unittest + +from zope.browser.interfaces import IAdding +from zope.component import getMultiAdapter +from zope.component.eventtesting import getEvents +from zope.component.interfaces import IFactory +from zope.component.interfaces import IComponentLookup +from zope.component.factory import Factory +from zope.interface import Interface, implements +from zope.lifecycleevent.interfaces import IObjectCreatedEvent, IObjectModifiedEvent +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.schema import TextLine, accessors +from zope.security.checker import CheckerPublic +from zope.site.site import SiteManagerAdapter + +from zope.formlib.widget import CustomWidgetFactory +from zope.app.form.browser import TextWidget as Text +from zope.app.form.browser.add import AddViewFactory, AddView +from zope.app.form.browser.metaconfigure import AddFormDirective +from zope.app.form.browser.submit import Update +from zope.app.testing import ztapi + +# Foo needs to be imported as globals() are checked +from zope.app.form.browser.tests.test_editview import IFoo, IBar, Foo +from zope.app.form.browser.tests.test_editview import FooBarAdapter + +from zope.component.testing import PlacelessSetup as CAPlacelessSetup +from zope.component.eventtesting import PlacelessSetup as EventPlacelessSetup + +class PlacelessSetup(CAPlacelessSetup, EventPlacelessSetup): + + def setUp(self, doctesttest=None): + CAPlacelessSetup.setUp(self) + EventPlacelessSetup.setUp(self) + + +class Context(object): + + def action(self, discriminator, callable, args=(), kw={}): + self.last_action = (discriminator, callable, args, kw) + +class I(Interface): + + name = TextLine() + first = TextLine() + last = TextLine() + email = TextLine() + address = TextLine() + getfoo, setfoo = accessors(TextLine()) + extra1 = TextLine() + extra2 = TextLine(required=False) + +class C(object): + + implements(I) + + def __init__(self, *args, **kw): + self.args = args + self.kw = kw + + def getfoo(self): return self._foo + def setfoo(self, v): self._foo = v + +class V(object): + name_widget = CustomWidgetFactory(Text) + first_widget = CustomWidgetFactory(Text) + last_widget = CustomWidgetFactory(Text) + email_widget = CustomWidgetFactory(Text) + address_widget = CustomWidgetFactory(Text) + getfoo_widget = CustomWidgetFactory(Text) + extra1_widget = CustomWidgetFactory(Text) + extra2_widget = CustomWidgetFactory(Text) + +class FooV(object): + bar_widget = CustomWidgetFactory(Text) + + +class SampleData(object): + + name = u"foo" + first = u"bar" + last = u"baz" + email = u"baz@dot.com" + address = u"aa" + getfoo = u"foo" + extra1 = u"extra1" + extra2 = u"extra2" + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + self._context = Context() + super(Test, self).setUp() + ztapi.provideAdapter(IFoo, IBar, FooBarAdapter) + + def _invoke_add(self, schema=I, name="addthis", permission="zope.Public", + label="Add this", content_factory=C, class_=V, + arguments=['first', 'last'], keyword_arguments=['email'], + set_before_add=['getfoo'], set_after_add=['extra1'], + fields=None): + """ Call the 'add' factory to process arguments into 'args'.""" + AddFormDirective(self._context, + schema=schema, + name=name, + permission=permission, + label=label, + content_factory=content_factory, + class_=class_, + arguments=arguments, + keyword_arguments=keyword_arguments, + set_before_add=set_before_add, + set_after_add=set_after_add, + fields=fields + )() + + def test_add_no_fields(self): + _context = self._context + self._invoke_add() + result1 = _context.last_action + self._invoke_add( + fields="name first last email address getfoo extra1 extra2".split(), + ) + result2 = _context.last_action + + self.assertEqual(result1, result2) + + def test_add_error_handling(self): + # cannot use a field in arguments if it is not mentioned in fields + self.assertRaises(ValueError, self._invoke_add, + fields="first email getfoo extra1".split()) + + # cannot use a field in keyword_arguments if it is not + # mentioned in fields + + self.assertRaises(ValueError, self._invoke_add, + fields="first last getfoo extra1".split()) + + # cannot use a field in set_before_add if it is not mentioned in fields + self.assertRaises(ValueError, self._invoke_add, + fields="first last email extra1".split()) + + # cannot use a field in set_after_add if it is not mentioned in fields + self.assertRaises(ValueError, self._invoke_add, + fields="first last email getfoo".split()) + + # cannot use an optional field in arguments + self.assertRaises(ValueError, self._invoke_add, arguments=["extra2"]) + + def test_add(self, args=None): + self._invoke_add() + (descriminator, callable, args, kw) = self._context.last_action + + self.assertEqual(descriminator, + ('view', IAdding, 'addthis', IBrowserRequest, + IDefaultBrowserLayer)) + + self.assertEqual(callable, AddViewFactory) + + (name, schema, label, permission, layer, template, + default_template, bases, for_, fields, content_factory, + arguments, keyword_arguments, set_before_add, + set_after_add) = args + + self.assertEqual(name, 'addthis') + self.assertEqual(schema, I) + self.assertEqual(label, 'Add this') + self.assertEqual(permission, CheckerPublic) # 'zope.Public' translated + self.assertEqual(layer, IDefaultBrowserLayer) + self.assertEqual(template, 'add.pt') + self.assertEqual(default_template, 'add.pt') + self.assertEqual(bases, (V, AddView, )) + self.assertEqual(for_, IAdding) + self.assertEqual(" ".join(fields), + "name first last email address getfoo extra1 extra2") + self.assertEqual(content_factory, C) + self.assertEqual(" ".join(arguments), + "first last") + self.assertEqual(" ".join(keyword_arguments), + "email") + self.assertEqual(" ".join(set_before_add), + "getfoo") + self.assertEqual(" ".join(set_after_add), + "extra1 name address extra2") + + return args + + def test_add_content_factory_id(self, args=None): + self._invoke_add(content_factory='C') + (descriminator, callable, args, kw) = self._context.last_action + + self.assertEqual(descriminator, + ('view', IAdding, 'addthis', IBrowserRequest, + IDefaultBrowserLayer)) + + self.assertEqual(callable, AddViewFactory) + + (name, schema, label, permission, layer, template, + default_template, bases, for_, fields, content_factory, + arguments, keyword_arguments, set_before_add, + set_after_add) = args + + self.assertEqual(name, 'addthis') + self.assertEqual(schema, I) + self.assertEqual(label, 'Add this') + self.assertEqual(permission, CheckerPublic) # 'zope.Public' translated + self.assertEqual(layer, IDefaultBrowserLayer) + self.assertEqual(template, 'add.pt') + self.assertEqual(default_template, 'add.pt') + self.assertEqual(bases, (V, AddView, )) + self.assertEqual(for_, IAdding) + self.assertEqual(" ".join(fields), + "name first last email address getfoo extra1 extra2") + self.assertEqual(content_factory, 'C') + self.assertEqual(" ".join(arguments), + "first last") + self.assertEqual(" ".join(keyword_arguments), + "email") + self.assertEqual(" ".join(set_before_add), + "getfoo") + self.assertEqual(" ".join(set_after_add), + "extra1 name address extra2") + + return args + + def test_create(self): + + class Adding(object): + + implements(IAdding) + + def __init__(self, test): + self.test = test + + def add(self, ob): + self.ob = ob + self.test.assertEqual( + ob.__dict__, + {'args': ("bar", "baz"), + 'kw': {'email': 'baz@dot.com'}, + '_foo': 'foo', + }) + return ob + def nextURL(self): + return "." + + adding = Adding(self) + self._invoke_add() + (descriminator, callable, args, kw) = self._context.last_action + factory = AddViewFactory(*args) + request = TestRequest() + view = getMultiAdapter((adding, request), name='addthis') + content = view.create('a',0,abc='def') + + self.failUnless(isinstance(content, C)) + self.assertEqual(content.args, ('a', 0)) + self.assertEqual(content.kw, {'abc':'def'}) + + def test_create_content_factory_id(self): + + class Adding(object): + + implements(IAdding) + + def __init__(self, test): + self.test = test + + def add(self, ob): + self.ob = ob + self.test.assertEqual( + ob.__dict__, + {'args': ("bar", "baz"), + 'kw': {'email': 'baz@dot.com'}, + '_foo': 'foo', + }) + return ob + def nextURL(self): + return "." + + # register content factory for content factory id lookup + ztapi.provideAdapter(None, IComponentLookup, SiteManagerAdapter) + ztapi.provideUtility(IFactory, Factory(C), name='C') + + adding = Adding(self) + self._invoke_add(content_factory='C') + (descriminator, callable, args, kw) = self._context.last_action + factory = AddViewFactory(*args) + request = TestRequest() + view = getMultiAdapter((adding, request), name='addthis') + content = view.create('a',0,abc='def') + + self.failUnless(isinstance(content, C)) + self.assertEqual(content.args, ('a', 0)) + self.assertEqual(content.kw, {'abc':'def'}) + + def test_createAndAdd(self): + + class Adding(object): + + implements(IAdding) + + def __init__(self, test): + self.test = test + + def add(self, ob): + self.ob = ob + self.test.assertEqual( + ob.__dict__, + {'args': ("bar", "baz"), + 'kw': {'email': 'baz@dot.com'}, + '_foo': 'foo', + }) + return ob + def nextURL(self): + return "." + + adding = Adding(self) + self._invoke_add() + (descriminator, callable, args, kw) = self._context.last_action + factory = AddViewFactory(*args) + request = TestRequest() + view = getMultiAdapter((adding, request), name='addthis') + + view.createAndAdd(SampleData.__dict__) + + self.assertEqual(adding.ob.extra1, "extra1") + self.assertEqual(adding.ob.extra2, "extra2") + self.assertEqual(adding.ob.name, "foo") + self.assertEqual(adding.ob.address, "aa") + self.assertEqual(len(getEvents(IObjectCreatedEvent)), 1) + self.assertEqual(len(getEvents(IObjectModifiedEvent)), 1) + + def test_createAndAdd_w_adapter(self): + + class Adding(object): + + implements(IAdding) + + def __init__(self, test): + self.test = test + + def add(self, ob): + self.ob = ob + self.test.assertEqual(ob.__dict__, {'foo': 'bar'}) + return ob + def nextURL(self): + return "." + + adding = Adding(self) + self._invoke_add( + schema=IBar, name="addthis", permission="zope.Public", + label="Add this", content_factory=Foo, class_=FooV, + arguments=None, keyword_arguments=None, + set_before_add=["bar"], set_after_add=None, + fields=None + ) + (descriminator, callable, args, kw) = self._context.last_action + factory = AddViewFactory(*args) + request = TestRequest() + view = getMultiAdapter((adding, request), name='addthis') + + view.createAndAdd({'bar': 'bar'}) + + def test_hooks(self): + + class Adding(object): + implements(IAdding) + + adding = Adding() + self._invoke_add() + (descriminator, callable, args, kw) = self._context.last_action + factory = AddViewFactory(*args) + request = TestRequest() + + request.form.update(dict([ + ("field.%s" % k, v) + for (k, v) in dict(SampleData.__dict__).items() + ])) + request.form[Update] = '' + view = getMultiAdapter((adding, request), name='addthis') + + # Add hooks to V + + l=[None] + + def add(aself, ob): + l[0] = ob + self.assertEqual( + ob.__dict__, + {'args': ("bar", "baz"), + 'kw': {'email': 'baz@dot.com'}, + '_foo': 'foo', + }) + return ob + + V.add = add + + V.nextURL = lambda self: 'next' + + try: + self.assertEqual(view.update(), '') + + self.assertEqual(view.errors, ()) + + self.assertEqual(l[0].extra1, "extra1") + self.assertEqual(l[0].extra2, "extra2") + self.assertEqual(l[0].name, "foo") + self.assertEqual(l[0].address, "aa") + + self.assertEqual(request.response.getHeader("Location"), "next") + + # Verify that calling update again doesn't do anything. + l[0] = None + self.assertEqual(view.update(), '') + self.assertEqual(l[0], None) + + finally: + # Uninstall hooks + del V.add + del V.nextURL + + +def test_suite(): + return unittest.makeSuite(Test) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/test_browserwidget.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_browserwidget.py --- zope3-3.4.0/src/zope/app/form/browser/tests/test_browserwidget.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_browserwidget.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 Browser Widget + +$Id: test_browserwidget.py 107392 2009-12-30 21:07:14Z faassen $ +""" +# BBB +from zope.formlib.tests.test_browserwidget import ( + BrowserWidgetTest, + SimpleInputWidgetTest, + TestWidget, + Test) diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/testconfigure.zcml zope3-3.5~bzr18/src/zope/app/form/browser/tests/testconfigure.zcml --- zope3-3.4.0/src/zope/app/form/browser/tests/testconfigure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/testconfigure.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,16 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/test_directives.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_directives.py --- zope3-3.4.0/src/zope/app/form/browser/tests/test_directives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_directives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,307 @@ +############################################################################# +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Form Directives Tests + +$Id: test_directives.py 107371 2009-12-30 18:36:02Z faassen $ +""" +import unittest +from cStringIO import StringIO + +from zope import component +from zope.component.testing import PlacelessSetup +from zope.configuration.xmlconfig import xmlconfig, XMLConfig +from zope.traversing.interfaces import TraversalError +from zope.interface import Interface, implements +from zope.publisher.browser import TestRequest +from zope.schema import TextLine, Int + +from zope.app.form.browser import TextWidget +from zope.app.form.tests import utils + +template = """ + %s + """ + + +request = TestRequest() + +class Schema(Interface): + + text = TextLine( + title=u'Text', + description=u'Nice text', + required=False) + +class IC(Schema): pass + +class Ob(object): + implements(IC) + +unwrapped_ob = Ob() +ob = utils.securityWrap(unwrapped_ob, IC) + +class ISomeWidget(Interface): + displayWidth = Int( + title=u"Display Width", + default=20, + required=True) + +class SomeWidget(TextWidget): + implements(ISomeWidget) + + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + import zope.component + XMLConfig('meta.zcml', zope.component)() + import zope.app.form.browser + XMLConfig('meta.zcml', zope.app.form.browser)() + import zope.browsermenu + XMLConfig('meta.zcml', zope.browsermenu)() + + from zope.traversing.adapters import DefaultTraversable + from zope.traversing.interfaces import ITraversable + + component.provideAdapter(DefaultTraversable, (None,), ITraversable) + + def testAddForm(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='add.html'), + None) + xmlconfig(StringIO(template % (""" + + + + """))) + + v = component.getMultiAdapter((ob, request), name='add.html') + # expect to fail as standard macros are not configured + self.assertRaises(TraversalError, v) + + def testEditForm(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='edit.html'), + None) + xmlconfig(StringIO(template % (""" + + + + """))) + + v = component.getMultiAdapter((ob, request), name='edit.html') + # expect to fail as standard macros are not configured + self.assertRaises(TraversalError, v) + + def testEditFormWithMenu(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='edit.html'), + None) + xmlconfig(StringIO(template % (''' + + + + '''))) + + v = component.queryMultiAdapter((ob, request), name='edit.html') + # expect to fail as standard macros are not configured + self.assertRaises(TraversalError, v) + + def testSchemaDisplay(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='view.html'), + None) + xmlconfig(StringIO(template % (''' + + + + '''))) + + v = component.queryMultiAdapter((ob, request), name='view.html') + # expect to fail as standard macros are not configured + self.assertRaises(TraversalError, v) + + def testAddFormWithWidget(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='add.html'), + None) + xmlconfig(StringIO(template % (''' + + + + + + + + ''')), ) + + view = component.queryMultiAdapter((ob, request), name='add.html') + self.assert_(hasattr(view, 'text_widget')) + self.assert_(isinstance(view.text_widget, SomeWidget)) + self.assertEqual(view.text_widget.extra, u'foo') + self.assertEqual(view.text_widget.displayWidth, 30) + + + def testEditFormWithWidget(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='edit.html'), + None) + xmlconfig(StringIO(template % (''' + + + + + + + + ''')), ) + + view = component.queryMultiAdapter((ob, request), name='edit.html') + self.assert_(hasattr(view, 'text_widget')) + self.assert_(isinstance(view.text_widget, SomeWidget)) + self.assertEqual(view.text_widget.extra, u'foo') + self.assertEqual(view.text_widget.displayWidth, 30) + + def testSchemaDisplayWithWidget(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='view.html'), + None) + xmlconfig(StringIO(template % (''' + + + + + + + '''))) + + view = component.queryMultiAdapter((ob, request), name='view.html') + self.assert_(hasattr(view, 'text_widget')) + self.assert_(isinstance(view.text_widget, SomeWidget)) + self.assertEqual(view.text_widget.extra, u'foo') + self.assertEqual(view.text_widget.displayWidth, 30) + + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/testeditform.pt zope3-3.5~bzr18/src/zope/app/form/browser/tests/testeditform.pt --- zope3-3.4.0/src/zope/app/form/browser/tests/testeditform.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/testeditform.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + Simple Edit Form + + + + +

This is a Schema produced form

+ +
+ Errors: +
+ : + +
+
+
+
+ + + + + + + + + + +
Title: + +
+ + + +
+ + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/test_editview.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_editview.py --- zope3-3.4.0/src/zope/app/form/browser/tests/test_editview.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_editview.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,210 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Edit View Tests + +$Id: test_editview.py 107371 2009-12-30 18:36:02Z faassen $ +""" +import unittest + +from zope.component.eventtesting import getEvents, clearEvents +from zope.component.testing import PlacelessSetup +from zope.interface import Interface, implements +from zope.location.interfaces import ILocation +from zope.publisher.browser import TestRequest +from zope.schema import TextLine, accessors +from zope.schema.interfaces import ITextLine + +from zope.app.testing import ztapi + +from zope.app.form.browser import TextWidget +from zope.app.form.browser.editview import EditView +from zope.app.form.browser.submit import Update +from zope.formlib.interfaces import IInputWidget +from zope.app.form.tests import utils + +class I(Interface): + foo = TextLine(title=u"Foo") + bar = TextLine(title=u"Bar") + a = TextLine(title=u"A") + b = TextLine(title=u"B", min_length=0, required=False) + getbaz, setbaz = accessors(TextLine(title=u"Baz")) + +class EV(EditView): + schema = I + object_factories = [] + +class C(object): + implements(I) + foo = u"c foo" + bar = u"c bar" + a = u"c a" + b = u"c b" + __Security_checker__ = utils.SchemaChecker(I) + + _baz = u"c baz" + def getbaz(self): return self._baz + def setbaz(self, v): self._baz = v + + +class IFoo(Interface): + foo = TextLine(title=u"Foo") + +class IBar(Interface): + bar = TextLine(title=u"Bar") + +class Foo(object): + implements(IFoo) + __Security_checker__ = utils.SchemaChecker(IFoo) + + foo = u'Foo foo' + +class ConformFoo(object): + implements(IFoo) + + foo = u'Foo foo' + + def __conform__(self, interface): + if interface is IBar: + return OtherFooBarAdapter(self) + + +class FooBarAdapter(object): + implements(IBar, ILocation) + __used_for__ = IFoo + + def __init__(self, context): + self.context = context + + def getbar(self): return self.context.foo + def setbar(self, v): self.context.foo = v + + bar = property(getbar, setbar) + + __Security_checker__ = utils.SchemaChecker(IBar) + +class OtherFooBarAdapter(FooBarAdapter): + pass + +class BarV(EditView): + schema = IBar + object_factories = [] + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget) + ztapi.provideAdapter(IFoo, IBar, FooBarAdapter) + clearEvents() + + def test_setPrefix_and_widgets(self): + v = EV(C(), TestRequest()) + v.setPrefix("test") + self.assertEqual( + [w.name for w in v.widgets()], + ['test.foo', 'test.bar', 'test.a', 'test.b', 'test.getbaz'] + ) + + def test_empty_prefix(self): + v = EV(C(), TestRequest()) + v.setPrefix("") + self.assertEqual( + [w.name for w in v.widgets()], + ['foo', 'bar', 'a', 'b', 'getbaz'] + ) + + def test_fail_wo_adapter(self): + c = Foo() + request = TestRequest() + self.assertRaises(TypeError, EV, c, request) + + def test_update_no_update(self): + c = C() + request = TestRequest() + v = EV(c, request) + self.assertEqual(v.update(), '') + self.assertEqual(c.foo, u'c foo') + self.assertEqual(c.bar, u'c bar') + self.assertEqual(c.a , u'c a') + self.assertEqual(c.b , u'c b') + self.assertEqual(c.getbaz(), u'c baz') + request.form['field.foo'] = u'r foo' + request.form['field.bar'] = u'r bar' + request.form['field.a'] = u'r a' + request.form['field.b'] = u'r b' + request.form['field.getbaz'] = u'r baz' + self.assertEqual(v.update(), '') + self.assertEqual(c.foo, u'c foo') + self.assertEqual(c.bar, u'c bar') + self.assertEqual(c.a , u'c a') + self.assertEqual(c.b , u'c b') + self.assertEqual(c.getbaz(), u'c baz') + self.failIf(getEvents()) + + def test_update(self): + c = C() + request = TestRequest() + v = EV(c, request) + request.form[Update] = '' + request.form['field.foo'] = u'r foo' + request.form['field.bar'] = u'r bar' + request.form['field.getbaz'] = u'r baz' + request.form['field.a'] = u'c a' + + message = v.update() + self.failUnless(message.startswith('Updated '), message) + self.assertEqual(c.foo, u'r foo') + self.assertEqual(c.bar, u'r bar') + self.assertEqual(c.a , u'c a') + self.assertEqual(c.b , u'c b') # missing from form - unchanged + self.assertEqual(c.getbaz(), u'r baz') + + # Verify that calling update multiple times has no effect + + c.__dict__.clear() + self.assertEqual(v.update(), message) + self.assertEqual(c.foo, u'c foo') + self.assertEqual(c.bar, u'c bar') + self.assertEqual(c.a , u'c a') + self.assertEqual(c.b , u'c b') + self.assertEqual(c.getbaz(), u'c baz') + + def test_update_via_adapter(self): + f = Foo() + request = TestRequest() + v = BarV(f, request) + # check adapter + self.assertEqual(f.foo, u'Foo foo') + a = IBar(f) + self.assertEqual(a.bar, u'Foo foo') + # update + request.form[Update] = '' + request.form['field.bar'] = u'r bar' + message = v.update() + self.failUnless(message.startswith('Updated '), message) + self.assertEqual(a.bar, u'r bar') + # wrong update + self.failIf(getEvents()) + + def test_setUpWidget_via_conform_adapter(self): + + f = ConformFoo() + request = TestRequest() + v = BarV(f, request) + +def test_suite(): + return unittest.makeSuite(Test) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/test_form.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_form.py --- zope3-3.4.0/src/zope/app/form/browser/tests/test_form.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_form.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,42 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 ZCML Documentation Module + +$Id: test_form.py 107401 2009-12-30 22:56:20Z faassen $ +""" +import unittest +import doctest + +from zope.schema.interfaces import ITextLine +from zope.component import testing + +from zope.app.testing import ztapi + +from zope.app.form.browser import TextWidget +from zope.formlib.interfaces import IInputWidget + +def setUp(test): + testing.setUp() + ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget) + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('../form.txt', + setUp=setUp, tearDown=testing.tearDown, + optionflags=doctest.NORMALIZE_WHITESPACE), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/test_functional_editview.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_functional_editview.py --- zope3-3.4.0/src/zope/app/form/browser/tests/test_functional_editview.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_functional_editview.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,98 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Editview tests + +$Id: test_functional_editview.py 81040 2007-10-24 15:27:12Z srichter $ +""" +import unittest +import transaction +from persistent import Persistent + +import zope.security.checker +from zope.interface import Interface, implements +from zope.schema import TextLine +from zope.traversing.api import traverse + +from zope.app.form.browser.editview import EditView +from zope.app.form.testing import AppFormLayer +from zope.app.form.browser.tests.support import * +from zope.app.testing.functional import BrowserTestCase + +class IFoo(Interface): + + optional_text = TextLine(required=False) + required_text = TextLine(required=True) + +class Foo(Persistent): + + implements(IFoo) + +class Test(BrowserTestCase): + + def setUp(self): + BrowserTestCase.setUp(self) + registerEditForm(IFoo) + defineSecurity(Foo, IFoo) + + def test_rollback_on_error(self): + """Tests rollback when a widget error occurs. + + When one or more errors are generated by input widgets, the current + transaction should be rolledback to ensure object integrity. + """ + self.getRootFolder()['foo'] = Foo() + self.getRootFolder()['foo'].required_text = u'initial required' + self.getRootFolder()['foo'].optional_text = u'initial optional' + transaction.commit() + + # submit form with legal value for optional_text and invalid for + # required_text + old_update = EditView.update + try: + def new_update(self): + # This update changes something after form validation has failed. + # Side effects like this should not be committed. + # http://www.zope.org/Collectors/Zope3-dev/655 + result = old_update(self) + self.context.required_text = u'changed after form validation' + return result + EditView.update = new_update + response = self.publish('/foo/edit.html', form={ + 'field.optional_text': u'', + 'field.required_text': u'', + 'UPDATE_SUBMIT': ''}) + self.assertEqual(response.getStatus(), 200) + finally: + EditView.update = old_update + + # confirm that one errors exists + self.assert_(patternExists( + 'There are 1 input errors.', response.getBody())) + + # confirm that foo was not modified + foo = traverse(self.getRootFolder(), 'foo') + self.assertEquals(foo.required_text, u'initial required') + self.assertEquals(foo.optional_text, u'initial optional') + + +def test_suite(): + suite = unittest.TestSuite() + Test.layer = AppFormLayer + suite.addTest(unittest.makeSuite(Test)) + return suite + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/test_functional_i18n.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_functional_i18n.py --- zope3-3.4.0/src/zope/app/form/browser/tests/test_functional_i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_functional_i18n.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,85 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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 form i18n + +$Id: test_functional_i18n.py 107401 2009-12-30 22:56:20Z faassen $ +""" + +import re +import unittest +import doctest +from persistent import Persistent +from zope.testing import renormalizing +from zope.interface import Interface, implements +from zope.schema import TextLine, Text, Int, List +from zope.i18nmessageid import MessageFactory +from zope.app.testing.functional import FunctionalDocFileSuite +from zope.app.form.testing import AppFormLayer + + +_ = MessageFactory('formtest') + +__docformat__ = "reStructuredText" + + +class IFieldContent(Interface): + + title = TextLine( + title=_(u"Title"), + description=_(u"A short description of the event."), + default=u"", + required=True + ) + + description = Text( + title=_(u"Description"), + description=_(u"A long description of the event."), + default=u"", + required=False + ) + + somenumber = Int( + title=_(u"Some number"), + default=0, + required=False + ) + + somelist = List( + title=_(u"Some List"), + value_type=TextLine(title=_(u"Some item")), + default=[], + required=False + ) + + +class FieldContent(Persistent): + implements(IFieldContent) + + +checker = renormalizing.RENormalizing([ + (re.compile(r"HTTP/1\.1 200 .*"), "HTTP/1.1 200 OK"), + ]) + + +def test_suite(): + i18n = FunctionalDocFileSuite('i18n.txt', package='zope.app.form.browser', + checker=checker) + i18n.layer = AppFormLayer + return unittest.TestSuite([ + i18n, + ]) + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/app/form/browser/tests/testlabeltranslation.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/app/form/browser/tests/testlabeltranslation.mo differ diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/testlabeltranslation.po zope3-3.5~bzr18/src/zope/app/form/browser/tests/testlabeltranslation.po --- zope3-3.4.0/src/zope/app/form/browser/tests/testlabeltranslation.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/testlabeltranslation.po 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,20 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2003-03-25 15:01-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=us-ascii\n" +"Content-Transfer-Encoding: 8bit\n" + +#: test_browserwidget.py:34 +msgid "Foo Title" +msgstr "oofay itletay" diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/test_widgetdirective.py zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_widgetdirective.py --- zope3-3.4.0/src/zope/app/form/browser/tests/test_widgetdirective.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/test_widgetdirective.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,93 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 subdirective for the generated form pages. + +$Id: test_widgetdirective.py 107371 2009-12-30 18:36:02Z faassen $ +""" +import unittest + +import zope.component +import zope.interface +import zope.configuration.xmlconfig +import zope.publisher.browser +import zope.schema +from zope.browser.interfaces import IAdding + +import zope.app.form.browser.interfaces +import zope.formlib.interfaces +from zope.app.form.tests import utils +import zope.component.testing + +__docformat__ = "reStructuredText" + + +class IContent(zope.interface.Interface): + + field = zope.schema.TextLine( + title=u"Field", + description=u"Sample input field", + required=False, + ) + + +class Content(object): + + zope.interface.implements(IContent) + __Security_checker__ = utils.SchemaChecker(IContent) + + __parent__ = None + __name__ = "sample-content" + + field = None + + +class Adding(object): + + zope.interface.implements(IAdding) + + def add(self, content): + self.content = content + + +class WidgetDirectiveTestCase(zope.component.testing.PlacelessSetup, + unittest.TestCase): + + def setUp(self): + super(WidgetDirectiveTestCase, self).setUp() + zope.configuration.xmlconfig.file("widgetDirectives.zcml", + zope.app.form.browser.tests) + + def get_widget(self, name, context): + request = zope.publisher.browser.TestRequest() + view = zope.component.getMultiAdapter((context, request), name=name) + return view.field_widget + + def test_addform_widget_without_class(self): + w = self.get_widget("add.html", Adding()) + self.assert_(zope.formlib.interfaces.IInputWidget.providedBy(w)) + self.assertEqual(w.extraAttr, "42") + + def test_editform_widget_without_class(self): + w = self.get_widget("edit.html", Content()) + self.assert_(zope.formlib.interfaces.IInputWidget.providedBy(w)) + self.assertEqual(w.extraAttr, "84") + + def test_subeditform_widget_without_class(self): + w = self.get_widget("subedit.html", Content()) + self.assert_(zope.formlib.interfaces.IInputWidget.providedBy(w)) + self.assertEqual(w.extraAttr, "168") + + +def test_suite(): + return unittest.makeSuite(WidgetDirectiveTestCase) diff -Nru zope3-3.4.0/src/zope/app/form/browser/tests/widgetDirectives.zcml zope3-3.5~bzr18/src/zope/app/form/browser/tests/widgetDirectives.zcml --- zope3-3.4.0/src/zope/app/form/browser/tests/widgetDirectives.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/tests/widgetDirectives.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/form/browser/textwidgets.py zope3-3.5~bzr18/src/zope/app/form/browser/textwidgets.py --- zope3-3.4.0/src/zope/app/form/browser/textwidgets.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/textwidgets.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,42 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Browser widgets with text-based input + +$Id: textwidgets.py 107385 2009-12-30 20:25:24Z faassen $ +""" +# BBB implementation moved to zope.formlib.textwidgets +from zope.formlib.textwidgets import ( + escape, + TextWidget, + Bytes, + BytesWidget, + BytesDisplayWidget, + ASCII, + ASCIIWidget, + ASCIIDisplayWidget, + URIDisplayWidget, + TextAreaWidget, + BytesAreaWidget, + ASCIIAreaWidget, + PasswordWidget, + FileWidget, + IntWidget, + FloatWidget, + DecimalWidget, + DatetimeWidget, + DateWidget, + DateI18nWidget, + DatetimeI18nWidget, + DateDisplayWidget, + DatetimeDisplayWidget) diff -Nru zope3-3.4.0/src/zope/app/form/browser/widget.py zope3-3.5~bzr18/src/zope/app/form/browser/widget.py --- zope3-3.4.0/src/zope/app/form/browser/widget.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/widget.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,31 @@ +############################################################################## +# +# Copyright (c) 2001-2004 Zope Corporation 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. +# +############################################################################## +"""Browser Widget Definitions + +$Id: widget.py 107818 2010-01-08 19:14:50Z faassen $ +""" +__docformat__ = 'restructuredtext' + +# BBB +from zope.formlib.widget import ( + quoteattr, + BrowserWidget, + SimpleInputWidget, + DisplayWidget, + UnicodeDisplayWidget, + renderTag, + renderElement, + escape, + setUp, + tearDown) diff -Nru zope3-3.4.0/src/zope/app/form/browser/widgets.txt zope3-3.5~bzr18/src/zope/app/form/browser/widgets.txt --- zope3-3.4.0/src/zope/app/form/browser/widgets.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/browser/widgets.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,241 @@ +============================================================ +Simple example showing ObjectWidget and SequenceWidget usage +============================================================ + +The following implements a Poll product (add it as +zope/app/demo/poll) which has poll options defined as: + +label + A `TextLine` holding the label of the option +description + Another `TextLine` holding the description of the option + +Simple stuff. + +Our Poll product holds an editable list of the `PollOption` instances. +This is shown in the ``poll.py`` source below:: + + from persistent import Persistent + from interfaces import IPoll, IPollOption + from zope.interface import implements, classImplements + + class PollOption(Persistent, object): + implements(IPollOption) + + class Poll(Persistent, object): + implements(IPoll) + + def getResponse(self, option): + return self._responses[option] + + def choose(self, option): + self._responses[option] += 1 + self._p_changed = 1 + + def get_options(self): + return self._options + + def set_options(self, options): + self._options = options + self._responses = {} + for option in self._options: + self._responses[option.label] = 0 + + options = property(get_options, set_options, None, 'fiddle options') + +And the Schemas are defined in the ``interfaces.py`` file below:: + + from zope.interface import Interface + from zope.schema import Object, Tuple, TextLine + from zope.schema.interfaces import ITextLine + from zope.i18nmessageid import MessageFactory + + _ = MessageFactory("poll") + + class IPollOption(Interface): + label = TextLine(title=u'Label', min_length=1) + description = TextLine(title=u'Description', min_length=1) + + class IPoll(Interface): + options = Tuple(title=u'Options', + value_type=Object(IPollOption, title=u'Poll Option')) + + def getResponse(option): "get the response for an option" + + def choose(option): 'user chooses an option' + +Note the use of the `Tuple` and `Object` schema fields above. The +`Tuple` could optionally have restrictions on the min or max number of +items - these will be enforced by the `SequenceWidget` form handling +code. The `Object` must specify the schema that is used to generate its +data. + +Now we have to specify the actual add and edit views. We use the existing +AddView and EditView, but we pre-define the widget for the sequence because +we need to pass in additional information. This is given in the +``browser.py`` file:: + + from zope.app.form.browser.editview import EditView + from zope.app.form.browser.add import AddView + from zope.formlib.widget import CustomWidgetFactory + from zope.app.form.browser import SequenceWidget, ObjectWidget + + from interfaces import IPoll + from poll import Poll, PollOption + + class PollVoteView: + __used_for__ = IPoll + + def choose(self, option): + self.context.choose(option) + self.request.response.redirect('.') + + ow = CustomWidgetFactory(ObjectWidget, PollOption) + sw = CustomWidgetFactory(SequenceWidget, subwidget=ow) + + class PollEditView(EditView): + __used_for__ = IPoll + + options_widget = sw + + class PollAddView(AddView): + __used_for__ = IPoll + + options_widget = sw + +Note the creation of the widget via a `CustomWidgetFactory`. So, +whenever the options_widget is used, a new +``SequenceWidget(subwidget=CustomWidgetFactory(ObjectWidget, +PollOption))`` is created. The subwidget argument indicates that each +item in the sequence should be represented by the indicated widget +instead of their default. If the contents of the sequence were just +`Text` fields, then the default would be just fine - the only odd cases +are Sequence and Object Widgets because they need additional arguments +when they're created. + +Each item in the sequence will be represented by a +``CustomWidgetFactory(ObjectWidget, PollOption)`` - thus a new +``ObjectWidget(context, request, PollOption)`` is created for each +one. The `PollOption` class ("factory") is used to create new instances +when new data is created in add forms (or edit forms when we're adding +new items to a Sequence). + +Tying all this together is the ``configure.zcml``:: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Note the use of the ``class`` attribute on the ``addform`` and +``editform`` elements. Otherwise, nothing much exciting here. + +Finally, we have some additional views... + +``results.zpt``:: + + + Poll results +
+ + + + + + + + + + + + +
Poll results
OptionResultsDescription
OptionResultOption
+
+ + +``vote.zpt``:: + + + Poll voting +
+
+ + + + + + + + + +
Poll voting
OptionOption
+ +
+
+ + diff -Nru zope3-3.4.0/src/zope/app/form/ftesting.zcml zope3-3.5~bzr18/src/zope/app/form/ftesting.zcml --- zope3-3.4.0/src/zope/app/form/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/form/__init__.py zope3-3.5~bzr18/src/zope/app/form/__init__.py --- zope3-3.4.0/src/zope/app/form/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Generic Widget base classes + +$Id: __init__.py 107385 2009-12-30 20:25:24Z faassen $ +""" +__docformat__ = 'restructuredtext' +# BBB +from zope.formlib.widget import Widget, InputWidget, CustomWidgetFactory diff -Nru zope3-3.4.0/src/zope/app/form/interfaces.py zope3-3.5~bzr18/src/zope/app/form/interfaces.py --- zope3-3.4.0/src/zope/app/form/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,33 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Validation Exceptions + +$Id: interfaces.py 107371 2009-12-30 18:36:02Z faassen $ +""" +__docformat__ = 'restructuredtext' + +# this moved to zope.formlib.interfaces +from zope.formlib.interfaces import (IWidgetInputError, + WidgetInputError, + MissingInputError, + ConversionError, + InputErrors, + ErrorContainer, + WidgetsError, + IWidget, + IInputWidget, + IDisplayWidget, + IWidgetFactory) + + diff -Nru zope3-3.4.0/src/zope/app/form/testing.py zope3-3.5~bzr18/src/zope/app/form/testing.py --- zope3-3.4.0/src/zope/app/form/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation 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.app.form common test related classes/functions/objects. + +$Id: testing.py 72343 2007-02-03 06:04:09Z baijum $ +""" + +__docformat__ = "reStructuredText" + +import os +from zope.app.testing.functional import ZCMLLayer + +AppFormLayer = ZCMLLayer( + os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'), + __name__, 'AppFormLayer', allow_teardown=True) + diff -Nru zope3-3.4.0/src/zope/app/form/tests/__init__.py zope3-3.5~bzr18/src/zope/app/form/tests/__init__.py --- zope3-3.4.0/src/zope/app/form/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/form/tests/test_macros.pt zope3-3.5~bzr18/src/zope/app/form/tests/test_macros.pt --- zope3-3.4.0/src/zope/app/form/tests/test_macros.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/tests/test_macros.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ + + + + +
+ + diff -Nru zope3-3.4.0/src/zope/app/form/tests/test_utility.py zope3-3.5~bzr18/src/zope/app/form/tests/test_utility.py --- zope3-3.4.0/src/zope/app/form/tests/test_utility.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/tests/test_utility.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1142 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Form Utilities Tests + +$Id: test_utility.py 107401 2009-12-30 22:56:20Z faassen $ +""" + +import doctest +import zope.security.checker +from zope.interface import Interface, implements +from zope.component import testing +from zope.component.interfaces import ComponentLookupError +from zope.publisher.browser import TestRequest, BrowserView +from zope.security.interfaces import ForbiddenAttribute, Unauthorized +from zope.schema import Field, Int, accessors +from zope.schema.interfaces import IField, IInt + +from zope.app.testing import ztapi + +from zope.formlib.widget import Widget +from zope.formlib.interfaces import IWidget, IInputWidget, IDisplayWidget +from zope.formlib.interfaces import ConversionError, InputErrors, WidgetsError +from zope.formlib.interfaces import IWidgetFactory +from zope.app.form.utility import no_value, setUpWidget, setUpWidgets +from zope.app.form.utility import setUpEditWidgets, setUpDisplayWidgets +from zope.app.form.utility import getWidgetsData, viewHasInput +from zope.app.form.utility import applyWidgetsChanges +from zope.app.form.tests import utils + +request = TestRequest() + +class IFoo(IField): + pass + +class Foo(Field): + implements(IFoo) + +class IBar(IField): + pass + +class Bar(Field): + implements(IBar) + +class IBaz(IInt): + pass + +class Baz(Int): + implements(IBaz) + +class IContent(Interface): + foo = Foo() + bar = Bar() + + +class Content(object): + implements(IContent) + __Security_checker__ = utils.SchemaChecker(IContent) + foo = 'Foo' + +class IFooWidget(IWidget): + pass + +class IBarWidget(IWidget): + pass + +class FooWidget(Widget): + implements(IFooWidget) + def getPrefix(self): return self._prefix # exposes _prefix for testing + def getRenderedValue(self): return self._data # exposes _data for testing + def renderedValueSet(self): return self._renderedValueSet() # for testing + +class BarWidget(Widget): + implements(IBarWidget) + def getRenderedValue(self): return self._data # exposes _data for testing + def renderedValueSet(self): return self._renderedValueSet() # for testing + +class BazWidget(Widget): + def getRenderedValue(self): return self._data # exposes _data for testing + def renderedValueSet(self): return self._renderedValueSet() # for testing + +class IExtendedContent(IContent): + getBaz, setBaz = accessors(Baz()) + getAnotherBaz, setAnotherBaz = accessors(Baz()) + shazam = Foo() + +class ExtendedContent(Content): + implements(IExtendedContent) + _baz = _anotherbaz = shazam = None + def getBaz(self): return self._baz + def setBaz(self, value): self._baz = value + def getAnotherBaz(self): return self._anotherbaz + def setAnotherBaz(self, value): self._anotherbaz = value + +extended_checker = utils.DummyChecker( + {'foo':True, 'bar': True, 'getBaz': True, 'setBaz': True, + 'getAnotherBaz': True, 'setAnotherBaz': False, 'shazam': False}, + {'foo':True, 'bar': False, 'shazam': True}) + +def setUp(): + """Setup for tests.""" + testing.setUp() + ztapi.browserView(IFoo, '', FooWidget, providing=IFooWidget) + ztapi.browserView(IBar, '', BarWidget, providing=IBarWidget) + +def tearDown(): + testing.tearDown() + +def assertRaises(exceptionType, callable, *args): + try: + callable(*args) + return False + except Exception, e: + return isinstance(e, exceptionType) + +class TestSetUpWidget(object): + + def test_typical(self): + """Documents and tests the typical uses of setUpWidget. + + >>> setUp() + + setUpWidget ensures that the appropriate widget exists as an + attribute of a view. There are four required arguments to the + function: + + >>> view = BrowserView(Content(), request) + >>> name = 'foo' + >>> field = IContent['foo'] + >>> typeView = IFooWidget + + setUpWidget will add the appropriate widget as attribute to view + named 'foo_widget'. + + >>> hasattr(view, 'foo_widget') + False + >>> setUpWidget(view, name, field, typeView) + >>> hasattr(view, 'foo_widget') + True + >>> IFooWidget.providedBy(view.foo_widget) + True + + If the view already has an attribute with that name, it attempts to + use the existing value to create a widget. Two types are supported: + + - IWidgetFactory + - IWidget + + If the existing attribute value implements IWidgetFactory, it is used + to create a widget: + + >>> widget = FooWidget(IContent['foo'], request) + >>> class Factory(object): + ... implements(IWidgetFactory) + ... def __call__(self, request, context): + ... return widget + >>> setattr(view, 'foo_widget', Factory()) + >>> view.foo_widget is widget + False + >>> setUpWidget(view, name, field, typeView) + >>> view.foo_widget is widget + True + + If the existing attribute value implements IWidget, it is used without + modification: + + >>> setattr(view, 'foo_widget', widget) + >>> IWidget.providedBy(widget) + True + >>> setUpWidget(view, name, field, typeView) + >>> view.foo_widget is widget + True + + We now have to cleanup, so that these tests can be run in a loop. We + modified the 'IContent' interface saying that 'foo' is not mandatory, + so we have to change that back. + + >>> IContent['foo'].required = True + + >>> tearDown() + """ + + def test_validation(self): + """Documents and tests validation performed by setUpWidget. + + >>> setUp() + + setUpWidget ensures that the the view has an attribute that implements + IWidget. If setUpWidget cannot configure a widget, it raises a + TypeError. + + E.g., if a view already has a widget attribute of the name + + '_widget' that does not implement IWidgetFactory or + IWidget, setUpWidget raises a TypeError: + + >>> view = BrowserView(Content(), request) + >>> setattr(view, 'foo_widget', 'not a widget') + >>> assertRaises(TypeError, setUpWidget, + ... view, 'foo', IContent['foo'], IFooWidget) + True + + Similarly, if a view has a widget attribute that implements + IWidgetFactory, the object created by the factory must implement IWidget. + + >>> class Factory(object): + ... implements(IWidgetFactory) + ... def __call__(self, request, context): + ... return 'not a widget' + >>> setattr(view, 'foo_widget', Factory()) + >>> assertRaises(TypeError, setUpWidget, + ... view, 'foo', IContent['foo'], IFooWidget) + True + + >>> tearDown() + """ + + def test_context(self): + """Documents and tests the role of context in setUpWidget. + + >>> setUp() + + setUpWidget configures a widget by associating it to a bound field, + which is a copy of a schema field that is bound to an object. The + object the field is bound to can be explicitly specified in the + setUpWidget 'context' argument. + + By default, the context used by setUpWidget is the view context: + + >>> context = Content() + >>> view = BrowserView(context, request) + >>> setUpWidget(view, 'foo', IContent['foo'], IFooWidget) + >>> view.foo_widget.context.context is context + True + + Alternatively, you can specify a context explicitly: + + >>> view = BrowserView(context, request) + >>> altContext = Content() + >>> setUpWidget(view, 'foo', IContent['foo'], IFooWidget, + ... context=altContext) + >>> view.foo_widget.context.context is context + False + >>> view.foo_widget.context.context is altContext + True + + >>> tearDown() + """ + + def test_widgetLookup(self): + """Documents and tests how widgets are looked up by type. + + >>> setUp() + + If the view does not already have a widget configured for the + specified field name, setUpWidget will look up a widget using + an interface specified for the widgetType argument. + + Widgets are typically looked up for IInputWidget and IDisplayWidget + types. To illustrate this, we'll create two widgets, one for editing + and another for displaying IFoo attributes. Each widget is registered + as a view providing the appropriate widget type. + + >>> class EditFooWidget(Widget): + ... implements(IInputWidget) + ... def hasInput(self): + ... return False + >>> ztapi.browserViewProviding(IFoo, EditFooWidget, IInputWidget) + >>> class DisplayFooWidget(Widget): + ... implements(IDisplayWidget) + >>> ztapi.browserViewProviding(IFoo, DisplayFooWidget, + ... IDisplayWidget) + + A call to setUpWidget will lookup the widgets based on the specified + type. + + >>> view = BrowserView(Content(), request) + >>> setUpWidget(view, 'foo', IContent['foo'], IInputWidget) + >>> IInputWidget.providedBy(view.foo_widget) + True + >>> delattr(view, 'foo_widget') + >>> setUpWidget(view, 'foo', IContent['foo'], IDisplayWidget) + >>> IDisplayWidget.providedBy(view.foo_widget) + True + + A ComponentError is raised if a widget is not registered for the + specified type: + + >>> class IUnregisteredWidget(IWidget): + ... pass + >>> delattr(view, 'foo_widget') + >>> assertRaises(ComponentLookupError, setUpWidget, + ... view, 'foo', IContent['foo'], IUnregisteredWidget) + True + + >>> tearDown() + """ + + def test_prefix(self): + """Documents and tests the specification of widget prefixes. + + >>> setUp() + + Widgets support a prefix that can be used to group related widgets + on a view. To specify the prefix for a widget, specify in the call to + setUpWidget: + + >>> view = BrowserView(Content(), request) + >>> setUpWidget(view, 'foo', IContent['foo'], IFooWidget, + ... prefix='mygroup') + >>> view.foo_widget.getPrefix() + 'mygroup.' + + >>> tearDown() + """ + + def test_value(self): + """Documents and tests values and setUpWidget. + + >>> setUp() + + setUpWidget configures the widget with the value specified in the + 'value' argument: + + >>> view = BrowserView(Content(), request) + >>> setUpWidget(view, 'foo', IContent['foo'], IFooWidget, + ... value='Explicit Widget Value') + >>> view.foo_widget.renderedValueSet() + True + >>> view.foo_widget.getRenderedValue() + 'Explicit Widget Value' + + The utility module provides a marker object 'no_value' that can be + used as setUpWidget's 'value' argument to indicate that a value + doesn't exist for the bound field. This may seem a bit unusual since + None is typically used for this purpose. However, None is a valid + value for many fields and does not indicate 'no value'. + + When no_value is specified in a call to setUpWidget, the effected + widget is not configured with a value: + + >>> delattr(view, 'foo_widget') + >>> setUpWidget(view, 'foo', IContent['foo'], IFooWidget, + ... value=no_value) + >>> view.foo_widget.renderedValueSet() + False + + This is the also default behavior when the value argument is omitted: + + >>> delattr(view, 'foo_widget') + >>> setUpWidget(view, 'foo', IContent['foo'], IFooWidget) + >>> view.foo_widget.renderedValueSet() + False + + Note that when None is specified for 'value', the widget is configured + with None: + + >>> delattr(view, 'foo_widget') + >>> setUpWidget(view, 'foo', IContent['foo'], IFooWidget, + ... value=None) + >>> view.foo_widget.renderedValueSet() + True + >>> view.foo_widget.getRenderedValue() is None + True + + >>> tearDown() + """ + + def test_stickyValues(self): + """Documents and tests setUpWidget's handling of sticky values. + + >>> setUp() + + setUpWidget supports the concept of 'sticky values'. A sticky value + is a value displayed by a widget that should persist across multiple + across multiple object edit sessions. Sticky values ensure that invalid + user is available for the user to modify rather than being replaced + by some other value. + + setUpWidget inferst that a widget has a sticky value if: + + - The widget implements IInputWidget + - The widget returns True for its hasInput method + + To illustrate, we'll create and register an edit widget for foo that + has input: + + >>> class EditFooWidget(Widget): + ... implements(IInputWidget) + ... _data = "Original Value" + ... def hasInput(self): return True + ... def getRenderedValue(self): return self._data + >>> ztapi.browserView(IFoo, '', EditFooWidget, + ... providing=IInputWidget) + + Specifying a value to setUpWidget would typically cause that value + to be set for the widget: + + >>> view = BrowserView(Content(), request) + >>> setUpWidget(view, 'foo', IContent['foo'], IInputWidget, + ... value="A New Value") + + However, because EditFooWidget has input (i.e. has a 'sticky' value), + setUpWidget will not overwrite its value: + + >>> view.foo_widget._data + 'Original Value' + + You can use setUpWidget's 'ignoreStickyValues' argument to override + this behavior and force the widget's value to be overwritten with + the 'value' argument: + + >>> delattr(view, 'foo_widget') + >>> setUpWidget(view, 'foo', IContent['foo'], IInputWidget, + ... value="A New Value", ignoreStickyValues=True) + >>> view.foo_widget.getRenderedValue() + 'A New Value' + + >>> tearDown() + """ + +class TestSetUpWidgets(object): + + def test_typical(self): + """Tests the typical use of setUpWidgets. + + >>> setUp() + + The simplest use of setUpWidget configures a view with widgets of a + particular type for a schema: + + >>> view = BrowserView(Content(), request) + >>> setUpWidgets(view, IContent, IWidget) + + The view now has two widgets, one for each field in the specified + schema: + + >>> IWidget.providedBy(view.foo_widget) + True + >>> IWidget.providedBy(view.bar_widget) + True + + Because we did not provide initial values, the widget values are not + configured: + + >>> view.foo_widget.renderedValueSet() + False + >>> view.bar_widget.renderedValueSet() + False + + To specify initial values for the widgets, we can use the 'initial' + argument: + + >>> view = BrowserView(Content(), request) + >>> initial = {'foo': 'Value of Foo', 'bar': 'Value of Bar'} + >>> setUpWidgets(view, IContent, IWidget, initial=initial) + >>> view.foo_widget.getRenderedValue() + 'Value of Foo' + >>> view.bar_widget.getRenderedValue() + 'Value of Bar' + + >>> tearDown() + """ + + def test_names(self): + """Documents and tests the use of names in setUpWidgets. + + >>> setUp() + + The names argument can be used to configure a specific set of widgets + for a view: + + >>> view = BrowserView(Content(), request) + >>> IContent.names() + ['foo', 'bar'] + >>> setUpWidgets(view, IContent, IWidget, names=('bar',)) + >>> hasattr(view, 'foo_widget') + False + >>> hasattr(view, 'bar_widget') + True + + >>> tearDown() + """ + + def test_delegation(self): + """Tests setUpWidgets' use of setUpWidget. + + >>> setUp() + + setUpWidgets delegates several of its arguments to multiple calls to + setUpWidget - one call for each widget being configured. The arguments + passed directly through to calls to setUpWidget are: + + view + viewType + prefix + ignoreStickyValues + context + + To illustrate this, we'll replace setUpWidget in the utility module + and capture arguments passed to it when setUpWidgets is called. + + >>> def setUpWidget(view, name, field, viewType, value=None, + ... prefix=None, ignoreStickyValues=False, + ... context=None): + ... print "view: %s" % view.__class__ + ... print "name: %s" % name + ... print "field: %s" % field.__class__ + ... print "viewType: %s" % viewType.__class__ + ... if value is no_value: + ... print "value: not specified" + ... else: + ... print "value: %s" % value + ... print "prefix %s" % prefix + ... print "ignoreStickyValues: %s" % ignoreStickyValues + ... print "context: %s" % context + ... print '---' + >>> import zope.formlib.utility + >>> setUpWidgetsSave = zope.formlib.utility.setUpWidget + >>> zope.formlib.utility.setUpWidget = setUpWidget + + When we call setUpWidgets, we should see that setUpWidget is called + for each field in the specified schema: + + >>> view = BrowserView(Content(), request) + >>> setUpWidgets(view, IContent, IWidget, 'prefix', True, + ... initial={ "bar":"Bar" }, + ... context="Alt Context") + view: + name: foo + field: + viewType: + value: not specified + prefix prefix + ignoreStickyValues: True + context: Alt Context + --- + view: + name: bar + field: + viewType: + value: Bar + prefix prefix + ignoreStickyValues: True + context: Alt Context + --- + >>> zope.formlib.utility.setUpWidget = setUpWidgetsSave + + >>> tearDown() + """ + + def test_forbiddenAttributes(self): + """Tests that forbidden attributes cause an error in widget setup. + + >>> setUp() + + If an attribute cannot be read from a source object because it's + forbidden, the ForbiddenAttribute error is allowed to pass through + to the caller. + + We'll create a field that raises a ForbiddenError itself to simulate + what would happen when a proxied object's attribute is accessed without + the required permission. + + >>> class AlwaysForbidden(Field): + ... def get(self, source): + ... raise ForbiddenAttribute(source, self.__name__) + + We'll also create a schema using this field: + + >>> class IMySchema(Interface): + ... tryme = AlwaysForbidden() + + When we use setUpEditWidgets to configure a view with IMySchema: + + >>> view = BrowserView('some context', TestRequest()) + >>> setUpEditWidgets(view, IMySchema) + Traceback (most recent call last): + ForbiddenAttribute: ('some context', 'tryme') + + The same applies to setUpDisplayWidgets: + + >>> setUpDisplayWidgets(view, IMySchema) + Traceback (most recent call last): + ForbiddenAttribute: ('some context', 'tryme') + + >>> tearDown() + """ + +class TestFormSetUp(object): + + def test_setUpEditWidgets(self): + """Documents and tests setUpEditWidgets. + + >>> setUp() + + setUpEditWidgets configures a view to collect field values from a + user. The function looks up widgets of type IInputWidget for the + specified schema. + + We'll first create and register widgets for the schema fields for + which we want input: + + >>> class InputWidget(Widget): + ... implements(IInputWidget) + ... def hasInput(self): + ... return False + ... def getRenderedValue(self): return self._data + >>> ztapi.browserViewProviding(IFoo, InputWidget, IInputWidget) + >>> ztapi.browserViewProviding(IBar, InputWidget, IInputWidget) + + Next we'll configure a view with a context object: + + >>> context = Content() + >>> context.foo = 'abc' + >>> context.bar = 'def' + >>> view = BrowserView(context, request) + + A call to setUpEditWidgets with the view: + + >>> setUpEditWidgets(view, IContent) + ['foo', 'bar'] + + configures the view with widgets that accept input for the context + field values: + + >>> isinstance(view.foo_widget, InputWidget) + True + >>> view.foo_widget.getRenderedValue() + 'abc' + >>> isinstance(view.bar_widget, InputWidget) + True + >>> view.bar_widget.getRenderedValue() + 'def' + + setUpEditWidgets provides a 'source' argument that provides an + alternate source of values to be edited: + + >>> view = BrowserView(context, request) + >>> source = Content() + >>> source.foo = 'abc2' + >>> source.bar = 'def2' + >>> setUpEditWidgets(view, IContent, source=source) + ['foo', 'bar'] + >>> view.foo_widget.getRenderedValue() + 'abc2' + >>> view.bar_widget.getRenderedValue() + 'def2' + + If a field is read only, setUpEditWidgets will use a display widget + (IDisplayWidget) intead of an input widget to display the field value. + + >>> class DisplayWidget(Widget): + ... implements(IDisplayWidget) + >>> ztapi.browserViewProviding(IFoo, DisplayWidget, IDisplayWidget) + >>> save = IContent['foo'].readonly # save readonly value + >>> IContent['foo'].readonly = True + >>> delattr(view, 'foo_widget') + >>> setUpEditWidgets(view, IContent) + ['foo', 'bar'] + >>> isinstance(view.foo_widget, DisplayWidget) + True + >>> IContent['foo'].readonly = save # restore readonly value + + By default, setUpEditWidgets raises Unauthorized if it is asked to + set up a field to which the user does not have permission to + access or to change. In the definition of the ExtendedContent + interface, notice the __Security_checker__ attribute, which stubs + out a checker that allows the user to view the bar attribute, + but not set it, and call getAnotherBaz but not setAnotherBaz. + + >>> view.context = context = zope.security.checker.Proxy( + ... ExtendedContent(), extended_checker) + >>> setUpEditWidgets(view, IExtendedContent, names=['bar']) + ... # can' write to bar + Traceback (most recent call last): + ... + Unauthorized: bar + >>> setUpEditWidgets( + ... view, IExtendedContent, names=['getAnotherBaz']) + ... # can't access the setter, setAnotherBaz + Traceback (most recent call last): + ... + Unauthorized: setAnotherBaz + >>> setUpEditWidgets( + ... view, IExtendedContent, names=['shazam']) + ... # can't even access shazam + Traceback (most recent call last): + ... + Unauthorized + + Two optional flags can change this behavior. degradeDisplay=True + causes the form machinery to skip fields silently that the user may + not access. In this case, the return value of setUpEditWidgets-- + a list of the field names set up--will be different that the names + provided to the function. + + >>> delattr(view, 'foo_widget') + >>> delattr(view, 'bar_widget') + >>> ztapi.browserViewProviding(IBaz, InputWidget, IInputWidget) + >>> setUpEditWidgets( + ... view, IExtendedContent, names=['foo', 'shazam', 'getBaz'], + ... degradeDisplay=True) + ['foo', 'getBaz'] + >>> IInputWidget.providedBy(view.foo_widget) + True + >>> IInputWidget.providedBy(view.getBaz_widget) + True + >>> view.shazam_widget + Traceback (most recent call last): + ... + AttributeError: 'BrowserView' object has no attribute 'shazam_widget' + + Similarly, degradeInput=True causes the function to degrade to + display widgets for any fields that the current user cannot change, + but can see. + + >>> delattr(view, 'foo_widget') + >>> delattr(view, 'getBaz_widget') + >>> ztapi.browserViewProviding(IBar, DisplayWidget, IDisplayWidget) + >>> ztapi.browserViewProviding(IBaz, DisplayWidget, IDisplayWidget) + >>> setUpEditWidgets( + ... view, IExtendedContent, + ... names=['foo', 'bar', 'getBaz', 'getAnotherBaz'], + ... degradeInput=True) + ['foo', 'bar', 'getBaz', 'getAnotherBaz'] + >>> IInputWidget.providedBy(view.foo_widget) + True + >>> IDisplayWidget.providedBy(view.bar_widget) + True + >>> IInputWidget.providedBy(view.getBaz_widget) + True + >>> IDisplayWidget.providedBy(view.getAnotherBaz_widget) + True + + Note that if the user cannot view the current value then they cannot + view the input widget. The two flags can then, of course, be used + together. + + >>> delattr(view, 'foo_widget') + >>> delattr(view, 'bar_widget') + >>> delattr(view, 'getBaz_widget') + >>> delattr(view, 'getAnotherBaz_widget') + >>> setUpEditWidgets( + ... view, IExtendedContent, + ... names=['foo', 'bar', 'shazam', 'getBaz', 'getAnotherBaz'], + ... degradeInput=True) + Traceback (most recent call last): + ... + Unauthorized + >>> setUpEditWidgets( + ... view, IExtendedContent, + ... names=['foo', 'bar', 'shazam', 'getBaz', 'getAnotherBaz'], + ... degradeInput=True, degradeDisplay=True) + ['foo', 'bar', 'getBaz', 'getAnotherBaz'] + >>> IInputWidget.providedBy(view.foo_widget) + True + >>> IDisplayWidget.providedBy(view.bar_widget) + True + >>> IInputWidget.providedBy(view.getBaz_widget) + True + >>> IDisplayWidget.providedBy(view.getAnotherBaz_widget) + True + >>> view.shazam_widget + Traceback (most recent call last): + ... + AttributeError: 'BrowserView' object has no attribute 'shazam_widget' + + >>> tearDown() + """ + + def test_setUpDisplayWidgets(self): + """Documents and tests setUpDisplayWidgets. + + >>> setUp() + + setUpDisplayWidgets configures a view for use as a display only form. + The function looks up widgets of type IDisplayWidget for the specified + schema. + + We'll first create and register widgets for the schema fields + we want to edit: + + >>> class DisplayWidget(Widget): + ... implements(IDisplayWidget) + ... def getRenderedValue(self): return self._data + >>> ztapi.browserViewProviding(IFoo, DisplayWidget, IDisplayWidget) + >>> ztapi.browserViewProviding(IBar, DisplayWidget, IDisplayWidget) + + Next we'll configure a view with a context object: + + >>> context = Content() + >>> context.foo = 'abc' + >>> context.bar = 'def' + >>> view = BrowserView(context, request) + + A call to setUpDisplayWidgets with the view: + + >>> setUpDisplayWidgets(view, IContent) + ['foo', 'bar'] + + configures the view with widgets that display the context fields: + + >>> isinstance(view.foo_widget, DisplayWidget) + True + >>> view.foo_widget.getRenderedValue() + 'abc' + >>> isinstance(view.bar_widget, DisplayWidget) + True + >>> view.bar_widget.getRenderedValue() + 'def' + + Like setUpEditWidgets, setUpDisplayWidgets accepts a 'source' + argument that provides an alternate source of values to be edited: + + >>> view = BrowserView(context, request) + >>> source = Content() + >>> source.foo = 'abc2' + >>> source.bar = 'def2' + >>> setUpDisplayWidgets(view, IContent, source=source) + ['foo', 'bar'] + >>> view.foo_widget.getRenderedValue() + 'abc2' + >>> view.bar_widget.getRenderedValue() + 'def2' + + Also like setUpEditWidgets, the degradeDisplay flag allows widgets + to silently disappear if they are unavailable. + + >>> view.context = context = zope.security.checker.Proxy( + ... ExtendedContent(), extended_checker) + >>> delattr(view, 'foo_widget') + >>> delattr(view, 'bar_widget') + >>> ztapi.browserViewProviding(IBaz, DisplayWidget, IDisplayWidget) + >>> setUpDisplayWidgets( + ... view, IExtendedContent, + ... names=['foo', 'bar', 'shazam', 'getBaz', 'getAnotherBaz']) + Traceback (most recent call last): + ... + Unauthorized + >>> setUpDisplayWidgets( + ... view, IExtendedContent, + ... names=['foo', 'bar', 'shazam', 'getBaz', 'getAnotherBaz'], + ... degradeDisplay=True) + ['foo', 'bar', 'getBaz', 'getAnotherBaz'] + >>> IDisplayWidget.providedBy(view.foo_widget) + True + >>> IDisplayWidget.providedBy(view.bar_widget) + True + >>> IDisplayWidget.providedBy(view.getBaz_widget) + True + >>> IDisplayWidget.providedBy(view.getAnotherBaz_widget) + True + >>> view.shazam_widget + Traceback (most recent call last): + ... + AttributeError: 'BrowserView' object has no attribute 'shazam_widget' + + >>> tearDown() + """ + +class TestForms(object): + + def test_viewHasInput(self): + """Tests viewHasInput. + + >>> setUp() + + viewHasInput returns True if any of the widgets for a set of fields + have user input. + + This method is typically invoked on a view that has been configured + with one setUpEditWidgets. + + >>> class InputWidget(Widget): + ... implements(IInputWidget) + ... input = None + ... def hasInput(self): + ... return self.input is not None + >>> ztapi.browserViewProviding(IFoo, InputWidget, IInputWidget) + >>> ztapi.browserViewProviding(IBar, InputWidget, IInputWidget) + >>> view = BrowserView(Content(), request) + >>> setUpEditWidgets(view, IContent) + ['foo', 'bar'] + + Because InputWidget is configured to not have input by default, the + view does not have input: + + >>> viewHasInput(view, IContent) + False + + But if we specify input for at least one widget: + + >>> view.foo_widget.input = 'Some Value' + >>> viewHasInput(view, IContent) + True + + >>> tearDown() + """ + + def test_applyWidgetsChanges(self): + """Documents and tests applyWidgetsChanges. + + >>> setUp() + + applyWidgetsChanges updates the view context, or an optional alternate + context, with widget values. This is typically called when a form + is submitted. + + We'll first create a simple edit widget that can be used to update + an object: + + >>> class InputWidget(Widget): + ... implements(IInputWidget) + ... input = None + ... valid = True + ... def hasInput(self): + ... return input is not None + ... def applyChanges(self, object): + ... if not self.valid: + ... raise ConversionError('invalid input') + ... field = self.context + ... field.set(object, self.input) + ... return True + >>> ztapi.browserViewProviding(IFoo, InputWidget, IInputWidget) + >>> ztapi.browserViewProviding(IBar, InputWidget, IInputWidget) + + Before calling applyWidgetsUpdate, we need to configure a context and + a view with edit widgets: + + >>> context = Content() + >>> view = BrowserView(context, request) + >>> setUpEditWidgets( + ... view, IContent, context=context, names=('foo',)) + ['foo'] + + We now specify new widget input and apply the changes: + + >>> view.foo_widget.input = 'The quick brown fox...' + >>> context.foo + 'Foo' + >>> applyWidgetsChanges(view, IContent, names=('foo',)) + True + >>> context.foo + 'The quick brown fox...' + + By default, applyWidgetsChanges applies the new widget values to the + view context. Alternatively, we can provide a 'target' argument to + be updated: + + >>> target = Content() + >>> target.foo + 'Foo' + >>> applyWidgetsChanges(view, IContent, target=target, + ... names=('foo',)) + True + >>> target.foo + 'The quick brown fox...' + + applyWidgetsChanges is typically used in conjunction with one of the + setUp utility functions. If applyWidgetsChanges is called using a + view that was not previously configured with a setUp function, or + was not otherwise configured with widgets for each of the applicable + fields, an AttributeError will be raised: + + >>> view = BrowserView(context, request) + >>> applyWidgetsChanges(view, IContent, names=('foo',)) + Traceback (most recent call last): + AttributeError: 'BrowserView' object has no attribute 'foo_widget' + + When applyWidgetsChanges is called with multiple form + fields, some with valid data and some with invalid data, + *changes may be applied*. For instance, below see that context.foo + changes from 'Foo' to 'a' even though trying to change context.bar + fails. Generally, ZODB transactional behavior is expected to + correct this sort of problem. + + >>> context = Content() + >>> view = BrowserView(context, request) + >>> setUpEditWidgets(view, IContent, names=('foo', 'bar')) + ['foo', 'bar'] + >>> view.foo_widget.input = 'a' + >>> view.bar_widget.input = 'b' + >>> view.bar_widget.valid = False + >>> context.foo + 'Foo' + >>> getattr(context, 'bar', 'not really') + 'not really' + >>> applyWidgetsChanges(view, IContent, names=('foo', 'bar')) + Traceback (most recent call last): + WidgetsError: ConversionError: ('invalid input', None) + >>> context.foo + 'a' + >>> getattr(context, 'bar', 'not really') + 'not really' + + >>> tearDown() + """ + +class TestGetWidgetsData(object): + + def test_typical(self): + """Documents and tests the typical use of getWidgetsData. + + >>> setUp() + + getWidgetsData retrieves the current values from widgets on a view. + For this test, we'll create a simple edit widget and register it + for the schema field types: + + >>> class InputWidget(Widget): + ... implements(IInputWidget) + ... input = None + ... def hasInput(self): + ... return self.input is not None + ... def getInputValue(self): + ... return self.input + >>> ztapi.browserViewProviding(IFoo, InputWidget, IInputWidget) + >>> ztapi.browserViewProviding(IBar, InputWidget, IInputWidget) + + We use setUpEditWidgets to configure a view with widgets for the + IContent schema: + + >>> view = BrowserView(Content(), request) + >>> setUpEditWidgets(view, IContent) + ['foo', 'bar'] + + The simplest form of getWidgetsData requires a view and a schema: + + >>> try: + ... result = getWidgetsData(view, IContent) + ... except Exception, e: + ... print 'getWidgetsData failed' + ... e + getWidgetsData failed + MissingInputError: ('foo', u'', 'the field is required') + MissingInputError: ('bar', u'', 'the field is required') + + We see that getWidgetsData raises a MissingInputError if a required + field does not have input from a widget.: + + >>> view.foo_widget.input = 'Input for foo' + >>> view.bar_widget.input = 'Input for bar' + >>> result = getWidgetsData(view, IContent) + + The result of getWidgetsData is a map of field names to widget values. + + >>> keys = result.keys(); keys.sort(); keys + ['bar', 'foo'] + >>> result['foo'] + 'Input for foo' + >>> result['bar'] + 'Input for bar' + + If a field is not required, however: + + >>> IContent['foo'].required = False + + we can omit input for its widget: + + >>> view.foo_widget.input = None + >>> result = getWidgetsData(view, IContent) + >>> 'foo' in result + False + + Note that when a widget indicates that is does not have input, its + results are omitted from getWidgetsData's return value. Users of + getWidgetsData should explicitly check for field values before + accessing them: + + >>> for name in IContent: + ... if name in result: + ... print (name, result[name]) + ('bar', 'Input for bar') + + You can also specify an optional 'names' argument (a tuple) to + request a subset of the schema fields: + + >>> result = getWidgetsData(view, IContent, names=('bar',)) + >>> result.keys() + ['bar'] + + >>> tearDown() + """ + + def test_widgetsErrorException(self): + """Documents and tests WidgetsError. + + WidgetsError wraps one or more errors, which are specified as a + sequence in the 'errors' argument: + + >>> error = WidgetsError(('foo',)) + >>> error + str: foo + + WidgetsError also provides a 'widgetsData' attribute, which is a + map of valid field values, keyed by field name, that were obtained + in the same read operation that generated the errors: + + >>> error = WidgetsError(('foo',), widgetsData={'bar': 'Bar'}) + >>> error.widgetsData + {'bar': 'Bar'} + + The most typical use of this error is when reading a set of widget + values -- the read operation can generate more than one error, as well + as a set of successfully read values: + + >>> values = {'foo': 'Foo'} + >>> errors = [] + >>> widgetsData = {} + >>> for name in ('foo', 'bar'): # some list of values to read + ... try: + ... widgetsData[name] = values[name] # read operation + ... except Exception, e: + ... errors.append(e) # capture all errors + >>> if errors: + ... widgetsError = WidgetsError(errors, widgetsData) + ... raise widgetsError + Traceback (most recent call last): + WidgetsError: KeyError: 'bar' + + The handler of error can access all of the widget error as well as + the widget values read: + + >>> for error in widgetsError: + ... error.__class__.__name__ + 'KeyError' + >>> widgetsError.widgetsData + {'foo': 'Foo'} + """ + +def test_suite(): + return doctest.DocTestSuite() diff -Nru zope3-3.4.0/src/zope/app/form/tests/utils.py zope3-3.5~bzr18/src/zope/app/form/tests/utils.py --- zope3-3.4.0/src/zope/app/form/tests/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/tests/utils.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,81 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 testing form machinery + +$Id: utils.py 29405 2005-03-07 18:22:16Z poster $ +""" +from zope.interface.interfaces import IMethod +from zope.security.interfaces import ForbiddenAttribute, Unauthorized +import zope.security.checker +from zope.schema import getFieldsInOrder + +class DummyChecker(object): + """a checker for testing that requires explicit declarations + + requires explicit declaration of what is and is not authorized; does not + require testing machinery to set up an interaction or a request. + + To instantiate, pass two dictionaries, the first for get access attribute + protection, and the second for set access attribute protection. keys + should be the attribute names, and values should be boolean True and + False, where True indicates authorized and False, unauthorized. Any + attributes that are not explicitly set and, in the case of get protection, + are not in the zope.security.checker._available_by_default list, + will cause ForbiddenAttribute to be raised when the name is checked, as + with the real zope.security checkers. + """ + def __init__(self, getnames, setnames): + self.getnames = getnames + self.setnames = setnames + def check_getattr(self, obj, name): + if name not in zope.security.checker._available_by_default: + try: + val = self.getnames[name] + except KeyError: + raise ForbiddenAttribute + else: + if not val: + raise Unauthorized + check = check_getattr + def check_setattr(self, obj, name): + try: + val = self.setnames[name] + except KeyError: + raise ForbiddenAttribute + else: + if not val: + raise Unauthorized + def proxy(self, value): + return value + +def SchemaChecker(schema, readonly=False): + """returns a checker that allows read and write access to fields in schema. + """ + get = {} + set = {} + for name, field in getFieldsInOrder(schema): + get[name] = True + if not field.readonly: + if IMethod.providedBy(field): + get[field.writer.__name__] = True + else: + set[name] = True + if readonly: + for nm in set: + set[nm] = False + return DummyChecker(get, set) + +def securityWrap(ob, schema, readonly=False): + return zope.security.checker.Proxy(ob, SchemaChecker(schema, readonly)) + diff -Nru zope3-3.4.0/src/zope/app/form/utility.py zope3-3.5~bzr18/src/zope/app/form/utility.py --- zope3-3.4.0/src/zope/app/form/utility.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/form/utility.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,228 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Form utility functions + +In Zope 2's formulator, forms provide a basic mechanism for +organizing collections of fields and providing user interfaces for +them, especially editing interfaces. + +In Zope 3, formulator's forms are replaced by Schema (See +zope.schema). In addition, the Formulator fields have been replaced by +schema fields and form widgets. Schema fields just express the semantics +of data values. They contain no presentation logic or parameters. +Widgets are views on fields that take care of presentation. The widget +view names represent styles that can be selected by applications to +customise the presentation. There can also be custom widgets with +specific parameters. + +This module provides some utility functions that provide some of the +functionality of formulator forms that isn't handled by schema, +fields, or widgets. + +$Id: utility.py 107385 2009-12-30 20:25:24Z faassen $ +""" +__docformat__ = 'restructuredtext' + +from zope import security +from zope.security.proxy import Proxy +from zope.proxy import isProxy +from zope.interface.interfaces import IMethod +from zope.security.interfaces import ForbiddenAttribute, Unauthorized +from zope.formlib.interfaces import WidgetsError, MissingInputError +from zope.formlib.interfaces import InputErrors +from zope.formlib.interfaces import IInputWidget, IDisplayWidget +# BBB +from zope.formlib.utility import ( + setUpWidget, + setUpWidgets, + applyWidgetsChanges, + _fieldlist, + no_value, + _widgetHasStickyValue) + +def setUpEditWidgets(view, schema, source=None, prefix=None, + ignoreStickyValues=False, names=None, context=None, + degradeInput=False, degradeDisplay=False): + """Sets up widgets to collect input on a view. + + See `setUpWidgets` for details on `view`, `schema`, `prefix`, + `ignoreStickyValues`, `names`, and `context`. + + `source`, if specified, is an object from which initial widget values are + read. If source is not specified, the view context is used as the source. + + `degradeInput` is a flag that changes the behavior when a user does not + have permission to edit a field in the names. By default, the function + raises Unauthorized. If degradeInput is True, the field is changed to + an IDisplayWidget. + + `degradeDisplay` is a flag that changes the behavior when a user does not + have permission to access a field in the names. By default, the function + raises Unauthorized. If degradeDisplay is True, the field is removed from + the form. + + Returns a list of names, equal to or a subset of the names that were + supposed to be drawn, with uninitialized undrawn fields missing. + """ + if context is None: + context = view.context + if source is None: + source = view.context + security_proxied = isProxy(source, Proxy) + res_names = [] + for name, field in _fieldlist(names, schema): + try: + value = field.get(source) + except ForbiddenAttribute: + raise + except AttributeError: + value = no_value + except Unauthorized: + if degradeDisplay: + continue + else: + raise + if field.readonly: + viewType = IDisplayWidget + else: + if security_proxied: + is_accessor = IMethod.providedBy(field) + if is_accessor: + set_name = field.writer.__name__ + authorized = security.canAccess(source, set_name) + else: + set_name = name + authorized = security.canWrite(source, name) + if not authorized: + if degradeInput: + viewType = IDisplayWidget + else: + raise Unauthorized(set_name) + else: + viewType = IInputWidget + else: + # if object is not security proxied, might be a standard + # adapter without a registered checker. If the feature of + # paying attention to the users ability to actually set a + # field is decided to be a must-have for the form machinery, + # then we ought to change this case to have a deprecation + # warning. + viewType = IInputWidget + setUpWidget(view, name, field, viewType, value, prefix, + ignoreStickyValues, context) + res_names.append(name) + return res_names + +def setUpDisplayWidgets(view, schema, source=None, prefix=None, + ignoreStickyValues=False, names=None, context=None, + degradeDisplay=False): + """Sets up widgets to display field values on a view. + + See `setUpWidgets` for details on `view`, `schema`, `prefix`, + `ignoreStickyValues`, `names`, and `context`. + + `source`, if specified, is an object from which initial widget values are + read. If source is not specified, the view context is used as the source. + + `degradeDisplay` is a flag that changes the behavior when a user does not + have permission to access a field in the names. By default, the function + raises Unauthorized. If degradeDisplay is True, the field is removed from + the form. + + Returns a list of names, equal to or a subset of the names that were + supposed to be drawn, with uninitialized undrawn fields missing. + """ + if context is None: + context = view.context + if source is None: + source = view.context + res_names = [] + for name, field in _fieldlist(names, schema): + try: + value = field.get(source) + except ForbiddenAttribute: + raise + except AttributeError: + value = no_value + except Unauthorized: + if degradeDisplay: + continue + else: + raise + setUpWidget(view, name, field, IDisplayWidget, value, prefix, + ignoreStickyValues, context) + res_names.append(name) + return res_names + +def viewHasInput(view, schema, names=None): + """Returns ``True`` if the any of the view's widgets contain user input. + + `schema` specifies the set of fields that correspond to the view widgets. + + `names` can be specified to provide a subset of these fields. + """ + for name, field in _fieldlist(names, schema): + if getattr(view, name + '_widget').hasInput(): + return True + return False + +def getWidgetsData(view, schema, names=None): + """Returns user entered data for a set of `schema` fields. + + The return value is a map of field names to data values. + + `view` is the view containing the widgets. `schema` is the schema that + defines the widget fields. An optional `names` argument can be provided + to specify an alternate list of field values to return. If `names` is + not specified, or is ``None``, `getWidgetsData` will attempt to return + values for all of the fields in the schema. + + A requested field value may be omitted from the result for one of two + reasons: + + - The field is read only, in which case its widget will not have + user input. + + - The field is editable and not required but its widget does not + contain user input. + + If a field is required and its widget does not have input, `getWidgetsData` + raises an error. + + A widget may raise a validation error if it cannot return a value that + satisfies its field's contraints. + + Errors, if any, are collected for all fields and reraised as a single + `WidgetsError`. + """ + result = {} + errors = [] + + for name, field in _fieldlist(names, schema): + widget = getattr(view, name + '_widget') + if IInputWidget.providedBy(widget): + if widget.hasInput(): + try: + result[name] = widget.getInputValue() + except InputErrors, error: + errors.append(error) + elif field.required: + errors.append(MissingInputError( + name, widget.label, 'the field is required')) + + if errors: + raise WidgetsError(errors, widgetsData=result) + + return result + diff -Nru zope3-3.4.0/src/zope/app/http/configure.zcml zope3-3.5~bzr18/src/zope/app/http/configure.zcml --- zope3-3.4.0/src/zope/app/http/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/http/delete.py zope3-3.5~bzr18/src/zope/app/http/delete.py --- zope3-3.4.0/src/zope/app/http/delete.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/delete.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,42 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""HTTP DELETE verb + +$Id: delete.py 100271 2009-05-23 01:49:25Z shane $ +""" +__docformat__ = 'restructuredtext' + +from zope.filerepresentation.interfaces import IWriteDirectory +from zope.publisher.interfaces.http import MethodNotAllowed + + +class DELETE(object): + """Delete handler for all objects + """ + + def __init__(self, context, request): + self.context = context + self.request = request + + def DELETE(self): + victim = self.context + container = victim.__parent__ + name = victim.__name__ + + # Get a "directory" surrogate for the container + dir = IWriteDirectory(container, None) + if dir is None: + raise MethodNotAllowed(self.context, self.request) + + del dir[name] + + return '' diff -Nru zope3-3.4.0/src/zope/app/http/exception/configure.zcml zope3-3.5~bzr18/src/zope/app/http/exception/configure.zcml --- zope3-3.4.0/src/zope/app/http/exception/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/exception/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/http/exception/__init__.py zope3-3.5~bzr18/src/zope/app/http/exception/__init__.py --- zope3-3.4.0/src/zope/app/http/exception/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/exception/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# empty __init__.py file to make this directory into a package diff -Nru zope3-3.4.0/src/zope/app/http/exception/methodnotallowed.py zope3-3.5~bzr18/src/zope/app/http/exception/methodnotallowed.py --- zope3-3.4.0/src/zope/app/http/exception/methodnotallowed.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/exception/methodnotallowed.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""HTTP 405: Method Not Allowed view + +$Id: methodnotallowed.py 108613 2010-01-28 16:07:04Z adamg $ +""" +from zope.component import getAdapters +from zope.interface import Interface +from zope.publisher.interfaces.http import IMethodNotAllowed + + +class MethodNotAllowedView(object): + """A view for MethodNotAllowed that renders a HTTP 405 response.""" + + __used_for__ = IMethodNotAllowed + + def __init__(self, error, request): + self.error = error + self.request = request + allow = [] + + try: + # see test_methodnotallowed.TestMethodNotAllowedView.test_defaultView + # I could not solve this with a while ... next() iterator + # because it seems like once the generator had an exception it + # stops returning items + self.allow = [ + name for name, adapter + in getAdapters((error.object, error.request), Interface) + if hasattr(adapter, name)] + self.allow.sort() + except TypeError: + self.allow = [] + + def __call__(self): + self.request.response.setHeader('Allow', ', '.join(self.allow)) + self.request.response.setStatus(405) + return 'Method Not Allowed' diff -Nru zope3-3.4.0/src/zope/app/http/exception/notfound.py zope3-3.5~bzr18/src/zope/app/http/exception/notfound.py --- zope3-3.4.0/src/zope/app/http/exception/notfound.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/exception/notfound.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Not found Exception + +$Id: notfound.py 100273 2009-05-23 02:20:25Z shane $ +""" +__docformat__ = 'restructuredtext' + +from zope.publisher.interfaces.http import IHTTPException +from zope.interface import implements + +class NotFound(object): + + implements(IHTTPException) + + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + if self.request.method in ['MKCOL'] and \ + self.request.getTraversalStack(): + # MKCOL with non-existing parent. + self.request.response.setStatus(409) + else: + self.request.response.setStatus(404) + return '' + + __str__ = __call__ diff -Nru zope3-3.4.0/src/zope/app/http/exception/tests/__init__.py zope3-3.5~bzr18/src/zope/app/http/exception/tests/__init__.py --- zope3-3.4.0/src/zope/app/http/exception/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/exception/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# empty __init__.py file to make this directory into a package diff -Nru zope3-3.4.0/src/zope/app/http/exception/tests/test_methodnotallowed.py zope3-3.5~bzr18/src/zope/app/http/exception/tests/test_methodnotallowed.py --- zope3-3.4.0/src/zope/app/http/exception/tests/test_methodnotallowed.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/exception/tests/test_methodnotallowed.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,122 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP error views + +$Id: test_methodnotallowed.py 110898 2010-04-14 14:40:14Z faassen $ +""" +from unittest import TestCase, TestSuite, main, makeSuite +from StringIO import StringIO + +from zope.interface import Interface, implements +from zope.publisher.http import HTTPRequest +from zope.publisher.interfaces.http import IHTTPRequest + +from zope.component import provideAdapter + + +class I(Interface): + pass + + +class C(object): + implements(I) + + +class GetView(object): + def __init__(self, context, request): + pass + def GET(self): + pass + + +class DeleteView(object): + def __init__(self, context, request): + pass + def DELETE(self): + pass + + +class TestMethodNotAllowedView(TestCase): + + def setUp(self): + from zope.publisher.interfaces.http import IHTTPRequest + + provideAdapter(GetView, (I, IHTTPRequest), Interface, 'GET') + provideAdapter(DeleteView, (I, IHTTPRequest), Interface, 'DELETE') + provideAdapter(GetView, (I, IHTTPRequest), Interface, 'irrelevant') + provideAdapter(DeleteView, (I, IHTTPRequest), Interface, 'also_irr.') + + from zope.publisher.interfaces import IDefaultViewName + from zope.publisher.interfaces.browser import IBrowserRequest + #do the same as defaultView would for something like: + # + + provideAdapter(u'index.html', (I, IBrowserRequest), IDefaultViewName) + + def test(self): + from zope.publisher.interfaces.http import MethodNotAllowed + from zope.app.http.exception.methodnotallowed \ + import MethodNotAllowedView + from zope.publisher.http import HTTPRequest + + context = C() + request = HTTPRequest(StringIO('PUT /bla/bla HTTP/1.1\n\n'), {}) + error = MethodNotAllowed(context, request) + view = MethodNotAllowedView(error, request) + + result = view() + + self.assertEqual(request.response.getStatus(), 405) + self.assertEqual(request.response.getHeader('Allow'), 'DELETE, GET') + self.assertEqual(result, 'Method Not Allowed') + + + def test_defaultView(self): + # do the same with a BrowserRequest + # edge case is that if someone does a defaultView for the context object + # but the app is not prepared for webdav or whatever + # and someone comes with a not allowed method, the exception + # view fails on getAdapters + # this might be an issue with zope.publisher, as it provides + # a unicode object with provideAdapter, but I don't think I can + # change zope.publisher + from zope.publisher.interfaces.http import MethodNotAllowed + from zope.app.http.exception.methodnotallowed \ + import MethodNotAllowedView + from zope.publisher.browser import BrowserRequest + + context = C() + request = BrowserRequest(StringIO('PUT /bla/bla HTTP/1.1\n\n'), {}) + + error = MethodNotAllowed(context, request) + view = MethodNotAllowedView(error, request) + + result = view() + + self.assertEqual(request.response.getStatus(), 405) + #well this is empty, but we're grateful that it does not break + self.assertEqual(request.response.getHeader('Allow'), '') + self.assertEqual(result, 'Method Not Allowed') + + +def test_suite(): + return TestSuite(( + makeSuite(TestMethodNotAllowedView), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/http/exception/tests/test_unauthorized.py zope3-3.5~bzr18/src/zope/app/http/exception/tests/test_unauthorized.py --- zope3-3.4.0/src/zope/app/http/exception/tests/test_unauthorized.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/exception/tests/test_unauthorized.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Unauthorized Exception Test + +$Id: test_unauthorized.py 100273 2009-05-23 02:20:25Z shane $ +""" +from unittest import TestCase, main, makeSuite +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces.http import IHTTPException + +class Test(TestCase): + + def testbasicauth(self): + from zope.app.http.exception.unauthorized import Unauthorized + exception = Exception() + try: + raise exception + except: + pass + request = TestRequest() + u = Unauthorized(exception, request) + + # Chech that we implement the right interface + self.failUnless(IHTTPException.providedBy(u)) + + # Call the view + u() + + # Make sure the response status was set + self.assertEqual(request.response.getStatus(), 401) + self.failUnless(request.response.getHeader('WWW-Authenticate', '', True).startswith('basic')) + +def test_suite(): + return makeSuite(Test) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/http/exception/unauthorized.py zope3-3.5~bzr18/src/zope/app/http/exception/unauthorized.py --- zope3-3.4.0/src/zope/app/http/exception/unauthorized.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/exception/unauthorized.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,35 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Unauthorized Exception + +$Id: unauthorized.py 100273 2009-05-23 02:20:25Z shane $ +""" +__docformat__ = 'restructuredtext' + +from zope.publisher.interfaces.http import IHTTPException +from zope.interface import implements + +class Unauthorized(object): + + implements(IHTTPException) + + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + self.request.unauthorized('basic realm="Zope"') + return '' + + __str__ = __call__ diff -Nru zope3-3.4.0/src/zope/app/http/ftesting.zcml zope3-3.5~bzr18/src/zope/app/http/ftesting.zcml --- zope3-3.4.0/src/zope/app/http/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/http/httpdate.py zope3-3.5~bzr18/src/zope/app/http/httpdate.py --- zope3-3.4.0/src/zope/app/http/httpdate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/httpdate.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,147 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""HTTP Server Date/Time utilities + +$Id: httpdate.py 38967 2005-10-08 16:27:57Z torsti $ +""" +import re +import string +import time +import calendar + +def concat(*args): + return ''.join(args) + +def join(seq, field=' '): + return field.join(seq) + +def group(s): + return '(' + s + ')' + +short_days = ['sun','mon','tue','wed','thu','fri','sat'] +long_days = ['sunday','monday','tuesday','wednesday', + 'thursday','friday','saturday'] + +short_day_reg = group(join(short_days, '|')) +long_day_reg = group(join(long_days, '|')) + +daymap = {} +for i in range(7): + daymap[short_days[i]] = i + daymap[long_days[i]] = i + +hms_reg = join(3 * [group('[0-9][0-9]')], ':') + +months = ['jan','feb','mar','apr','may','jun','jul', + 'aug','sep','oct','nov','dec'] + +monmap = {} +for i in range(12): + monmap[months[i]] = i+1 + +months_reg = group(join(months, '|')) + +# From draft-ietf-http-v11-spec-07.txt/3.3.1 +# Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 +# Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 +# Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + +# rfc822 format +rfc822_date = join( + [concat (short_day_reg,','), # day + group('[0-9][0-9]?'), # date + months_reg, # month + group('[0-9]+'), # year + hms_reg, # hour minute second + 'gmt' + ], + ' ' + ) + +rfc822_reg = re.compile(rfc822_date) + +def unpack_rfc822(m): + g = m.group + a = string.atoi + return ( + a(g(4)), # year + monmap[g(3)], # month + a(g(2)), # day + a(g(5)), # hour + a(g(6)), # minute + a(g(7)), # second + 0, + 0, + 0 + ) + + # rfc850 format +rfc850_date = join( + [concat(long_day_reg,','), + join( + [group ('[0-9][0-9]?'), + months_reg, + group ('[0-9]+') + ], + '-' + ), + hms_reg, + 'gmt' + ], + ' ' + ) + +rfc850_reg = re.compile(rfc850_date) +# they actually unpack the same way +def unpack_rfc850(m): + g = m.group + a = string.atoi + return ( + a(g(4)), # year + monmap[g(3)], # month + a(g(2)), # day + a(g(5)), # hour + a(g(6)), # minute + a(g(7)), # second + 0, + 0, + 0 + ) + + # parsdate.parsedate - ~700/sec. + # parse_http_date - ~1333/sec. + +weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] +monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + +def build_http_date(when): + year, month, day, hh, mm, ss, wd, y, z = time.gmtime(when) + return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( + weekdayname[wd], + day, monthname[month], year, + hh, mm, ss) + +def parse_http_date(d): + d = d.lower() + m = rfc850_reg.match(d) + if m and m.end() == len(d): + retval = int(calendar.timegm(unpack_rfc850(m))) + else: + m = rfc822_reg.match(d) + if m and m.end() == len(d): + retval = int(calendar.timegm(unpack_rfc822(m))) + else: + return 0 + return retval diff -Nru zope3-3.4.0/src/zope/app/http/__init__.py zope3-3.5~bzr18/src/zope/app/http/__init__.py --- zope3-3.4.0/src/zope/app/http/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/http/interfaces.py zope3-3.5~bzr18/src/zope/app/http/interfaces.py --- zope3-3.4.0/src/zope/app/http/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,30 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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-specific HTTP interfaces + +$Id: interfaces.py 100360 2009-05-25 16:35:29Z shane $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface, Attribute + +from zope.publisher.interfaces.http import IHTTPException #BBB import + +class INullResource(Interface): + """Placeholder objects for new container items to be created via PUT + """ + + container = Attribute("The container of the future resource") + name = Attribute("The name of the object to be created.") + diff -Nru zope3-3.4.0/src/zope/app/http/options.py zope3-3.5~bzr18/src/zope/app/http/options.py --- zope3-3.4.0/src/zope/app/http/options.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/options.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,51 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""HTTP method `OPTIONS` + +$Id: options.py 85611 2008-04-22 19:25:24Z lgs $ +""" +__docformat__ = 'restructuredtext' + +_allowed_methods = ['PUT', 'DELETE', 'CONNECT', \ + 'OPTIONS', 'PATCH', 'PROPFIND', 'PROPPATCH', 'MKCOL', \ + 'COPY', 'MOVE', 'LOCK', 'UNLOCK', 'TRACE'] + # 'GET', 'HEAD', 'POST' are always available. See OPTIONS() method. + +from zope.component import queryMultiAdapter + +class OPTIONS(object): + """`OPTIONS` handler for all objects + """ + + def __init__(self, context, request): + self.context = context + self.request = request + + def OPTIONS(self): + allowed = ['GET', 'HEAD', 'POST'] + # TODO: This could be cleaned up by providing special target + # interfaces for HTTP methods. This way we can even list verbs that + # are not in the lists above. + for m in _allowed_methods: + view = queryMultiAdapter((self.context, self.request), name=m) + if view is not None: + allowed.append(m) + + self.request.response.setHeader('Allow', ', '.join(allowed)) + # TODO: Most of the time, this is a lie. We not fully support + # DAV 2 on all objects, so probably an interface check is needed. + self.request.response.setHeader('DAV', '1,2', literal=True) + # UGLY! Some clients rely on this. eg: MacOS X + self.request.response.setHeader('MS-Author-Via', 'DAV', literal=True) + self.request.response.setStatus(200) + return '' + diff -Nru zope3-3.4.0/src/zope/app/http/put.py zope3-3.5~bzr18/src/zope/app/http/put.py --- zope3-3.4.0/src/zope/app/http/put.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/put.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,111 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""HTTP `PUT` verb + +$Id: put.py 97983 2009-03-12 13:36:41Z nadako $ +""" +__docformat__ = 'restructuredtext' + +from zope.component import queryAdapter +from zope.event import notify +from zope.lifecycleevent import ObjectCreatedEvent +from zope.interface import implements +from zope.filerepresentation.interfaces import IWriteFile +from zope.filerepresentation.interfaces import \ + IWriteDirectory, IReadDirectory, IFileFactory +import zope.traversing.browser + +from zope.app.http.interfaces import INullResource + +class NullResource(object): + """Object representing objects to be created by a `PUT`. + """ + + implements(INullResource) + + def __init__(self, container, name): + self.container = container + self.name = name + + +class NullPUT(object): + """Put handler for null resources (new file-like things) + + This view creates new objects in containers. + + """ + + def __init__(self, context, request): + self.context = context + self.request = request + + def PUT(self): + request = self.request + + body = request.bodyStream + name = self.context.name + container = self.context.container + + # Find the extension + ext_start = name.rfind('.') + if ext_start > 0: + ext = name[ext_start:] + else: + ext = "." + + # Get a "directory" surrogate for the container + # TODO: Argh. Why don't we have a unioned Interface for that?!? + dir_write = IWriteDirectory(container) + dir_read = IReadDirectory(container) + + # Now try to get a custom factory for he container + factory = queryAdapter(container, IFileFactory, ext) + + # Fall back to a non-custom one + if factory is None: + factory = IFileFactory(container) + + # TODO: Need to add support for large files + data = body.read() + + newfile = factory(name, request.getHeader('content-type', ''), data) + notify(ObjectCreatedEvent(newfile)) + + dir_write[name] = newfile + # Ickyness with non-predictable support for containment: + # make sure we get a containment proxy + newfile = dir_read[name] + + request.response.setStatus(201) + request.response.setHeader( + 'Location', zope.traversing.browser.absoluteURL(newfile, request)) + return '' + + +class FilePUT(object): + """Put handler for existing file-like things + + """ + + def __init__(self, context, request): + self.context = context + self.request = request + + def PUT(self): + body = self.request.bodyStream + file = self.context + adapter = IWriteFile(file) + + length = int(self.request.get('CONTENT_LENGTH', -1)) + adapter.write(body.read(length)) + + return '' diff -Nru zope3-3.4.0/src/zope/app/http/tests/__init__.py zope3-3.5~bzr18/src/zope/app/http/tests/__init__.py --- zope3-3.4.0/src/zope/app/http/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/http/tests/test_delete.py zope3-3.5~bzr18/src/zope/app/http/tests/test_delete.py --- zope3-3.4.0/src/zope/app/http/tests/test_delete.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/tests/test_delete.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,65 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP DELETE verb + +$Id: test_delete.py 110890 2010-04-14 14:05:18Z faassen $ +""" +from unittest import TestCase, TestSuite, makeSuite + +from zope.interface import implements +from zope.publisher.browser import TestRequest +from zope.filerepresentation.interfaces import IWriteDirectory, IFileFactory + +import zope.app.http.delete +from zope.container.contained import contained +from zope.publisher.interfaces.http import MethodNotAllowed + +class UnwritableContainer(object): + pass + + +class Container(object): + + implements(IWriteDirectory, IFileFactory) + + def __delitem__(self, name): + delattr(self, name) + + +class TestDelete(TestCase): + + def test(self): + container = Container() + container.a = 'spam' + item = contained(Container(), container, name='a') + + request = TestRequest() + delete = zope.app.http.delete.DELETE(item, request) + self.assert_(hasattr(container, 'a')) + self.assertEqual(delete.DELETE(), '') + self.assert_(not hasattr(container, 'a')) + + def test_not_deletable(self): + container = UnwritableContainer() + container.a = 'spam' + item = contained(UnwritableContainer(), container, name='a') + request = TestRequest() + delete = zope.app.http.delete.DELETE(item, request) + self.assertRaises(MethodNotAllowed, delete.DELETE) + + +def test_suite(): + return TestSuite(( + makeSuite(TestDelete), + )) diff -Nru zope3-3.4.0/src/zope/app/http/tests/test_functional_put.py zope3-3.5~bzr18/src/zope/app/http/tests/test_functional_put.py --- zope3-3.4.0/src/zope/app/http/tests/test_functional_put.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/tests/test_functional_put.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,63 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP PUT verb + +$Id: test_functional_put.py 111128 2010-04-19 17:50:18Z faassen $ +""" + +from unittest import TestSuite, TestCase, makeSuite + +from zope.app.wsgi.testlayer import http, BrowserLayer +import zope.app.http + +class TestPUT(TestCase): + + layer = BrowserLayer(zope.app.http) + + def test_put(self): + # PUT something for the first time + response = http(r"""PUT /testfile.txt HTTP/1.1 +Authorization: Basic globalmgr:globalmgrpw +Content-Length: 20 +Content-Type: text/plain + +This is just a test.""") + + self.assertEquals(response.getStatus(), 201) + self.assertEquals(response.getHeader("Location"), + "http://localhost/testfile.txt") + + response = http(r"""GET /testfile.txt HTTP/1.1 +Authorization: Basic globalmgr:globalmgrpw""") + self.assertEquals(response.getBody(), "This is just a test.") + + # now modify it + response = http(r"""PUT /testfile.txt HTTP/1.1 +Authorization: Basic globalmgr:globalmgrpw +Content-Length: 23 +Content-Type: text/plain + +And now it is modified.""") + self.assertEquals(response.getStatus(), 200) + self.assertEquals(response.getBody(), "") + + response = http(r"""GET /testfile.txt HTTP/1.1 +Authorization: Basic globalmgr:globalmgrpw""") + self.assertEquals(response.getBody(), "And now it is modified.") + + +def test_suite(): + return TestSuite(( + makeSuite(TestPUT), + )) diff -Nru zope3-3.4.0/src/zope/app/http/tests/test_options.py zope3-3.5~bzr18/src/zope/app/http/tests/test_options.py --- zope3-3.4.0/src/zope/app/http/tests/test_options.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/tests/test_options.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,76 @@ +############################################################################## +# +# Copyright (c) 2008 Zope Corporation 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 HTTP OPTIONS verb + +$Id: test_options.py 110890 2010-04-14 14:05:18Z faassen $ +""" +from unittest import TestCase, TestSuite, makeSuite + +import zope.interface + +from zope.publisher.browser import IBrowserRequest +from zope.publisher.browser import TestRequest + +import zope.app.http.options + + +class IDeletable(zope.interface.Interface): + "Marker interface that says that something understand the DELETE method" + + +class Deletable(object): + "Rocket science implementation of IDeletable" + zope.interface.implements(IDeletable) + + +class DeleteView(object): + "A view for a deletable object" + def __init__(self, context, request): + self.context = context + self.request = request + + +class TestOptions(TestCase): + + def testDefaultMethods(self): + dumbObj = object() + + request = TestRequest() + options = zope.app.http.options.OPTIONS(dumbObj, request) + + self.assertEqual(options.OPTIONS(), '') + getHeader = request.response.getHeader + self.assertEqual(getHeader('Allow'), 'GET, HEAD, POST') + self.assertEqual(getHeader('DAV', literal=True), '1,2') + self.assertEqual(getHeader('MS-Author-Via', literal=True), 'DAV') + self.assertEqual(request.response.getStatus(), 200) + + def testExtendedMethods(self): + gst = zope.component.getGlobalSiteManager() + gst.registerAdapter(DeleteView, (IDeletable, IBrowserRequest), + zope.interface.Interface, 'PUT') + + deletableObject = Deletable() + + request = TestRequest() + options = zope.app.http.options.OPTIONS(deletableObject, request) + self.assertEqual(options.OPTIONS(), '') + getHeader = request.response.getHeader + self.assertEqual(getHeader('Allow'), 'GET, HEAD, POST, PUT') + + +def test_suite(): + return TestSuite(( + makeSuite(TestOptions), + )) diff -Nru zope3-3.4.0/src/zope/app/http/tests/test_put.py zope3-3.5~bzr18/src/zope/app/http/tests/test_put.py --- zope3-3.4.0/src/zope/app/http/tests/test_put.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/tests/test_put.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,158 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP PUT verb + +$Id: test_put.py 110890 2010-04-14 14:05:18Z faassen $ +""" +from unittest import TestCase, TestSuite, makeSuite +from StringIO import StringIO + +from zope.interface import implements +from zope.publisher.browser import TestRequest +from zope.filerepresentation.interfaces import IWriteFile +from zope.filerepresentation.interfaces import IWriteDirectory, IReadDirectory, IFileFactory + + +import zope.app.http.put +from zope.location.interfaces import ILocation +from zope.site.folder import rootFolder +from zope.app.wsgi.testlayer import BrowserLayer + +class File(object): + + implements(IWriteFile, ILocation) + + def __init__(self, name, content_type, data): + self.name = name + self.content_type = content_type + self.data = data + + def write(self, data): + self.data = data + +class Container(object): + + implements(IWriteDirectory, IReadDirectory, IFileFactory, ILocation) + + __name__ = None + __parent__ = None + + def __init__(self, path): + self.path = path + + def __setitem__(self, name, object): + object.__name__ = name + object.__parent__ = self + setattr(self, name, object) + + def __getitem__(self, name): + return getattr(self, name) + + def __call__(self, name, content_type, data): + return File(name, content_type, data) + + +class TestNullPUT(TestCase): + + layer = BrowserLayer(zope.app.http) + + def test(self): + self.rootFolder = rootFolder() + + container = Container("put") + self.rootFolder["put"] = container + content = "some content\n for testing" + request = TestRequest(StringIO(content), + {'CONTENT_TYPE': 'test/foo', + 'CONTENT_LENGTH': str(len(content)), + }) + null = zope.app.http.put.NullResource(container, 'spam') + put = zope.app.http.put.NullPUT(null, request) + self.assertEqual(getattr(container, 'spam', None), None) + self.assertEqual(put.PUT(), '') + request.response.setResult('') + file = container.spam + self.assertEqual(file.__class__, File) + self.assertEqual(file.name, 'spam') + self.assertEqual(file.content_type, 'test/foo') + self.assertEqual(file.data, content) + + # Check HTTP Response + self.assertEqual(request.response.getStatus(), 201) + self.assertEqual(request.response.getHeader("Location"), + "http://127.0.0.1/put/spam") + + def test_bad_content_header(self): + ## The previous behavour of the PUT method was to fail if the request + ## object had a key beginning with 'HTTP_CONTENT_' with a status of 501. + ## This was breaking the new Twisted server, so I am now allowing this + ## this type of request to be valid. + self.rootFolder = rootFolder() + container = Container("/put") + self.rootFolder["put"] = container + content = "some content\n for testing" + request = TestRequest(StringIO(content), + {'CONTENT_TYPE': 'test/foo', + 'CONTENT_LENGTH': str(len(content)), + 'HTTP_CONTENT_FOO': 'Bar', + }) + null = zope.app.http.put.NullResource(container, 'spam') + put = zope.app.http.put.NullPUT(null, request) + self.assertEqual(getattr(container, 'spam', None), None) + self.assertEqual(put.PUT(), '') + request.response.setResult('') + + # Check HTTP Response + self.assertEqual(request.response.getStatus(), 201) + +class TestFilePUT(TestCase): + layer = BrowserLayer(zope.app.http) + + def test(self): + file = File("thefile", "text/x", "initial content") + content = "some content\n for testing" + request = TestRequest(StringIO(content), + {'CONTENT_TYPE': 'test/foo', + 'CONTENT_LENGTH': str(len(content)), + }) + put = zope.app.http.put.FilePUT(file, request) + self.assertEqual(put.PUT(), '') + request.response.setResult('') + self.assertEqual(file.data, content) + + def test_bad_content_header(self): + ## The previous behavour of the PUT method was to fail if the request + ## object had a key beginning with 'HTTP_CONTENT_' with a status of 501. + ## This was breaking the new Twisted server, so I am now allowing this + ## this type of request to be valid. + file = File("thefile", "text/x", "initial content") + content = "some content\n for testing" + request = TestRequest(StringIO(content), + {'CONTENT_TYPE': 'test/foo', + 'CONTENT_LENGTH': str(len(content)), + 'HTTP_CONTENT_FOO': 'Bar', + }) + put = zope.app.http.put.FilePUT(file, request) + self.assertEqual(put.PUT(), '') + request.response.setResult('') + self.assertEqual(file.data, content) + + # Check HTTP Response + self.assertEqual(request.response.getStatus(), 200) + +def test_suite(): + return TestSuite(( + makeSuite(TestFilePUT), + makeSuite(TestNullPUT), + )) diff -Nru zope3-3.4.0/src/zope/app/http/tests/test_traversers.py zope3-3.5~bzr18/src/zope/app/http/tests/test_traversers.py --- zope3-3.4.0/src/zope/app/http/tests/test_traversers.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/tests/test_traversers.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,81 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP-specific object traversers + +$Id: test_traversers.py 28261 2004-10-26 22:22:37Z jim $ +""" +from unittest import TestCase, TestSuite, main, makeSuite +from zope.publisher.interfaces import NotFound +from zope.app.http.traversal import ContainerTraverser, ItemTraverser +from zope.publisher.browser import TestRequest +from zope.app.http.put import NullResource + +class Items(object): + + def __init__(self, data): + self.data = data + + def __getitem__(self, name): + return self.data[name] + +class Container(Items): + + def get(self, name, default=None): + return self.data.get(name, default) + + +class TestContainer(TestCase): + + Container = Container + Traverser = ContainerTraverser + + def testSubobject(self): + container = self.Container({'foo': 42}) + request = TestRequest() + traverser = self.Traverser(container, request) + self.assertEqual(traverser.publishTraverse(request, 'foo'), 42) + + def testNotFound(self): + container = self.Container({'foo': 42}) + request = TestRequest() + traverser = self.Traverser(container, request) + self.assertRaises(NotFound, + traverser.publishTraverse, request, 'bar') + + + def testNull(self): + container = self.Container({'foo': 42}) + request = TestRequest() + request.method = 'PUT' + traverser = self.Traverser(container, request) + null = traverser.publishTraverse(request, 'bar') + self.assertEqual(null.__class__, NullResource) + self.assertEqual(null.container, container) + self.assertEqual(null.name, 'bar') + + +class TestItem(TestContainer): + + Container = Items + Traverser = ItemTraverser + + +def test_suite(): + return TestSuite(( + makeSuite(TestContainer), + makeSuite(TestItem), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/http/traversal.py zope3-3.5~bzr18/src/zope/app/http/traversal.py --- zope3-3.4.0/src/zope/app/http/traversal.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/http/traversal.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,65 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""HTTP-specific traversers + +For straight HTTP, we need to be able to create null resources. +We also never traverse to views. + +$Id: traversal.py 95484 2009-01-29 18:32:36Z faassen $ +""" +__docformat__ = 'restructuredtext' + +from zope.publisher.interfaces.http import IHTTPPublisher +from zope.container.interfaces import ISimpleReadContainer, IItemContainer +from zope.app.http.put import NullResource +from zope.publisher.interfaces import NotFound +from zope.interface import implements + +class ContainerTraverser(object): + implements(IHTTPPublisher) + __used_for__ = ISimpleReadContainer + + def __init__(self, container, request): + self.context = container + self.request = request + + def publishTraverse(self, request, name): + subob = self.context.get(name, None) + if subob is None: + subob = self.nullResource(request, name) + + return subob + + def nullResource(self, request, name): + # we traversed to something that doesn't exist. + + # The name must be the last name in the path, so the traversal + # name stack better be empty: + if request.getTraversalStack(): + raise NotFound(self.context, name, request) + + # This should only happen for a PUT or MKCOL: + if request.method not in ['PUT', 'MKCOL']: + raise NotFound(self.context, name, request) + + return NullResource(self.context, name) + +class ItemTraverser(ContainerTraverser): + __used_for__ = IItemContainer + + def publishTraverse(self, request, name): + context = self.context + + try: + return context[name] + except KeyError: + return self.nullResource(request, name) diff -Nru zope3-3.4.0/src/zope/app/__init__.py zope3-3.5~bzr18/src/zope/app/__init__.py --- zope3-3.4.0/src/zope/app/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/configure.zcml zope3-3.5~bzr18/src/zope/app/pagetemplate/configure.zcml --- zope3-3.4.0/src/zope/app/pagetemplate/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/engine.py zope3-3.5~bzr18/src/zope/app/pagetemplate/engine.py --- zope3-3.4.0/src/zope/app/pagetemplate/engine.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/engine.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +# BBB imports: do not remove + +from zope.pagetemplate.engine import InlineCodeError +from zope.pagetemplate.engine import ZopeTraverser +from zope.pagetemplate.engine import zopeTraverser +from zope.pagetemplate.engine import trustedZopeTraverser +from zope.pagetemplate.engine import ZopePathExpr +from zope.pagetemplate.engine import TrustedZopePathExpr +from zope.pagetemplate.engine import ZopePythonExpr +from zope.pagetemplate.engine import ZopeContextBase +from zope.pagetemplate.engine import ZopeContext +from zope.pagetemplate.engine import TrustedZopeContext +from zope.pagetemplate.engine import AdapterNamespaces +from zope.pagetemplate.engine import ZopeBaseEngine +from zope.pagetemplate.engine import ZopeEngine +from zope.pagetemplate.engine import TrustedZopeEngine +from zope.pagetemplate.engine import TraversableModuleImporter +from zope.pagetemplate.engine import Engine +from zope.pagetemplate.engine import TrustedEngine +from zope.pagetemplate.engine import AppPT +from zope.pagetemplate.engine import TrustedAppPT diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/i18n.py zope3-3.5~bzr18/src/zope/app/pagetemplate/i18n.py --- zope3-3.4.0/src/zope/app/pagetemplate/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/i18n.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Customization of zope.i18n for the Zope application server + +$Id: i18n.py 73619 2007-03-26 11:54:13Z dobe $ +""" +__docformat__ = 'restructuredtext' + +# import this as _ to create i18n messages in the zope domain +from zope.i18nmessageid import MessageFactory +ZopeMessageFactory = MessageFactory('zope') diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/__init__.py zope3-3.5~bzr18/src/zope/app/pagetemplate/__init__.py --- zope3-3.4.0/src/zope/app/pagetemplate/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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-specific Page Templates + +$Id: __init__.py 106892 2009-12-22 18:40:31Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +from zope.browserpage import ViewPageTemplateFile diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/interfaces.py zope3-3.5~bzr18/src/zope/app/pagetemplate/interfaces.py --- zope3-3.4.0/src/zope/app/pagetemplate/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,35 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Interfaces for apis to make available to TALES + +$Id: interfaces.py 28653 2004-12-20 18:17:18Z fdrake $ +""" +__docformat__ = 'restructuredtext' + +import zope.interface + + +class IURLQuote(zope.interface.Interface): + + def quote(): + """Return the objects URL quote representation.""" + + def quote_plus(): + """Return the objects URL quote_plus representation.""" + + def unquote(): + """Return the objects URL unquote representation.""" + + def unquote_plus(): + """Return the objects URL unquote_plus representation.""" diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/metaconfigure.py zope3-3.5~bzr18/src/zope/app/pagetemplate/metaconfigure.py --- zope3-3.4.0/src/zope/app/pagetemplate/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""ZCML configuration directives for configuring the default zope: +namespace in TALES. + +$Id: metaconfigure.py 111432 2010-04-26 15:59:42Z faassen $ +""" +__docformat__ = 'restructuredtext' + +# BBB +from zope.browserpage.metaconfigure import clear +from zope.browserpage.metadirectives import IExpressionTypeDirective +from zope.browserpage.metaconfigure import expressiontype + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/meta.zcml zope3-3.5~bzr18/src/zope/app/pagetemplate/meta.zcml --- zope3-3.4.0/src/zope/app/pagetemplate/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/namedtemplate.py zope3-3.5~bzr18/src/zope/app/pagetemplate/namedtemplate.py --- zope3-3.4.0/src/zope/app/pagetemplate/namedtemplate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/namedtemplate.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,24 @@ +############################################################################## +# +# Copyright (c) 2005-2009 Zope Corporation 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. +# +############################################################################## +""" + +$Id: namedtemplate.py 107626 2010-01-04 14:00:19Z janwijbrand $ +""" + +# BBB +from zope.browserpage.namedtemplate import implementation +from zope.browserpage.namedtemplate import INamedTemplate +from zope.browserpage.namedtemplate import NamedTemplate +from zope.browserpage.namedtemplate import NamedTemplateImplementation +from zope.browserpage.namedtemplate import NamedTemplatePathAdapter diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/simpleviewclass.py zope3-3.5~bzr18/src/zope/app/pagetemplate/simpleviewclass.py --- zope3-3.4.0/src/zope/app/pagetemplate/simpleviewclass.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/simpleviewclass.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Simple View Class + +$Id: simpleviewclass.py 106892 2009-12-22 18:40:31Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +# BBB +from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile +from zope.browserpage.simpleviewclass import simple +from zope.browserpage.simpleviewclass import SimpleViewClass diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/talesapi.py zope3-3.5~bzr18/src/zope/app/pagetemplate/talesapi.py --- zope3-3.4.0/src/zope/app/pagetemplate/talesapi.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/talesapi.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,80 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Implementation of the Zope TALES API + +$Id: talesapi.py 67630 2006-04-27 00:54:03Z jim $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import implements +from zope.size.interfaces import ISized +from zope.security.interfaces import Unauthorized +from zope.tales.interfaces import ITALESFunctionNamespace +from zope.dublincore.interfaces import IDCDescriptiveProperties +from zope.dublincore.interfaces import IDCTimes +from zope.dublincore.interfaces import IZopeDublinCore +from zope.traversing.api import getName + +class ZopeTalesAPI(object): + + implements(IDCTimes, IDCDescriptiveProperties, ITALESFunctionNamespace) + + def __init__(self, context): + self.context = context + + def setEngine(self, engine): + self._engine = engine + + def title(self): + a = IZopeDublinCore(self.context, None) + if a is None: + raise AttributeError('title') + return a.title + title = property(title) + + def description(self): + a = IZopeDublinCore(self.context, None) + if a is None: + raise AttributeError('description') + return a.description + description = property(description) + + def created(self): + a = IZopeDublinCore(self.context, None) + if a is None: + raise AttributeError('created') + return a.created + created = property(created) + + def modified(self): + a = IZopeDublinCore(self.context, None) + if a is None: + raise AttributeError('modified') + return a.modified + modified = property(modified) + + def name(self): + return getName(self.context) + + def title_or_name(self): + try: + return getattr(self, 'title', '') or getName(self.context) + except Unauthorized: + return getName(self.context) + + def size(self): + a = ISized(self.context, None) + if a is None: + raise AttributeError('size') + return a.sizeForDisplay() diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/__init__.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/__init__.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/inner.pt zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/inner.pt --- zope3-3.4.0/src/zope/app/pagetemplate/tests/inner.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/inner.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ + + +hello +
+inner body slot content +
+ + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/intermediate.pt zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/intermediate.pt --- zope3-3.4.0/src/zope/app/pagetemplate/tests/intermediate.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/intermediate.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,12 @@ + + +hello +
+
+body slot +
+intermediate body slot stuff +
+ + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/outer.pt zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/outer.pt --- zope3-3.4.0/src/zope/app/pagetemplate/tests/outer.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/outer.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ + + +Example: outer + + +hello +
+outer body slot +
+ + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/test_binding.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_binding.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/test_binding.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_binding.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,52 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Binding Tests + +$Id: test_binding.py 106658 2009-12-16 20:51:45Z hannosch $ +""" +import unittest + +from zope.component import provideAdapter +from zope.component.testing import PlacelessSetup +from zope.container.interfaces import ISimpleReadContainer +from zope.container.traversal import ContainerTraversable +from zope.traversing.interfaces import ITraversable + +from zope.app.pagetemplate.tests.testpackage.content import Content +from zope.app.pagetemplate.tests.testpackage.content import PTComponent + +def setUpTraversal(): + from zope.traversing.testing import setUp + setUp() + provideAdapter(ContainerTraversable, (ISimpleReadContainer,), ITraversable) + + +class BindingTestCase(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(BindingTestCase, self).setUp() + setUpTraversal() + + def test_binding(self): + from zope.publisher.browser import TestRequest + comp = PTComponent(Content(), TestRequest()) + self.assertEqual(comp.index(), "42\n") + self.assertEqual(comp.nothing(), "\n") + self.assertEqual(comp.default(), "42\n") + +def test_suite(): + return unittest.makeSuite(BindingTestCase) + +if __name__=='__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/test_engine.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_engine.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/test_engine.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_engine.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ +import unittest + +class BBBTests(unittest.TestCase): + + def test_BBB_imports(self): + from zope.app.pagetemplate.engine import InlineCodeError + from zope.app.pagetemplate.engine import ZopeTraverser + from zope.app.pagetemplate.engine import zopeTraverser + from zope.app.pagetemplate.engine import trustedZopeTraverser + from zope.app.pagetemplate.engine import ZopePathExpr + from zope.app.pagetemplate.engine import TrustedZopePathExpr + from zope.app.pagetemplate.engine import ZopePythonExpr + from zope.app.pagetemplate.engine import ZopeContextBase + from zope.app.pagetemplate.engine import ZopeContext + from zope.app.pagetemplate.engine import TrustedZopeContext + from zope.app.pagetemplate.engine import AdapterNamespaces + from zope.app.pagetemplate.engine import ZopeBaseEngine + from zope.app.pagetemplate.engine import ZopeEngine + from zope.app.pagetemplate.engine import TrustedZopeEngine + from zope.app.pagetemplate.engine import TraversableModuleImporter + from zope.app.pagetemplate.engine import Engine + from zope.app.pagetemplate.engine import TrustedEngine + from zope.app.pagetemplate.engine import AppPT + from zope.app.pagetemplate.engine import TrustedAppPT + +def test_suite(): + return unittest.makeSuite(BBBTests) diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/test_namedtemplate.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_namedtemplate.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/test_namedtemplate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_namedtemplate.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,15 @@ +"""Test that the BBB imports in the namedtemplate module work. +""" +__docformat__ = "reStructuredText" + +import unittest + +from zope.component.testing import PlacelessSetup +class Test(PlacelessSetup, unittest.TestCase): + + def testImportNamedtemplateModule(self): + from zope.app.pagetemplate import namedtemplate + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/test_nested.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_nested.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/test_nested.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_nested.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,63 @@ +"""Test that nested macro references do the right thing. +""" +__docformat__ = "reStructuredText" + +import unittest + +from zope.component.testing import PlacelessSetup +from zope.publisher.browser import TestRequest + +from zope.browserpage import ViewPageTemplateFile + +class Context(object): + pass + + +class View(object): + def __init__(self, context, request): + self.context = context + self.request = request + + +EXPECTED = u"""\ + + +Example: outer + + +hello +
+
+inner body slot content +
+intermediate body slot stuff +
+ + +""" + + +class Test(PlacelessSetup, unittest.TestCase): + + def testMacroExtension(self): + # This test demonstrates how macro extension allows a macro to extend + # and re-offer a slot for a client template to fill. + outer = ViewPageTemplateFile('outer.pt') + intermediate = ViewPageTemplateFile('intermediate.pt') + inner = ViewPageTemplateFile('inner.pt') + + context = Context() + request = TestRequest() + view = View(context, request) + self.failUnless('outer body slot' in outer(view)) + + namespace = inner.pt_getContext(view, request) + namespace['outer'] = outer + namespace['intermediate'] = intermediate + result = inner.pt_render(namespace) + self.assertEquals(result, EXPECTED) + + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/content.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/content.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/content.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/content.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 Content + +$Id: content.py 106892 2009-12-22 18:40:31Z hannosch $ +""" +from zope.browserpage import ViewPageTemplateFile + +class Content(object): + def getSomething(self): + return 42 + + +class PTComponent(object): + def __init__(self, content, request=None): + self.context = content + self.request = request + + index = ViewPageTemplateFile("view.pt") + default = ViewPageTemplateFile("default.pt") + nothing = ViewPageTemplateFile("nothing.pt") diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/default.pt zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/default.pt --- zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/default.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/default.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +42 diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/__init__.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/__init__.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/nothing.pt zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/nothing.pt --- zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/nothing.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/nothing.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/view.pt zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/view.pt --- zope3-3.4.0/src/zope/app/pagetemplate/tests/testpackage/view.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testpackage/view.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/test_talesapi.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_talesapi.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/test_talesapi.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_talesapi.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,106 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Tales API Tests + +$Id: test_talesapi.py 106892 2009-12-22 18:40:31Z hannosch $ +""" +from datetime import datetime +from zope.testing.doctest import DocTestSuite +from zope.interface import implements +from zope.size.interfaces import ISized +from zope.traversing.interfaces import IPhysicallyLocatable +from zope.dublincore.interfaces import IZopeDublinCore + +from zope.app.pagetemplate.talesapi import ZopeTalesAPI + +class TestObject(object): + + implements(IZopeDublinCore, # not really, but who's checking. ;) + IPhysicallyLocatable, # not really + ISized) + + description = u"This object stores some number of apples" + title = u"apple cart" + created = datetime(2000, 10, 1, 23, 11, 00) + modified = datetime(2003, 1, 2, 3, 4, 5) + + def sizeForSorting(self): + return u'apples', 5 + + def sizeForDisplay(self): + return u'5 apples' + + def getName(self): + return u'apples' + +testObject = TestObject() + +def title(): + """ + >>> api = ZopeTalesAPI(testObject) + >>> api.title + u'apple cart' + """ + +def description(): + """ + >>> api = ZopeTalesAPI(testObject) + >>> api.description + u'This object stores some number of apples' + """ + +def name(): + """ + >>> api = ZopeTalesAPI(testObject) + >>> api.name() + u'apples' + """ + +def title_or_name(): + """ + >>> api = ZopeTalesAPI(testObject) + >>> api.title_or_name() + u'apple cart' + + >>> testObject = TestObject() + >>> testObject.title = u"" + >>> api = ZopeTalesAPI(testObject) + >>> api.title_or_name() + u'apples' + """ + +def size(): + """ + >>> api = ZopeTalesAPI(testObject) + >>> api.size() + u'5 apples' + """ + +def modified(): + """ + >>> api = ZopeTalesAPI(testObject) + >>> api.modified + datetime.datetime(2003, 1, 2, 3, 4, 5) + """ + +def created(): + """ + >>> api = ZopeTalesAPI(testObject) + >>> api.created + datetime.datetime(2000, 10, 1, 23, 11) + """ + + +def test_suite(): + return DocTestSuite() diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/test_urlquote.py zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_urlquote.py --- zope3-3.4.0/src/zope/app/pagetemplate/tests/test_urlquote.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/test_urlquote.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,67 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""URLQuote Tests + +I kept the tests quite small, just covering that the functions actually do +something (and don't really scramble stuff). We are relying on the python urllib +to be functional to avoid test duplication. + +$Id: test_urlquote.py 106892 2009-12-22 18:40:31Z hannosch $ +""" + +import unittest + +from zope.testing.doctest import DocTestSuite +from zope.app.pagetemplate.urlquote import URLQuote + + +class TestObject(object): + + def __str__(self): + return "www.google.de" + +def quote_simple(): + """ + >>> q = URLQuote(u"www.google.de") + >>> q.quote() + 'www.google.de' + >>> q.unquote() + u'www.google.de' + >>> q.quote_plus() + 'www.google.de' + >>> q.unquote_plus() + u'www.google.de' + """ + +def quote_cast_needed(): + """ + >>> q = URLQuote(TestObject()) + >>> q.quote() + 'www.google.de' + >>> q.unquote() + u'www.google.de' + >>> q.quote_plus() + 'www.google.de' + >>> q.unquote_plus() + u'www.google.de' + """ + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite(), + DocTestSuite('zope.app.pagetemplate.urlquote'), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/tests/testusage.pt zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testusage.pt --- zope3-3.4.0/src/zope/app/pagetemplate/tests/testusage.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/tests/testusage.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/urlquote.py zope3-3.5~bzr18/src/zope/app/pagetemplate/urlquote.py --- zope3-3.4.0/src/zope/app/pagetemplate/urlquote.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/urlquote.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,91 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""URL quoting for ZPT + +$Id: urlquote.py 67630 2006-04-27 00:54:03Z jim $ +""" +__docformat__ = 'restructuredtext' + +import urllib +from zope.interface import implements +from zope.traversing.interfaces import IPathAdapter + +class URLQuote(object): + r"""An adapter for URL quoting. + + It quotes unicode strings according to the recommendation in RFC 2718. + Before the unicode string gets quoted, it gets encoded with UTF-8. + + >>> quoter = URLQuote(u'Roki\u0161kis') + >>> quoter.quote() + 'Roki%C5%A1kis' + + >>> quoter.quote_plus() + 'Roki%C5%A1kis' + + And when unquoting, it assumes the unquoted string is encoded with + UTF-8, and tries to convert it to unicode. + + >>> quoter = URLQuote('Roki%C5%A1kis') + >>> quoter.unquote() + u'Roki\u0161kis' + + >>> quoter.unquote_plus() + u'Roki\u0161kis' + + If the unquoted string can't be converted to unicode, the unquoted + string is returned. + + >>> quoter = URLQuote('S%F6derk%F6ping') + >>> quoter.unquote() + 'S\xf6derk\xf6ping' + + >>> quoter.unquote_plus() + 'S\xf6derk\xf6ping' + """ + + __used_for__ = basestring + implements(IPathAdapter) + + def __init__(self, context): + if not isinstance(context, basestring): + context = str(context) + elif isinstance(context, unicode): + context = context.encode('utf-8') + self.context = context + + def quote(self): + """Return the object's URL quote representation.""" + return urllib.quote(self.context) + + def quote_plus(self): + """Return the object's URL quote_plus representation.""" + return urllib.quote_plus(self.context) + + def unquote(self): + """Return the object's URL unquote representation.""" + unquoted = urllib.unquote(self.context) + try: + return unicode(unquoted, 'utf-8') + except UnicodeDecodeError: + return unquoted + + def unquote_plus(self): + """Return the object's URL unquote_plus representation.""" + unquoted = urllib.unquote_plus(self.context) + try: + return unicode(unquoted, 'utf-8') + except UnicodeDecodeError: + return unquoted + diff -Nru zope3-3.4.0/src/zope/app/pagetemplate/viewpagetemplatefile.py zope3-3.5~bzr18/src/zope/app/pagetemplate/viewpagetemplatefile.py --- zope3-3.4.0/src/zope/app/pagetemplate/viewpagetemplatefile.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/pagetemplate/viewpagetemplatefile.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,24 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""File-based page templates that can be used as methods on views. + +$Id: viewpagetemplatefile.py 106892 2009-12-22 18:40:31Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +# BBB +from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile +from zope.browserpage.viewpagetemplatefile import ViewMapper +from zope.browserpage.viewpagetemplatefile import BoundPageTemplate +from zope.browserpage.viewpagetemplatefile import NoTraverser diff -Nru zope3-3.4.0/src/zope/app/publication/browser.py zope3-3.5~bzr18/src/zope/app/publication/browser.py --- zope3-3.4.0/src/zope/app/publication/browser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/browser.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,59 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Browser Publication Code + +This module implements browser-specific publication and traversal components +for the publisher. + +$Id: browser.py 101160 2009-06-20 10:59:31Z jim $ +""" +__docformat__ = 'restructuredtext' + +from zope.component import queryMultiAdapter +from zope.publisher.interfaces.browser import IBrowserPublisher + +from zope.app.publication.http import BaseHTTPPublication + +from zope.traversing.publicationtraverse import PublicationTraverser #BBB import + +class BrowserPublication(BaseHTTPPublication): + """Web browser publication handling.""" + + def getDefaultTraversal(self, request, ob): + if IBrowserPublisher.providedBy(ob): + # ob is already proxied, so the result of calling a method will be + return ob.browserDefault(request) + else: + adapter = queryMultiAdapter((ob, request), IBrowserPublisher) + if adapter is not None: + ob, path = adapter.browserDefault(request) + ob = self.proxy(ob) + return ob, path + else: + # ob is already proxied + return ob, None + + def afterCall(self, request, ob): + super(BrowserPublication, self).afterCall(request, ob) + if request.method == 'HEAD': + request.response.setResult('') + +# For now, have a factory that returns a singleton +class PublicationFactory(object): + + def __init__(self, db): + self.__pub = BrowserPublication(db) + + def __call__(self): + return self.__pub diff -Nru zope3-3.4.0/src/zope/app/publication/configure.zcml zope3-3.5~bzr18/src/zope/app/publication/configure.zcml --- zope3-3.4.0/src/zope/app/publication/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publication/ftesting.zcml zope3-3.5~bzr18/src/zope/app/publication/ftesting.zcml --- zope3-3.4.0/src/zope/app/publication/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publication/ftp.py zope3-3.5~bzr18/src/zope/app/publication/ftp.py --- zope3-3.4.0/src/zope/app/publication/ftp.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/ftp.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,52 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 FTP Publication. + +$Id: ftp.py 85703 2008-04-24 19:02:11Z lgs $ +""" +__docformat__ = 'restructuredtext' +import zope.component + +from zope.publisher.interfaces import NotFound +from zope.publisher.publish import mapply + +from zope.app.publication.zopepublication import ZopePublication + + + +class FTPPublication(ZopePublication): + """The Publication will do all the work for the FTP""" + + def callObject(self, request, ob): + method = request['command'] + view = zope.component.queryMultiAdapter((ob, request), + name=method, + default=self) + if view is self: + raise NotFound(ob, method, request) + + return mapply(getattr(view, method), (), request) + + def annotateTransaction(self, txn, request, ob): + txn = super(FTPPublication, self).annotateTransaction(txn, request, ob) + request_info = [request['command']] + path = request.get('path', '') + if path: + request_info.append(path) + name = request.get('name', '') + if name: + request_info.append(name) + request_info = ' '.join(request_info) + txn.setExtendedInfo('request_info', request_info) + return txn diff -Nru zope3-3.4.0/src/zope/app/publication/httpfactory.py zope3-3.5~bzr18/src/zope/app/publication/httpfactory.py --- zope3-3.4.0/src/zope/app/publication/httpfactory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/httpfactory.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,61 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""HTTP Factory + +$Id: httpfactory.py 97752 2009-03-10 01:14:59Z rogerineichen $ +""" +__docformat__ = 'restructuredtext' + +from zope import interface + +from zope.publisher.interfaces import ISkinnable +from zope.publisher.skinnable import setDefaultSkin + +from zope.app.publication import interfaces +from zope.app.publication.requestpublicationregistry import factoryRegistry + + +def chooseClasses(method, environment): + """Given the method and environment, choose the correct request and + publication factory.""" + content_type = environment.get('CONTENT_TYPE', '') + factory = factoryRegistry.lookup(method, content_type, environment) + request_class, publication = factory() + return request_class, publication + + +class HTTPPublicationRequestFactory(object): + interface.implements(interfaces.IPublicationRequestFactory) + + def __init__(self, db): + """See `zope.app.publication.interfaces.IPublicationRequestFactory`""" + self._db = db + self._publication_cache = {} + + def __call__(self, input_stream, env): + """See `zope.app.publication.interfaces.IPublicationRequestFactory`""" + method = env.get('REQUEST_METHOD', 'GET').upper() + request_class, publication_class = chooseClasses(method, env) + + publication = self._publication_cache.get(publication_class) + if publication is None: + publication = publication_class(self._db) + self._publication_cache[publication_class] = publication + + request = request_class(input_stream, env) + request.setPublication(publication) + if ISkinnable.providedBy(request): + # only ISkinnable requests have skins + setDefaultSkin(request) + return request diff -Nru zope3-3.4.0/src/zope/app/publication/httpfactory.txt zope3-3.5~bzr18/src/zope/app/publication/httpfactory.txt --- zope3-3.4.0/src/zope/app/publication/httpfactory.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/httpfactory.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,73 @@ +================= +HTTPFactory tests +================= + +This tests that httpfactory provide the right publication class, +for each request type, defined in the configure.zcml with publisher directive. + +The publication class is chosen upon the method name, +the mime type and sometimes some request headers + +A regular GET, POST or HEAD + + >>> from zope.app.wsgi.testlayer import http + + >>> print http(r""" + ... GET / HTTP/1.1 + ... """) + HTTP/1.0 200 OK + Content-Length: ... + Content-Type: text/html;charset=utf-8 + ... + >>> print http(r""" + ... POST / HTTP/1.1 + ... """) + HTTP/1.0 200 OK + Content-Length: ... + Content-Type: text/html;charset=utf-8 + ... + >>> print http(r""" + ... HEAD / HTTP/1.1 + ... """) + HTTP/1.0 200 OK + Content-Length: 0 + Content-Type: text/html;charset=utf-8 + + +A text/xml POST request, wich is an xml-rpc call + + >>> print http(r""" + ... POST /RPC2 HTTP/1.0 + ... Content-Type: text/xml + ... """) + HTTP/1.0 200 OK + Content-Length: ... + Content-Type: text/xml;charset=utf-8 + ... + +A text/xml POST request, with a HTTP_SOAPACTION in the headers, +wich is an xml-rpc call: + +TODO need to create a real SOAP exchange test here + + >>> print http(r""" + ... POST /RPC2 HTTP/1.0 + ... Content-Type: text/xml + ... HTTP_SOAPACTION: soap#action + ... """) + HTTP/1.0 200 OK + Content-Length: ... + Content-Type: text/xml;charset=utf-8 + ... + +Unknown request types: + +TODO need more testing here + + >>> print http(r""" + ... POST /BUBA HTTP/1.0 + ... Content-Type: text/topnotch + ... """) + HTTP/1.0 404 Not Found + ... + diff -Nru zope3-3.4.0/src/zope/app/publication/http.py zope3-3.5~bzr18/src/zope/app/publication/http.py --- zope3-3.4.0/src/zope/app/publication/http.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/http.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,56 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""HTTP Publication + +$Id: http.py 100362 2009-05-25 16:40:17Z shane $ +""" + +__docformat__ = 'restructuredtext' + +from zope.interface import Attribute +from zope.interface import implements +from zope.publisher.interfaces.http import IHTTPException +from zope.publisher.interfaces.http import MethodNotAllowed +from zope.publisher.publish import mapply +import zope.component + +from zope.app.publication.zopepublication import ZopePublication + +from zope.publisher.interfaces.http import IMethodNotAllowed #BBB import + + +class BaseHTTPPublication(ZopePublication): + """Base for HTTP-based protocol publications""" + + def annotateTransaction(self, txn, request, ob): + txn = super(BaseHTTPPublication, self).annotateTransaction( + txn, request, ob) + request_info = request.method + ' ' + request.getURL() + txn.setExtendedInfo('request_info', request_info) + return txn + + +class HTTPPublication(BaseHTTPPublication): + """Non-browser HTTP publication""" + + def callObject(self, request, ob): + # Exception handling, dont try to call request.method + orig = ob + if not IHTTPException.providedBy(ob): + ob = zope.component.queryMultiAdapter((ob, request), + name=request.method) + ob = getattr(ob, request.method, None) + if ob is None: + raise MethodNotAllowed(orig, request) + return mapply(ob, request.getPositionalArguments(), request) diff -Nru zope3-3.4.0/src/zope/app/publication/__init__.py zope3-3.5~bzr18/src/zope/app/publication/__init__.py --- zope3-3.4.0/src/zope/app/publication/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/publication/interfaces.py zope3-3.5~bzr18/src/zope/app/publication/interfaces.py --- zope3-3.4.0/src/zope/app/publication/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,98 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Publication Interfaces + +$Id: interfaces.py 106567 2009-12-15 19:50:20Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +from zope import interface + +# BBB: Re-import symbols to their old location. +from zope.publisher.interfaces import IEndRequestEvent +from zope.publisher.interfaces import EndRequestEvent +from zope.traversing.interfaces import IBeforeTraverseEvent +from zope.traversing.interfaces import BeforeTraverseEvent + + +class IPublicationRequestFactory(interface.Interface): + """Publication request factory""" + + def __call__(input_stream, env): + """Create a request object to handle the given inputs + + A request is created and configured with a publication object. + """ + +class IRequestFactory(interface.Interface): + + def __call__(input_stream, env): + """Create a request object to handle input.""" + +class ISOAPRequestFactory(IRequestFactory): + """SOAP request factory""" + +class IHTTPRequestFactory(IRequestFactory): + # TODO: should SOAP, XMLRPC, and Browser extend this? + """generic HTTP request factory""" + +class IXMLRPCRequestFactory(IRequestFactory): + """XMLRPC request factory""" + +class IBrowserRequestFactory(IRequestFactory): + """Browser request factory""" + +class IFileContent(interface.Interface): + """Marker interface for content that can be managed as files. + + The default view for file content has effective URLs that don't end in + `/`. In particular, if the content included HTML, relative links in + the HTML are relative to the container the content is in. + """ + + +class IRequestPublicationFactory(interface.Interface): + """ request-publication factory """ + + def canHandle(environment): + """Return ``True`` if it can handle the request, otherwise ``False``. + + `environment` can be used by the factory to make a decision based on + the HTTP headers. + """ + + def __call__(): + """Return a tuple (request, publication)""" + +class IRequestPublicationRegistry(interface.Interface): + """A registry to lookup a RequestPublicationFactory by + request method + mime-type. Multiple factories can be configured + for the same method+mimetype. The factory itself can introspect + the environment to decide if it can handle the request as given + by the environment or not. Factories are sorted in the order + of their registration in ZCML. + """ + + def register(method, mimetype, name, priority, factory): + """Registers a factory for method+mimetype.""" + + def lookup(method, mimetype, environment): + """Lookup a factory for a given method+mimetype and a + environment. + """ + + def getFactoriesFor(method, mimetype): + """Return the internal datastructure representing the configured + factories (basically for testing, not for introspection). + """ diff -Nru zope3-3.4.0/src/zope/app/publication/metaconfigure.py zope3-3.5~bzr18/src/zope/app/publication/metaconfigure.py --- zope3-3.4.0/src/zope/app/publication/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,35 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +""" Directive handler for publication factory + +See metadirective.py + +$Id: metaconfigure.py 100281 2009-05-23 06:04:43Z shane $ +""" +__docformat__ = 'restructuredtext' + +from zope.app.publication.requestpublicationregistry import factoryRegistry + +def publisher(_context, name, factory, methods=['*'], mimetypes=['*'], + priority=0): + + factory = factory() + + for method in methods: + for mimetype in mimetypes: + _context.action( + discriminator = (method, mimetype, priority), + callable = factoryRegistry.register, + args = (method, mimetype, name, priority, factory) + ) diff -Nru zope3-3.4.0/src/zope/app/publication/metadirectives.py zope3-3.5~bzr18/src/zope/app/publication/metadirectives.py --- zope3-3.4.0/src/zope/app/publication/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,63 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Directive schema, for publication factory + +This module provides the schema for the new zcml directive, +that let the developer associate a publication factory to a given +request, based on its method and mimetype. + +Each directive also has a name and a sortkey. + +The sortkey helps when several directives can handle a request: +they are sorted by this key and the highest one is taken. + +$Id: metadirectives.py 100281 2009-05-23 06:04:43Z shane $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface +from zope.configuration.fields import GlobalObject, Tokens +from zope.schema import TextLine, Int + +class IRequestPublicationDirective(Interface): + """Link a request type to a request and publication factory""" + + name = TextLine( + title=u'Name', + description=u'The name of the publication factory.') + + factory = GlobalObject( + title=u'Factory', + description=u'The request-publication factory') + + methods = Tokens( + title=u'Methods', + description=(u'A list of HTTP method names. If the method is a "*", ' + u'then all methods will match. Example: "GET POST"'), + value_type=TextLine(), + required=False) + + mimetypes = Tokens( + title=u'Mime Types', + description=(u'A list of content/mime types of the request. If the ' + u'type is a "*" then all types will be matched. ' + u'Example: "text/html text/xml"'), + value_type=TextLine(), + required=False) + + priority = Int( + title=u'Priority', + description=(u'A priority key used to concurrent' + ' publication factories.'), + required=False) diff -Nru zope3-3.4.0/src/zope/app/publication/meta.zcml zope3-3.5~bzr18/src/zope/app/publication/meta.zcml --- zope3-3.4.0/src/zope/app/publication/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ + + + diff -Nru zope3-3.4.0/src/zope/app/publication/methodnotallowed.txt zope3-3.5~bzr18/src/zope/app/publication/methodnotallowed.txt --- zope3-3.4.0/src/zope/app/publication/methodnotallowed.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/methodnotallowed.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +Method Not Allowed errors +========================= + +If we get a request with a method that does not have a corresponding +view, HTTP 405 Method Not Allowed response is returned: + + >>> from zope.app.wsgi.testlayer import http + + >>> print http(r""" + ... FROG / HTTP/1.1 + ... """) + HTTP/1.0 405 Method Not Allowed + Content-Length: 18 + Allow: DELETE, OPTIONS, PUT + ... + + >>> print http(r""" + ... DELETE / HTTP/1.1 + ... """) + HTTP/1.0 405 Method Not Allowed + ... + + +The request below should return 405, but instead crashes with a TypeError, +when the view tries to adapt context to IWriteFile. + +# >>> print http(r""" +# ... PUT / HTTP/1.1 +# ... Authorization: Basic mgr:mgrpw +# ... """, handle_errors=False) +# HTTP/1.1 405 Method Not Allowed +# ... diff -Nru zope3-3.4.0/src/zope/app/publication/notfound.txt zope3-3.5~bzr18/src/zope/app/publication/notfound.txt --- zope3-3.4.0/src/zope/app/publication/notfound.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/notfound.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,62 @@ +NotFound errors and traversal errors +==================================== + +Not found errors should only be displayed when someone provides a URL +to an object that doesn't exist, as in: + + >>> print http(r""" + ... GET /eek HTTP/1.1 + ... """) + HTTP/1.1 404 Not Found + ... + The page that you are trying to access is not available + ... + +On the other hand, if we create something that raises an exception +inside we should get an internal server error: + + >>> print http(r""" + ... POST /+/zope.app.zptpage.ZPTPage%3D HTTP/1.1 + ... Authorization: Basic mgr:mgrpw + ... Content-Length: 739 + ... Content-Type: multipart/form-data; boundary=---------------------------125598457818223697821067764270 + ... Referer: http://localhost:8081/+/zope.app.zptpage.ZPTPage= + ... + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="field.source" + ... + ... + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="field.expand.used" + ... + ... + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="field.evaluateInlineCode.used" + ... + ... + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="UPDATE_SUBMIT" + ... + ... Add + ... -----------------------------125598457818223697821067764270 + ... Content-Disposition: form-data; name="add_input_name" + ... + ... test.html + ... -----------------------------125598457818223697821067764270-- + ... """) + HTTP/1.1 303 See Other + ... + Location: http://localhost/@@contents.html + ... + + +We get a system error, because the problem is in the template, not in +the URL: + + >>> print http(r""" + ... GET /test.html HTTP/1.1 + ... """) + HTTP/1.1 500 Internal Server Error + ... + A system error occurred. + ... diff -Nru zope3-3.4.0/src/zope/app/publication/publicationtraverse.py zope3-3.5~bzr18/src/zope/app/publication/publicationtraverse.py --- zope3-3.4.0/src/zope/app/publication/publicationtraverse.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/publicationtraverse.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,33 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 warnings + +warnings.warn("""%s is deprecated + +If you want PublicationTraverser, it's now in +zope.traversing.publicationtraverse. Anything else that was here is +deprecated. +""" % __name__, DeprecationWarning, stacklevel=1) + + +from zope.traversing.publicationtraverse import PublicationTraverse #BBB import +from zope.traversing.publicationtraverse import PublicationTraverser #BBB import + +#BBB: do not use +class DuplicateNamespaces(Exception): + """More than one namespace was specified in a request""" + +#BBB: do not use +class UnknownNamespace(Exception): + """A parameter specified an unknown namespace""" diff -Nru zope3-3.4.0/src/zope/app/publication/requestpublicationfactories.py zope3-3.5~bzr18/src/zope/app/publication/requestpublicationfactories.py --- zope3-3.4.0/src/zope/app/publication/requestpublicationfactories.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/requestpublicationfactories.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,81 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Publication factories. + +This module provides factories for tuples (request, publication). + +$Id: requestpublicationfactories.py 80953 2007-10-22 21:53:37Z fdrake $ +""" +__docformat__ = 'restructuredtext' + +from zope import component +from zope.interface import implements +from zope.app.publication.interfaces import IRequestPublicationFactory +from zope.app.publication import interfaces +from zope.app.publication.soap import SOAPPublication +from zope.app.publication.xmlrpc import XMLRPCPublication +from zope.app.publication.http import HTTPPublication +from zope.publisher.xmlrpc import XMLRPCRequest +from zope.app.publication.browser import BrowserPublication +from zope.publisher.http import HTTPRequest +from zope.publisher.browser import BrowserRequest + +class SOAPFactory(object): + + implements(IRequestPublicationFactory) + + def canHandle(self, environment): + self.soap_req = component.queryUtility(interfaces.ISOAPRequestFactory) + return bool(environment.get('HTTP_SOAPACTION') and self.soap_req) + + def __call__(self): + return self.soap_req, SOAPPublication + + +class XMLRPCFactory(object): + + implements(IRequestPublicationFactory) + + def canHandle(self, environment): + return True + + def __call__(self): + request_class = component.queryUtility( + interfaces.IXMLRPCRequestFactory, default=XMLRPCRequest) + return request_class, XMLRPCPublication + + +class HTTPFactory(object): + + implements(IRequestPublicationFactory) + + def canHandle(self, environment): + return True + + def __call__(self): + request_class = component.queryUtility( + interfaces.IHTTPRequestFactory, default=HTTPRequest) + return request_class, HTTPPublication + +class BrowserFactory(object): + + implements(IRequestPublicationFactory) + + def canHandle(self, environment): + return True + + def __call__(self): + request_class = component.queryUtility( + interfaces.IBrowserRequestFactory, default=BrowserRequest) + return request_class, BrowserPublication diff -Nru zope3-3.4.0/src/zope/app/publication/requestpublicationregistry.py zope3-3.5~bzr18/src/zope/app/publication/requestpublicationregistry.py --- zope3-3.4.0/src/zope/app/publication/requestpublicationregistry.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/requestpublicationregistry.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,118 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""A registry for Request-Publication factories. + +$Id: requestpublicationregistry.py 107069 2009-12-25 22:44:38Z rogerineichen $ +""" +__docformat__ = 'restructuredtext' +from zope.interface import implements +from zope.app.publication.interfaces import IRequestPublicationRegistry +from zope.configuration.exceptions import ConfigurationError + +class RequestPublicationRegistry(object): + """The registry implements a three stage lookup for registered factories + that have to deal with requests:: + + {method > { mimetype -> [{'priority' : some_int, + 'factory' : factory, + 'name' : some_name }, ... + ] + }, + } + + The `priority` is used to define a lookup-order when multiple factories + are registered for the same method and mime-type. + """ + implements(IRequestPublicationRegistry) + + def __init__(self): + self._d = {} # method -> { mimetype -> {factories_data}} + + def register(self, method, mimetype, name, priority, factory): + """Register a factory for method+mimetype """ + + # initialize the two-level deep nested datastructure if necessary + if not self._d.has_key(method): + self._d[method] = {} + if not self._d[method].has_key(mimetype): + self._d[method][mimetype] = [] + l = self._d[method][mimetype] + + # Check if there is already a registered publisher factory (check by + # name). If yes then it will be removed and replaced by a new + # publisher. + for pos, d in enumerate(l): + if d['name'] == name: + del l[pos] + break + # add the publisher factory + additional informations + l.append({'name' : name, 'factory' : factory, 'priority' : priority}) + + # order by descending priority + l.sort(lambda x,y: -cmp(x['priority'], y['priority'])) + + # check if the priorities are unique + priorities = [item['priority'] for item in l] + if len(set(priorities)) != len(l): + raise ConfigurationError('All registered publishers for a given ' + 'method+mimetype must have distinct ' + 'priorities. Please check your ZCML ' + 'configuration') + + + def getFactoriesFor(self, method, mimetype): + + if ';' in mimetype: + # `mimetype` might be something like 'text/xml; charset=utf8'. In + # this case we are only interested in the first part. + mimetype = mimetype.split(';')[0] + + try: + return self._d[method][mimetype.strip()] + except KeyError: + return None + + + def lookup(self, method, mimetype, environment): + """Lookup a factory for a given method+mimetype and a environment.""" + + for m,mt in ((method, mimetype), (method, '*'), ('*', '*')): + factory_lst = self.getFactoriesFor(m, mt) + if factory_lst: + break + else: + raise ConfigurationError('No registered publisher found ' + 'for (%s/%s)' % (method, mimetype)) + + # now iterate over all factory candidates and let them introspect + # the request environment to figure out if they can handle the + # request + for d in factory_lst: + factory = d['factory'] + if factory.canHandle(environment): + return factory + + # Actually we should never get here unless of improper + # configuration (no default handler for method=* and mimetype=*) + return None + + +factoryRegistry = RequestPublicationRegistry() + +try: + import zope.testing.cleanup +except ImportError: + pass +else: + zope.testing.cleanup.addCleanUp(lambda : factoryRegistry.__init__()) diff -Nru zope3-3.4.0/src/zope/app/publication/site.txt zope3-3.5~bzr18/src/zope/app/publication/site.txt --- zope3-3.4.0/src/zope/app/publication/site.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/site.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ +====================== +Using the site manager +====================== + +This test ensures that the site is correctly set and cleared in a thread +during traversal using event subscribers. Before we start, no site is set: + + >>> from zope.component import hooks + >>> hooks.getSite() is None + True + + >>> request = object() + + >>> from zope.app.publication import interfaces + >>> from zope import site + +On the other hand, if a site is traversed, + + >>> from zope.site.tests.test_site import SiteManagerStub, CustomFolder + >>> sm = SiteManagerStub() + >>> mysite = CustomFolder('mysite') + >>> mysite.setSiteManager(sm) + + >>> from zope.traversing.interfaces import BeforeTraverseEvent + >>> ev = BeforeTraverseEvent(mysite, request) + >>> site.threadSiteSubscriber(mysite, ev) + + >>> hooks.getSite() + + +Once the request is completed, + + >>> from zope.publisher.interfaces import EndRequestEvent + >>> ev = EndRequestEvent(mysite, request) + >>> site.clearThreadSiteSubscriber(ev) + +the site assignment is cleared again: + + >>> hooks.getSite() is None + True diff -Nru zope3-3.4.0/src/zope/app/publication/soap.py zope3-3.5~bzr18/src/zope/app/publication/soap.py --- zope3-3.4.0/src/zope/app/publication/soap.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/soap.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,38 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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. +# +############################################################################## +""" +SOAP Publication Handler. Note that there is no *standard* SOAP +implementation that is currently appropriate for the Zope3 core. + +The current architecture allows external packages to register a +utility for zope.app.publication.interfaces.SOAPRequestFactory +in order to implement SOAP support. If no utility is registered +for this interface, SOAP requests are handled as if they were +browser requests. + +$Id: soap.py 70826 2006-10-20 03:41:16Z baijum $ +""" + +from zope.app.publication.http import BaseHTTPPublication + +# Don't need any special handling for SOAP +SOAPPublication = BaseHTTPPublication + +class SOAPPublicationFactory(object): + + def __init__(self, db): + self.__pub = SOAPPublication(db) + + def __call__(self): + return self.__pub diff -Nru zope3-3.4.0/src/zope/app/publication/testing.py zope3-3.5~bzr18/src/zope/app/publication/testing.py --- zope3-3.4.0/src/zope/app/publication/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,35 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation 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.app.publication common test related classes/functions/objects. + +$Id: testing.py 110812 2010-04-13 16:30:31Z thefunny42 $ +""" + +__docformat__ = "reStructuredText" + +from zope.app.wsgi.testlayer import BrowserLayer +from zope.publisher.browser import BrowserPage +import zope.app.publication + + +class DefaultTestView(BrowserPage): + + def __call__(self): + self.request.response.setHeader( + 'Content-Type', 'text/html;charset=utf-8') + return "Test" + + +PublicationLayer = BrowserLayer(zope.app.publication, name='PublicationLayer') + diff -Nru zope3-3.4.0/src/zope/app/publication/tests/ftest_zcml_dependencies.zcml zope3-3.5~bzr18/src/zope/app/publication/tests/ftest_zcml_dependencies.zcml --- zope3-3.4.0/src/zope/app/publication/tests/ftest_zcml_dependencies.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/ftest_zcml_dependencies.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publication/tests/__init__.py zope3-3.5~bzr18/src/zope/app/publication/tests/__init__.py --- zope3-3.4.0/src/zope/app/publication/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/publication/tests/support.py zope3-3.5~bzr18/src/zope/app/publication/tests/support.py --- zope3-3.4.0/src/zope/app/publication/tests/support.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/support.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +# an API to help us do some component registrations for +# the publisher to help with the testing. +# This functionality is also implemented in zope.app.testing. +# this functionality has two problems: +# * the code is extremely hard to understand as it papers over +# the zope.component APIs +# * we want to lift the dependency on zope.app.testing for other reasons +# it's possible that this code should end up in published test support modules +# in other packages deeper down (zope.publisher, zope.traversing). The +# fact that we have to import interfaces from these gives us this +# clue. We will investigate pushing them down to these packages +# later. +from zope import component +from zope.traversing.interfaces import ITraversable +from zope.publisher.interfaces import IDefaultViewName +from zope.publisher.interfaces.browser import (IDefaultBrowserLayer, + IBrowserRequest) + +def provideNamespaceHandler(name, handler): + component.provideAdapter(handler, (None,), ITraversable, + name=name) + component.provideAdapter(handler, (None, None), ITraversable, + name=name) + +def setDefaultViewName(for_, name, layer=IDefaultBrowserLayer, + type=IBrowserRequest): + if layer is None: + layer = type + component.provideAdapter(name, (for_, layer), IDefaultViewName) diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_browserpublication.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_browserpublication.py --- zope3-3.4.0/src/zope/app/publication/tests/test_browserpublication.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_browserpublication.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,338 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Browser Publication Tests + +$Id: test_browserpublication.py 110812 2010-04-13 16:30:31Z thefunny42 $ +""" +import unittest + +from StringIO import StringIO + +from zope import component +from zope.security.interfaces import ForbiddenAttribute +from zope.interface import Interface, implements + +from zope.publisher.publish import publish +from zope.publisher.browser import TestRequest, BrowserView +from zope.publisher.interfaces.browser import (IBrowserPublisher, + IDefaultBrowserLayer) +from zope.proxy import getProxiedObject +from zope.security.proxy import Proxy, removeSecurityProxy +from zope.security.checker import defineChecker, NamesChecker + +from zope.principalregistry.principalregistry import principalRegistry + +from zope.app.publication.browser import BrowserPublication +from zope.app.publication.httpfactory import HTTPPublicationRequestFactory +from zope.app.publication.traversers import TestTraverser +from zope.app.publication.tests.test_zopepublication \ + import BasePublicationTests as BasePublicationTests_ +from zope.app.publication.tests import support + +from persistent import Persistent + +def foo(): + "I am an otherwise empty docstring." + return 'hello base fans' + +class DummyPublished(object): + implements(IBrowserPublisher) + + def publishTraverse(self, request, name): + if name == 'bruce': + return foo + raise KeyError(name) + + def browserDefault(self, request): + return self, ['bruce'] + + + +class DummyView(DummyPublished, BrowserView): + + __Security_checker__ = NamesChecker(["browserDefault", "publishTraverse"]) + + +class BasePublicationTests(BasePublicationTests_): + + def _createRequest(self, path, publication, **kw): + request = TestRequest(PATH_INFO=path, **kw) + request.setPublication(publication) + return request + +class SimpleObject(object): + def __init__(self, v): + self.v = v + +class I1(Interface): + pass + +class mydict(dict): + implements(I1) + + +class O1(Persistent): + implements(I1) + + +class BrowserDefaultTests(BasePublicationTests): + """ + test browser default + + many views lead to a default view + + + """ + klass = BrowserPublication + + def testBaseTagNoBase(self): + self._testBaseTags('/somepath/@@view/', '') + + def testBaseTag1(self): + self._testBaseTags('/somepath/@@view', + 'http://127.0.0.1/somepath/@@view/bruce') + + def testBaseTag2(self): + self._testBaseTags('/somepath/', + 'http://127.0.0.1/somepath/@@view/bruce') + + def testBaseTag3(self): + self._testBaseTags('/somepath', + 'http://127.0.0.1/somepath/@@view/bruce') + + + + def _testBaseTags(self, url, expected): + # Make sure I1 and O1 are visible in the module namespace + # so that the classes can be pickled. + import transaction + + pub = BrowserPublication(self.db) + + component.provideAdapter(DummyView, (I1, IDefaultBrowserLayer), + Interface, name='view') + + support.setDefaultViewName(I1, 'view') + component.provideAdapter(TestTraverser, (None, IDefaultBrowserLayer), + IBrowserPublisher) + + ob = O1() + + ## the following is for running the tests standalone + principalRegistry.defineDefaultPrincipal( + 'tim', 'timbot', 'ai at its best') + + # now place our object inside the application + + connection = self.db.open() + app = connection.root()['Application'] + app.somepath = ob + transaction.commit() + connection.close() + + defineChecker(app.__class__, NamesChecker(somepath='xxx')) + + req = self._createRequest(url, pub) + response = req.response + + publish(req, handle_errors=0) + + self.assertEqual(response.getBase(), expected) + + + def _createRequest(self, path, publication, **kw): + request = TestRequest(PATH_INFO=path, **kw) + request.setPublication(publication) + return request + + + +class BrowserPublicationTests(BasePublicationTests): + + klass = BrowserPublication + + def testAdaptedTraverseNameWrapping(self): + + class Adapter(object): + implements(IBrowserPublisher) + def __init__(self, context, request): + self.context = context + self.counter = 0 + + def publishTraverse(self, request, name): + self.counter += 1 + return self.context[name] + + component.provideAdapter(Adapter, (I1, IDefaultBrowserLayer), + IBrowserPublisher) + + ob = mydict() + ob['bruce'] = SimpleObject('bruce') + ob['bruce2'] = SimpleObject('bruce2') + pub = self.klass(self.db) + ob2 = pub.traverseName(self._createRequest('/bruce', pub), ob, 'bruce') + self.assertRaises(ForbiddenAttribute, getattr, ob2, 'v') + self.assertEqual(removeSecurityProxy(ob2).v, 'bruce') + + def testAdaptedTraverseDefaultWrapping(self): + # Test default content and make sure that it's wrapped. + class Adapter(object): + implements(IBrowserPublisher) + def __init__(self, context, request): + self.context = context + + def browserDefault(self, request): + return (self.context['bruce'], 'dummy') + + component.provideAdapter(Adapter, + (I1, IDefaultBrowserLayer), + IBrowserPublisher) + + ob = mydict() + ob['bruce'] = SimpleObject('bruce') + ob['bruce2'] = SimpleObject('bruce2') + pub = self.klass(self.db) + ob2, x = pub.getDefaultTraversal(self._createRequest('/bruce',pub), ob) + self.assertEqual(x, 'dummy') + self.assertRaises(ForbiddenAttribute, getattr, ob2, 'v') + self.assertEqual(removeSecurityProxy(ob2).v, 'bruce') + + def testTraverseName(self): + pub = self.klass(self.db) + class C(object): + x = SimpleObject(1) + ob = C() + r = self._createRequest('/x',pub) + component.provideAdapter(TestTraverser, + (None, IDefaultBrowserLayer), + IBrowserPublisher) + ob2 = pub.traverseName(r, ob, 'x') + self.assertRaises(ForbiddenAttribute, getattr, ob2, 'v') + self.assertEqual(removeSecurityProxy(ob2).v, 1) + self.assert_(pub.traverseName(r, ob, '.') is ob) + + def testTraverseNameView(self): + pub = self.klass(self.db) + class I(Interface): pass + class C(object): + implements(I) + ob = C() + class V(object): + def __init__(self, context, request): pass + r = self._createRequest('/@@spam',pub) + component.provideAdapter(V, (I, IDefaultBrowserLayer), Interface, + name='spam') + ob2 = pub.traverseName(r, ob, '@@spam') + self.assertEqual(ob2.__class__, V) + + def testTraverseNameSiteManager(self): + pub = self.klass(self.db) + class C(object): + def getSiteManager(self): + return SimpleObject(1) + ob = C() + r = self._createRequest('/++etc++site',pub) + ob2 = pub.traverseName(r, ob, '++etc++site') + self.assertRaises(ForbiddenAttribute, getattr, ob2, 'v') + self.assertEqual(removeSecurityProxy(ob2).v, 1) + + def testTraverseNameApplicationControl(self): + from zope.applicationcontrol.applicationcontrol \ + import applicationController, applicationControllerRoot + from zope.traversing.interfaces import IEtcNamespace + component.provideUtility(applicationController, + IEtcNamespace, name='process') + + pub = self.klass(self.db) + r = self._createRequest('/++etc++process',pub) + ac = pub.traverseName(r, + applicationControllerRoot, + '++etc++process') + self.assertEqual(ac, applicationController) + r = self._createRequest('/++etc++process',pub) + app = r.publication.getApplication(r) + self.assertEqual(app, applicationControllerRoot) + + def testHEADFuxup(self): + pub = self.klass(None) + + class User(object): + id = 'bob' + + # With a normal request, we should get a body: + request = TestRequest(StringIO(''), {'PATH_INFO': '/'}) + request.setPrincipal(User()) + request.response.setResult(u"spam") + pub.afterCall(request, None) + self.assertEqual(request.response.consumeBody(), 'spam' ) + + # But with a HEAD request, the body should be empty + request = TestRequest(StringIO(''), {'PATH_INFO': '/'}) + request.setPrincipal(User()) + request.method = 'HEAD' + request.response.setResult(u"spam") + pub.afterCall(request, None) + self.assertEqual(request.response.consumeBody(), '') + + def testUnicode_NO_HTTP_CHARSET(self): + # Test so that a unicode body doesn't cause a UnicodeEncodeError + request = TestRequest(StringIO(''), {}) + request.response.setResult(u"\u0442\u0435\u0441\u0442") + headers = request.response.getHeaders() + headers.sort() + self.assertEqual( + headers, + [('Content-Length', '8'), + ('Content-Type', 'text/plain;charset=utf-8'), + ('X-Content-Type-Warning', 'guessed from content'), + ('X-Powered-By', 'Zope (www.zope.org), Python (www.python.org)')]) + self.assertEqual( + request.response.consumeBody(), + '\xd1\x82\xd0\xb5\xd1\x81\xd1\x82') + + +class HTTPPublicationRequestFactoryTests(BasePublicationTests): + + def setUp(self): + super(BasePublicationTests, self).setUp() + from zope.app.publication.requestpublicationregistry import \ + factoryRegistry + from zope.app.publication.requestpublicationfactories \ + import SOAPFactory, XMLRPCFactory, HTTPFactory, BrowserFactory + + factoryRegistry.register('*', '*', 'HTTP', 0, HTTPFactory()) + factoryRegistry.register('POST', 'text/xml', 'SOAP', 20, SOAPFactory()) + factoryRegistry.register('POST', 'text/xml', 'XMLRPC', 10, + XMLRPCFactory()) + factoryRegistry.register('GET', '*', 'BROWSER', 10, BrowserFactory()) + factoryRegistry.register('POST', '*', 'BROWSER', 10, BrowserFactory()) + factoryRegistry.register('HEAD', '*', 'BROWSER', 10, BrowserFactory()) + + def testGetBackSamePublication(self): + factory = HTTPPublicationRequestFactory(db=None) + args = (StringIO(''), {}) + self.assert_(id(factory(*args).publication) == + id(factory(*args).publication)) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(BrowserPublicationTests, 'test'), + unittest.makeSuite(BrowserDefaultTests, 'test'), + unittest.makeSuite(HTTPPublicationRequestFactoryTests, 'test'), + )) + + +if __name__ == '__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_dependencies.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_dependencies.py --- zope3-3.4.0/src/zope/app/publication/tests/test_dependencies.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_dependencies.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,36 @@ +import os +import unittest + +from zope.app.publication.traversers import SimpleComponentTraverser +from zope.component import getMultiAdapter +from zope.component.testlayer import ZCMLFileLayer +from zope.interface import implements +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces.browser import IBrowserPublisher + +import zope.app.publication.tests + + +class ZCMLDependencies(unittest.TestCase): + layer = ZCMLFileLayer(zope.app.publication.tests, + zcml_file='ftest_zcml_dependencies.zcml', + name='PublicationDependenciesLayer') + + def test_zcml_can_load_with_only_zope_component_meta(self): + # this is just an example. It is supposed to show that the + # configure.zcml file has loaded successfully. + + request = TestRequest() + + sample = object() + res = getMultiAdapter( + (sample, request), IBrowserPublisher) + self.failUnless(isinstance(res, SimpleComponentTraverser)) + self.failUnless(res.context is sample) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(ZCMLDependencies)) + return suite + diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_functional.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_functional.py --- zope3-3.4.0/src/zope/app/publication/tests/test_functional.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_functional.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,53 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 not found errors + +$Id: test_functional.py 110812 2010-04-13 16:30:31Z thefunny42 $ +""" + +import re +import unittest + +from zope.testing import renormalizing +from zope.app.publication.testing import PublicationLayer +from zope.testing import doctest + + +checker = renormalizing.RENormalizing([ + (re.compile(r"HTTP/1\.([01]) (\d\d\d) .*"), r"HTTP/1.\1 \2 "), + ]) + +optionflags = doctest.ELLIPSIS+doctest.NORMALIZE_WHITESPACE + +def test_suite(): + methodnotallowed = doctest.DocFileSuite( + '../methodnotallowed.txt', + optionflags=optionflags) + methodnotallowed.layer = PublicationLayer + httpfactory = doctest.DocFileSuite( + '../httpfactory.txt', checker=checker, + optionflags=optionflags) + httpfactory.layer = PublicationLayer + site = doctest.DocFileSuite( + '../site.txt', + optionflags=optionflags) + site.layer = PublicationLayer + return unittest.TestSuite(( + methodnotallowed, + httpfactory, + site,)) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') + diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_httpfactory.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_httpfactory.py --- zope3-3.4.0/src/zope/app/publication/tests/test_httpfactory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_httpfactory.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,149 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP Publication Request Factory. + +$Id: test_httpfactory.py 108332 2010-01-20 15:31:25Z faassen $ +""" +from unittest import TestCase, TestSuite, main, makeSuite + +from StringIO import StringIO + +from zope import component, interface +from zope.publisher.browser import BrowserRequest +from zope.publisher.http import HTTPRequest +from zope.publisher.xmlrpc import XMLRPCRequest +from zope.component.testing import PlacelessSetup + +from zope.app.publication.httpfactory import HTTPPublicationRequestFactory +from zope.app.publication.browser import BrowserPublication +from zope.app.publication.http import HTTPPublication +from zope.app.publication.xmlrpc import XMLRPCPublication +from zope.app.publication import interfaces +from zope.app.publication.requestpublicationregistry import factoryRegistry +from zope.app.publication.requestpublicationfactories import \ + HTTPFactory, SOAPFactory, BrowserFactory, XMLRPCFactory + +class DummyRequestFactory(object): + def __call__(self, input_stream, env): + self.input_stream = input_stream + self.env = env + return self + + def setPublication(self, pub): + self.pub = pub + +class Test(PlacelessSetup, TestCase): + + def setUp(self): + super(Test, self).setUp() + self.__factory = HTTPPublicationRequestFactory(None) + self.__env = { + 'SERVER_URL': 'http://127.0.0.1', + 'HTTP_HOST': '127.0.0.1', + 'CONTENT_LENGTH': '0', + 'GATEWAY_INTERFACE': 'TestFooInterface/1.0', + } + + # Simulate standard configuration + factoryRegistry.register('GET', '*', 'browser', 0, BrowserFactory()) + factoryRegistry.register('POST', '*', 'browser', 0, BrowserFactory()) + factoryRegistry.register('HEAD', '*', 'browser', 0, BrowserFactory()) + factoryRegistry.register('*', '*', 'http', 0, HTTPFactory()) + factoryRegistry.register('POST', 'text/xml', 'xmlrpc', 20, + XMLRPCFactory()) + factoryRegistry.register('POST', 'text/xml', 'soap', 30, + SOAPFactory()) + + def test_override(self): + # TODO: making a SOAP request without configuring a SOAP request + # currently generates an XMLRPC request. Not sure what the right thing + # is, but that doesn't seem to be the right thing. + soaprequestfactory = DummyRequestFactory() + interface.directlyProvides( + soaprequestfactory, interfaces.ISOAPRequestFactory) + component.provideUtility(soaprequestfactory) + xmlrpcrequestfactory = DummyRequestFactory() + interface.directlyProvides( + xmlrpcrequestfactory, interfaces.IXMLRPCRequestFactory) + component.provideUtility(xmlrpcrequestfactory) + httprequestfactory = DummyRequestFactory() + interface.directlyProvides( + httprequestfactory, interfaces.IHTTPRequestFactory) + component.provideUtility(httprequestfactory) + browserrequestfactory = DummyRequestFactory() + interface.directlyProvides( + browserrequestfactory, interfaces.IBrowserRequestFactory) + component.provideUtility(browserrequestfactory) + httpfactory = HTTPPublicationRequestFactory(None) + env = self.__env + env['REQUEST_METHOD'] = 'POST' + env['CONTENT_TYPE'] = 'text/xml' + input = StringIO('') + env['HTTP_SOAPACTION'] = 'foo' + self.assertEqual(httpfactory(input, env), soaprequestfactory) + del env['HTTP_SOAPACTION'] + self.assertEqual(httpfactory(input, env), xmlrpcrequestfactory) + env['CONTENT_TYPE'] = 'text/foo' + self.assertEqual( + httpfactory(input, env), browserrequestfactory) + env['REQUEST_METHOD'] = 'FLOO' + self.assertEqual(httpfactory(input, env), httprequestfactory) + + def test_browser(self): + r = self.__factory(StringIO(''), self.__env) + self.assertEqual(r.__class__, BrowserRequest) + self.assertEqual(r.publication.__class__, BrowserPublication) + + for method in ('GET', 'HEAD', 'POST', 'get', 'head', 'post'): + self.__env['REQUEST_METHOD'] = method + r = self.__factory(StringIO(''), self.__env) + self.assertEqual(r.__class__, BrowserRequest) + self.assertEqual(r.publication.__class__, BrowserPublication) + + def test_http(self): + for method in ('PUT', 'put', 'ZZZ'): + self.__env['REQUEST_METHOD'] = method + r = self.__factory(StringIO(''), self.__env) + self.assertEqual(r.__class__, HTTPRequest) + self.assertEqual(r.publication.__class__, HTTPPublication) + + def test_xmlrpc(self): + self.__env['CONTENT_TYPE'] = 'text/xml' + for method in ('POST', 'post'): + self.__env['REQUEST_METHOD'] = method + r = self.__factory(StringIO(''), self.__env) + self.assertEqual(r.__class__, XMLRPCRequest) + self.assertEqual(r.publication.__class__, XMLRPCPublication) + + # content type doesn't matter for non post + for method in ('GET', 'HEAD', 'get', 'head'): + self.__env['REQUEST_METHOD'] = method + r = self.__factory(StringIO(''), self.__env) + self.assertEqual(r.__class__, BrowserRequest) + self.assertEqual(r.publication.__class__, BrowserPublication) + + for method in ('PUT', 'put', 'ZZZ'): + self.__env['REQUEST_METHOD'] = method + r = self.__factory(StringIO(''), self.__env) + self.assertEqual(r.__class__, HTTPRequest) + self.assertEqual(r.publication.__class__, HTTPPublication) + + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_http.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_http.py --- zope3-3.4.0/src/zope/app/publication/tests/test_http.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_http.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,65 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP Publication + +$Id: test_http.py 110812 2010-04-13 16:30:31Z thefunny42 $ +""" +from unittest import TestCase, TestSuite, main, makeSuite +from StringIO import StringIO + +from zope.interface import Interface, implements +from zope.publisher.http import HTTPRequest +from zope.publisher.interfaces.http import IHTTPRequest + +import zope.app.publication.http +from zope import component + +class I(Interface): + pass + +class C(object): + spammed = 0 + implements(I) + +class V(object): + + def __init__(self, context, request): + self.context = context + + def SPAM(self): + self.context.spammed += 1 + + +class Test(TestCase): + # Note that zope publication tests cover all of the code but callObject + + def test_callObject(self): + pub = zope.app.publication.http.HTTPPublication(None) + request = HTTPRequest(StringIO(''), {}) + request.method = 'SPAM' + + component.provideAdapter(V, (I, IHTTPRequest), Interface, name='SPAM') + + ob = C() + pub.callObject(request, ob) + self.assertEqual(ob.spammed, 1) + + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_proxycontrol.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_proxycontrol.py --- zope3-3.4.0/src/zope/app/publication/tests/test_proxycontrol.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_proxycontrol.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,134 @@ +############################################################################## +# +# Copyright (c) Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +from zope.testing import doctest, cleanup +import unittest +import zope.app.publication.browser +import zope.component +import zope.interface +import zope.publisher.interfaces.browser +import zope.traversing.namespace + +class Proxy: + + def __init__(self, context): + print "Proxy called" + self.context = context + +class Publication(zope.app.publication.browser.BrowserPublication): + + def proxy(self, ob): + return Proxy(ob) + + +class Sample: + "Sample object for testing" + +class Response: + def reset(self): + pass + def handleException(self, *args): + pass + +class Request: + principal = None + method = 'GET' + def __init__(self): + self.annotations = {} + self.response = Response() + def hold(self, ob): + pass + def getTraversalStack(self): + return [] + def getURL(self): + return '/' + +class DB: + def open(self): + return self + def root(self): + return self + def get(self, key, default=None): + assert key == 'Application' + return Sample() + def close(self): + pass + +class Publisher: + + zope.interface.implements( + zope.publisher.interfaces.browser.IBrowserPublisher) + + def __init__(self, context, request): + self.context = context + + def publishTraverse(self, request, name): + result = Sample() + result.__name__ = name + return result + + def browserDefault(self, request): + return self.context, ['foo'] + +def proxy_control(): + """You can override proxy control in a subclass + + This test makes sure the override is called in the cases where the + publication wants to call ProxyFactory. + + >>> cleanup.cleanUp() + + >>> zope.component.provideAdapter(Publisher, (Sample, Request)) + >>> zope.component.provideAdapter(Publisher, (Sample, Request), name='foo') + >>> zope.component.provideAdapter(zope.traversing.namespace.view, + ... (Sample, Request), name='view') + + >>> pub = Publication(DB()) + >>> request = Request() + + >>> ob = pub.getApplication(request) + Proxy called + >>> isinstance(ob, Proxy) and isinstance(ob.context, Sample) + True + + >>> sample = Sample() + + >>> ob = pub.traverseName(request, sample, 'x') + Proxy called + >>> isinstance(ob, Proxy) and ob.context.__name__ == 'x' + True + + >>> ob = pub.traverseName(request, sample, '@@foo') + Proxy called + >>> isinstance(ob, Proxy) and isinstance(ob.context, Publisher) + True + + >>> ob, path = pub.getDefaultTraversal(request, sample) + Proxy called + >>> isinstance(ob, Proxy) and ob.context == sample and path == ['foo'] + True + + >>> pub.handleException(sample, request, (ValueError, ValueError(), None)) + Proxy called + + >>> cleanup.cleanUp() + """ + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite(), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') + diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_requestpublicationfactories.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_requestpublicationfactories.py --- zope3-3.4.0/src/zope/app/publication/tests/test_requestpublicationfactories.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_requestpublicationfactories.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,112 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP Publication Request Factory. + +$Id: test_requestpublicationfactories.py 80953 2007-10-22 21:53:37Z fdrake $ +""" +from unittest import TestCase, TestSuite, main, makeSuite + +from zope import component, interface +from zope.publisher.browser import BrowserRequest +from zope.publisher.http import HTTPRequest +from zope.publisher.xmlrpc import XMLRPCRequest +from zope.component.testing import PlacelessSetup + +from zope.app.publication.browser import BrowserPublication +from zope.app.publication.http import HTTPPublication +from zope.app.publication.xmlrpc import XMLRPCPublication +from zope.app.publication import interfaces +from zope.app.publication.requestpublicationfactories import \ + SOAPFactory, XMLRPCFactory, HTTPFactory, BrowserFactory +from zope.app.publication.soap import SOAPPublication +from zope.app.publication.browser import BrowserPublication + +class DummyRequestFactory(object): + def __call__(self, input_stream, env): + self.input_stream = input_stream + self.env = env + return self + + def setPublication(self, pub): + self.pub = pub + + +class Test(PlacelessSetup, TestCase): + + def setUp(self): + super(Test, self).setUp() + self.__env = { + 'SERVER_URL': 'http://127.0.0.1', + 'HTTP_HOST': '127.0.0.1', + 'CONTENT_LENGTH': '0', + 'GATEWAY_INTERFACE': 'TestFooInterface/1.0', + } + + def test_soapfactory(self): + soaprequestfactory = DummyRequestFactory() + interface.directlyProvides( + soaprequestfactory, interfaces.ISOAPRequestFactory) + component.provideUtility(soaprequestfactory) + env = self.__env + factory = SOAPFactory() + self.assertEqual(factory.canHandle(env), False) + env['HTTP_SOAPACTION'] = 'server:foo' + self.assertEqual(factory.canHandle(env), True) + request, publication = factory() + self.assertEqual(isinstance(request, DummyRequestFactory), True) + self.assertEqual(publication, SOAPPublication) + + def test_xmlrpcfactory(self): + xmlrpcrequestfactory = DummyRequestFactory() + interface.directlyProvides( + xmlrpcrequestfactory, interfaces.IXMLRPCRequestFactory) + component.provideUtility(xmlrpcrequestfactory) + env = self.__env + factory = XMLRPCFactory() + self.assertEqual(factory.canHandle(env), True) + request, publication = factory() + self.assertEqual(isinstance(request, DummyRequestFactory), True) + self.assertEqual(publication, XMLRPCPublication) + + def test_httpfactory(self): + httprequestfactory = DummyRequestFactory() + interface.directlyProvides( + httprequestfactory, interfaces.IHTTPRequestFactory) + component.provideUtility(httprequestfactory) + env = self.__env + factory = HTTPFactory() + self.assertEqual(factory.canHandle(env), True) + request, publication = factory() + self.assertEqual(isinstance(request, DummyRequestFactory), True) + self.assertEqual(publication, HTTPPublication) + + def test_browserfactory(self): + browserrequestfactory = DummyRequestFactory() + interface.directlyProvides( + browserrequestfactory, interfaces.IBrowserRequestFactory) + component.provideUtility(browserrequestfactory) + env = self.__env + factory = BrowserFactory() + self.assertEqual(factory.canHandle(env), True) + request, publication = factory() + self.assertEqual(isinstance(request, DummyRequestFactory), True) + self.assertEqual(publication, BrowserPublication) + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_requestpublicationregistry.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_requestpublicationregistry.py --- zope3-3.4.0/src/zope/app/publication/tests/test_requestpublicationregistry.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_requestpublicationregistry.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,130 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 HTTP Publication Request Factory. + +$Id: test_requestpublicationregistry.py 70826 2006-10-20 03:41:16Z baijum $ +""" +from unittest import TestCase, TestSuite, main, makeSuite + +from StringIO import StringIO + +from zope import component, interface +from zope.interface.verify import verifyClass +from zope.component.testing import PlacelessSetup + +from zope.configuration.exceptions import ConfigurationError +from zope.app.publication import interfaces +from zope.app.publication.interfaces import IRequestPublicationRegistry +from zope.app.publication.requestpublicationregistry import \ + RequestPublicationRegistry +from zope.app.publication.requestpublicationfactories import \ + HTTPFactory, SOAPFactory, BrowserFactory, XMLRPCFactory + + +def DummyFactory(): + return object + +class DummyRequestFactory(object): + def __call__(self, input_stream, env): + self.input_stream = input_stream + self.env = env + return self + + def setPublication(self, pub): + self.pub = pub + +class Test(PlacelessSetup, TestCase): + + def test_interface(self): + verifyClass(IRequestPublicationRegistry, RequestPublicationRegistry) + + def test_registration(self): + r = RequestPublicationRegistry() + xmlrpc_f = DummyFactory() + r.register('POST', 'text/xml', 'xmlrpc', 0, xmlrpc_f) + soap_f = DummyFactory() + r.register('POST', 'text/xml', 'soap', 1, soap_f) + browser_f = DummyFactory() + r.register('*', '*', 'browser_default', 0, browser_f) + l = r.getFactoriesFor('POST', 'text/xml') + self.assertEqual( + l, + [{'name' : 'soap', 'priority' : 1, 'factory' : object}, + {'name' : 'xmlrpc', 'priority' : 0, 'factory' : object}]) + self.assertEqual(r.getFactoriesFor('POST', 'text/html'), None) + + def test_configuration_same_priority(self): + r = RequestPublicationRegistry() + xmlrpc_f = DummyFactory() + r.register('POST', 'text/xml', 'xmlrpc', 0, DummyFactory) + r.register('POST', 'text/xml', 'soap', 1, DummyFactory()) + # try to register a factory with the same priority + self.assertRaises(ConfigurationError, r.register, + 'POST', 'text/xml', 'soap2', 1, DummyFactory()) + + def test_configuration_reregistration(self): + r = RequestPublicationRegistry() + xmlrpc_f = DummyFactory() + r.register('POST', 'text/xml', 'xmlrpc', 0, DummyFactory) + r.register('POST', 'text/xml', 'soap', 1, DummyFactory()) + # re-register 'soap' but with priority 2 + r.register('POST', 'text/xml', 'soap', 2, DummyFactory()) + factory_data = r.getFactoriesFor('POST', 'text/xml') + priorities = [item['priority'] for item in factory_data] + self.assertEqual(priorities, [2, 0]) + + def test_realfactories(self): + r = RequestPublicationRegistry() + r.register('POST', '*', 'post_fallback', 0, HTTPFactory()) + r.register('POST', 'text/xml', 'xmlrpc', 1, XMLRPCFactory()) + r.register('POST', 'text/xml', 'soap', 2, SOAPFactory()) + r.register('GET', '*', 'http', 0, HTTPFactory()) + r.register('PUT', '*', 'http', 0, HTTPFactory()) + r.register('HEAD', '*', 'http', 0, HTTPFactory()) + r.register('*', '*', 'http', 1, BrowserFactory()) + + self.assertEqual(len(r.getFactoriesFor('POST', 'text/xml')) , 2) + self.assertEqual(len(r.getFactoriesFor('POST', 'text/xml; charset=utf-8')) , 2) + self.assertEqual(len(r.getFactoriesFor('POST', '*')) , 1) + self.assertEqual(r.getFactoriesFor('GET', 'text/html') , None) + self.assertEqual(len(r.getFactoriesFor('HEAD', '*')) , 1) + + env = { + 'SERVER_URL': 'http://127.0.0.1', + 'HTTP_HOST': '127.0.0.1', + 'CONTENT_LENGTH': '0', + 'GATEWAY_INTERFACE': 'TestFooInterface/1.0', + } + + soaprequestfactory = DummyRequestFactory() + interface.directlyProvides( + soaprequestfactory, interfaces.ISOAPRequestFactory) + component.provideUtility(soaprequestfactory) + + self.assert_( + isinstance(r.lookup('POST', 'text/xml', env), XMLRPCFactory)) + env['HTTP_SOAPACTION'] = 'foo' + self.assert_( + isinstance(r.lookup('POST', 'text/xml', env), SOAPFactory)) + self.assert_( + isinstance(r.lookup('FOO', 'zope/sucks', env), BrowserFactory)) + + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_simplecomponenttraverser.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_simplecomponenttraverser.py --- zope3-3.4.0/src/zope/app/publication/tests/test_simplecomponenttraverser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_simplecomponenttraverser.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,87 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 Component Traverser Test + +$Id: test_simplecomponenttraverser.py 110812 2010-04-13 16:30:31Z thefunny42 $ +""" +import unittest +from zope.publisher.interfaces import NotFound +from zope.interface import Interface, directlyProvides + +from zope.app.publication.traversers import SimpleComponentTraverser +from zope import component + +class I(Interface): + pass + + +class Container(object): + def __init__(self, **kw): + for k in kw: + setattr(self, k , kw[k]) + + def get(self, name, default=None): + return getattr(self, name, default) + + +class Request(object): + + def __init__(self, type): + directlyProvides(self, type) + + def getEffectiveURL(self): + return '' + + +class View(object): + def __init__(self, comp, request): + self._comp = comp + + +class Test(unittest.TestCase): + + def testAttr(self): + # test container traver + foo = Container() + c = Container(foo=foo) + req = Request(I) + + T = SimpleComponentTraverser(c, req) + + self.assertRaises(NotFound , T.publishTraverse, req ,'foo') + + + def testView(self): + # test getting a view + foo = Container() + c = Container(foo=foo) + req = Request(I) + + T = SimpleComponentTraverser(c, req) + component.provideAdapter(View, (None, I), Interface, + name='foo') + + self.failUnless(T.publishTraverse(req, 'foo').__class__ is View) + + self.assertRaises(NotFound, T.publishTraverse, req , 'morebar') + + + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + + +if __name__ == '__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_xmlrpcpublication.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_xmlrpcpublication.py --- zope3-3.4.0/src/zope/app/publication/tests/test_xmlrpcpublication.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_xmlrpcpublication.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,135 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""XML-RPC Publication Tests + +$Id: test_xmlrpcpublication.py 110812 2010-04-13 16:30:31Z thefunny42 $ +""" +import unittest + +from zope.app.publication.tests.test_zopepublication import \ + BasePublicationTests +from zope.app.publication.traversers import TestTraverser +from zope.app.publication.traversers import SimpleComponentTraverser +from zope.app.publication.xmlrpc import XMLRPCPublication +from zope.interface import Interface, implements +from zope.proxy import removeAllProxies +from zope.publisher.interfaces import NotFound +from zope.publisher.interfaces.xmlrpc import IXMLRPCView +from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest +from zope.publisher.interfaces.xmlrpc import IXMLRPCPublisher +from zope.publisher.xmlrpc import TestRequest +from zope.app.publication.tests import support +from zope import component + +class SimpleObject(object): + def __init__(self, v): + self.v = v + + +class XMLRPCPublicationTests(BasePublicationTests): + + klass = XMLRPCPublication + + def _createRequest(self, path, publication, **kw): + request = TestRequest(PATH_INFO=path, **kw) + request.setPublication(publication) + return request + + def testTraverseName(self): + pub = self.klass(self.db) + class C(object): + x = SimpleObject(1) + ob = C() + r = self._createRequest('/x', pub) + component.provideAdapter(TestTraverser, (None, IXMLRPCRequest), + IXMLRPCPublisher) + + ob2 = pub.traverseName(r, ob, 'x') + self.assertEqual(removeAllProxies(ob2).v, 1) + + def testDenyDirectMethodAccess(self): + pub = self.klass(self.db) + class I(Interface): + pass + + class C(object): + implements(I) + + def foo(self): + return 'bar' + + class V(object): + def __init__(self, context, request): + pass + implements(IXMLRPCView) + + ob = C() + r = self._createRequest('/foo', pub) + + component.provideAdapter(V, (I, IXMLRPCView), Interface, + name='view') + + support.setDefaultViewName(I, 'view', type=IXMLRPCView) + self.assertRaises(NotFound, pub.traverseName, r, ob, 'foo') + + + def testTraverseNameView(self): + pub = self.klass(self.db) + + class I(Interface): + pass + + class C(object): + implements(I) + + ob = C() + + class V(object): + def __init__(self, context, request): + pass + implements(IXMLRPCView) + + + # Register the simple traverser so we can traverse without @@ + component.provideAdapter(SimpleComponentTraverser, + (Interface, IXMLRPCRequest), + IXMLRPCPublisher) + + r = self._createRequest('/@@spam', pub) + component.provideAdapter(V, (I, IXMLRPCRequest), Interface, + name='spam') + ob2 = pub.traverseName(r, ob, '@@spam') + self.assertEqual(removeAllProxies(ob2).__class__, V) + + ob2 = pub.traverseName(r, ob, 'spam') + self.assertEqual(removeAllProxies(ob2).__class__, V) + + + def testTraverseNameSiteManager(self): + pub = self.klass(self.db) + class C(object): + def getSiteManager(self): + return SimpleObject(1) + ob = C() + r = self._createRequest('/++etc++site',pub) + ob2 = pub.traverseName(r, ob, '++etc++site') + self.assertEqual(removeAllProxies(ob2).v, 1) + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(XMLRPCPublicationTests), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/app/publication/tests/test_zopepublication.py zope3-3.5~bzr18/src/zope/app/publication/tests/test_zopepublication.py --- zope3-3.4.0/src/zope/app/publication/tests/test_zopepublication.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/tests/test_zopepublication.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,678 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 Publication Tests + +$Id: test_zopepublication.py 110812 2010-04-13 16:30:31Z thefunny42 $ +""" +import unittest +import sys +from cStringIO import StringIO + +from persistent import Persistent +from ZODB.DB import DB +from ZODB.DemoStorage import DemoStorage +import transaction + +from zope.interface.verify import verifyClass +from zope.interface import implements, classImplements, implementedBy +from zope.component.interfaces import ComponentLookupError, ISite +from zope.error.interfaces import IErrorReportingUtility +from zope.location import Location +from zope.publisher.base import TestPublication, TestRequest +from zope.publisher.interfaces import IRequest, IPublishTraverse +from zope.security import simplepolicies +from zope.security.management import setSecurityPolicy, queryInteraction +from zope.security.management import endInteraction +from zope.traversing.interfaces import IPhysicallyLocatable +from zope.location.interfaces import ILocation +from zope.testing.cleanup import cleanUp +from zope.site.site import LocalSiteManager + +from zope import component +from zope.app.publication.tests import support + +from zope.authentication.interfaces import IAuthentication +from zope.authentication.interfaces import IFallbackUnauthenticatedPrincipal +from zope.authentication.interfaces import IUnauthenticatedPrincipal +from zope.security.interfaces import IPrincipal +from zope.principalregistry.principalregistry import principalRegistry +from zope.app.publication.zopepublication import ZopePublication +from zope.site.folder import Folder, rootFolder + + +class Principal(object): + implements(IPrincipal) + def __init__(self, id): + self.id = id + self.title = '' + self.description = '' + +class UnauthenticatedPrincipal(Principal): + implements(IUnauthenticatedPrincipal) + +class AuthUtility1(object): + + def authenticate(self, request): + return None + + def unauthenticatedPrincipal(self): + return UnauthenticatedPrincipal('test.anonymous') + + def unauthorized(self, id, request): + pass + + def getPrincipal(self, id): + return UnauthenticatedPrincipal(id) + +class AuthUtility2(AuthUtility1): + + def authenticate(self, request): + return Principal('test.bob') + + def getPrincipal(self, id): + return Principal(id) + +class AuthUtility3(AuthUtility1): + + def unauthenticatedPrincipal(self): + return None + +class ErrorReportingUtility(object): + implements(IErrorReportingUtility) + + def __init__(self): + self.exceptions = [] + + def raising(self, info, request=None): + self.exceptions.append([info, request]) + +class LocatableObject(Location): + + def foo(self): + pass + +class TestRequest(TestRequest): + URL='http://test.url' + + +def addUtility(sitemanager, name, iface, utility, suffix=''): + """Add a utility to a site manager + + This helper function is useful for tests that need to set up utilities. + """ + folder_name = (name or (iface.__name__ + 'Utility')) + suffix + default = sitemanager['default'] + default[folder_name] = utility + utility = default[folder_name] + sitemanager.registerUtility(utility, iface, name) + return utility + + +class BasePublicationTests(unittest.TestCase): + + def setUp(self): + from zope.security.management import endInteraction + endInteraction() + self.policy = setSecurityPolicy( + simplepolicies.PermissiveSecurityPolicy + ) + self.storage = DemoStorage('test_storage') + self.db = db = DB(self.storage) + + component.provideUtility(principalRegistry, IAuthentication) + + connection = db.open() + root = connection.root() + app = getattr(root, ZopePublication.root_name, None) + + if app is None: + from zope.site.folder import rootFolder + app = rootFolder() + root[ZopePublication.root_name] = app + transaction.commit() + + connection.close() + self.app = app + + from zope.traversing.namespace import view, resource, etc + + support.provideNamespaceHandler('view', view) + support.provideNamespaceHandler('resource', resource) + support.provideNamespaceHandler('etc', etc) + + self.request = TestRequest('/f1/f2') + self.user = Principal('test.principal') + self.request.setPrincipal(self.user) + from zope.interface import Interface + self.presentation_type = Interface + self.request._presentation_type = self.presentation_type + self.object = object() + self.publication = ZopePublication(self.db) + + def tearDown(self): + # Close the request, otherwise a Cleanup object will start logging + # messages from its __del__ method at some inappropriate moment. + self.request.close() + cleanUp() + + +class ZopePublicationErrorHandling(BasePublicationTests): + + def testInterfacesVerify(self): + for interface in implementedBy(ZopePublication): + verifyClass(interface, TestPublication) + + def testRetryAllowed(self): + from ZODB.POSException import ConflictError + from zope.publisher.interfaces import Retry + try: + raise ConflictError + except: + self.assertRaises(Retry, self.publication.handleException, + self.object, self.request, sys.exc_info(), retry_allowed=True) + + try: + raise Retry(sys.exc_info()) + except: + self.assertRaises(Retry, self.publication.handleException, + self.object, self.request, sys.exc_info(), retry_allowed=True) + + def testRetryNotAllowed(self): + from ZODB.POSException import ConflictError + from zope.publisher.interfaces import Retry + try: + raise ConflictError + except: + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + value = ''.join(self.request.response._result).split() + self.assertEqual(' '.join(value[:6]), + 'Traceback (most recent call last): File') + self.assertEqual(' '.join(value[-8:]), + 'in testRetryNotAllowed raise ConflictError' + ' ConflictError: database conflict error') + + try: + raise Retry(sys.exc_info()) + except: + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + value = ''.join(self.request.response._result).split() + self.assertEqual(' '.join(value[:6]), + 'Traceback (most recent call last): File') + self.assertEqual(' '.join(value[-8:]), + 'in testRetryNotAllowed raise Retry(sys.exc_info())' + ' Retry: database conflict error') + + try: + raise Retry + except: + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + value = ''.join(self.request.response._result).split() + self.assertEqual(' '.join(value[:6]), + 'Traceback (most recent call last): File') + self.assertEqual(' '.join(value[-6:]), + 'in testRetryNotAllowed raise Retry' + ' Retry: None') + + def testViewOnException(self): + from zope.interface import Interface + class E1(Exception): + pass + + support.setDefaultViewName(E1, 'name', + layer=None, + type=self.presentation_type) + view_text = 'You had a conflict error' + + def _view(obj, request): + return lambda: view_text + + component.provideAdapter(_view, (E1, self.presentation_type), + Interface, name='name') + + try: + raise E1 + except: + pass + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + self.assertEqual(self.request.response._result, view_text) + + def testHandlingSystemErrors(self): + + # Generally, when there is a view for an excepton, we assume + # it is a user error, not a system error and we don't log it. + + from zope.testing import loggingsupport + handler = loggingsupport.InstalledHandler('SiteError') + + self.testViewOnException() + + self.assertEqual( + str(handler), + 'SiteError ERROR\n' + ' Error while reporting an error to the Error Reporting utility') + + # Here we got a single log record, because we haven't + # installed an error reporting utility. That's OK. + + handler.uninstall() + handler = loggingsupport.InstalledHandler('SiteError') + + # Now, we'll register an exception view that indicates that we + # have a system error. + + from zope.interface import Interface, implements + class E2(Exception): + pass + + support.setDefaultViewName(E2, 'name', + layer=self.presentation_type, + type=self.presentation_type) + view_text = 'You had a conflict error' + + from zope.browser.interfaces import ISystemErrorView + class MyView: + implements(ISystemErrorView) + def __init__(self, context, request): + pass + + def isSystemError(self): + return True + + def __call__(self): + return view_text + + component.provideAdapter(MyView, (E2, self.presentation_type), + Interface, name='name') + + try: + raise E2 + except: + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + + # Now, since the view was a system error view, we should have + # a log entry for the E2 error (as well as the missing + # error reporting utility). + self.assertEqual( + str(handler), + 'SiteError ERROR\n' + ' Error while reporting an error to the Error Reporting utility\n' + 'SiteError ERROR\n' + ' http://test.url' + ) + + handler.uninstall() + + def testNoViewOnClassicClassException(self): + from zope.interface import Interface + from types import ClassType + class ClassicError: + __metaclass__ = ClassType + class IClassicError(Interface): + pass + classImplements(ClassicError, IClassicError) + support.setDefaultViewName(IClassicError, 'name', + self.presentation_type) + view_text = 'You made a classic error ;-)' + def _view(obj, request): + return lambda: view_text + component.provideAdapter( + _view, (ClassicError, self.presentation_type), Interface, + name='name') + try: + raise ClassicError + except: + pass + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + # check we don't get the view we registered + self.failIf(''.join(self.request.response._result) == view_text) + # check we do actually get something + self.failIf(''.join(self.request.response._result) == '') + + def testExceptionSideEffects(self): + from zope.publisher.interfaces import IExceptionSideEffects + class SideEffects(object): + implements(IExceptionSideEffects) + def __init__(self, exception): + self.exception = exception + def __call__(self, obj, request, exc_info): + self.obj = obj + self.request = request + self.exception_type = exc_info[0] + self.exception_from_info = exc_info[1] + class SideEffectsFactory: + def __call__(self, exception): + self.adapter = SideEffects(exception) + return self.adapter + factory = SideEffectsFactory() + from ZODB.POSException import ConflictError + from zope.interface import Interface + class IConflictError(Interface): + pass + classImplements(ConflictError, IConflictError) + component.provideAdapter(factory, (IConflictError,), + IExceptionSideEffects) + exception = ConflictError() + try: + raise exception + except: + pass + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + adapter = factory.adapter + self.assertEqual(exception, adapter.exception) + self.assertEqual(exception, adapter.exception_from_info) + self.assertEqual(ConflictError, adapter.exception_type) + self.assertEqual(self.object, adapter.obj) + self.assertEqual(self.request, adapter.request) + + def testExceptionResetsResponse(self): + from zope.publisher.browser import TestRequest + request = TestRequest() + request.response.setHeader('Content-Type', 'application/pdf') + request.response.setCookie('spam', 'eggs') + from ZODB.POSException import ConflictError + try: + raise ConflictError + except: + pass + self.publication.handleException( + self.object, request, sys.exc_info(), retry_allowed=False) + self.assertEqual(request.response.getHeader('Content-Type'), + 'text/html;charset=utf-8') + self.assertEqual(request.response._cookies, {}) + + def testAbortOrCommitTransaction(self): + txn = transaction.get() + try: + raise Exception + except: + pass + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + # assert that we get a new transaction + self.assert_(txn is not transaction.get()) + + def testAbortTransactionWithErrorReportingUtility(self): + # provide our fake error reporting utility + component.provideUtility(ErrorReportingUtility()) + + class FooError(Exception): + pass + + last_txn_info = self.storage.lastTransaction() + try: + raise FooError + except FooError: + pass + self.publication.handleException( + self.object, self.request, sys.exc_info(), retry_allowed=False) + + # assert that the last transaction is NOT our transaction + new_txn_info = self.storage.lastTransaction() + self.assertEqual(last_txn_info, new_txn_info) + + # instead, we expect a message in our logging utility + error_log = component.getUtility(IErrorReportingUtility) + self.assertEqual(len(error_log.exceptions), 1) + error_info, request = error_log.exceptions[0] + self.assertEqual(error_info[0], FooError) + self.assert_(isinstance(error_info[1], FooError)) + self.assert_(request is self.request) + + def testLogBeforeAbort(self): + # If we get an exception, and then (a catastrophe, but one that has + # been experienced) transaction.abort fails, we really want to know + # what happened before that abort. + # (Set up:) + component.provideUtility(ErrorReportingUtility()) + abort = transaction.abort + class AbortError(Exception): + pass + class AnEarlierError(Exception): + pass + def faux_abort(): + raise AbortError + try: + raise AnEarlierError() + except AnEarlierError: + pass + transaction.abort = faux_abort + try: + # (Test:) + try: + self.publication.handleException( + self.object, self.request, sys.exc_info(), + retry_allowed=False) + except AbortError: + pass + else: + self.fail('Aborting should have failed') + # we expect a message in our logging utility + error_log = component.getUtility(IErrorReportingUtility) + self.assertEqual(len(error_log.exceptions), 1) + error_info, request = error_log.exceptions[0] + self.assertEqual(error_info[0], AnEarlierError) + self.failUnless(isinstance(error_info[1], AnEarlierError)) + self.failUnless(request is self.request) + finally: + # (Tear down:) + transaction.abort = abort + + +class ZopePublicationTests(BasePublicationTests): + + def testGlobalAuth(self): + # Replace the global registry with a stub that doesn't return an + # unauthenticated principal. + authentication = AuthUtility3() + component.provideUtility(authentication, IAuthentication) + + # We need a fallback unauthenticated principal, otherwise we'll get a + # ComponentLookupError: + self.assertRaises(ComponentLookupError, + self.publication.beforeTraversal, self.request) + + # Let's register an unauthenticated principal instance for the lookup: + principal = UnauthenticatedPrincipal('fallback') + component.provideUtility(principal, IFallbackUnauthenticatedPrincipal) + + self.publication.beforeTraversal(self.request) + self.failUnless(self.request.principal is principal) + + def testTransactionCommitAfterCall(self): + root = self.db.open().root() + txn = transaction.get() + # we just need a change in the database to make the + # transaction notable in the undo log + root['foo'] = object() + last_txn_info = self.storage.lastTransaction() + self.publication.afterCall(self.request, self.object) + self.assert_(txn is not transaction.get()) + new_txn_info = self.storage.lastTransaction() + self.failIfEqual(last_txn_info, new_txn_info) + + def testDoomedTransaction(self): + # Test that a doomed transaction is aborted without error in afterCall + root = self.db.open().root() + txn = transaction.get() + # we just need a change in the database to make the + # transaction notable in the undo log + root['foo'] = object() + last_txn_info = self.storage.lastTransaction() + # doom the transaction + txn.doom() + self.publication.afterCall(self.request, self.object) + # assert that we get a new transaction + self.assert_(txn is not transaction.get()) + new_txn_info = self.storage.lastTransaction() + # No transaction should be committed + self.assertEqual(last_txn_info, new_txn_info) + + def testTransactionAnnotation(self): + from zope.interface import directlyProvides + from zope.location.traversing import LocationPhysicallyLocatable + from zope.location.interfaces import ILocation + from zope.traversing.interfaces import IPhysicallyLocatable + from zope.traversing.interfaces import IContainmentRoot + component.provideAdapter(LocationPhysicallyLocatable, + (ILocation,), IPhysicallyLocatable) + + def get_txn_info(): + if hasattr(self.storage, 'iterator'): + # ZODB 3.9 + txn_id = self.storage.lastTransaction() + txn = list(self.storage.iterator(txn_id, txn_id))[0] + txn_info = dict(location=txn.extension['location'], + user_name=txn.user, + request_type=txn.extension['request_type']) + else: + # ZODB 3.8 + txn_info = self.storage.undoInfo()[0] + return txn_info + + root = self.db.open().root() + root['foo'] = foo = LocatableObject() + root['bar'] = bar = LocatableObject() + bar.__name__ = 'bar' + foo.__name__ = 'foo' + bar.__parent__ = foo + foo.__parent__ = root + directlyProvides(root, IContainmentRoot) + + from zope.publisher.interfaces import IRequest + expected_path = "/foo/bar" + expected_user = "/ " + self.user.id + expected_request = IRequest.__module__ + '.' + IRequest.getName() + + self.publication.afterCall(self.request, bar) + txn_info = get_txn_info() + self.assertEqual(txn_info['location'], expected_path) + self.assertEqual(txn_info['user_name'], expected_user) + self.assertEqual(txn_info['request_type'], expected_request) + + # also, assert that we still get the right location when + # passing an instance method as object. + self.publication.afterCall(self.request, bar.foo) + txn_info = get_txn_info() + self.assertEqual(txn_info['location'], expected_path) + + def testSiteEvents(self): + from zope.publisher.interfaces import IEndRequestEvent + from zope.traversing.interfaces import IBeforeTraverseEvent + + set = [] + clear = [] + + component.provideHandler(set.append, (IBeforeTraverseEvent,)) + component.provideHandler(clear.append, (IEndRequestEvent,)) + + ob = object() + + # This should fire the BeforeTraverseEvent + self.publication.callTraversalHooks(self.request, ob) + + self.assertEqual(len(set), 1) + self.assertEqual(len(clear), 0) + self.assertEqual(set[0].object, ob) + + ob2 = object() + + # This should fire the EndRequestEvent + self.publication.endRequest(self.request, ob2) + + self.assertEqual(len(set), 1) + self.assertEqual(len(clear), 1) + self.assertEqual(clear[0].object, ob2) + + def testConnectionAnnotation(self): + """The request is annotated with the connection to the main ZODB. + """ + self.assertRaises( + KeyError, + lambda: self.request.annotations['ZODB.interfaces.IConnection']) + + app = self.publication.getApplication(self.request) + conn = self.request.annotations['ZODB.interfaces.IConnection'] + self.assertEqual(conn.db(), self.db) + + +class AuthZopePublicationTests(BasePublicationTests): + + def setUp(self): + super(AuthZopePublicationTests, self).setUp() + principalRegistry.defineDefaultPrincipal('anonymous', '') + + root = self.db.open().root() + app = root[ZopePublication.root_name] + app['f1'] = rootFolder() + f1 = app['f1'] + f1['f2'] = Folder() + if not ISite.providedBy(f1): + f1.setSiteManager(LocalSiteManager(f1)) + sm1 = f1.getSiteManager() + addUtility(sm1, '', IAuthentication, AuthUtility1()) + + f2 = f1['f2'] + if not ISite.providedBy(f2): + f2.setSiteManager(LocalSiteManager(f2)) + sm2 = f2.getSiteManager() + addUtility(sm2, '', IAuthentication, AuthUtility2()) + transaction.commit() + + from zope.container.interfaces import ISimpleReadContainer + from zope.container.traversal import ContainerTraverser + + component.provideAdapter(ContainerTraverser, + (ISimpleReadContainer, IRequest), + IPublishTraverse, name='') + + from zope.site.interfaces import IFolder + from zope.security.checker import defineChecker, InterfaceChecker + defineChecker(Folder, InterfaceChecker(IFolder)) + + def testPlacefulAuth(self): + self.publication.beforeTraversal(self.request) + self.assertEqual(list(queryInteraction().participations), + [self.request]) + self.assertEqual(self.request.principal.id, 'anonymous') + root = self.publication.getApplication(self.request) + self.publication.callTraversalHooks(self.request, root) + self.assertEqual(self.request.principal.id, 'anonymous') + ob = self.publication.traverseName(self.request, root, 'f1') + self.publication.callTraversalHooks(self.request, ob) + self.assertEqual(self.request.principal.id, 'test.anonymous') + ob = self.publication.traverseName(self.request, ob, 'f2') + self.publication.afterTraversal(self.request, ob) + self.assertEqual(self.request.principal.id, 'test.bob') + self.assertEqual(list(queryInteraction().participations), + [self.request]) + self.publication.endRequest(self.request, ob) + self.assertEqual(queryInteraction(), None) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(ZopePublicationTests), + unittest.makeSuite(AuthZopePublicationTests), + unittest.makeSuite(ZopePublicationErrorHandling), + )) + +if __name__ == '__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/app/publication/traversers.py zope3-3.5~bzr18/src/zope/app/publication/traversers.py --- zope3-3.4.0/src/zope/app/publication/traversers.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/traversers.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,108 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Generic object traversers + +$Id: traversers.py 100281 2009-05-23 06:04:43Z shane $ +""" +__docformat__ = 'restructuredtext' + +import zope.component + +from zope.interface import providedBy, implements +from zope.publisher.interfaces import Unauthorized, NotFound +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.interfaces.xmlrpc import IXMLRPCPublisher +from zope.publisher.defaultview import getDefaultViewName + +class SimpleComponentTraverser(object): + """Browser traverser for simple components that can only traverse to views + """ + implements(IBrowserPublisher, IXMLRPCPublisher) + + def __init__(self, context, request): + self.context = context + self.request = request + + def browserDefault(self, request): + ob = self.context + view_name = getDefaultViewName(ob, request) + return ob, (view_name,) + + def publishTraverse(self, request, name): + ob = self.context + view = zope.component.queryMultiAdapter((ob, request), name=name) + if view is None: + raise NotFound(ob, name) + return view + +class FileContentTraverser(SimpleComponentTraverser): + """Browser traverser for file content. + + The default view for file content has effective URLs that don't end in + /. In particular, if the content inclused HTML, relative links in + the HTML are relative to the container the content is in. + """ + + def browserDefault(self, request): + ob = self.context + + view_name = getDefaultViewName(ob, request) + view = self.publishTraverse(request, view_name) + if hasattr(view, 'browserDefault'): + view, path = view.browserDefault(request) + if len(path) == 1: + view = view.publishTraverse(request, path[0]) + path = () + else: + path = () + + return view, path + +def NoTraverser(ob, request): + return None + +class TestTraverser(object): + """Bobo-style traverser, mostly useful for testing""" + implements(IBrowserPublisher) + + def __init__(self, context, request): + self.context = context + + def browserDefault(self, request): + ob = self.context + + if list(providedBy(ob)): + view_name = getDefaultViewName(ob, request) + return ob, (("@@%s" % view_name),) + + return ob, () + + def publishTraverse(self, request, name): + ob = self.context + if name.startswith('@@'): + return zope.component.getMultiAdapter((ob, request), name=name[6:]) + + if name.startswith('_'): + raise Unauthorized(name) + + subob = getattr(ob, name, self) # self is marker here + if subob is self: + # no attribute + try: + subob = ob[name] + except (KeyError, IndexError, + TypeError, AttributeError): + raise NotFound(ob, name, request) + + return subob diff -Nru zope3-3.4.0/src/zope/app/publication/xmlrpc.py zope3-3.5~bzr18/src/zope/app/publication/xmlrpc.py --- zope3-3.4.0/src/zope/app/publication/xmlrpc.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/xmlrpc.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,33 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""XML-RPC Publication Handler. + +This module specifically implements a custom `nameTraverse()` method. + +$Id: xmlrpc.py 27311 2004-08-27 21:22:43Z jim $ +""" +__docformat__ = 'restructuredtext' + +from zope.app.publication.http import BaseHTTPPublication + +# Don't need any special handling for xml-rpc +XMLRPCPublication = BaseHTTPPublication + +class XMLRPCPublicationFactory(object): + + def __init__(self, db): + self.__pub = XMLRPCPublication(db) + + def __call__(self): + return self.__pub diff -Nru zope3-3.4.0/src/zope/app/publication/zopepublication.py zope3-3.5~bzr18/src/zope/app/publication/zopepublication.py --- zope3-3.4.0/src/zope/app/publication/zopepublication.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publication/zopepublication.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,467 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +__docformat__ = 'restructuredtext' + +import sys +import logging +from new import instancemethod + +from ZODB.POSException import ConflictError +import transaction + +import zope.component +import zope.component.interfaces +from zope.component import queryMultiAdapter +from zope.event import notify +from zope.security.interfaces import Unauthorized +from zope.interface import implements, providedBy +from zope.publisher.publish import mapply +from zope.publisher.interfaces import IExceptionSideEffects, IHeld +from zope.publisher.interfaces import IPublication, IPublishTraverse, IRequest +from zope.publisher.interfaces import NotFound, Retry +from zope.security.management import newInteraction, endInteraction +from zope.security.checker import ProxyFactory +from zope.security.proxy import removeSecurityProxy +from zope.traversing.interfaces import BeforeTraverseEvent +from zope.traversing.interfaces import IPhysicallyLocatable +from zope.traversing.interfaces import IEtcNamespace +from zope.traversing.interfaces import TraversalError +from zope.traversing.namespace import namespaceLookup, nsParse +from zope.location import LocationProxy +from zope.error.interfaces import IErrorReportingUtility + +import zope.authentication.interfaces +from zope.browser.interfaces import ISystemErrorView +from zope.publisher.defaultview import queryDefaultViewName +from zope.publisher.interfaces import EndRequestEvent +from zope.authentication.interfaces import IUnauthenticatedPrincipal +from zope.authentication.interfaces import IFallbackUnauthenticatedPrincipal +from zope.authentication.interfaces import IAuthentication + + +class Cleanup(object): + + implements(IHeld) + + def __init__(self, f): + self._f = f + + def release(self): + self._f() + self._f = None + + def __del__(self): + if self._f is not None: + logging.getLogger('SiteError').error( + "Cleanup without request close") + self._f() + +class ZopePublication(object): + """Base Zope publication specification.""" + implements(IPublication) + + root_name = 'Application' + + def __init__(self, db): + # db is a ZODB.DB.DB object. + self.db = db + + def proxy(self, ob): + """Security-proxy an object + + Subclasses may override this to use a different proxy (or + checker) implementation or to not proxy at all. + """ + return ProxyFactory(ob) + + def beforeTraversal(self, request): + # Try to authenticate against the root authentication utility. + auth = zope.component.getGlobalSiteManager().getUtility( + zope.authentication.interfaces.IAuthentication) + principal = auth.authenticate(request) + if principal is None: + principal = auth.unauthenticatedPrincipal() + if principal is None: + # Get the fallback unauthenticated principal + principal = zope.component.getUtility( + IFallbackUnauthenticatedPrincipal) + + request.setPrincipal(principal) + newInteraction(request) + transaction.begin() + + def _maybePlacefullyAuthenticate(self, request, ob): + if not IUnauthenticatedPrincipal.providedBy(request.principal): + # We've already got an authenticated user. There's nothing to do. + # Note that beforeTraversal guarentees that user is not None. + return + + if not zope.component.interfaces.ISite.providedBy(ob): + # We won't find an authentication utility here, so give up. + return + + sm = removeSecurityProxy(ob).getSiteManager() + + auth = sm.queryUtility(IAuthentication) + if auth is None: + # No auth utility here + return + + # Try to authenticate against the auth utility + principal = auth.authenticate(request) + if principal is None: + principal = auth.unauthenticatedPrincipal() + if principal is None: + # nothing to do here + return + + request.setPrincipal(principal) + + def callTraversalHooks(self, request, ob): + # Call __before_publishing_traverse__ hooks + notify(BeforeTraverseEvent(ob, request)) + # This is also a handy place to try and authenticate. + self._maybePlacefullyAuthenticate(request, ob) + + def afterTraversal(self, request, ob): + #recordMetaData(object, request) + self._maybePlacefullyAuthenticate(request, ob) + + + def openedConnection(self, conn): + # Hook for auto-refresh + pass + + def getApplication(self, request): + # If '++etc++process' is in the path, then we should + # get the parent of the application controller rather than + # open the database. + stack = request.getTraversalStack() + + if '++etc++process' in stack: + obj = zope.component.getUtility(IEtcNamespace, 'process') + return obj.__parent__ + + # Open the database. + conn = self.db.open() + + cleanup = Cleanup(conn.close) + request.hold(cleanup) # Close the connection on request.close() + + request.annotations['ZODB.interfaces.IConnection'] = conn + self.openedConnection(conn) + #conn.setDebugInfo(getattr(request, 'environ', None), request.other) + + root = conn.root() + app = root.get(self.root_name, None) + + if app is None: + raise SystemError("Zope Application Not Found") + + return self.proxy(app) + + def traverseName(self, request, ob, name): + nm = name # the name to look up the object with + + if name and name[:1] in '@+': + # Process URI segment parameters. + ns, nm = nsParse(name) + if ns: + try: + ob2 = namespaceLookup(ns, nm, ob, request) + except TraversalError: + raise NotFound(ob, name) + + return self.proxy(ob2) + + if nm == '.': + return ob + + if IPublishTraverse.providedBy(ob): + ob2 = ob.publishTraverse(request, nm) + else: + # self is marker + adapter = queryMultiAdapter((ob, request), IPublishTraverse, + default=self) + if adapter is not self: + ob2 = adapter.publishTraverse(request, nm) + else: + raise NotFound(ob, name, request) + + return self.proxy(ob2) + + def callObject(self, request, ob): + return mapply(ob, request.getPositionalArguments(), request) + + def afterCall(self, request, ob): + txn = transaction.get() + if txn.isDoomed(): + txn.abort() + else: + self.annotateTransaction(txn, request, ob) + txn.commit() + + def endRequest(self, request, ob): + endInteraction() + notify(EndRequestEvent(ob, request)) + + def annotateTransaction(self, txn, request, ob): + """Set some useful meta-information on the transaction. This + information is used by the undo framework, for example. + + This method is not part of the `IPublication` interface, since + it's specific to this particular implementation. + """ + if request.principal is not None: + txn.setUser(request.principal.id) + + # Work around methods that are usually used for views + bare = removeSecurityProxy(ob) + if isinstance(bare, instancemethod): + ob = bare.im_self + + # set the location path + path = None + locatable = IPhysicallyLocatable(ob, None) + if locatable is not None: + # Views are made children of their contexts, but that + # doesn't necessarily mean that we can fully resolve the + # path. E.g. the family tree of a resource cannot be + # resolved completely, as the site manager is a dead end. + try: + path = locatable.getPath() + except (AttributeError, TypeError): + pass + if path is not None: + txn.setExtendedInfo('location', path) + + # set the request type + iface = IRequest + for iface in providedBy(request): + if iface.extends(IRequest): + break + iface_dotted = iface.__module__ + '.' + iface.getName() + txn.setExtendedInfo('request_type', iface_dotted) + return txn + + def _logErrorWithErrorReportingUtility(self, object, request, exc_info): + # Record the error with the ErrorReportingUtility + self.beginErrorHandlingTransaction(request, object, + 'error reporting utility') + try: + errUtility = zope.component.getUtility(IErrorReportingUtility) + + # It is important that an error in errUtility.raising + # does not propagate outside of here. Otherwise, nothing + # meaningful will be returned to the user. + # + # The error reporting utility should not be doing database + # stuff, so we shouldn't get a conflict error. + # Even if we do, it is more important that we log this + # error, and proceed with the normal course of events. + # We should probably (somehow!) append to the standard + # error handling that this error occurred while using + # the ErrorReportingUtility, and that it will be in + # the zope log. + + errUtility.raising(exc_info, request) + transaction.commit() + except: + tryToLogException( + 'Error while reporting an error to the Error Reporting utility' + ) + transaction.abort() + + def handleException(self, object, request, exc_info, retry_allowed=True): + # This transaction had an exception that reached the publisher. + # It must definitely be aborted. + try: + transaction.abort() + except: + # Hm, a catastrophe. We might want to know what preceded it. + self._logErrorWithErrorReportingUtility(object, request, exc_info) + raise + + # Reraise Retry exceptions for the publisher to deal with. + if retry_allowed and isinstance(exc_info[1], Retry): + raise + + # Convert ConflictErrors to Retry exceptions. + if retry_allowed and isinstance(exc_info[1], ConflictError): + tryToLogWarning( + 'ZopePublication', + 'Competing writes/reads at %s: %s' + % (request.get('PATH_INFO', '???'), + exc_info[1], + ), + ) + raise Retry(exc_info) + # Are there any reasons why we'd want to let application-level error + # handling determine whether a retry is allowed or not? + # Assume not for now. + + # Record the error with the ErrorReportingUtility. + self._logErrorWithErrorReportingUtility(object, request, exc_info) + + response = request.response + response.reset() + exception = None + legacy_exception = not isinstance(exc_info[1], Exception) + if legacy_exception: + response.handleException(exc_info) + if isinstance(exc_info[1], str): + tryToLogWarning( + 'Publisher received a legacy string exception: %s.' + ' This will be handled by the request.' % + exc_info[1]) + else: + tryToLogWarning( + 'Publisher received a legacy classic class exception: %s.' + ' This will be handled by the request.' % + exc_info[1].__class__) + else: + # We definitely have an Exception + # Set the request body, and abort the current transaction. + self.beginErrorHandlingTransaction( + request, object, 'application error-handling') + view = None + try: + # We need to get a location, because some template content of + # the exception view might require one. + # + # The object might not have a parent, because it might be a + # method. If we don't have a `__parent__` attribute but have + # an im_self or a __self__, use it. + loc = object + if not hasattr(object, '__parent__'): + loc = removeSecurityProxy(object) + # Try to get an object, since we apparently have a method + # Note: We are guaranteed that an object has a location, + # so just getting the instance the method belongs to is + # sufficient. + loc = getattr(loc, 'im_self', loc) + loc = getattr(loc, '__self__', loc) + # Protect the location with a security proxy + loc = self.proxy(loc) + + # Give the exception instance its location and look up the + # view. + exception = LocationProxy(exc_info[1], loc, '') + name = queryDefaultViewName(exception, request) + if name is not None: + view = zope.component.queryMultiAdapter( + (exception, request), name=name) + except: + # Problem getting a view for this exception. Log an error. + tryToLogException( + 'Exception while getting view on exception') + + + if view is not None: + try: + # We use mapply instead of self.callObject here + # because we don't want to pass positional + # arguments. The positional arguments were meant + # for the published object, not an exception view. + body = mapply(view, (), request) + response.setResult(body) + transaction.commit() + if (ISystemErrorView.providedBy(view) + and view.isSystemError()): + # Got a system error, want to log the error + + # Lame hack to get around logging missfeature + # that is fixed in Python 2.4 + try: + raise exc_info[0], exc_info[1], exc_info[2] + except: + logging.getLogger('SiteError').exception( + str(request.URL), + ) + + except: + # Problem rendering the view for this exception. + # Log an error. + tryToLogException( + 'Exception while rendering view on exception') + + # Record the error with the ErrorReportingUtility + self._logErrorWithErrorReportingUtility( + object, request, sys.exc_info()) + + view = None + + if view is None: + # Either the view was not found, or view was set to None + # because the view couldn't be rendered. In either case, + # we let the request handle it. + response.handleException(exc_info) + transaction.abort() + + # See if there's an IExceptionSideEffects adapter for the + # exception + try: + adapter = IExceptionSideEffects(exception, None) + except: + tryToLogException( + 'Exception while getting IExceptionSideEffects adapter') + adapter = None + + if adapter is not None: + self.beginErrorHandlingTransaction( + request, object, 'application error-handling side-effect') + try: + # Although request is passed in here, it should be + # considered read-only. + adapter(object, request, exc_info) + transaction.commit() + except: + tryToLogException( + 'Exception while calling' + ' IExceptionSideEffects adapter') + transaction.abort() + + def beginErrorHandlingTransaction(self, request, ob, note): + txn = transaction.begin() + txn.note(note) + self.annotateTransaction(txn, request, ob) + return txn + +def tryToLogException(arg1, arg2=None): + if arg2 is None: + subsystem = 'SiteError' + message = arg1 + else: + subsystem = arg1 + message = arg2 + try: + logging.getLogger(subsystem).exception(message) + # Bare except, because we want to swallow any exception raised while + # logging an exception. + except: + pass + +def tryToLogWarning(arg1, arg2=None, exc_info=False): + if arg2 is None: + subsystem = 'SiteError' + message = arg1 + else: + subsystem = arg1 + message = arg2 + try: + logging.getLogger(subsystem).warn(message, exc_info=exc_info) + # Bare except, because we want to swallow any exception raised while + # logging a warning. + except: + pass diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/configure.zcml zope3-3.5~bzr18/src/zope/app/publisher/browser/configure.zcml --- zope3-3.4.0/src/zope/app/publisher/browser/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/directoryresource.py zope3-3.5~bzr18/src/zope/app/publisher/browser/directoryresource.py --- zope3-3.4.0/src/zope/app/publisher/browser/directoryresource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/directoryresource.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,28 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Resource Directory + +A 'resource directory' is an on-disk directory which is registered as +a resource using the ZCML directive. The +directory is treated as a source for individual resources; it can be +traversed to retrieve resources represented by contained files, which +can in turn be treated as resources. The contained files have +__name__ values which include a '/' separating the __name__ of the +resource directory from the name of the file within the directory. + +$Id: directoryresource.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browserresource.directory import Directory, DirectoryResource +from zope.browserresource.directory import DirectoryResourceFactory diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/fields.py zope3-3.5~bzr18/src/zope/app/publisher/browser/fields.py --- zope3-3.4.0/src/zope/app/publisher/browser/fields.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/fields.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,18 @@ +############################################################################# +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Browser-Presentation related Fields. + +$Id: fields.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.browsermenu.field import MenuField # BBB import diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/fileresource.py zope3-3.5~bzr18/src/zope/app/publisher/browser/fileresource.py --- zope3-3.4.0/src/zope/app/publisher/browser/fileresource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/fileresource.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""File-based browser resources. + +$Id: fileresource.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browserresource.file import FileResource, setCacheControl +from zope.browserresource.file import FileResourceFactory +ImageResourceFactory = FileResourceFactory diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/i18nfileresource.py zope3-3.5~bzr18/src/zope/app/publisher/browser/i18nfileresource.py --- zope3-3.4.0/src/zope/app/publisher/browser/i18nfileresource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/i18nfileresource.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Internationalized file resource. + +$Id: i18nfileresource.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browserresource.i18nfile import I18nFileResource +from zope.browserresource.i18nfile import I18nFileResourceFactory diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/i18nresourcemeta.py zope3-3.5~bzr18/src/zope/app/publisher/browser/i18nresourcemeta.py --- zope3-3.4.0/src/zope/app/publisher/browser/i18nresourcemeta.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/i18nresourcemeta.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,18 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Browser configuration code + +$Id: i18nresourcemeta.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.browserresource.metaconfigure import I18nResource # BBB import diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/icon.py zope3-3.5~bzr18/src/zope/app/publisher/browser/icon.py --- zope3-3.4.0/src/zope/app/publisher/browser/icon.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/icon.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Icon support + +$Id: icon.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browserresource.icon import IconView, IconViewFactory +from zope.browserresource.metaconfigure import icon as IconDirective diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/__init__.py zope3-3.5~bzr18/src/zope/app/publisher/browser/__init__.py --- zope3-3.4.0/src/zope/app/publisher/browser/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 zope app-server customizatioin of publisher browser facilities + +$Id: __init__.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.publisher.browser import BrowserLanguages +from zope.publisher.browser import CacheableBrowserLanguages +from zope.publisher.browser import ModifiableBrowserLanguages +from zope.publisher.browser import NotCompatibleAdapterError +from zope.publisher.defaultview import IDefaultViewNameAPI +from zope.publisher.defaultview import getDefaultViewName +from zope.publisher.defaultview import queryDefaultViewName diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/managementviewselector.py zope3-3.5~bzr18/src/zope/app/publisher/browser/managementviewselector.py --- zope3-3.4.0/src/zope/app/publisher/browser/managementviewselector.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/managementviewselector.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Selecting first available and allowed management view + +$Id: managementviewselector.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.interface import implements +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.browser import BrowserView +from zope.browsermenu.menu import getFirstMenuItem + +class ManagementViewSelector(BrowserView): + """View that selects the first available management view. + + Support 'zmi_views' actions like: 'javascript:alert("hello")', + '../view_on_parent.html' or '++rollover++'. + """ + implements(IBrowserPublisher) + + def browserDefault(self, request): + return self, () + + def __call__(self): + item = getFirstMenuItem('zmi_views', self.context, self.request) + + if item: + redirect_url = item['action'] + if not (redirect_url.startswith('../') or \ + redirect_url.lower().startswith('javascript:') or \ + redirect_url.lower().startswith('++')): + self.request.response.redirect(redirect_url) + return u'' + + self.request.response.redirect('.') # Redirect to content/ + return u'' diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/menumeta.py zope3-3.5~bzr18/src/zope/app/publisher/browser/menumeta.py --- zope3-3.4.0/src/zope/app/publisher/browser/menumeta.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/menumeta.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Menu Directives Configuration Handlers + +$Id: menumeta.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browsermenu.metaconfigure import ( + menuDirective, + menuItemDirective, + subMenuItemDirective, + MenuItemFactory, + menuItemsDirective, + _checkViewFor, + addMenuItem, +) diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/menu.py zope3-3.5~bzr18/src/zope/app/publisher/browser/menu.py --- zope3-3.4.0/src/zope/app/publisher/browser/menu.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/menu.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,26 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Menu Registration code. + +$Id: menu.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browsermenu.menu import ( + BrowserMenu, + BrowserMenuItem, + BrowserSubMenuItem, + getMenu, + getFirstMenuItem, + MenuAccessView, +) diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/metaconfigure.py zope3-3.5~bzr18/src/zope/app/publisher/browser/metaconfigure.py --- zope3-3.4.0/src/zope/app/publisher/browser/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Browser configuration code + +$Id: metaconfigure.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browserresource.metaconfigure import resource +from zope.browserresource.metaconfigure import resourceDirectory +from zope.browserresource.metaconfigure import I18nResource +from zope.browserpage.metaconfigure import view +from zope.publisher.zcml import setDefaultSkin, defaultSkin, defaultView diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/metadirectives.py zope3-3.5~bzr18/src/zope/app/publisher/browser/metadirectives.py --- zope3-3.4.0/src/zope/app/publisher/browser/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################# +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Browser configuration code + +This module defines the schemas for browser directives. + +$Id: metadirectives.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browserresource.metadirectives import ( + IBasicResourceInformation, + IResourceDirective, + II18nResourceDirective, + II18nResourceTranslationSubdirective, + IResourceDirectoryDirective, + IIconDirective +) +from zope.browsermenu.metadirectives import ( + IMenuDirective, + IMenuItemsDirective, + IMenuItem, + IMenuItemSubdirective, + IMenuItemDirective, + ISubMenuItemSubdirective, + ISubMenuItemDirective, + IAddMenuItemDirective, +) +from zope.browserpage.metadirectives import ( + IPagesDirective, + IViewDirective, + IViewPageSubdirective, + IViewDefaultPageSubdirective, + IPagesPageSubdirective, + IPageDirective, +) +from zope.publisher.zcml import IDefaultSkinDirective, IDefaultViewDirective diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/meta.zcml zope3-3.5~bzr18/src/zope/app/publisher/browser/meta.zcml --- zope3-3.4.0/src/zope/app/publisher/browser/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/pagetemplateresource.py zope3-3.5~bzr18/src/zope/app/publisher/browser/pagetemplateresource.py --- zope3-3.4.0/src/zope/app/publisher/browser/pagetemplateresource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/pagetemplateresource.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Page Template Resource + +$Id: pagetemplateresource.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.ptresource.ptresource import PageTemplateResource +from zope.ptresource.ptresource import PageTemplateResourceFactory diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/resourcemeta.py zope3-3.5~bzr18/src/zope/app/publisher/browser/resourcemeta.py --- zope3-3.4.0/src/zope/app/publisher/browser/resourcemeta.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/resourcemeta.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,24 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Browser configuration code + +$Id: resourcemeta.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browserresource.metaconfigure import ( + allowed_names, + ResourceFactoryWrapper, + resource, + resourceDirectory +) diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/resource.py zope3-3.5~bzr18/src/zope/app/publisher/browser/resource.py --- zope3-3.4.0/src/zope/app/publisher/browser/resource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/resource.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,18 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Browser Resource + +$Id: resource.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.browserresource.resource import Resource, AbsoluteURL # BBB imports diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/resources.py zope3-3.5~bzr18/src/zope/app/publisher/browser/resources.py --- zope3-3.4.0/src/zope/app/publisher/browser/resources.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/resources.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,19 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Resource URL acess + +$Id: resources.py 103291 2009-08-27 15:12:51Z nadako $ +""" + +from zope.browserresource.resources import Resources, empty # BBB imports diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/viewmeta.py zope3-3.5~bzr18/src/zope/app/publisher/browser/viewmeta.py --- zope3-3.4.0/src/zope/app/publisher/browser/viewmeta.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/viewmeta.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,30 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Browser configuration code + +$Id: viewmeta.py 103291 2009-08-27 15:12:51Z nadako $ +""" +# BBB imports +from zope.browserpage.metaconfigure import ( + page, + pages, + view, + _handle_menu, + _handle_permission, + _handle_allowed_interface, + _handle_allowed_attributes, + _handle_for, + simple, + providesCallable, +) diff -Nru zope3-3.4.0/src/zope/app/publisher/browser/vocabulary.py zope3-3.5~bzr18/src/zope/app/publisher/browser/vocabulary.py --- zope3-3.4.0/src/zope/app/publisher/browser/vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/browser/vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2006 Zope Corporation 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. +# +############################################################################## +"""Browser vocabularies + +$Id: vocabulary.py 100152 2009-05-19 20:47:49Z faassen $ +""" +from zope.interface import classProvides +from zope.publisher.interfaces.browser import IBrowserSkinType +from zope.schema.interfaces import IVocabularyFactory +from zope.componentvocabulary.vocabulary import UtilityVocabulary + +class BrowserSkinsVocabulary(UtilityVocabulary): + classProvides(IVocabularyFactory) + interface = IBrowserSkinType diff -Nru zope3-3.4.0/src/zope/app/publisher/configure.zcml zope3-3.5~bzr18/src/zope/app/publisher/configure.zcml --- zope3-3.4.0/src/zope/app/publisher/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,12 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/fieldconverters.py zope3-3.5~bzr18/src/zope/app/publisher/fieldconverters.py --- zope3-3.4.0/src/zope/app/publisher/fieldconverters.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/fieldconverters.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,61 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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-specific request field converters. + +$Id: fieldconverters.py 67630 2006-04-27 00:54:03Z jim $ +""" +from datetime import datetime + +from zope.publisher.browser import registerTypeConverter +from zope.datetime import parse as parseDateTime + +def field2date_via_datetimeutils(v): + """Converter for request fields marshalled as ':date'. + + o TODO: Uses the non-localized and non-tzinfo-aware 'parseDateTime' + utility from zope.datetime; a better alternative + would be more I18N / L10N aware, perhaps even adapting to + the expressed preferences of the user. + """ + if hasattr(v,'read'): + v = v.read() + else: + v = str(v) + + # *Don't* force a timezone if not passed explicitly; leave it as + # "naive" datetime. + year, month, day, hour, minute, second, tzname = parseDateTime(v, local=0) + + # TODO: look up a real tzinfo object using 'tzname' + # + # Option 1: Use 'timezones' module as global registry:: + # + # from zope.app.timezones import getTimezoneInfo + # tzinfo = getTimezoneInfo(tzname) + # + # Option 2: Use a utility (or perhaps a view, for L10N). + # + # tz_lookup = getUtility(ITimezoneLookup) + # tzinfo = tz_lookup(tzname) + # + return datetime(year, month, day, hour, minute, second, + # tzinfo=tzinfo + ) + +ZOPE_CONVERTERS = [('date', field2date_via_datetimeutils)] + +def registerZopeConverters(): + + for field_type, converter in ZOPE_CONVERTERS: + registerTypeConverter(field_type, converter) diff -Nru zope3-3.4.0/src/zope/app/publisher/fileresource.py zope3-3.5~bzr18/src/zope/app/publisher/fileresource.py --- zope3-3.4.0/src/zope/app/publisher/fileresource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/fileresource.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Browser File Resource + +$Id: fileresource.py 103291 2009-08-27 15:12:51Z nadako $ +""" + +# BBB imports +from zope.browserresource.file import File +Image = File diff -Nru zope3-3.4.0/src/zope/app/publisher/ftesting.zcml zope3-3.5~bzr18/src/zope/app/publisher/ftesting.zcml --- zope3-3.4.0/src/zope/app/publisher/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/http.zcml zope3-3.5~bzr18/src/zope/app/publisher/http.zcml --- zope3-3.4.0/src/zope/app/publisher/http.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/http.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/i18n.py zope3-3.5~bzr18/src/zope/app/publisher/i18n.py --- zope3-3.4.0/src/zope/app/publisher/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/i18n.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Customization of zope.i18n for the Zope application server + +$Id: i18n.py 73638 2007-03-26 16:27:10Z dobe $ +""" +__docformat__ = 'restructuredtext' + +# import this as _ to create i18n messages in the zope domain +from zope.i18nmessageid import MessageFactory +ZopeMessageFactory = MessageFactory('zope') diff -Nru zope3-3.4.0/src/zope/app/publisher/__init__.py zope3-3.5~bzr18/src/zope/app/publisher/__init__.py --- zope3-3.4.0/src/zope/app/publisher/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/publisher/interfaces/browser.py zope3-3.5~bzr18/src/zope/app/publisher/interfaces/browser.py --- zope3-3.4.0/src/zope/app/publisher/interfaces/browser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/interfaces/browser.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Browser-Specific Publisher interfaces + +$Id: browser.py 103291 2009-08-27 15:12:51Z nadako $ +""" + +# BBB imports +from zope.browsermenu.interfaces import ( + IMenuItemType, + AddMenu, + IBrowserMenu, + IBrowserMenuItem, + IBrowserSubMenuItem, + IMenuAccessView +) diff -Nru zope3-3.4.0/src/zope/app/publisher/interfaces/ftp.py zope3-3.5~bzr18/src/zope/app/publisher/interfaces/ftp.py --- zope3-3.4.0/src/zope/app/publisher/interfaces/ftp.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/interfaces/ftp.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,179 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Virtual File System interfaces for the publisher. + +$Id: ftp.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.publisher.interfaces.ftp import IFTPPublisher, IFTPView + + +class IFTPDirectoryPublisher(IFTPPublisher, IFTPView): + + def type(name): + """Return the file type at the given name + + The return valie is 'd', for a directory, 'f', for a file, and + None if there is no file at the path. + """ + + def names(filter=None): + """Return a sequence of the names in a directory + + If the filter is not None, include only those names for which + the filter returns a true value. + """ + + def ls(filter=None): + """Return a sequence of information objects + + Return item info objects (see lsinfo) for the files in a directory. + + If the filter is not None, include only those names for which + the filter returns a true value. + """ + return list(tuple(str, str)) + + def readfile(name, outstream, start=0, end=None): + """Outputs the file at name to a stream. + + Data are copied starting from start. If end is not None, + data are copied up to end. + + """ + + def lsinfo(name): + """Return information for a unix-style ls listing for the path + + Data are returned as a dictionary containing the following keys: + + type + + The path type, either 'd' or 'f'. + + owner_name + + Defaults to "na". Must not include spaces. + + owner_readable + + defaults to True + + owner_writable + + defaults to True + + owner_executable + + defaults to True for directories and false otherwise. + + group_name + + Defaults to "na". Must not include spaces. + + group_readable + + defaults to True + + group_writable + + defaults to True + + group_executable + + defaults to True for directories and false otherwise. + + other_readable + + defaults to False + + other_writable + + defaults to False + + other_executable + + defaults to True for directories and false otherwise. + + mtime + + Optional time, as a datetime. + + nlinks + + The number of links. Defaults to 1. + + size + + The file size. Defaults to 0. + + name + + The file name. + """ + + def mtime(name): + """Return the modification time for the file + + Return None if it is unknown. + """ + + def size(name): + """Return the size of the file at path + """ + + def mkdir(name): + """Create a directory. + """ + + def remove(name): + """Remove a file. Same as unlink. + """ + + def rmdir(name): + """Remove a directory. + """ + + def rename(old, new): + """Rename a file or directory. + """ + + def writefile(name, instream, start=None, end=None, append=False): + """Write data to a file. + + If start or end is not None, then only part of the file is + written. The remainder of the file is unchanged. + If start or end are specified, they must ne non-negative. + + If end is None, then the file is truncated after the data are + written. If end is not None, parts of the file after end, if + any, are unchanged. If end is not None and there isn't enough + data in instream to fill out the file, then the missing data + are undefined. + + If neither start nor end are specified, then the file contents + are overwritten. + + If start is specified and the file doesn't exist or is shorter + than start, the file will contain undefined data before start. + + If append is true, start and end are ignored. + """ + + def writable(name): + """Return boolean indicating whether a file at path is writable + + Note that a true value should be returned if the file doesn't + exist but it's directory is writable. + + """ diff -Nru zope3-3.4.0/src/zope/app/publisher/interfaces/http.py zope3-3.5~bzr18/src/zope/app/publisher/interfaces/http.py --- zope3-3.4.0/src/zope/app/publisher/interfaces/http.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/interfaces/http.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,42 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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's HTTP-specific Publisher interfaces. + +$Id: http.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.publisher.interfaces.http import IHTTPView + + +class ILogin(IHTTPView): + """A simple login interface.""" + + def login(): + """Login the user. + + This method should generate some sort of UI to request the username + and password. + """ + + +class ILogout(IHTTPView): + """A simple logout interface.""" + + def logout(): + """Logout the user. + + This can mean different things. For example, when dealing with + cookie-based logins (browser), then it simply means deleting the + cookie. If we deal with HTTP Authentication, we just want to send + another challenge. + """ diff -Nru zope3-3.4.0/src/zope/app/publisher/interfaces/__init__.py zope3-3.5~bzr18/src/zope/app/publisher/interfaces/__init__.py --- zope3-3.4.0/src/zope/app/publisher/interfaces/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/interfaces/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,18 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""Resource + +$Id: __init__.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.browserresource.interfaces import IResource # BBB import diff -Nru zope3-3.4.0/src/zope/app/publisher/interfaces/xmlrpc.py zope3-3.5~bzr18/src/zope/app/publisher/interfaces/xmlrpc.py --- zope3-3.4.0/src/zope/app/publisher/interfaces/xmlrpc.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/interfaces/xmlrpc.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,18 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""XML-RPC Publisher Interfaces + +$Id: xmlrpc.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.publisher.interfaces.xmlrpc import IXMLRPCView # BBB import diff -Nru zope3-3.4.0/src/zope/app/publisher/meta.zcml zope3-3.5~bzr18/src/zope/app/publisher/meta.zcml --- zope3-3.4.0/src/zope/app/publisher/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/pagetemplateresource.py zope3-3.5~bzr18/src/zope/app/publisher/pagetemplateresource.py --- zope3-3.4.0/src/zope/app/publisher/pagetemplateresource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/pagetemplateresource.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,19 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Page Template Resource + +$Id: pagetemplateresource.py 103291 2009-08-27 15:12:51Z nadako $ +""" +from zope.ptresource.ptresource import PageTemplate # BBB import + diff -Nru zope3-3.4.0/src/zope/app/publisher/testing.py zope3-3.5~bzr18/src/zope/app/publisher/testing.py --- zope3-3.4.0/src/zope/app/publisher/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,26 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation 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.app.publisher common test related classes/functions/objects. + +$Id: testing.py 72426 2007-02-07 13:57:45Z baijum $ +""" + +__docformat__ = "reStructuredText" + +import os +from zope.app.testing.functional import ZCMLLayer + +AppPublisherLayer = ZCMLLayer( + os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'), + __name__, 'AppPublisherLayer', allow_teardown=True) diff -Nru zope3-3.4.0/src/zope/app/publisher/tests/__init__.py zope3-3.5~bzr18/src/zope/app/publisher/tests/__init__.py --- zope3-3.4.0/src/zope/app/publisher/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Unit tests for zope.app.publisher. diff -Nru zope3-3.4.0/src/zope/app/publisher/tests/test_fieldconverters.py zope3-3.5~bzr18/src/zope/app/publisher/tests/test_fieldconverters.py --- zope3-3.4.0/src/zope/app/publisher/tests/test_fieldconverters.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/tests/test_fieldconverters.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,60 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 of field converters. + +$Id: test_fieldconverters.py 26560 2004-07-15 21:38:42Z srichter $ +""" +from unittest import TestCase, TestSuite, main, makeSuite +from datetime import datetime + +class TestFieldConverters(TestCase): + + def test_field2date_dateonly(self): + + from zope.app.publisher.fieldconverters \ + import field2date_via_datetimeutils + + dt = field2date_via_datetimeutils('2003/05/04') + self.failUnless(isinstance(dt, datetime)) + self.assertEqual(dt.year, 2003) + self.assertEqual(dt.month, 5) + self.assertEqual(dt.day, 4) + self.assertEqual(dt.hour, 0) + self.assertEqual(dt.minute, 0) + self.assertEqual(dt.second, 0) + self.assertEqual(dt.tzinfo, None) + + def test_field2date_timestamp(self): + + from zope.app.publisher.fieldconverters \ + import field2date_via_datetimeutils + + dt = field2date_via_datetimeutils('2003/05/04 19:26:54') + self.failUnless(isinstance(dt, datetime)) + self.assertEqual(dt.year, 2003) + self.assertEqual(dt.month, 5) + self.assertEqual(dt.day, 4) + self.assertEqual(dt.hour, 19) + self.assertEqual(dt.minute, 26) + self.assertEqual(dt.second, 54) + self.assertEqual(dt.tzinfo, None) + +def test_suite(): + suite = TestSuite() + suite.addTest(makeSuite(TestFieldConverters)) + return suite + + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/configure.zcml zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/configure.zcml --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/__init__.py zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/__init__.py --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,61 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""XML-RPC Publisher Components + +This module contains the XMLRPCView. + +$Id: __init__.py 103291 2009-08-27 15:12:51Z nadako $ +""" +import zope.interface +import zope.location +import zope.publisher.interfaces.xmlrpc +import zope.app.publisher.interfaces.xmlrpc + +from zope.publisher.xmlrpc import XMLRPCView + + +class IMethodPublisher(zope.interface.Interface): + """Marker interface for an object that wants to publish methods + """ + +# Need to test new __parent__ attribute +class MethodPublisher(XMLRPCView, zope.location.Location): + """Base class for very simple XML-RPC views that publish methods + + This class is meant to be more of an example than a standard base class. + + This example is explained in the README.txt file for this package + """ + zope.interface.implements(IMethodPublisher) + + def __getParent(self): + return hasattr(self, '_parent') and self._parent or self.context + + def __setParent(self, parent): + self._parent = parent + + __parent__ = property(__getParent, __setParent) + + +class MethodTraverser(object): + zope.interface.implements( + zope.publisher.interfaces.xmlrpc.IXMLRPCPublisher) + + __used_for__ = IMethodPublisher + + def __init__(self, context, request): + self.context = context + + def publishTraverse(self, request, name): + return getattr(self.context, name) diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/metaconfigure.py zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/metaconfigure.py --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,120 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""XMLRPC configuration code + +$Id: metaconfigure.py 82484 2007-12-28 13:40:58Z ctheune $ +""" +from zope.interface import Interface +from zope.configuration.exceptions import ConfigurationError +from zope.security.checker import CheckerPublic, Checker +from zope.security.checker import defineChecker, getCheckerForInstancesOf +from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest +from zope.component.interface import provideInterface +from zope.component.zcml import handler + +from zope.app.publisher.xmlrpc import MethodPublisher + +def view(_context, for_=None, interface=None, methods=None, + class_=None, permission=None, name=None): + + interface = interface or [] + methods = methods or [] + + # If there were special permission settings provided, then use them + if permission == 'zope.Public': + permission = CheckerPublic + + require = {} + for attr_name in methods: + require[attr_name] = permission + + if interface: + for iface in interface: + for field_name in iface: + require[field_name] = permission + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', for_) + ) + + # Make sure that the class inherits MethodPublisher, so that the views + # have a location + if class_ is None: + class_ = original_class = MethodPublisher + else: + original_class = class_ + class_ = type(class_.__name__, (class_, MethodPublisher), {}) + + if name: + # Register a single view + + if permission: + checker = Checker(require) + + def proxyView(context, request, class_=class_, checker=checker): + view = class_(context, request) + # We need this in case the resource gets unwrapped and + # needs to be rewrapped + view.__Security_checker__ = checker + return view + + class_ = proxyView + class_.factory = original_class + else: + # No permission was defined, so we defer to the checker + # of the original class + def proxyView(context, request, class_=class_): + view = class_(context, request) + view.__Security_checker__ = getCheckerForInstancesOf(original_class) + return view + class_ = proxyView + class_.factory = original_class + + # Register the new view. + _context.action( + discriminator = ('view', for_, name, IXMLRPCRequest), + callable = handler, + args = ('registerAdapter', + class_, (for_, IXMLRPCRequest), Interface, name, + _context.info) + ) + else: + if permission: + checker = Checker({'__call__': permission}) + else: + raise ConfigurationError( + "XML/RPC view has neither a name nor a permission. " + "You have to specify at least one of the two.") + + for name in require: + # create a new callable class with a security checker; + cdict = {'__Security_checker__': checker, + '__call__': getattr(class_, name)} + new_class = type(class_.__name__, (class_,), cdict) + _context.action( + discriminator = ('view', for_, name, IXMLRPCRequest), + callable = handler, + args = ('registerAdapter', + new_class, (for_, IXMLRPCRequest), Interface, name, + _context.info) + ) + + # Register the used interfaces with the site manager + if for_ is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', for_) + ) diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/metadirectives.py zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/metadirectives.py --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,86 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""'xmlrpc' ZCML Namespace Schemas + +$Id: metadirectives.py 82484 2007-12-28 13:40:58Z ctheune $ +""" +import zope.configuration.fields +import zope.interface +import zope.schema +import zope.security.zcml + +class IViewDirective(zope.interface.Interface): + """View Directive for XML-RPC methods.""" + + for_ = zope.configuration.fields.GlobalObject( + title=u"Published Object Type", + description=u"""The types of objects to be published via XML-RPC + + This can be expressed with either a class or an interface + """, + required=True, + ) + + interface = zope.configuration.fields.Tokens( + title=u"Interface to be published.", + required=False, + value_type=zope.configuration.fields.GlobalInterface() + ) + + methods = zope.configuration.fields.Tokens( + title=u"Methods (or attributes) to be published", + required=False, + value_type=zope.configuration.fields.PythonIdentifier() + ) + + class_ = zope.configuration.fields.GlobalObject( + title=u"Class", + description=u"A class that provides attributes used by the view.", + required=False + ) + + permission = zope.security.zcml.Permission( + title=u"Permission", + description=u"""The permission needed to use the view. + + If this option is used and a name is given for the view, then + the names defined by the given methods or interfaces will be + under the given permission. + + If a name is not given for the view, then, this option is required and + the given permission is required to call the individual views defined + by the given interface and methods. + + (See the name attribute.) + + If no permission is given, then permissions should be declared + for the view using other means, such as the class directive. + """, + required=False) + + name = zope.schema.TextLine( + title=u"The name of the view.", + description=u""" + + If a name is given, then rpc methods are accessed by + traversing the name and then accessing the methods. In this + case, the class should implement + zope.pubisher.interfaces.IPublishTraverse. + + If no name is provided, then the names given by the attributes + and interfaces are published directly as callable views. + + """, + required=False, + ) diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/meta.zcml zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/meta.zcml --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/README.txt zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/README.txt --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,388 @@ +XML-RPC views +============= + +XML-RPC Methods +--------------- + +There are two ways to write XML-RPC views. You can write views that +provide "methods" for other objects, and you can write views that have +their own methods. Let's look at the former case first, since it's a +little bit simpler. + +Let's write a view that returns a folder listing: + + >>> class FolderListing: + ... def contents(self): + ... return list(self.context.keys()) + +Now we'll register it as a view: + + >>> from zope.configuration import xmlconfig + >>> ignored = xmlconfig.string(""" + ... + ... + ... + ... + ... + ... + ... """) + +Now, we'll add some items to the root folder: + + >>> print http(r""" + ... POST /@@contents.html HTTP/1.1 + ... Authorization: Basic bWdyOm1ncnB3 + ... Content-Length: 73 + ... Content-Type: application/x-www-form-urlencoded + ... + ... type_name=BrowserAdd__zope.site.folder.Folder&new_value=f1""") + HTTP/1.1 303 See Other + ... + + >>> print http(r""" + ... POST /@@contents.html HTTP/1.1 + ... Authorization: Basic bWdyOm1ncnB3 + ... Content-Length: 73 + ... Content-Type: application/x-www-form-urlencoded + ... + ... type_name=BrowserAdd__zope.site.folder.Folder&new_value=f2""") + HTTP/1.1 303 See Other + ... + +And call our xmlrpc method: + + >>> from zope.app.testing.xmlrpc import ServerProxy + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/") + >>> proxy.contents() + ['f1', 'f2'] + +Note that we get an unauthorized error if we don't supply authentication +credentials: + + >>> proxy = ServerProxy("http://localhost/") + >>> proxy.contents() + Traceback (most recent call last): + ProtocolError: + + +Named XML-RPC Views +------------------- + +Now let's look at views that have their own methods or other +subobjects. Views that have their own methods have names that appear +in URLs and they get traversed to get to their methods, as in:: + + .../somefolder/listing/contents + +To make this possible, the view has to support traversal, so that, +when it is traversed, it traverses to its attributes. To support +traversal, you can implement or provide an adapter to +`zope.publisher.interfaces.IPublishTraverse`. It's actually better to +provide an adapter so that accesses to attributes during traversal are +mediated by the security machinery. (Object methods are always bound +to unproxied objects, but adapters are bound to proxied objects unless +they are trusted adapters.) + +The 'zope.app.publisher.xmlrpc' package provides a base class, +`MethodPublisher`, that provides the necessary traversal support. In +particulat, it has an adapter that simply traverses to attributes. + +If an XML-RPC view isn't going to be public, then it also has to +implement 'zope.location.ILocation' so that security grants can be +acquired for it, at least with Zope's default security policy. The +`MethodPublisher` class does that too. + +Let's modify our view class to use `MethodPublisher`: + + >>> from zope.app.publisher.xmlrpc import MethodPublisher + + >>> class FolderListing(MethodPublisher): + ... + ... def contents(self): + ... return list(self.context.keys()) + +Note that `MethodPublisher` also provides a suitable `__init__` +method, so we don't need one any more. This time, we'll register it +as as a named view: + + >>> ignored = xmlconfig.string(""" + ... + ... + ... + ... + ... + ... + ... """) + +Now, when we access the `contents`, we do so through the listing view: + + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/listing/") + >>> proxy.contents() + ['f1', 'f2'] + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/") + >>> proxy.listing.contents() + ['f1', 'f2'] + +as before, we will get an error if we don't supply credentials: + + >>> proxy = ServerProxy("http://localhost/listing/") + >>> proxy.contents() + Traceback (most recent call last): + ProtocolError: + +Parameters +---------- + +Of course, XML-RPC views can take parameters, too: + + >>> class ParameterDemo: + ... def __init__(self, context, request): + ... self.context = context + ... self.request = request + ... + ... def add(self, first, second): + ... return first + second + +Now we'll register it as a view: + + >>> from zope.configuration import xmlconfig + >>> ignored = xmlconfig.string(""" + ... + ... + ... + ... + ... + ... + ... """) + +Then we can issue a remote procedure call with a parameter and get +back, surprise!, the sum: + + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/") + >>> proxy.add(20, 22) + 42 + +Faults +------ + +If you need to raise an error, the prefered way to do it is via an +`xmlrpclib.Fault`: + + >>> import xmlrpclib + + >>> class FaultDemo: + ... def __init__(self, context, request): + ... self.context = context + ... self.request = request + ... + ... def your_fault(self): + ... return xmlrpclib.Fault(42, "It's your fault!") + +Now we'll register it as a view: + + >>> from zope.configuration import xmlconfig + >>> ignored = xmlconfig.string(""" + ... + ... + ... + ... + ... + ... + ... """) + +Now, when we call it, we get a proper XML-RPC fault: + + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/") + >>> proxy.your_fault() + Traceback (most recent call last): + Fault: + + +DateTime values +--------------- + +Unfortunately, `xmlrpclib` does not support Python 2.3's new +`datetime.datetime` class (it should be made to, really). DateTime +values need to be encoded as `xmlrpclib.DateTime` instances: + + >>> import xmlrpclib + + >>> class DateTimeDemo: + ... def __init__(self, context, request): + ... self.context = context + ... self.request = request + ... + ... def epoch(self): + ... return xmlrpclib.DateTime("19700101T01:00:01") + +Now we'll register it as a view: + + >>> from zope.configuration import xmlconfig + >>> ignored = xmlconfig.string(""" + ... + ... + ... + ... + ... + ... + ... """) + +Now, when we call it, we get a DateTime value + + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/") + >>> proxy.epoch() + + +Protecting XML/RPC views with class-based permissions +----------------------------------------------------- + +When setting up an XML/RPC view with no permission, the permission check is +deferred to the class that provides the view's implementation: + + >>> class ProtectedView(object): + ... def public(self): + ... return u'foo' + ... def protected(self): + ... return u'bar' + + >>> from zope.configuration import xmlconfig + >>> ignored = xmlconfig.string(""" + ... + ... + ... + ... + ... + ... + ... + ... + ... + ... + ... + ... + ... """) + +An unauthenticated user can access the public method, but not the protected +one: + + >>> proxy = ServerProxy("http://usr:usrpw@localhost/index", handleErrors=False) + >>> proxy.public() + 'foo' + >>> proxy.protected() # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + Unauthorized: (, 'protected', 'zope.ManageContent') + +As a manager, we can access both: + + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/index") + >>> proxy.public() + 'foo' + >>> proxy.protected() + 'bar' + +Handling errors with the ServerProxy +------------------------------------ + +Our server proxy for functional testing also supports getting the original +errors from Zope by not handling the errors in the publisher: + + + >>> class ExceptionDemo: + ... def __init__(self, context, request): + ... self.context = context + ... self.request = request + ... + ... def your_exception(self): + ... raise Exception("Something went wrong!") + +Now we'll register it as a view: + + >>> from zope.configuration import xmlconfig + >>> ignored = xmlconfig.string(""" + ... + ... + ... + ... + ... + ... + ... """) + +Now, when we call it, we get an XML-RPC fault: + + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/") + >>> proxy.your_exception() + Traceback (most recent call last): + Fault: + +We can also give the parameter `handleErrors` to have the errors not be +handled: + + >>> proxy = ServerProxy("http://mgr:mgrpw@localhost/", handleErrors=False) + >>> proxy.your_exception() + Traceback (most recent call last): + Exception: Something went wrong! diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/__init__.py zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/__init__.py --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/test_directives.py zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/test_directives.py --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/test_directives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/test_directives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,102 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 'xmlrpc' ZCML Namespace directives. + +$Id: test_directives.py 100223 2009-05-22 00:40:23Z asmith $ +""" +import unittest + +from zope import component +from zope.configuration import xmlconfig +from zope.configuration.exceptions import ConfigurationError +from zope.component.testfiles.views import Request, IC, V1 +from zope.app.testing.placelesssetup import PlacelessSetup +from zope.security.proxy import ProxyFactory +from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest + +from zope.app.publisher import xmlrpc +from zope.interface import implements + + +request = Request(IXMLRPCRequest) + +class Ob(object): + implements(IC) + +ob = Ob() + +class DirectivesTest(PlacelessSetup, unittest.TestCase): + + def testView(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), None) + xmlconfig.file("xmlrpc.zcml", xmlrpc.tests) + view = component.queryMultiAdapter((ob, request), name='test') + self.assert_(V1 in view.__class__.__bases__) + self.assert_(xmlrpc.MethodPublisher in view.__class__.__bases__) + + def testInterfaceProtectedView(self): + xmlconfig.file("xmlrpc.zcml", xmlrpc.tests) + v = component.getMultiAdapter((ob, request), name='test2') + v = ProxyFactory(v) + self.assertEqual(v.index(), 'V1 here') + self.assertRaises(Exception, getattr, v, 'action') + + def testAttributeProtectedView(self): + xmlconfig.file("xmlrpc.zcml", xmlrpc.tests) + v = component.getMultiAdapter((ob, request), name='test3') + v = ProxyFactory(v) + self.assertEqual(v.action(), 'done') + self.assertRaises(Exception, getattr, v, 'index') + + def testInterfaceAndAttributeProtectedView(self): + xmlconfig.file("xmlrpc.zcml", xmlrpc.tests) + v = component.getMultiAdapter((ob, request), name='test4') + self.assertEqual(v.index(), 'V1 here') + self.assertEqual(v.action(), 'done') + + def testDuplicatedInterfaceAndAttributeProtectedView(self): + xmlconfig.file("xmlrpc.zcml", xmlrpc.tests) + v = component.getMultiAdapter((ob, request), name='test5') + self.assertEqual(v.index(), 'V1 here') + self.assertEqual(v.action(), 'done') + + def testIncompleteProtectedView(self): + self.assertRaises(ConfigurationError, xmlconfig.file, + "xmlrpc_error.zcml", xmlrpc.tests) + + def testNoPermission(self): + xmlconfig.file("xmlrpc_noperm.zcml", xmlrpc.tests) + v = component.getMultiAdapter((ob, request), name='index') + self.assertEqual(v.index(), 'V1 here') + + def test_no_name_no_permission(self): + self.assertRaises(ConfigurationError, xmlconfig.file, + "xmlrpc_nonamenoperm.zcml", xmlrpc.tests) + + def test_no_name(self): + xmlconfig.file("xmlrpc.zcml", xmlrpc.tests) + v = component.getMultiAdapter((ob, request), name='index') + self.assertEqual(v(), 'V1 here') + v = component.getMultiAdapter((ob, request), name='action') + self.assertEqual(v(), 'done') + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(DirectivesTest), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/test_functional.py zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/test_functional.py --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/test_functional.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/test_functional.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,61 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Functional tests for xmlrpc + +$Id: test_functional.py 95607 2009-01-30 21:55:25Z ctheune $ +""" +import re + +import zope.component +import zope.interface +import zope.publisher.interfaces.xmlrpc +from zope.testing import renormalizing + +import zope.site.interfaces +from zope.app.testing import functional, setup +from zope.app.publisher.testing import AppPublisherLayer + +def setUp(test): + setup.setUpTestAsModule(test, 'zope.app.publisher.xmlrpc.README') + +def tearDown(test): + # clean up the views we registered: + + # we use the fact that registering None unregisters whatever is + # registered. We can't use an unregistration call because that + # requires the object that was registered and we don't have that handy. + # (OK, we could get it if we want. Maybe later.) + + zope.component.provideAdapter(None, ( + zope.site.interfaces.IFolder, + zope.publisher.interfaces.xmlrpc.IXMLRPCRequest + ), zope.interface, 'contents') + + setup.tearDownTestAsModule(test) + +def test_suite(): + checker = renormalizing.RENormalizing(( + (re.compile(''), + )) + suite = functional.FunctionalDocFileSuite( + '../README.txt', setUp=setUp, tearDown=tearDown, + checker=checker + ) + suite.layer = AppPublisherLayer + return suite + +if __name__ == '__main__': + import unittest + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/test.pt zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/test.pt --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/test.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/test.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

test

diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/xmlrpc_error.zcml zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/xmlrpc_error.zcml --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/xmlrpc_error.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/xmlrpc_error.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/xmlrpc_nonamenoperm.zcml zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/xmlrpc_nonamenoperm.zcml --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/xmlrpc_nonamenoperm.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/xmlrpc_nonamenoperm.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,12 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/xmlrpc_noperm.zcml zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/xmlrpc_noperm.zcml --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/xmlrpc_noperm.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/xmlrpc_noperm.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,13 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/xmlrpc.zcml zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/xmlrpc.zcml --- zope3-3.4.0/src/zope/app/publisher/xmlrpc/tests/xmlrpc.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/publisher/xmlrpc/tests/xmlrpc.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/schema/configure.zcml zope3-3.5~bzr18/src/zope/app/schema/configure.zcml --- zope3-3.4.0/src/zope/app/schema/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/schema/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/schema/__init__.py zope3-3.5~bzr18/src/zope/app/schema/__init__.py --- zope3-3.4.0/src/zope/app/schema/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/schema/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2006 Zope Corporation 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. +# +############################################################################## +"""Schemas + +$Id: __init__.py 66068 2006-03-19 16:27:42Z philikon $ +""" +# we want to hook-up our own vocabulary registry; importing the module +# is enough for that +import zope.app.schema.vocabulary diff -Nru zope3-3.4.0/src/zope/app/schema/README.txt zope3-3.5~bzr18/src/zope/app/schema/README.txt --- zope3-3.4.0/src/zope/app/schema/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/schema/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,31 @@ +Component-based Vocabulary Registry +=================================== + +This package provides a vocabulary registry for zope.schema, +based on the component architecture. + +It replaces the zope.schema's simple vocabulary registry +when ``zope.app.schema`` package is imported, so it's done +automatically. All we need is provide vocabulary factory +utilities: + + >>> from zope.component import provideUtility + >>> from zope.schema.interfaces import IVocabularyFactory + >>> from zope.schema.vocabulary import SimpleTerm + >>> from zope.schema.vocabulary import SimpleVocabulary + + >>> def SomeVocabulary(context=None): + ... terms = [SimpleTerm(1), SimpleTerm(2)] + ... return SimpleVocabulary(terms) + + >>> provideUtility(SomeVocabulary, IVocabularyFactory, + ... name='SomeVocabulary') + +Now we can get the vocabulary using standard zope.schema +way: + + >>> from zope.schema.vocabulary import getVocabularyRegistry + >>> vr = getVocabularyRegistry() + >>> voc = vr.get(None, 'SomeVocabulary') + >>> [term.value for term in voc] + [1, 2] \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/app/schema/SETUP.cfg zope3-3.5~bzr18/src/zope/app/schema/SETUP.cfg --- zope3-3.4.0/src/zope/app/schema/SETUP.cfg 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/schema/SETUP.cfg 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ +# Tell zpkg how to install the ZCML slugs. + + + zope.app.schema-*.zcml + diff -Nru zope3-3.4.0/src/zope/app/schema/tests.py zope3-3.5~bzr18/src/zope/app/schema/tests.py --- zope3-3.4.0/src/zope/app/schema/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/schema/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,19 @@ +import unittest +from zope.testing import doctest +from zope.app.testing import setup +from zope.app.schema.vocabulary import _clear + +def setUp(test): + setup.placefulSetUp() + _clear() + +def tearDown(test): + setup.placefulTearDown() + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('README.txt', + setUp=setUp, tearDown=tearDown, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, + ), + )) diff -Nru zope3-3.4.0/src/zope/app/schema/vocabulary.py zope3-3.5~bzr18/src/zope/app/schema/vocabulary.py --- zope3-3.4.0/src/zope/app/schema/vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/schema/vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,51 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Implementation of ZCML action to register vocabulary factories. + +$Id: vocabulary.py 73667 2007-03-27 07:25:21Z dobe $ +""" +import zope.component +from zope.interface import implements +from zope.schema.interfaces import IVocabularyRegistry +from zope.schema import vocabulary +from zope.schema.interfaces import IVocabularyFactory + +class ZopeVocabularyRegistry(object): + """IVocabularyRegistry that supports global and local utilities.""" + + implements(IVocabularyRegistry) + __slots__ = () + + def get(self, context, name): + """See zope.schema.interfaces.IVocabularyRegistry""" + factory = zope.component.getUtility(IVocabularyFactory, name) + return factory(context) + +def _clear(): + """Re-initialize the vocabulary registry.""" + # This should normally only be needed by the testing framework, + # but is also used for module initialization. + global vocabularyRegistry + vocabulary._clear() + vocabularyRegistry = vocabulary.getVocabularyRegistry() + vocabulary._clear() + vocabulary.setVocabularyRegistry(ZopeVocabularyRegistry()) + +_clear() +try: + from zope.testing import cleanup +except ImportError: + pass +else: + cleanup.addCleanUp(_clear) diff -Nru zope3-3.4.0/src/zope/app/schema/zope.app.schema-configure.zcml zope3-3.5~bzr18/src/zope/app/schema/zope.app.schema-configure.zcml --- zope3-3.4.0/src/zope/app/schema/zope.app.schema-configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/schema/zope.app.schema-configure.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/app/server/accesslog.py zope3-3.5~bzr18/src/zope/app/server/accesslog.py --- zope3-3.4.0/src/zope/app/server/accesslog.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/accesslog.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,38 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 support for the access log. + +This assumes that access logging is being performed through the logger +object returned by logging.getLogger('accesslog'). + +$Id: accesslog.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import logging + +from ZConfig.components.logger.logger import LoggerFactoryBase + + +class AccessLogFactory(LoggerFactoryBase): + """Logger factory that returns the access logger.""" + + name = "accesslog" + + def create(self): + logger = LoggerFactoryBase.create(self) + logger.setLevel(logging.INFO) + logger.propagate = False + formatter = logging.Formatter() + for handler in logger.handlers: + handler.setFormatter(formatter) + return logger diff -Nru zope3-3.4.0/src/zope/app/server/accesslog.xml zope3-3.5~bzr18/src/zope/app/server/accesslog.xml --- zope3-3.4.0/src/zope/app/server/accesslog.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/accesslog.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ + + + + + + + + Configuration for the access logger. + + Note that the setting of verbosity level and message formats are not + used. All logging is done using the Common Log Format. + + + + diff -Nru zope3-3.4.0/src/zope/app/server/configure.zcml zope3-3.5~bzr18/src/zope/app/server/configure.zcml --- zope3-3.4.0/src/zope/app/server/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,28 @@ + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/server/DEPENDENCIES.cfg zope3-3.5~bzr18/src/zope/app/server/DEPENDENCIES.cfg --- zope3-3.4.0/src/zope/app/server/DEPENDENCIES.cfg 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/DEPENDENCIES.cfg 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,4 @@ +# TODO: Figure out what these should be. +ThreadedAsync +ZConfig +zdaemon diff -Nru zope3-3.4.0/src/zope/app/server/ftp.py zope3-3.5~bzr18/src/zope/app/server/ftp.py --- zope3-3.4.0/src/zope/app/server/ftp.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/ftp.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""FTP server + +$Id: ftp.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +from zope.app.publication.ftp import FTPPublication +from zope.app.publication.interfaces import IPublicationRequestFactory +from zope.publisher.ftp import FTPRequest +from zope.server.ftp.logger import CommonFTPActivityLogger +from zope.server.ftp.publisher import PublisherFTPServer +from zope.app.server.servertype import ServerType +import zope.interface + +class FTPRequestFactory(object): + """FTP Request factory + + FTP request factories for a given database create FTP requests with + publications on the given database: + +.. The test below has been disabled and moved to test_ftp.py (LP #257954) + + >>> from ZODB.tests.util import DB + >>> db = DB() + >>> factory = FTPRequestFactory(db) + >>> from cStringIO import StringIO + >>> request = factory(StringIO(''), {'credentials': None, 'path': '/'}) + >>> request.publication.db is db + True + >>> db.close() + + """ + zope.interface.implements(IPublicationRequestFactory) + + def __init__(self, db): + self.publication = FTPPublication(db) + + def __call__(self, input_stream, env): + request = FTPRequest(input_stream, env) + request.setPublication(self.publication) + return request + +server = ServerType( + PublisherFTPServer, + FTPRequestFactory, + CommonFTPActivityLogger, + 8021, True) diff -Nru zope3-3.4.0/src/zope/app/server/__init__.py zope3-3.5~bzr18/src/zope/app/server/__init__.py --- zope3-3.4.0/src/zope/app/server/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Make this a package. diff -Nru zope3-3.4.0/src/zope/app/server/main.py zope3-3.5~bzr18/src/zope/app/server/main.py --- zope3-3.4.0/src/zope/app/server/main.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/main.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,134 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Functions that control how the Zope appserver knits itself together. + +$Id: main.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import asyncore +import logging +import os +import sys +import time + +from zdaemon import zdoptions + +import zope.app.appsetup.appsetup +import zope.processlifetime +import zope.app.appsetup.product +from zope.event import notify +from zope.server.taskthreads import ThreadedTaskDispatcher + +CONFIG_FILENAME = "zope.conf" + +class ZopeOptions(zdoptions.ZDOptions): + + logsectionname = None + + def default_configfile(self): + dir = os.path.normpath( + os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, os.pardir, os.pardir)) + for filename in [CONFIG_FILENAME, CONFIG_FILENAME + ".in"]: + filename = os.path.join(dir, filename) + if os.path.isfile(filename): + return filename + return None + + + +exit_status = None +def main(args=None): + # Record start times (real time and CPU time) + t0 = time.time() + c0 = time.clock() + + setup(load_options(args)) + + t1 = time.time() + c1 = time.clock() + logging.info("Startup time: %.3f sec real, %.3f sec CPU", t1-t0, c1-c0) + + run() + sys.exit(exit_status or 0) + + +def debug(args=None): + options = load_options(args) + + zope.app.appsetup.product.setProductConfigurations( + options.product_config) + + zope.app.appsetup.config(options.site_definition) + + db = zope.app.appsetup.appsetup.multi_database(options.databases)[0][0] + notify(zope.processlifetime.DatabaseOpened(db)) + return db + + +def run(): + try: + global exit_status + while asyncore.socket_map and exit_status is None: + asyncore.poll(30.0) + except KeyboardInterrupt: + # Exit without spewing an exception. + pass + + +def load_options(args=None): + if args is None: + args = sys.argv[1:] + options = ZopeOptions() + options.schemadir = os.path.dirname(os.path.abspath(__file__)) + options.realize(args) + options = options.configroot + + if options.path: + sys.path[:0] = [os.path.abspath(p) for p in options.path] + return options + + +def setup(options): + sys.setcheckinterval(options.check_interval) + + zope.app.appsetup.product.setProductConfigurations( + options.product_config) + options.eventlog() + options.accesslog() + for logger in options.loggers: + logger() + + features = ('zserver',) + # Provide the devmode, if activated + if options.devmode: + features += ('devmode',) + logging.warning("Developer mode is enabled: this is a security risk " + "and should NOT be enabled on production servers. Developer mode " + "can be turned off in etc/zope.conf") + + zope.app.appsetup.config(options.site_definition, features=features) + + db = zope.app.appsetup.appsetup.multi_database(options.databases)[0][0] + + notify(zope.processlifetime.DatabaseOpened(db)) + + task_dispatcher = ThreadedTaskDispatcher() + task_dispatcher.setThreadCount(options.threads) + + for server in options.servers: + server.create(task_dispatcher, db) + + notify(zope.processlifetime.ProcessStarting()) + + return db diff -Nru zope3-3.4.0/src/zope/app/server/mkzopeinstance.py zope3-3.5~bzr18/src/zope/app/server/mkzopeinstance.py --- zope3-3.4.0/src/zope/app/server/mkzopeinstance.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/mkzopeinstance.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,346 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Implementation of the mkzopeinstance script. + +This creates a new instances of the Zope server instance home. An +'instance home' contains two things: + +- application server configuration and data + +- server process control scripts and data + +$Id: mkzopeinstance.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import optparse +import os +import shutil +import sys +from xml.sax.saxutils import quoteattr as xml_quoteattr + +import zope.app.server +from zope.password import password +from zope.app.applicationcontrol import zopeversion + +def main(argv=None, from_checkout=False): + """Top-level script function to create a new Zope instance.""" + if argv is None: + argv = sys.argv + try: + options = parse_args(argv, from_checkout) + except SystemExit, e: + if e.code: + return 2 + else: + return 0 + app = Application(options) + try: + return app.process() + except KeyboardInterrupt: + return 1 + except SystemExit, e: + return e.code + + +class Application(object): + + def __init__(self, options): + self.options = options + self.need_blank_line = False + + def read_input_line(self, prompt): + # The tests replace this to make sure the right things happen. + if not self.options.interactive: + print >>sys.stderr, ('Error: Tried to ask for user input in' + ' non-interactive mode.') + sys.exit(1) + return raw_input(prompt) + + def read_password(self, prompt): + # The tests replace this to make sure the right things happen. + if not self.options.interactive: + print >>sys.stderr, ('Error: Tried to ask for user input in' + ' non-interactive mode.') + sys.exit(1) + import getpass + try: + return getpass.getpass(prompt) + except KeyboardInterrupt: + # The cursor was left on the same line as the prompt, + # which we don't like. Print a blank line. + print + raise + + def process(self): + options = self.options + + # make sure we can find the skeleton + if not os.path.isdir(options.skeleton): + print >>sys.stderr, "skeleton directory", options.skeleton + print >>sys.stderr, "does not exist or is not a directory" + return 1 + + # create the destination + if not options.destination: + options.destination = self.get_skeltarget() + options.destination = os.path.abspath(options.destination) + if not os.path.exists(options.destination): + try: + os.mkdir(options.destination) + except OSError, e: + print >>sys.stderr, "could not create instance home:", e + return 1 + elif not os.path.isdir(options.destination): + print >>sys.stderr, options.destination, "is not a directory" + print >>sys.stderr, ("(instance homes cannot be created in" + " non-directories)") + return 1 + + if not options.username: + options.username = self.get_username() + + (options.password_manager, + password_manager) = self.get_password_manager() + + if not options.password: + options.password = self.get_password() + + options.password = password_manager.encodePassword(options.password) + + # now create the instance! + self.copy_skeleton() + if options.add_package_includes: + # need to copy ZCML differently since it's not in the skeleton: + import __main__ + swhome = os.path.dirname( + os.path.dirname(os.path.realpath(__main__.__file__))) + shutil.copy2(os.path.join(swhome, "securitypolicy.zcml"), + os.path.join(options.destination, "etc")) + return 0 + + def get_skeltarget(self): + self.print_message(SKELTARGET_MESSAGE) + self.need_blank_line = True + while 1: + skeltarget = self.read_input_line("Directory: ").strip() + if skeltarget == '': + print >>sys.stderr, 'You must specify a directory' + continue + return os.path.expanduser(skeltarget) + + def get_username(self): + self.print_message(USERNAME_MESSAGE) + self.need_blank_line = True + while 1: + username = self.read_input_line("Username: ").strip() + if not username: + print >>sys.stderr, "You must specify an administrative user" + continue + return username + + def get_password(self): + self.print_message(PASSWORD_MESSAGE) + while 1: + password = self.read_password("Password: ") + if not password: + print >>sys.stderr, "Password may not be empty" + continue + if password != password.strip() or password.split() != [password]: + print >>sys.stderr, "Password may not contain spaces" + continue + break + again = self.read_password("Verify password: ") + if again != password: + print >>sys.stderr, "Password not verified!" + sys.exit(1) + return password + + def get_password_manager(self): + if not self.options.password_manager and not self.options.interactive: + self.options.password_manager = password.managers[0][0] + + if self.options.password_manager: + for name, manager in password.managers: + if name == self.options.password_manager: + return (name, manager) + print >>sys.stderr, "Unknown password manager!" + sys.exit(1) + + self.print_message(PASSWORD_MANAGER_MESSAGE) + for i, (name, manager) in enumerate(password.managers): + print "% i. %s" % (i + 1, name) + print + self.need_blank_line = True + while 1: + password_manager = self.read_input_line( + "Password Manager Number [1]: ") + if not password_manager: + index = 0 + break + elif password_manager.isdigit(): + index = int(password_manager) + if index > 0 and index <= len(password.managers): + index -= 1 + break + print >>sys.stderr, "You must select a password manager" + print "%r password manager selected" % password.managers[index][0] + return password.managers[index] + + def print_message(self, message): + if self.need_blank_line: + print + self.need_blank_line = False + print message + + def copy_skeleton(self): + options = self.options + # TODO we should be able to compute the script + script = os.path.abspath(sys.argv[0]) + zope_home = os.path.dirname(os.path.dirname(script)) + zope_init = os.path.dirname(os.path.abspath(zope.app.__file__)) + software_home = os.path.dirname(os.path.dirname(zope_init)) + self.replacements = [ + ("<>", options.username), + ("<>", xml_quoteattr(options.username)), + ("<>", options.password), + ("<>", xml_quoteattr(options.password)), + ("<>", options.password_manager), + ("<>", sys.executable), + ("<>", options.destination), + ("<>", zope_home), + ("<>", software_home), + ] + self.copytree(self.options.skeleton, self.options.destination) + if options.zserver: + self.copytree( + os.path.join(os.path.dirname(zope.app.server.__file__), + 'zopeskel'), + self.options.destination, + ) + + + def copytree(self, src, dst): + # Similar to shutil.copytree(), but doesn't care about + # symlinks, doesn't collect errors, and uses self.copyfile() + # instead of shutil.copy2(). + assert os.path.isdir(dst), dst + names = os.listdir(src) + if ".svn" in names: + names.remove(".svn") + for name in names: + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + if os.path.isdir(srcname): + if not os.path.exists(dstname): + os.mkdir(dstname) + self.copytree(srcname, dstname) + else: + self.copyfile(srcname, dstname) + # There shouldn't be any need to deal with devices, sockets etc. + + def copyfile(self, src, dst): + if dst.endswith(".in"): + dst = dst[:-3] + text = open(src, "rU").read() + # perform replacements + for var, string in self.replacements: + text = text.replace(var, string) + + # If the file exists, keep the old file. This is a + # hopefully temporary hack to get around distutils + # stripping the permissions on the server skeletin files. + # We reuse the original default files, which have the + # right permissions. + old = os.path.exists(dst) + if old: + f = open(dst, "r+") + f.truncate(0) + else: + f = open(dst, "w") + + f.write(text) + f.close() + + if not old: + shutil.copymode(src, dst) + shutil.copystat(src, dst) + else: + shutil.copy2(src, dst) + +SKELTARGET_MESSAGE = """\ +Please choose a directory in which you'd like to install Zope +'instance home' files such as database files, configuration files, +etc. +""" + +USERNAME_MESSAGE = """\ +Please choose a username for the initial administrator account. +This is required to allow Zope's management interface to be used. +""" + +PASSWORD_MESSAGE = """\ +Please provide a password for the initial administrator account. +""" + +PASSWORD_MANAGER_MESSAGE = """\ +Please select a password manager which will be used for encode the password of +the initial administrator account. +""" + + +def parse_args(argv, from_checkout=False): + """Parse the command line, returning an object representing the input.""" + path, prog = os.path.split(os.path.realpath(argv[0])) + version = "%prog for " + zopeversion.ZopeVersionUtility.getZopeVersion() + p = optparse.OptionParser(prog=prog, + usage="%prog [options]", + version=version) + p.add_option("-d", "--dir", dest="destination", metavar="DIR", + help="the dir in which the instance home should be created") + p.add_option("-s", "--skelsrc", dest="skeleton", metavar="DIR", + help="template skeleton directory") + p.add_option("-m", "--password-manager", + dest="password_manager", metavar="NAME", + help=("set the name of the password manager" + " to be used for encode the password")) + p.add_option("-u", "--user", dest="username", metavar="USER:PASSWORD", + help="set the user name and password of the initial user") + p.add_option("--non-interactive", dest="interactive", action="store_false", + default=True, help="do no interactive prompting") + p.add_option("--zserver", dest="zserver", action="store_true", + help="""\ +Use the older ZServer network server rather than the default Twisted +server. The Twisted integration with Zope is experimental and not +recommended for use in production sites. We do encourage it's +use in development or in sites that can stand a little uncertianty, so +that we can gain more experience with it. +""", + ) + + + options, args = p.parse_args(argv[1:]) + if options.skeleton is None: + options.add_package_includes = from_checkout + basedir = os.path.dirname(path) + # no assurance that this exists! + options.skeleton = os.path.join(basedir, "zopeskel") + else: + options.add_package_includes = False + options.program = prog + options.version = version + if args: + p.error("too many arguments") + options.password = None + if options.username and ":" in options.username: + options.username, options.password = options.username.split(":", 1) + return options diff -Nru zope3-3.4.0/src/zope/app/server/schema.xml zope3-3.5~bzr18/src/zope/app/server/schema.xml --- zope3-3.4.0/src/zope/app/server/schema.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/schema.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,47 @@ + + + + + + + + + + + +
+ + Configuration for the access log. + +
+ + + + + + Value passed to Python's sys.setcheckinterval() function. + + This integer value determines how often the interpreter checks + for periodic things such as thread switches and signal handlers. + Setting it to a larger value may increase performance for + programs using threads. Setting it to a value <= 0 checks every + virtual instruction, maximizing responsiveness as well as + overhead. + + + + + + The number of threads which should be used to serve requests. + + The threads are placed in a pool and are used to serve requests + received from the servers configured using <server> + sections. This does not constrain the total number of threads + used by the application server; additional threads may be used + for internal purposes. + + + +
diff -Nru zope3-3.4.0/src/zope/app/server/servercontrol.py zope3-3.5~bzr18/src/zope/app/server/servercontrol.py --- zope3-3.4.0/src/zope/app/server/servercontrol.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/servercontrol.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2001,2002,2003 Zope Corporation 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. +# +############################################################################## +"""Server Control Implementation + +$Id: servercontrol.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" + +from zope.app.applicationcontrol.interfaces import IServerControl +from zope.interface import implements + +import zope.app.server.main + +class ServerControl(object): + + implements(IServerControl) + + def shutdown(self, time=0): + """See zope.app.applicationcontrol.interfaces.IServerControl""" + # TODO: Graceful shutdown does not work yet. + + # This will work for servers started directly and by zdaemon. Passing + # an exit status of 0 causes zdaemon to not restart the process. + zope.app.server.main.exit_status = 0 + + def restart(self, time=0): + """See zope.app.applicationcontrol.interfaces.IServerControl""" + # TODO: Graceful restart does not work yet. + + # TODO: Make sure this is only called if we are running via zdaemon. + + # Passing an exit status of 1 causes zdaemon to restart the process. + zope.app.server.main.exit_status = 1 + + +serverControl = ServerControl() diff -Nru zope3-3.4.0/src/zope/app/server/server.py zope3-3.5~bzr18/src/zope/app/server/server.py --- zope3-3.4.0/src/zope/app/server/server.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/server.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Datatype for a section in a Zope 3 configuration file. + +This is called by the ZConfig machinery while processing a configuration. + +$Id: server.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import zope.component +from zope.app.server.servertype import IServerType + +class ServerFactory(object): + """Factory for server objects. + + The factories are part of the configuration data returned by + ZConfig. + """ + + def __init__(self, section): + """Initialize the factory based on a section.""" + self.type = section.type + self.address = section.address + self.verbose = section.verbose + + def create(self, task_dispatcher, database): + """Return a server based on the server types defined via ZCML.""" + + servertype = zope.component.getUtility(IServerType, self.type) + # The server object self-registers with the asyncore mainloop. + return servertype.create( + self.type, + task_dispatcher, database, + ip=self.address[0], + port=self.address[1], + verbose=self.verbose) diff -Nru zope3-3.4.0/src/zope/app/server/servertype.py zope3-3.5~bzr18/src/zope/app/server/servertype.py --- zope3-3.4.0/src/zope/app/server/servertype.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/servertype.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,67 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Server Type + +$Id: servertype.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +from zope.interface import Interface, implements + + +class IServerType(Interface): + """Server type utility. + + This is a pure read-only interface, since the values are set through + a ZCML directive and we shouldn't be able to change them. + """ + + def create(name, task_dispatcher, db, port=None, verbose=None, ip=None): + """Create the server knowing the port, task dispatcher and the ZODB. + + Returns the new server. + """ + +class ServerType(object): + + implements(IServerType) + + def __init__(self, factory, requestFactory, logFactory, + defaultPort, defaultVerbose, defaultIP=''): + self._factory = factory + self._requestFactory = requestFactory + self._logFactory = logFactory + self._defaultPort = defaultPort + self._defaultVerbose = defaultVerbose + self._defaultIP = defaultIP + + + def create(self, name, task_dispatcher, db, port=None, + verbose=None, ip=None): + 'See IServerType' + + request_factory = self._requestFactory(db) + + if port is None: + port = self._defaultPort + + if ip is None: + ip = self._defaultIP + + if verbose is None: + verbose = self._defaultVerbose + + return self._factory(request_factory, name, ip, port, + task_dispatcher=task_dispatcher, + verbose=verbose, + hit_log=self._logFactory(), + ) diff -Nru zope3-3.4.0/src/zope/app/server/SETUP.cfg zope3-3.5~bzr18/src/zope/app/server/SETUP.cfg --- zope3-3.4.0/src/zope/app/server/SETUP.cfg 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/SETUP.cfg 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ +# Tell zpkg how to install the ZCML slugs. + + + zope.app.server-*.zcml + diff -Nru zope3-3.4.0/src/zope/app/server/tests/__init__.py zope3-3.5~bzr18/src/zope/app/server/tests/__init__.py --- zope3-3.4.0/src/zope/app/server/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Make this a package. diff -Nru zope3-3.4.0/src/zope/app/server/tests/site.zcml zope3-3.5~bzr18/src/zope/app/server/tests/site.zcml --- zope3-3.4.0/src/zope/app/server/tests/site.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/site.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/app/server/tests/test_accesslog.py zope3-3.5~bzr18/src/zope/app/server/tests/test_accesslog.py --- zope3-3.4.0/src/zope/app/server/tests/test_accesslog.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/test_accesslog.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,74 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 zope.app.server.accesslog. + +$Id: test_accesslog.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import logging +import unittest + +from ZConfig.components.logger import loghandler +from ZConfig.components.logger.tests import test_logger + + +class TestAccessLogging(test_logger.LoggingTestBase): + + name = "accesslog" + + _schematext = """ + + +
+ + """ + + def test_config_without_logger(self): + conf = self.get_config("") + self.assert_(conf.accesslog is None) + + def test_config_without_handlers(self): + logger = self.check_simple_logger("") + # Make sure there's a NullHandler, since a warning gets + # printed if there are no handlers: + self.assertEqual(len(logger.handlers), 1) + self.assert_(isinstance(logger.handlers[0], + loghandler.NullHandler)) + + def test_formatter(self): + logger = self.check_simple_logger("\n" + " \n" + " level error\n" + " facility local3\n" + " format xyzzy\n" + " \n" + "") + self.assertEqual(len(logger.handlers), 1) + syslog = logger.handlers[0] + self.assertEqual(syslog.level, logging.ERROR) + self.assert_(isinstance(syslog, loghandler.SysLogHandler)) + self.assertEqual(syslog.formatter._fmt, "%(message)s") + + def check_simple_logger(self, text): + conf = self.get_config(text) + self.assert_(conf.accesslog is not None) + logger = conf.accesslog() + self.assert_(isinstance(logger, logging.Logger)) + self.assert_(not logger.propagate) + self.assertEquals(logger.name, "accesslog") + self.assertEquals(logger.level, logging.INFO) + return logger + + +def test_suite(): + return unittest.makeSuite(TestAccessLogging) diff -Nru zope3-3.4.0/src/zope/app/server/tests/test_ftp.py zope3-3.5~bzr18/src/zope/app/server/tests/test_ftp.py --- zope3-3.4.0/src/zope/app/server/tests/test_ftp.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/test_ftp.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,38 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Doc tests for the FTP server. + +$Id: test_ftp.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import unittest + +class Tests(unittest.TestCase): + + def test_ftp(self): + from ZODB.tests.util import DB + from zope.app.server.ftp import FTPRequestFactory + from cStringIO import StringIO + db = DB() + factory = FTPRequestFactory(db) + request = factory(StringIO(''), {'credentials': None, 'path': '/'}) + self.assertTrue(request.publication.db is db) + db.close() + + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Tests) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/server/tests/test_mkzopeinstance.py zope3-3.5~bzr18/src/zope/app/server/tests/test_mkzopeinstance.py --- zope3-3.4.0/src/zope/app/server/tests/test_mkzopeinstance.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/test_mkzopeinstance.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,423 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 implementation of the mkzopeinstance script. + +$Id: test_mkzopeinstance.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import os +import shutil +import sys +import tempfile +import unittest +import zope.app.server + +from StringIO import StringIO + +from zope.app.server import mkzopeinstance + + +class TestBase(unittest.TestCase): + + def setUp(self): + self.stdout = StringIO() + self.stderr = StringIO() + self.old_stdout = sys.stdout + self.old_stderr = sys.stderr + sys.stdout = self.stdout + sys.stderr = self.stderr + + def tearDown(self): + sys.stdout = self.old_stdout + sys.stderr = self.old_stderr + + +class ArgumentParsingTestCase(TestBase): + """Ensure the command line is properly converted to an options + object. + """ + + def parse_args(self, args): + argv = ["foo/bar.py"] + args + options = mkzopeinstance.parse_args(argv) + self.assertEqual(options.program, "bar.py") + self.assert_(options.version) + return options + + def test_no_arguments(self): + options = self.parse_args([]) + + def test_version_long(self): + self.check_stdout_content(["--version"]) + + def test_help_long(self): + self.check_stdout_content(["--help"]) + + def test_help_short(self): + self.check_stdout_content(["-h"]) + + def check_stdout_content(self, args): + try: + options = self.parse_args(args) + except SystemExit, e: + self.assertEqual(e.code, 0) + self.assert_(self.stdout.getvalue()) + self.failIf(self.stderr.getvalue()) + else: + self.fail("expected SystemExit") + + def test_without_destination(self): + options = self.parse_args([]) + self.assertEqual(options.destination, None) + + def test_destination_long(self): + options = self.parse_args(["--dir", "some/dir"]) + self.assertEqual(options.destination, "some/dir") + + def test_destination_short(self): + options = self.parse_args(["-d", "some/dir"]) + self.assertEqual(options.destination, "some/dir") + + def test_without_skeleton(self): + # make sure we get *some* skeleton directory by default + # there's no claim that it exists + options = self.parse_args([]) + self.assertNotEqual(options.skeleton, None) + + def test_with_skeleton_long(self): + options = self.parse_args(["--skelsrc", "some/dir"]) + self.assertEqual(options.skeleton, "some/dir") + self.failIf(options.add_package_includes) + + def test_with_skeleton_short(self): + options = self.parse_args(["-s", "some/dir"]) + self.assertEqual(options.skeleton, "some/dir") + self.failIf(options.add_package_includes) + + def test_without_username(self): + options = self.parse_args([]) + self.assertEqual(options.username, None) + self.assertEqual(options.password, None) + + def test_username_without_password_long(self): + options = self.parse_args(["--user", "User"]) + self.assertEqual(options.username, "User") + self.assertEqual(options.password, None) + + def test_username_without_password_short(self): + options = self.parse_args(["-u", "User"]) + self.assertEqual(options.username, "User") + self.assertEqual(options.password, None) + + def test_username_with_password_long(self): + options = self.parse_args(["--user", "User:Pass"]) + self.assertEqual(options.username, "User") + self.assertEqual(options.password, "Pass") + + def test_username_with_password_short(self): + options = self.parse_args(["-u", "User:Pass"]) + self.assertEqual(options.username, "User") + self.assertEqual(options.password, "Pass") + + def test_without_password_manager(self): + options = self.parse_args([]) + self.assertEqual(options.password_manager, None) + + def test_password_manager_short(self): + options = self.parse_args(["-m", "Manager"]) + self.assertEqual(options.password_manager, "Manager") + + def test_password_manager_long(self): + options = self.parse_args(["--password-manager", "Manager"]) + self.assertEqual(options.password_manager, "Manager") + + def test_junk_positional_arg(self): + try: + self.parse_args(["junk"]) + except SystemExit, e: + self.assert_(e.code) + else: + self.fail("expected SystemExit") + + +class InputCollectionTestCase(TestBase): + + def setUp(self): + super(InputCollectionTestCase, self).setUp() + self.tmpdir = tempfile.mkdtemp(prefix="test-mkzopeinstance-") + self.skeleton = os.path.join(self.tmpdir, "skel") + self.instance = os.path.join(self.tmpdir, "inst") + os.mkdir(self.skeleton) + + def tearDown(self): + shutil.rmtree(self.tmpdir) + super(InputCollectionTestCase, self).tearDown() + + def createOptions(self): + options = Options() + options.skeleton = self.skeleton + options.interactive = True + return options + + def test_get_skeltarget(self): + options = self.createOptions() + input = [" ", " foo "] + app = ControlledInputApplication(options, input) + skel = app.get_skeltarget() + self.assertEqual(skel, "foo") + self.assertEqual(input, []) + self.assert_(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_process_creates_destination(self): + options = self.createOptions() + input = [self.instance] + app = ControlledInputApplication(options, input) + self.assertEqual(app.process(), 0) + self.assert_(os.path.isdir(self.instance)) + self.assertEqual(input, []) + self.failUnless(app.all_input_consumed()) + + def test_zserver_support(self): + # test the zserver option. We should get zserver versions of + # runzope, zopectl, debugzope and zope.conf. Any of these + # that we provide in put skeleton should be overritten. + + # privide a dummy runzope + os.mkdir(os.path.join(self.skeleton, 'bin')) + f = open(os.path.join(self.skeleton, 'bin', 'runzope.in'), 'w') + f.write('runzope') + f.close() + + options = self.createOptions() + options.destination = self.instance + options.interactive = False + options.zserver = True + app = ControlledInputApplication(options, []) + self.assertEqual(app.process(), 0) + self.assert_( + 'from zope.app.server.main import main' in + open(os.path.join(self.instance, 'bin', 'runzope')).read() + ) + self.assert_( + 'from zope.app.server.main import debug' in + open(os.path.join(self.instance, 'bin', 'debugzope')).read() + ) + self.assert_(os.path.exists( + os.path.join(self.instance, 'etc', 'zope.conf') + )) + + + def test_process_aborts_on_file_destination(self): + options = self.createOptions() + options.destination = self.instance + open(self.instance, "w").close() + app = ControlledInputApplication(options, []) + self.assertEqual(app.process(), 1) + self.assert_(self.stderr.getvalue()) + + def test_process_aborts_on_failed_destination_creation(self): + options = self.createOptions() + options.destination = os.path.join(self.instance, "foo") + app = ControlledInputApplication(options, []) + self.assertEqual(app.process(), 1) + self.assert_(self.stderr.getvalue()) + + def test_get_username(self): + options = self.createOptions() + app = ControlledInputApplication(options, ["myuser"]) + usr = app.get_username() + self.assertEqual(usr, "myuser") + self.failIf(self.stderr.getvalue()) + self.failUnless(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_get_username_strips_whitespace(self): + options = self.createOptions() + app = ControlledInputApplication(options, [" myuser\t"]) + usr = app.get_username() + self.assertEqual(usr, "myuser") + self.failIf(self.stderr.getvalue()) + self.failUnless(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_get_username_ignores_empty_names(self): + options = self.createOptions() + app = ControlledInputApplication(options, ["", " ", "\t", "myuser"]) + usr = app.get_username() + self.assertEqual(usr, "myuser") + self.failUnless(self.stderr.getvalue()) + self.failUnless(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_get_password_manager(self): + options = self.createOptions() + options.password_manager = None + app = ControlledInputApplication(options, ["3"]) + name, pwm = app.get_password_manager() + self.assertEqual(name, "SHA1") + self.assertEqual(pwm.encodePassword("foo")[-40:], + "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33") + self.failIf(self.stderr.getvalue()) + self.failUnless(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_get_wrong_password_manager(self): + options = self.createOptions() + options.password_manager = "Unknown" + app = ControlledInputApplication(options, []) + try: + app.get_password_manager() + except SystemExit, e: + self.assertEqual(e.code, 1) + else: + self.fail("expected SystemExit") + self.failUnless(self.stderr.getvalue()) + self.failIf(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_get_password(self): + options = self.createOptions() + app = ControlledInputApplication(options, ["foo", "foo"]) + pw = app.get_password() + self.assertEqual(pw, "foo") + self.failIf(self.stderr.getvalue()) + self.failUnless(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_get_password_not_verified(self): + options = self.createOptions() + app = ControlledInputApplication(options, ["foo", "bar"]) + try: + app.get_password() + except SystemExit, e: + self.assertEqual(e.code, 1) + else: + self.fail("expected SystemExit") + self.failUnless(self.stderr.getvalue()) + self.failUnless(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_get_password_empty(self): + # Make sure the empty password is ignored. + options = self.createOptions() + app = ControlledInputApplication(options, ["", "foo", "foo"]) + pw = app.get_password() + self.assertEqual(pw, "foo") + self.failUnless(self.stderr.getvalue()) + self.failUnless(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_get_password_disallows_whitespace(self): + # Any password that contains spaces is disallowed. + options = self.createOptions() + app = ControlledInputApplication(options, [" ", "\t", "a b", + " a", "b ", "foo", "foo"]) + pw = app.get_password() + self.assertEqual(pw, "foo") + self.failUnless(self.stderr.getvalue()) + self.failUnless(self.stdout.getvalue()) + self.failUnless(app.all_input_consumed()) + + def test_can_rewrite_existing_instance(self): + # Fill out the skeleton a little so we test more cases: + os.mkdir(os.path.join(self.skeleton, "etc")) + f = open(os.path.join(self.skeleton, "etc", "README.txt"), "w") + f.write("Configuration goes here.\n") + f.close() + + # Create an instance home: + options = self.createOptions() + options.destination = self.instance + app = ControlledInputApplication(options, []) + rc = app.process() + self.assertEqual(rc, 0) + self.failUnless(app.all_input_consumed()) + self.failUnless(os.path.exists(os.path.join(self.instance, "etc"))) + + # Make sure we can do it again: + options = self.createOptions() + options.destination = self.instance + app = ControlledInputApplication(options, []) + rc = app.process() + self.assertEqual(rc, 0) + self.failUnless(app.all_input_consumed()) + self.failUnless(os.path.exists(os.path.join(self.instance, "etc"))) + + def test_zope_namespace_package_doesnt_affect_software_home(self): + # Make sure that a zope namespace package in a different + # location won't affect SOFTWARE_HOME + + # let's mess with zope's __file__ + import zope + old_path = zope.__file__ + zope.__file__ = os.path.join( + *'and now for something completely different'.split()) + + # place a test file into the skeleton dir that'll be expanded + # to SOFTWARE_HOME by mkzopeinstance + f = file(os.path.join(self.skeleton, 'test.in'), 'w') + f.write('<>') + f.close() + + # run mkzopeinstance + options = self.createOptions() + options.destination = self.instance + app = ControlledInputApplication(options, []) + rc = app.process() + + # check for the expected output: mkzopeinstance should take + # zope.app as an anchor for determining SOFTWARE_HOME + import zope.app + expected = os.path.dirname(os.path.dirname( + os.path.dirname(zope.app.__file__))) + self.assertEqual(file(os.path.join(self.instance, 'test')).read(), + expected) + + # cleanup the fake 'zope' module + zope.__file__ = old_path + +class ControlledInputApplication(mkzopeinstance.Application): + + def __init__(self, options, input_lines): + mkzopeinstance.Application.__init__(self, options) + self.__input = input_lines + + def read_input_line(self, prompt): + return self.__input.pop(0) + + read_password = read_input_line + + def all_input_consumed(self): + return not self.__input + + +class Options(object): + + username = "[test-username]" + password_manager = "Plain Text" + password = "[test-password]" + destination = None + version = "[test-version]" + program = "[test-program]" + add_package_includes = False + zserver = False + + +def test_suite(): + suite = unittest.makeSuite(ArgumentParsingTestCase) + suite.addTest(unittest.makeSuite(InputCollectionTestCase)) + return suite + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff -Nru zope3-3.4.0/src/zope/app/server/tests/test_schema.py zope3-3.5~bzr18/src/zope/app/server/tests/test_schema.py --- zope3-3.4.0/src/zope/app/server/tests/test_schema.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/test_schema.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,42 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 that the Zope appserver configuration schema can be loaded. + +$Id: test_schema.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" + +import os.path +import unittest + +import ZConfig + + +class TestConfiguration(unittest.TestCase): + + def test_schema(self): + dir = os.path.dirname(os.path.dirname(__file__)) + filename = os.path.join(dir, "schema.xml") + ZConfig.loadSchema(filename) + + +def test_suite(): + return unittest.makeSuite(TestConfiguration) + +if __name__ == "__main__": + try: + __file__ + except NameError: + import sys + __file__ = sys.argv[0] + unittest.main(defaultTest="test_suite") diff -Nru zope3-3.4.0/src/zope/app/server/tests/test_server.py zope3-3.5~bzr18/src/zope/app/server/tests/test_server.py --- zope3-3.4.0/src/zope/app/server/tests/test_server.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/test_server.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,94 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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 zope.app.server.server + +$Id: test_server.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import unittest + +from zope.interface.verify import verifyObject +from zope.testing import doctest +from zope.app.testing import ztapi, setup, placelesssetup + + +def doctest_ServerFactory(): + r"""Tests for ServerFactory + + Zope 3 has many server types -- HTTP, FTP, HTTP with postmortem debugging, + etc. All of them are registered as IServerType utilities in ZCML. + + >>> setup.placelessSetUp() + + >>> from zope.interface import implements + >>> from zope.app.server.servertype import IServerType + >>> class MyServerType: + ... implements(IServerType) + ... def create(self, name, task_dispatcher, db, port='unknown', + ... verbose='unspecified', ip=''): + ... if not ip: + ... ip = '*' # listen on all interfaces + ... return ('%s server on %s:%d, registered with %s,\n' + ... 'serving from %s, verbosity %s' + ... % (name, ip, port, task_dispatcher, db, verbose)) + >>> ztapi.provideUtility(IServerType, MyServerType(), name='HTTP') + >>> ztapi.provideUtility(IServerType, MyServerType(), name='FTP') + + ServerFactory is used to hook into ZConfig and create instances of servers + specified in zope.conf. It gets a `section` argument that contains + settings specified in a ZConfig section. + + >>> class ServerSectionStub: + ... type = 'HTTP' + ... address = ('', 8080) + ... verbose = False + >>> my_section = ServerSectionStub() + + >>> from zope.app.server.server import ServerFactory + >>> sf = ServerFactory(my_section) + + The server factory object knows how to create a server, given a task + dispatcher (see IDispatcher from zope.server.interfaces) and a ZODB + database object. + + >>> task_dispatcher = 'my task dispatcher' + >>> db = 'my db' + >>> print sf.create(task_dispatcher, db) + HTTP server on *:8080, registered with my task dispatcher, + serving from my db, verbosity False + + The settings actually work + + >>> my_section.type = 'FTP' + >>> my_section.address = ('127.0.0.1', 8021) + >>> my_section.verbose = True + >>> sf = ServerFactory(my_section) + >>> print sf.create(task_dispatcher, db) + FTP server on 127.0.0.1:8021, registered with my task dispatcher, + serving from my db, verbosity True + + That's it. + + >>> setup.placelessTearDown() + + """ + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite(), + )) + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/server/tests/test_servertype.py zope3-3.5~bzr18/src/zope/app/server/tests/test_servertype.py --- zope3-3.4.0/src/zope/app/server/tests/test_servertype.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/test_servertype.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,128 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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 zope.app.server.servertype + +$Id: test_servertype.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import unittest + +from zope.interface.verify import verifyObject +from zope.testing import doctest + + +def doctest_ServerType(): + r"""Tests for ServerType + + Zope 3 has many server types -- HTTP, FTP, HTTP with postmortem debugging, + etc. All of them are registered as IServerType utilities in ZCML. + + ServerType is an implementation of IServerType. The constructor of + ServerType takes quite a few arguments, a number of which are factories. + We will use stubs of those. + + The 'factory' argument specifies the server factory (e.g. + PublisherHTTPServer from zope.server.http.httpserver) + + >>> def factory(request_factory, name, ip, port, + ... task_dispatcher=None, hit_log=None, verbose=False): + ... if ip == '': + ... ip = '*' # listen on all network interfaces + ... print "Starting a server (%s) on %s:%d" % (name, ip, port) + ... print "This server will use %s to construct requests" % \ + ... request_factory + ... print "This server will use %s for hit logging" % hit_log + ... if verbose: + ... print "This server will be verbose" + ... else: + ... print "This server will not be verbose" + ... print "This server will be managed by %s" % task_dispatcher + + The 'requestFactory' argument specifies a function that returns a factory + for requests (e.g. HTTPPublicationRequestFactory from + zope.server.http.publisherhttpserver). It is, in fact, a request factory + factory. + + >>> def requestFactory(db): + ... return 'my request factory for %s' % db + + The 'logFactory' argument specifies the factory for an access logger (e.g. + CommonAccessLogger from zope.server.http.commonaccesslogger). + + >>> def logFactory(): + ... return 'my logger' + + The 'defaultPort' argument specifies the default TCP port number for the + server. + + The 'defaultVerbose' argument specifies the default verbosity. + + The 'defaultIP' argument specifies the network interface for listening on. + You can specify the network interface IP address, or an empty string if you + want to listen on all interfaces. + + >>> from zope.app.server.servertype import IServerType + >>> from zope.app.server.servertype import ServerType + >>> st = ServerType(factory, requestFactory, logFactory, + ... defaultPort=8080, defaultVerbose=False) + >>> verifyObject(IServerType, st) + True + + A server type is then registered as a named utility. These utilities are + used while interpreting sections of zope.conf to create instances + of servers listening on a specific port. + + When you create an instance of a server, you need to tell it the task + dispatcher (see IDispatcher in zope.server.interfaces), and the ZODB + database object. + + The `name` argument to create is, as far as I can tell, purely informative. + It is used to construct a server identifier that appears in log files and, + for example, the 'Server' HTTP header. + + >>> dispatcher = 'my task dispatcher' + >>> db = 'my database' + >>> st.create('Sample Server', dispatcher, db) + Starting a server (Sample Server) on *:8080 + This server will use my request factory for my database to construct requests + This server will use my logger for hit logging + This server will not be verbose + This server will be managed by my task dispatcher + + You can, of course, create multiple instances of the same server type, and + bind them to different ports. + + >>> st.create('Sample Server 2', dispatcher, db, port=1234, verbose=True) + Starting a server (Sample Server 2) on *:1234 + This server will use my request factory for my database to construct requests + This server will use my logger for hit logging + This server will be verbose + This server will be managed by my task dispatcher + + >>> st.create('Sample Server 3', dispatcher, db, port=9090, + ... ip='127.0.0.1') + Starting a server (Sample Server 3) on 127.0.0.1:9090 + This server will use my request factory for my database to construct requests + This server will use my logger for hit logging + This server will not be verbose + This server will be managed by my task dispatcher + + """ + + +def test_suite(): + return doctest.DocTestSuite() + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/server/tests/test_zpasswd.py zope3-3.5~bzr18/src/zope/app/server/tests/test_zpasswd.py --- zope3-3.4.0/src/zope/app/server/tests/test_zpasswd.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/tests/test_zpasswd.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,139 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 zpasswd script. + +$Id: test_zpasswd.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" + +import os +import unittest, doctest + +from zope.password import password +from zope.app.server.tests.test_mkzopeinstance import TestBase + +from zope.app.server import zpasswd + + +class ArgumentParsingTestCase(TestBase): + + config = os.path.join(os.path.dirname(__file__), "site.zcml") + + def parse_args(self, args): + argv = ["foo/bar.py"] + args + options = zpasswd.parse_args(argv) + self.assertEqual(options.program, "bar.py") + self.assert_(options.version) + return options + + def check_stdout_content(self, args): + try: + options = self.parse_args(args) + except SystemExit, e: + self.assertEqual(e.code, 0) + self.assert_(self.stdout.getvalue()) + self.failIf(self.stderr.getvalue()) + else: + self.fail("expected SystemExit") + + def test_no_arguments(self): + options = self.parse_args([]) + self.assert_(options.managers) + self.assert_(not options.destination) + + def test_version_long(self): + self.check_stdout_content(["--version"]) + + def test_help_long(self): + self.check_stdout_content(["--help"]) + + def test_help_short(self): + self.check_stdout_content(["-h"]) + + def test_destination_short(self): + options = self.parse_args(["-o", "filename"]) + self.assertEqual(options.destination, "filename") + + def test_destination_long(self): + options = self.parse_args(["--output", "filename"]) + self.assertEqual(options.destination, "filename") + + def test_config_short(self): + options = self.parse_args(["-c", self.config]) + self.assert_(options.managers) + + def test_config_long(self): + options = self.parse_args(["--config", self.config]) + self.assert_(options.managers) + +class ControlledInputApplication(zpasswd.Application): + + def __init__(self, options, input_lines): + super(ControlledInputApplication, self).__init__(options) + self.__input = input_lines + + def read_input_line(self, prompt): + return self.__input.pop(0) + + read_password = read_input_line + + def all_input_consumed(self): + return not self.__input + +class Options(object): + + config = None + destination = None + version = "[test-version]" + program = "[test-program]" + managers = password.managers + +class InputCollectionTestCase(TestBase): + + def createOptions(self): + return Options() + + def check_principal(self, expected): + output = self.stdout.getvalue() + self.failUnless(output) + + principal_lines = output.splitlines()[-(len(expected) + 1):-1] + for line, expline in zip(principal_lines, expected): + self.failUnlessEqual(line.strip(), expline) + + def test_principal_information(self): + options = self.createOptions() + app = ControlledInputApplication(options, + ["id", "title", "login", "1", "passwd", "passwd", "description"]) + app.process() + self.failUnless(not self.stderr.getvalue()) + self.failUnless(app.all_input_consumed()) + self.check_principal([ + '' + ]) + + +def test_suite(): + suite = doctest.DocTestSuite('zope.app.server.zpasswd') + suite.addTest(unittest.makeSuite(ArgumentParsingTestCase)) + suite.addTest(unittest.makeSuite(InputCollectionTestCase)) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/server/wsgi.py zope3-3.5~bzr18/src/zope/app/server/wsgi.py --- zope3-3.4.0/src/zope/app/server/wsgi.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/wsgi.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,76 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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. +# +############################################################################## +"""WSGI-compliant HTTP server setup. + +$Id: wsgi.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +__docformat__ = "reStructuredText" + +import zope.interface +from zope.server.http.commonaccesslogger import CommonAccessLogger +from zope.server.http import wsgihttpserver + +from zope.app.publication.httpfactory import HTTPPublicationRequestFactory +from zope.app.wsgi import WSGIPublisherApplication + +import servertype + +class ServerType(object): + + zope.interface.implements(servertype.IServerType) + + def __init__(self, factory, applicationFactory, logFactory, + defaultPort, defaultVerbose, defaultIP='', + requestFactory=HTTPPublicationRequestFactory): + self._factory = factory + self._applicationFactory = applicationFactory + self._requestFactory = requestFactory + self._logFactory = logFactory + self._defaultPort = defaultPort + self._defaultVerbose = defaultVerbose + self._defaultIP = defaultIP + + + def create(self, name, task_dispatcher, db, port=None, + verbose=None, ip=None): + 'See IServerType' + + application = self._applicationFactory( + db, factory=self._requestFactory) + + if port is None: + port = self._defaultPort + + if ip is None: + ip = self._defaultIP + + if verbose is None: + verbose = self._defaultVerbose + + return self._factory(application, name, ip, port, + task_dispatcher=task_dispatcher, + verbose=verbose, + hit_log=self._logFactory(), + ) + + +http = ServerType(wsgihttpserver.WSGIHTTPServer, + WSGIPublisherApplication, + CommonAccessLogger, + 8080, True) + +pmhttp = ServerType(wsgihttpserver.PMDBWSGIHTTPServer, + WSGIPublisherApplication, + CommonAccessLogger, + 8013, True) diff -Nru zope3-3.4.0/src/zope/app/server/zope.app.server-configure.zcml zope3-3.5~bzr18/src/zope/app/server/zope.app.server-configure.zcml --- zope3-3.4.0/src/zope/app/server/zope.app.server-configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/zope.app.server-configure.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/app/server/zopeskel/bin/debugzope.in zope3-3.5~bzr18/src/zope/app/server/zopeskel/bin/debugzope.in --- zope3-3.4.0/src/zope/app/server/zopeskel/bin/debugzope.in 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/zopeskel/bin/debugzope.in 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,50 @@ +#!<> -i +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Script to run the Zope Application Server from the Python prompt. + +$Id$ +""" +import os +import sys + + +SOFTWARE_HOME = r"<>" +INSTANCE_HOME = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +CONFIG_FILE = os.path.join(INSTANCE_HOME, "etc", "zope.conf") + + +def startup(): + # This removes the script directory from sys.path, which we do + # since there are no modules here. + # + basepath = filter(None, sys.path) + + sys.path[:] = [os.path.join(INSTANCE_HOME, "lib", "python"), + SOFTWARE_HOME] + basepath + + from zope.app.server.main import debug + db = debug(["-C", CONFIG_FILE] + sys.argv[1:]) + if "PYTHONSTARTUP" in os.environ: + execfile(os.environ["PYTHONSTARTUP"]) + return db + + +if __name__ == '__main__': + db = startup() + del startup + from zope.app.debug import Debugger + debugger = app = Debugger.fromDatabase(db) + del db + del Debugger diff -Nru zope3-3.4.0/src/zope/app/server/zopeskel/bin/runzope.in zope3-3.5~bzr18/src/zope/app/server/zopeskel/bin/runzope.in --- zope3-3.4.0/src/zope/app/server/zopeskel/bin/runzope.in 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/zopeskel/bin/runzope.in 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ +#!<> +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Script to run the Zope Application Server in the foreground. + +$Id$ +""" +import os +import sys + + +SOFTWARE_HOME = r"<>" +INSTANCE_HOME = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +CONFIG_FILE = os.path.join(INSTANCE_HOME, "etc", "zope.conf") + + +def run(): + if sys.version_info < ( 2,3,5 ): + print """\ + ERROR: Your python version is not supported by Zope3. + Zope3 needs Python 2.3.5 or greater. You are running:""" + sys.version + sys.exit(1) + + # This removes the script directory from sys.path, which we do + # since there are no modules here. + # + basepath = filter(None, sys.path) + + sys.path[:] = [os.path.join(INSTANCE_HOME, "lib", "python"), + SOFTWARE_HOME] + basepath + + from zope.app.server.main import main + main(["-C", CONFIG_FILE] + sys.argv[1:]) + + +if __name__ == '__main__': + run() diff -Nru zope3-3.4.0/src/zope/app/server/zopeskel/etc/zope.conf.in zope3-3.5~bzr18/src/zope/app/server/zopeskel/etc/zope.conf.in --- zope3-3.4.0/src/zope/app/server/zopeskel/etc/zope.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/zopeskel/etc/zope.conf.in 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,91 @@ +# This is the configuration file for the Zope Application Server. + +%define INSTANCE <> + +%define CONFDIR $INSTANCE/etc +%define DATADIR $INSTANCE/var +%define LOGDIR $INSTANCE/log + +# identify the component configuration used to define the site: +# +site-definition $INSTANCE/etc/site.zcml + +# number of bytecode instructions to execute between checks for +# interruptions (SIGINTR, thread switches): +# +interrupt-check-interval 200 + + + type WSGI-HTTP + address 8080 + + +# For debugging purposes, you can use this publisher instead/as well +# (obviously if it's as well, use a different port number). If there's +# an exception, Zope will drop into pdb at the point of the exception. +# +# +# type WSGI-PostmortemDebuggingHTTP +# address 8080 +# + +# uncomment this if you want the FTP server up and running +# +# type FTP +# address 8021 +# + +# Standard Filestorage + + + path $DATADIR/Data.fs + + +# uncomment this if you want to connect to a local ZEO server +# instead: +# +# server localhost:9999 +# storage 1 +# # ZEO client cache, in bytes +# cache-size 20MB +# # Uncomment to have a persistent disk cache +# #client zeo1 +# + + + + # This sets up logging to both a file (access.log) and to standard + # output (STDOUT). The "path" setting can be a relative or absolute + # filesystem path or the tokens STDOUT or STDERR. + + + path $LOGDIR/access.log + + + + path STDOUT + + + + + # This sets up logging to both a file and to standard output + # (STDOUT). The "path" setting can be a relative or absolute + # filesystem path or the tokens STDOUT or STDERR. + + + path $LOGDIR/z3.log + + + + path STDOUT + + + +# devmode +# +# Switches the Developer Mode on and off. +# +# Default: +# devmode on +# +#devmode off diff -Nru zope3-3.4.0/src/zope/app/server/zpasswd.py zope3-3.5~bzr18/src/zope/app/server/zpasswd.py --- zope3-3.4.0/src/zope/app/server/zpasswd.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/server/zpasswd.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,279 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Implementation of the zpasswd script. + +$Id: zpasswd.py 106796 2009-12-20 04:43:19Z fafhrd $ +""" +import optparse +import os +import sys +from xml.sax.saxutils import quoteattr + +from zope.app.applicationcontrol import zopeversion + + +# TODO: We need to resolve code duplication in zpasswd.py and mkzopeinstance.py + +def main(argv=None): + """Top-level script function to create a new principals.""" + if argv is None: + argv = sys.argv + try: + options = parse_args(argv) + except SystemExit, e: + if e.code: + return 2 + else: + return 0 + app = Application(options) + try: + return app.process() + except KeyboardInterrupt: + return 1 + except SystemExit, e: + return e.code + +class Principal(object): + """Principal. + + >>> principal = Principal("id", "title", "login", "password") + >>> print principal + + + >>> principal = Principal("id", "title", "login", "password", + ... "description", "SHA1") + >>> print principal + + """ + + def __init__(self, id, title, login, password, + description="", password_manager_name="Plain Text"): + self.id = id + self.login = login + self.password = password + self.title = title + self.description = description + self.password_manager_name = password_manager_name + + def getLines(self): + lines = [ + ' ') + return lines + + def __str__(self): + return "\n".join(self.getLines()) + +TITLE = """ +============================================ +Principal information for inclusion in ZCML: +""" + +ID_TITLE = """ +Please choose an id for the principal. +""" + +TITLE_TITLE = """ +Please choose a title for the principal. +""" + +LOGIN_TITLE = """ +Please choose a login for the principal. +""" + +PASSWORD_TITLE = """ +Please provide a password for the principal. +""" + +DESCRIPTION_TITLE = """ +Please provide an optional description for the principal. +""" + +class Application(object): + + title = TITLE + id_title = ID_TITLE + title_title = TITLE_TITLE + login_title = LOGIN_TITLE + password_title = PASSWORD_TITLE + description_title = DESCRIPTION_TITLE + + def __init__(self, options): + self.options = options + self.need_blank_line = False + + def read_input_line(self, prompt): + # The tests replace this to make sure the right things happen. + return raw_input(prompt) + + def read_password(self, prompt): + # The tests replace this to make sure the right things happen. + import getpass + try: + return getpass.getpass(prompt) + except KeyboardInterrupt: + # The cursor was left on the same line as the prompt, + # which we don't like. Print a blank line. + print + raise + + def process(self): + options = self.options + + if not options.destination: + destination = sys.stdout + else: + destination = open(options.destination, "wb") + + principal = self.get_principal() + + if destination is sys.stdout: + print self.title + print >>destination, principal + print + + return 0 + + def get_principal(self): + id = self.get_value(self.id_title, "Id: ", "Id may not be empty") + title = self.get_value(self.title_title, "Title: ", + "Title may not be empty") + login = self.get_value(self.login_title, "Login: ", + "Login may not be empty") + password_manager_name, password_manager = self.get_password_manager() + password = self.get_password() + description = self.get_value(self.description_title, "Description: ",) + + password = password_manager.encodePassword(password) + return Principal(id, title, login, password, description, + password_manager_name) + + def get_value(self, title, prompt, error=""): + self.print_message(title) + self.need_blank_line = True + while True: + value = self.read_input_line(prompt).strip() + if not value and error: + print >>sys.stderr, error + continue + return value + + def get_password_manager(self): + self.print_message("Password manager:") + print + managers = self.options.managers + for i, (name, manager) in enumerate(managers): + print "% i. %s" % (i + 1, name) + print + self.need_blank_line = True + while True: + password_manager = self.read_input_line( + "Password Manager Number [1]: ") + if not password_manager: + index = 0 + break + elif password_manager.isdigit(): + index = int(password_manager) + if index > 0 and index <= len(managers): + index -= 1 + break + print >>sys.stderr, "You must select a password manager" + print "%s password manager selected" % managers[index][0] + return managers[index] + + def get_password(self): + self.print_message(self.password_title) + while True: + password = self.read_password("Password: ") + if not password: + print >>sys.stderr, "Password may not be empty" + continue + if password != password.strip() or password.split() != [password]: + print >>sys.stderr, "Password may not contain spaces" + continue + break + again = self.read_password("Verify password: ") + if again != password: + print >>sys.stderr, "Password not verified!" + sys.exit(1) + return password + + def print_message(self, message): + if self.need_blank_line: + print + self.need_blank_line = False + print message + +def get_password_managers(config_path=None): + if not config_path: + from zope.password.password import managers + else: + from zope.configuration import xmlconfig + from zope.component import getUtilitiesFor + from zope.password.interfaces import IPasswordManager + + print "Loading configuration..." + config = xmlconfig.file(config_path) + managers = [] + for name, manager in getUtilitiesFor(IPasswordManager): + if name == "Plain Text": + managers.insert(0, (name, manager)) + else: + managers.append((name, manager)) + if not managers: + from zope.password.password import managers + return managers + +def parse_args(argv): + """Parse the command line, returning an object representing the input.""" + path, prog = os.path.split(os.path.realpath(argv[0])) + version = "%prog for " + zopeversion.ZopeVersionUtility.getZopeVersion() + p = optparse.OptionParser(prog=prog, + usage="%prog [options]", + version=version) + p.add_option("-c", "--config", dest="config", metavar="FILE", + help=("path to the site.zcml configuration file" + " (more accurate but slow password managers registry creation)")) + p.add_option("-o", "--output", dest="destination", metavar="FILE", + help=("the file in which the output will be saved" + " (STDOUT by default)")) + options, args = p.parse_args(argv[1:]) + options.managers = get_password_managers(options.config) + options.program = prog + options.version = version + if args: + p.error("too many arguments") + return options diff -Nru zope3-3.4.0/src/zope/app/testing/cookieTestOne.txt zope3-3.5~bzr18/src/zope/app/testing/cookieTestOne.txt --- zope3-3.4.0/src/zope/app/testing/cookieTestOne.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/cookieTestOne.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,62 @@ +======================== +DocTest Functional Tests +======================== + +This file documents and tests doctest-based functional tests and basic +Zope web-application functionality. + +This second DocTest, zope/app/testing/cookieTestOne.txt, has specifically +been created in order to make sure cookie information is not being saved +across a test suite. If we are saving these via a global 'http' instance, +we will see more results than those listed below. 'http' is instead +created in setUp within _prepare_doctest_keywords, rather than in the +global declarations. + +Now we will run tests to ensure that cookies are not saved across doctests. + + >>> from zope.app.testing import functional + >>> from zope.app.testing.tests import DummyCookiesResponse + +We will create some cookie values and saved them in our 'http' value, which +is a CookieHandler() object. + + >>> response = DummyCookiesResponse(dict( + ... proton=dict(value='fromCookieTestOne', + ... path='/foo', + ... comment='rest is ignored'), + ... neutron=dict(value='fromCookieTestOne'))) + +If 'http' is created as a global variable, then every doctest in this +suite will be saving cookies in it, and one doctest may see cookies for +another doctest. We only want two cookies in 'http' - the ones we just +created. + + >>> http.saveCookies(response) + >>> len(http.cookies) + 2 + + >>> http.cookies['proton'].OutputString() + 'proton=fromCookieTestOne; Path=/foo...' + + >>> http.cookies + + + >>> http.cookies['proton'] = 'fromCookieTestOne' + >>> http.cookies['proton']['path'] = '/foo' + >>> http.cookies['electron'] = 'fromCookieTestOne' + >>> http.cookies['electron']['path'] = '/foo/baz' + >>> http.cookies['neutron'] = 'fromCookieTestOne' + + >>> cookieHeader = http.httpCookie('/foo/bar') + >>> parts = cookieHeader.split('; ') + >>> parts.sort() + >>> parts + ['neutron=fromCookieTestOne', 'proton=fromCookieTestOne'] + + >>> cookieHeader = http.httpCookie('/foo/baz') + >>> parts = cookieHeader.split('; ') + >>> parts.sort() + >>> parts + ['electron=fromCookieTestOne', + 'neutron=fromCookieTestOne', + 'proton=fromCookieTestOne'] diff -Nru zope3-3.4.0/src/zope/app/testing/cookieTestTwo.txt zope3-3.5~bzr18/src/zope/app/testing/cookieTestTwo.txt --- zope3-3.4.0/src/zope/app/testing/cookieTestTwo.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/cookieTestTwo.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,52 @@ +======================== +DocTest Functional Tests +======================== + +This file documents and tests doctest-based functional tests and basic +Zope web-application functionality. + +This second DocTest, zope/app/testing/cookieTestTwo.txt, has specifically +been created in order to make sure cookie information is not being saved +across a test suite. If we are saving these via a global 'http' instance, +we will see more results than those listed below. 'http' is instead +created in setUp within _prepare_doctest_keywords, rather than in the +global declarations. + + >>> from zope.app.testing import functional + >>> from zope.app.testing.tests import DummyCookiesResponse + + >>> response = DummyCookiesResponse(dict( + ... cobalt=dict(value='fromCookieTestTwo', + ... path='/foo', + ... comment='rest is ignored'), + ... crimson=dict(value='fromCookieTestTwo'))) + + >>> http.saveCookies(response) + >>> len(http.cookies) + 2 + + >>> http.cookies['cobalt'].OutputString() + 'cobalt=fromCookieTestTwo; Path=/foo...' + + >>> http.cookies + + + >>> http.cookies['cobalt'] = 'fromCookieTestTwo' + >>> http.cookies['cobalt']['path'] = '/foo' + >>> http.cookies['amber'] = 'fromCookieTestTwo' + >>> http.cookies['amber']['path'] = '/foo/baz' + >>> http.cookies['crimson'] = 'fromCookieTestTwo' + + >>> cookieHeader = http.httpCookie('/foo/bar') + >>> parts = cookieHeader.split('; ') + >>> parts.sort() + >>> parts + ['cobalt=fromCookieTestTwo', 'crimson=fromCookieTestTwo'] + + >>> cookieHeader = http.httpCookie('/foo/baz') + >>> parts = cookieHeader.split('; ') + >>> parts.sort() + >>> parts + ['amber=fromCookieTestTwo', + 'cobalt=fromCookieTestTwo', + 'crimson=fromCookieTestTwo'] diff -Nru zope3-3.4.0/src/zope/app/testing/dochttp.py zope3-3.5~bzr18/src/zope/app/testing/dochttp.py --- zope3-3.4.0/src/zope/app/testing/dochttp.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/dochttp.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,226 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""Convert an http tcpwatch session to a doctest + +$Id: dochttp.py 110724 2010-04-11 00:04:11Z tseaver $ +""" + +import errno +import optparse +import os +import re +import rfc822 +import sys + +usage = """usage: %prog directory + +Convert an http tcpwatch recorded sesssion to a doctest file, which is +written to standard output. + +""" + +parser = optparse.OptionParser(usage) +parser.add_option("-p", "--prefix", default="watch", + help="Prefix for recorded tcpwatch session files") +parser.add_option("-U", "--skip-url", action="append", + help="Regular expression for URLs to skip") +parser.add_option("-E", "--skip-extension", action="append", + help="URL file-extension to skip") +parser.add_option("-e", "--extension", action="append", + help="URL file-extension to include") +parser.add_option("-I", "--skip-request-header", action="append", + help="Request header to skip") +parser.add_option("-O", "--skip-response-header", action="append", + help="Response header to skip") +parser.add_option("-r", "--clean-redirects", action="store_true", + help="Strip content from redirect responses", + default=False) + +default_options = [ + '-e', 'html', + + '-I', 'Accept-Charset', '-I', 'Accept-Encoding', '-I', 'Accept-Language', + '-I', 'Accept', '-I', 'Connection', '-I', 'Host', '-I', 'Keep-Alive', + '-I', 'User-Agent', + + '-O', 'Date', '-O', 'Server', '-O', 'X-Content-Type-Warning', + '-O', 'X-Powered-By', + + ] + +def dochttp(args=sys.argv[1:], default=None): + """Convert a tcpwatch recorded sesssion to a doctest file""" + if default is None: + default = default_options + + options, args = parser.parse_args(default+args) + try: + directory, = args + except: + parser.print_help() + sys.exit(1) + + skip_extensions = options.skip_extension or () + extensions = [ext for ext in (options.extension or ()) + if ext not in skip_extensions] + skip_urls = [re.compile(pattern) for pattern in (options.skip_url or ())] + + names = [name[:-len(".request")] + for name in os.listdir(directory) + if name.startswith(options.prefix) and name.endswith('.request') + ] + names.sort() + + extre = re.compile("[.](\w+)$") + + for name in names: + requests = Requests( + open(os.path.join(directory, name + ".request"), 'rb'), + options.skip_request_header, + ) + responses = Responses( + open(os.path.join(directory, name + ".response"), 'rb'), + options.skip_response_header, + ) + + # We use map so as *not* to truncate at shortest input. + # We want an error if the number of requests and responses + # is different. + for request, response in map(None, requests, responses): + assert (request and response) or not (request or response) + + path = request.path + ext = extre.search(path) + if ext: + ext = ext.group(1) + if extensions: + if ext not in extensions: + continue + else: + if ext in skip_extensions: + continue + + for skip_url in skip_urls: + if skip_url.search(request.path): + break + else: + try: + output_test(request, response, options.clean_redirects) + except IOError, e: + if e.errno == errno.EPIPE: + return + raise + + +def output_test(request, response, clean_redirects=False): + print + print + print ' >>> print http(r"""' + print ' ...', '\n ... '.join(request.lines())+'""")' + if response.code in (301, 302, 303) and clean_redirects: + content_length = None + if response.headers: + for i in range(len(response.headers)): + h, v = response.headers[i] + if h == "Content-Length": + content_length = int(v) + response.headers[i] = (h, "...") + lines = response.header_lines() + if lines and content_length == 0: + lines.append("...") + else: + lines = response.lines() + print ' ', '\n '.join([line.rstrip() and line or '' + for line in lines]) + +class Message: + + start = '' + + def __init__(self, file, skip_headers): + start = file.readline().rstrip() + if start: + self.start = start + if start.startswith("HTTP/"): + # This is a response; extract the response code: + self.code = int(start.split()[1]) + headers = [split_header(header) + for header in rfc822.Message(file).headers + ] + headers = [ + ('-'.join([s.capitalize() for s in name.split('-')]), + v.rstrip() + ) + for (name, v) in headers + if name.lower() not in skip_headers + ] + self.headers = headers + content_length = int(dict(headers).get('Content-Length', '0')) + if content_length: + self.body = file.read(content_length).split('\n') + else: + self.body = [] + + def __nonzero__(self): + return bool(self.start) + + def lines(self): + output = self.header_lines() + if output: + output.extend(self.body) + return output + + def header_lines(self): + if self.start: + output = [self.start] + headers = ["%s: %s" % (name, v) for (name, v) in self.headers] + headers.sort() + output.extend(headers) + output.append('') + else: + output = [] + return output + +headerre = re.compile('(\S+): (.+)$') +def split_header(header): + return headerre.match(header).group(1, 2) + +def messages(cls, file, skip_headers): + skip_headers = [name.lower() for name in (skip_headers or ())] + while 1: + message = cls(file, skip_headers) + if message: + yield message + else: + break + +class Request(Message): + + path = '' + + def __init__(self, file, skip_headers): + Message.__init__(self, file, skip_headers) + if self.start: + self.command, self.path, self.protocol = self.start.split() + +def Requests(file, skip_headers): + return messages(Request, file, skip_headers) + +def Responses(file, skip_headers): + return messages(Message, file, skip_headers) + +main = dochttp + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/zope/app/testing/dochttp.txt zope3-3.5~bzr18/src/zope/app/testing/dochttp.txt --- zope3-3.4.0/src/zope/app/testing/dochttp.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/dochttp.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,37 @@ +================= +FDocTest (How-To) +================= + +Steps to get started: + +1. Use a clean/missing Data.fs + +2. Create a manager with the name "mgr", password "mgrpw", and grant + the zope.Manager role. + +3. Install tcpwatch. + +4. Create a temporary directory to record tcpwatch output. + +5. Run tcpwatch using: + tcpwatch.py -L 8081:8080 -s -r tmpdir + (the ports are the listening port and forwarded-to port; the + second need to match the Zope configuration) + +6. In a browser, connect to the listening port and do whatever needs + to be recorded. + +7. Shut down tcpwatch. + +8. Run the script src/zope/app/testing/dochttp.py: + python2.4 src/zope/app/testing/dochttp.py tmpdir > somefile.txt + +9. Edit the generated text file to add explanations and elide + uninteresting portions of the output. + +10. In a functional test module (usually ftests.py), import + FunctionalDocFileSuite from zope.app.testing.functional and + instantiate it, passing the name of the text file containing + the test. + + diff -Nru zope3-3.4.0/src/zope/app/testing/doctest.txt zope3-3.5~bzr18/src/zope/app/testing/doctest.txt --- zope3-3.4.0/src/zope/app/testing/doctest.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/doctest.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,146 @@ +======================== +DocTest Functional Tests +======================== + +This file documents and tests doctest-based functional tests and basic +Zope web-application functionality. + +Request/Response Functional Tests +--------------------------------- + +You can create Functional tests as doctests. Typically, this is done +by using a script such as src/zope/app/testing/dochttp.py to convert +tcpwatch recorded output to a doctest, which is then edited to provide +explanation and to remove uninteresting details. That is how this +file was created. + +Here we'll test some of the most basic types of access. + +First, we'll test accessing a protected page without credentials: + + >>> print http(r""" + ... GET /@@contents.html HTTP/1.1 + ... """) + HTTP/1.1 401 Unauthorized + Cache-Control: no-store, no-cache, must-revalidate + Content-Length: ... + Content-Type: text/html;charset=utf-8 + Expires: Mon, 26 Jul 1997 05:00:00 GMT + Pragma: no-cache + WWW-Authenticate: basic realm="Zope" + + >> print http(r""" + ... GET /@@contents.html HTTP/1.1 + ... Authorization: Basic mgr:mgrpw + ... """) + HTTP/1.1 200 OK + Content-Length: ... + Content-Type: text/html;charset=utf-8 + + >> print http(r""" + ... GET /++etc++site/@@manage HTTP/1.1 + ... Authorization: Basic mgr:mgrpw + ... Referer: http://localhost:8081/ + ... """) + HTTP/1.1 303 See Other + Content-Length: 0 + Content-Type: text/plain;charset=utf-8 + Location: @@contents.html + + +Note that, in this case, we got a 303 response. A 303 response is the +prefered response for this sort of redirect with HTTP 1.1. If we used +HTTP 1.0, we'd get a 302 response: + + >>> print http(r""" + ... GET /++etc++site/@@manage HTTP/1.0 + ... Authorization: Basic mgr:mgrpw + ... Referer: http://localhost:8081/ + ... """) + HTTP/1.0 302 Moved Temporarily + Content-Length: 0 + Content-Type: text/plain;charset=utf-8 + Location: @@contents.html + + +Lets visit the page we were redirected to: + + >>> print http(r""" + ... GET /++etc++site/@@contents.html HTTP/1.1 + ... Authorization: Basic mgr:mgrpw + ... Referer: http://localhost:8081/ + ... """) + HTTP/1.1 200 OK + Content-Length: ... + Content-Type: text/html;charset=utf-8 + + >> print http(r""" + ... GET / HTTP/1.1 + ... Authorization: Basic mgr:mgrpw + ... """) + HTTP/1.1 200 OK + Content-Length: ... + Content-Type: text/html;charset=utf-8 + + >> root = getRootFolder() + >>> root + + +You can intermix HTTP requests with regular Python calls. Note, +however, that making an `http()` call implied a transaction commit. +If you want to throw away changes made in Python code, abort the +transaction before the HTTP request. + + >>> print http(r""" + ... POST /@@contents.html HTTP/1.1 + ... Authorization: Basic mgr:mgrpw + ... Content-Length: 73 + ... Content-Type: application/x-www-form-urlencoded + ... + ... type_name=BrowserAdd__zope.site.folder.Folder&new_value=f1""", + ... handle_errors=False) + HTTP/1.1 303 See Other + Content-Length: ... + Content-Type: text/html;charset=utf-8 + Location: http://localhost/@@contents.html + + >> list(root.keys()) + [u'f1'] diff -Nru zope3-3.4.0/src/zope/app/testing/empty.zcml zope3-3.5~bzr18/src/zope/app/testing/empty.zcml --- zope3-3.4.0/src/zope/app/testing/empty.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/empty.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ + + diff -Nru zope3-3.4.0/src/zope/app/testing/ftesting.zcml zope3-3.5~bzr18/src/zope/app/testing/ftesting.zcml --- zope3-3.4.0/src/zope/app/testing/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/testing/functional.py zope3-3.5~bzr18/src/zope/app/testing/functional.py --- zope3-3.4.0/src/zope/app/testing/functional.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/functional.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,811 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Functional testing framework for Zope 3. + +There should be a file 'ftesting.zcml' in the current directory. + +$Id: functional.py 110724 2010-04-11 00:04:11Z tseaver $ +""" +import copy +import doctest +import logging +import os.path +import re +import rfc822 +import sys +import traceback +import unittest +from StringIO import StringIO +from Cookie import SimpleCookie +from transaction import abort, commit +from ZODB.DB import DB +from ZODB.DemoStorage import DemoStorage +from ZODB.interfaces import IDatabase + +from zope import component +from zope.publisher.interfaces import ISkinnable +from zope.publisher.browser import BrowserRequest +from zope.publisher.http import HTTPRequest +from zope.publisher.publish import publish +from zope.publisher.skinnable import setDefaultSkin +from zope.security.interfaces import Forbidden, Unauthorized + +import zope.app.appsetup.product +import zope.app.testing.setup +from zope.app.appsetup.appsetup import multi_database +from zope.app.debug import Debugger +from zope.app.publication.http import HTTPPublication +from zope.app.publication.zopepublication import ZopePublication +from zope.app.publication.httpfactory import chooseClasses +from zope.component.hooks import setSite, getSite + + +class ResponseWrapper(object): + """A wrapper that adds several introspective methods to a response.""" + + def __init__(self, response, path, omit=()): + self._response = response + self._path = path + self.omit = omit + self._body = None + + def getOutput(self): + """Returns the full HTTP output (headers + body)""" + body = self.getBody() + omit = self.omit + headers = [x + for x in self._response.getHeaders() + if x[0].lower() not in omit] + headers.sort() + headers = '\n'.join([("%s: %s" % (n, v)) for (n, v) in headers]) + statusline = '%s %s' % (self._response._request['SERVER_PROTOCOL'], + self._response.getStatusString()) + if body: + return '%s\n%s\n\n%s' %(statusline, headers, body) + else: + return '%s\n%s\n' % (statusline, headers) + + def getBody(self): + """Returns the response body""" + if self._body is None: + self._body = ''.join(self._response.consumeBody()) + + return self._body + + def getPath(self): + """Returns the path of the request""" + return self._path + + def __getattr__(self, attr): + return getattr(self._response, attr) + + __str__ = getOutput + + +class IManagerSetup(zope.interface.Interface): + """Utility for enabling up a functional testing manager with needed grants + + TODO This is an interim solution. It tries to break the dependence + on a particular security policy, however, we need a much better + way of managing functional-testing configurations. + """ + + def setUpManager(): + """Set up the manager, zope.mgr + """ + +class BaseDatabaseFactory(object): + """Factory object for passing to appsetup.multi_databases + + This class is an internal implementation detail, subject to change + without notice! + + It is currently used by FunctionalTestSetUp.__init__ to create the + basic storage(s) containing the data that is common to all tests + in a layer. + + The constructor takes the name of the new database, and a + dictionary of storages. The 'open' method creates a new + DemoStorage, and adds it to the storage dictionary under the given + name. Then creates and returns a named DB object using the + storage. + """ + + def __init__(self, name, base_storages): + self.name = name + self.base_storages = base_storages + + def open(self): + name = self.name + if name in self.base_storages: + raise ValueError("Duplicate database name: %r" % name) + storage = DemoStorage("Memory storage %r" % name) + self.base_storages[name] = storage + return DB(storage, database_name=name) + + +class DerivedDatabaseFactory(object): + """Factory object for passing to appsetup.multi_databases + + This class is an internal implementation detail, subject to change + without notice! + + It is currently used by FunctionalTestSetUp.setUp to create the + derived storage(s) used for each test in a layer. + + The constructor takes the name of the new database, and a + dictionary of storages. The 'open' method creates a new + DemoStorage as a wrapper around the storage with the given + name. Then creates and returns a named DB object using the + storage. + """ + + def __init__(self, name, base_storages): + self.name = name + self.storage = DemoStorage("Demo storage %r" % name, + base_storages[name]) + + def open(self): + return DB(self.storage, database_name=self.name) + + +class FunctionalTestSetup(object): + """Keeps shared state across several functional test cases.""" + + __shared_state = { '_init': False } + + def __init__(self, config_file=None, database_names=None, + product_config=None): + """Initializes Zope 3 framework. + + Creates a volatile memory storage. Parses Zope3 configuration files. + """ + self.__dict__ = self.__shared_state + + if database_names is not None: + database_names = tuple(database_names) + + if not self._init: + + # Make sure unit tests are cleaned up + zope.app.testing.setup.placefulSetUp() + zope.app.testing.setup.placefulTearDown() + + if not config_file: + config_file = 'ftesting.zcml' + if database_names is None: + database_names = ('unnamed',) + self.log = StringIO() + # Make it silent but keep the log available for debugging + logging.root.addHandler(logging.StreamHandler(self.log)) + + self.old_product_config = copy.deepcopy( + zope.app.appsetup.product.saveConfiguration()) + configs = [] + if product_config: + configs = zope.app.appsetup.product.loadConfiguration( + StringIO(product_config)) + configs = [ + zope.app.appsetup.product.FauxConfiguration(name, values) + for name, values in configs.items() + ] + self.local_product_config = configs + zope.app.appsetup.product.setProductConfigurations(configs) + + self._base_storages = {} + self.db = multi_database( + BaseDatabaseFactory(name, self._base_storages) + for name in database_names + )[0][0] + # This handles anything added by generations or other bootstrap + # subscribers. + commit() + self.dbstack = [] + self.app = Debugger(self.db, config_file) + + self.connection = None + self._config_file = config_file + self._product_config = product_config + self._database_names = database_names + self._init = True + + # Make a local grant for the test user + setup = component.queryUtility(IManagerSetup) + if setup is not None: + setup.setUpManager() + + FunctionalTestSetup().connection = None + + elif config_file and config_file != self._config_file: + # Running different tests with different configurations is not + # supported at the moment + raise NotImplementedError('Already configured' + ' with a different config file') + + elif product_config and product_config != self._product_config: + raise NotImplementedError('Already configured' + ' with different product configuration') + + elif database_names and database_names != self._database_names: + # Running different tests with different configurations is not + # supported at the moment + raise NotImplementedError('Already configured' + ' with different database names') + + # BBB: Simulate the old base_storage attribute, but only when not using + # multiple databases. There *is* code in the wild that uses the attribute. + def _get_base_storage(self): + if len(self._database_names)!=1: + raise AttributeError('base_storage') + return self._base_storages[self._database_names[0]] + + def _set_base_storage(self, value): + if len(self._database_names)!=1: + raise AttributeError('base_storage') + self._base_storages[self._database_names[0]] = value + + base_storage = property(_get_base_storage, _set_base_storage) + + def _close_databases(self): + # This is really careful to unregister the databases before attempting + # to close anything. Zope Corporation has a couple of large + # multi-database applications that get bitten if we're not careful + # like this, but we've not been able to write a concise test case yet. + base = component.getGlobalSiteManager() + dbs = [] + for name, db in list(component.getUtilitiesFor(IDatabase)): + ok = base.unregisterUtility(db, IDatabase, name) + assert ok + dbs.append(db) + abort() + if self.connection: + self.connection.close() + self.connection = None + for db in dbs: + db.close() + + def setUp(self): + """Prepares for a functional test case.""" + # Tear down the old demo storages (if any) and create fresh ones + abort() + self.dbstack.append((self.db, self.connection)) + self.connection = None + zope.app.appsetup.product.setProductConfigurations( + self.local_product_config) + self.db = self.app.db = multi_database( + DerivedDatabaseFactory(name, self._base_storages) + for name in self._database_names + )[0][0] + + def tearDown(self): + """Cleans up after a functional test case.""" + self._close_databases() + self.db, self.connection = self.dbstack.pop() + setSite(None) + + def tearDownCompletely(self): + """Cleans up the setup done by the constructor.""" + self._close_databases() + assert self.dbstack == [] + zope.app.testing.setup.placefulTearDown() + zope.app.appsetup.product.restoreConfiguration( + self.old_product_config) + self._config_file = False + self._product_config = None + self._database_names = None + self._init = False + + def getRootFolder(self): + """Returns the Zope root folder.""" + if not self.connection: + self.connection = self.db.open() + root = self.connection.root() + return root[ZopePublication.root_name] + + def getApplication(self): + """Returns the Zope application instance.""" + return self.app + + +class ZCMLLayer: + """ZCML-defined test layer + """ + + __bases__ = () + + def __init__(self, config_file, module, name, allow_teardown=False, + product_config=None): + self.config_file = config_file + self.__module__ = module + self.__name__ = name + self.allow_teardown = allow_teardown + self.product_config = product_config + + def setUp(self): + self.setup = FunctionalTestSetup( + self.config_file, product_config=self.product_config) + + def tearDown(self): + self.setup.tearDownCompletely() + if not self.allow_teardown: + # Some ZCML directives change globals but are not accompanied + # with registered CleanUp handlers to undo the changes. Let + # packages which use such directives indicate that they do not + # support tearing down. + raise NotImplementedError + + +def defineLayer(name, zcml='test.zcml', allow_teardown=False): + """Helper function for defining layers. + + Usage: defineLayer('foo') + """ + globals = sys._getframe(1).f_globals + globals[name] = ZCMLLayer( + os.path.join(os.path.dirname(globals['__file__']), zcml), + globals['__name__'], + name, + allow_teardown=allow_teardown, + ) + +if os.path.exists(os.path.join('zopeskel', 'etc', 'ftesting.zcml')): + Functional = os.path.join('zopeskel', 'etc', 'ftesting.zcml') + FunctionalNoDevMode = os.path.join('zopeskel', 'etc', 'ftesting-base.zcml') +elif os.path.exists(os.path.join('etc', 'ftesting.zcml')): + Functional = os.path.join('etc', 'ftesting.zcml') + FunctionalNoDevMode = os.path.join('etc', 'ftesting-base.zcml') +else: + # let's hope that the file is in our CWD. If not, we'll get an + # error anyways, but we can't just throw an error if we don't find + # that file. This module might be imported for other things as + # well, not only starting up Zope from ftesting.zcml. + Functional = 'ftesting.zcml' + FunctionalNoDevMode = 'ftesting-base.zcml' + +Functional = os.path.abspath(Functional) +FunctionalNoDevMode = os.path.abspath(FunctionalNoDevMode) + +Functional = ZCMLLayer(Functional, __name__, 'Functional') +FunctionalNoDevMode = ZCMLLayer(FunctionalNoDevMode, __name__, + 'FunctionalNoDevMode') + +class FunctionalTestCase(unittest.TestCase): + """Functional test case.""" + + layer = Functional + + def setUp(self): + """Prepares for a functional test case.""" + super(FunctionalTestCase, self).setUp() + FunctionalTestSetup().setUp() + + def tearDown(self): + """Cleans up after a functional test case.""" + FunctionalTestSetup().tearDown() + super(FunctionalTestCase, self).tearDown() + + def getRootFolder(self): + """Returns the Zope root folder.""" + return FunctionalTestSetup().getRootFolder() + + def commit(self): + commit() + + def abort(self): + abort() + + +class CookieHandler(object): + + def __init__(self, *args, **kw): + # Somewhere to store cookies between consecutive requests + self.cookies = SimpleCookie() + super(CookieHandler, self).__init__(*args, **kw) + + + def httpCookie(self, path): + """Return self.cookies as an HTTP_COOKIE environment value.""" + l = [m.OutputString().split(';')[0] for m in self.cookies.values() + if path.startswith(m['path'])] + return '; '.join(l) + + def loadCookies(self, envstring): + self.cookies.load(envstring) + + def saveCookies(self, response): + """Save cookies from the response.""" + # Urgh - need to play with the response's privates to extract + # cookies that have been set + # TODO: extend the IHTTPRequest interface to allow access to all + # cookies + # TODO: handle cookie expirations + for k,v in response._cookies.items(): + k = k.encode('utf8') + self.cookies[k] = v['value'].encode('utf8') + if v.has_key('path'): + self.cookies[k]['path'] = v['path'] + + +class BrowserTestCase(CookieHandler, FunctionalTestCase): + """Functional test case for Browser requests.""" + + def setSite(self, site): + """Set the site which will be used to look up local components""" + setSite(site) + + def getSite(self): + """Returns the site which is used to look up local components""" + return getSite() + + def makeRequest(self, path='', basic=None, form=None, env={}): + """Creates a new request object. + + Arguments: + path -- the path to be traversed (e.g. "/folder1/index.html") + basic -- basic HTTP authentication credentials ("user:password") + form -- a dictionary emulating a form submission + (Note that field values should be Unicode strings) + env -- a dictionary of additional environment variables + (You can emulate HTTP request header + X-Header: foo + by adding 'HTTP_X_HEADER': 'foo' to env) + """ + environment = {"HTTP_HOST": 'localhost', + "HTTP_REFERER": 'localhost', + "HTTP_COOKIE": self.httpCookie(path)} + environment.update(env) + app = FunctionalTestSetup().getApplication() + request = app._request(path, '', + environment=environment, + basic=basic, form=form, + request=BrowserRequest) + setDefaultSkin(request) + return request + + def publish(self, path, basic=None, form=None, env={}, + handle_errors=False): + """Renders an object at a given location. + + Arguments are the same as in makeRequest with the following exception: + handle_errors -- if False (default), exceptions will not be caught + if True, exceptions will return a formatted error + page. + + Returns the response object enhanced with the following methods: + getOutput() -- returns the full HTTP output as a string + getBody() -- returns the full response body as a string + getPath() -- returns the path used in the request + """ + old_site = self.getSite() + self.setSite(None) + # A cookie header has been sent - ensure that future requests + # in this test also send the cookie, as this is what browsers do. + # We pull it apart and reassemble the header to block cookies + # with invalid paths going through, which may or may not be correct + if env.has_key('HTTP_COOKIE'): + self.loadCookies(env['HTTP_COOKIE']) + del env['HTTP_COOKIE'] # Added again in makeRequest + + request = self.makeRequest(path, basic=basic, form=form, env=env) + if env.has_key('HTTP_COOKIE'): + self.loadCookies(env['HTTP_COOKIE']) + + request = publish(request, handle_errors=handle_errors) + + response = ResponseWrapper(request.response, path) + + self.saveCookies(response) + self.setSite(old_site) + return response + + def checkForBrokenLinks(self, body, path, basic=None): + """Looks for broken links in a page by trying to traverse relative + URIs. + """ + if not body: return + + old_site = self.getSite() + self.setSite(None) + + from htmllib import HTMLParser + from formatter import NullFormatter + class SimpleHTMLParser(HTMLParser): + def __init__(self, fmt, base): + HTMLParser.__init__(self, fmt) + self.base = base + def do_base(self, attrs): + self.base = dict(attrs).get('href', self.base) + + parser = SimpleHTMLParser(NullFormatter(), path) + parser.feed(body) + parser.close() + base = parser.base + while not base.endswith('/'): + base = base[:-1] + if base.startswith('http://localhost/'): + base = base[len('http://localhost/') - 1:] + + errors = [] + for a in parser.anchorlist: + if a.startswith('http://localhost/'): + a = a[len('http://localhost/') - 1:] + elif a.find(':') != -1: + # Assume it is an external link + continue + elif not a.startswith('/'): + a = base + a + if a.find('#') != -1: + a = a[:a.index('#')] + # ??? what about queries (/path/to/foo?bar=baz&etc)? + request = None + try: + try: + request = self.makeRequest(a, basic=basic) + publication = request.publication + request.processInputs() + publication.beforeTraversal(request) + object = publication.getApplication(request) + object = request.traverse(object) + publication.afterTraversal(request, object) + except (KeyError, NameError, AttributeError, Unauthorized, + Forbidden): + e = traceback.format_exception_only( + *sys.exc_info()[:2])[-1] + errors.append((a, e.strip())) + finally: + publication.endRequest(request, object) + self.setSite(old_site) + + # Make sure we don't have pending changes + abort() + + # The request should always be closed to free resources + # held by the request + if request: + request.close() + if errors: + self.fail("%s contains broken links:\n" % path + + "\n".join([" %s:\t%s" % (a, e) for a, e in errors])) + + +class HTTPTestCase(FunctionalTestCase): + """Functional test case for HTTP requests.""" + + def makeRequest(self, path='', basic=None, form=None, env={}, + instream=None): + """Creates a new request object. + + Arguments: + path -- the path to be traversed (e.g. "/folder1/index.html") + basic -- basic HTTP authentication credentials ("user:password") + form -- a dictionary emulating a form submission + (Note that field values should be Unicode strings) + env -- a dictionary of additional environment variables + (You can emulate HTTP request header + X-Header: foo + by adding 'HTTP_X_HEADER': 'foo' to env) + instream -- a stream from where the HTTP request will be read + """ + if instream is None: + instream = '' + environment = {"HTTP_HOST": 'localhost', + "HTTP_REFERER": 'localhost'} + environment.update(env) + app = FunctionalTestSetup().getApplication() + request = app._request(path, instream, + environment=environment, + basic=basic, form=form, + request=HTTPRequest, publication=HTTPPublication) + return request + + def publish(self, path, basic=None, form=None, env={}, + handle_errors=False, request_body=''): + """Renders an object at a given location. + + Arguments are the same as in makeRequest with the following exception: + handle_errors -- if False (default), exceptions will not be caught + if True, exceptions will return a formatted error + page. + + Returns the response object enhanced with the following methods: + getOutput() -- returns the full HTTP output as a string + getBody() -- returns the full response body as a string + getPath() -- returns the path used in the request + """ + request = self.makeRequest(path, basic=basic, form=form, env=env, + instream=request_body) + response = ResponseWrapper(request.response, path) + publish(request, handle_errors=handle_errors) + return response + + +headerre = re.compile(r'(\S+): (.+)$') +def split_header(header): + return headerre.match(header).group(1, 2) + +basicre = re.compile('Basic (.+)?:(.+)?$') +def auth_header(header): + match = basicre.match(header) + if match: + import base64 + u, p = match.group(1, 2) + if u is None: + u = '' + if p is None: + p = '' + auth = base64.encodestring('%s:%s' % (u, p)) + return 'Basic %s' % auth[:-1] + return header + +def getRootFolder(): + return FunctionalTestSetup().getRootFolder() + +def sync(): + getRootFolder()._p_jar.sync() + +# +# Sample functional test case +# + +class SampleFunctionalTest(BrowserTestCase): + + def testRootPage(self): + response = self.publish('/') + self.assertEquals(response.getStatus(), 200) + + def testRootPage_preferred_languages(self): + response = self.publish('/', env={'HTTP_ACCEPT_LANGUAGE': 'en'}) + self.assertEquals(response.getStatus(), 200) + + def testNotExisting(self): + response = self.publish('/nosuchthing', handle_errors=True) + self.assertEquals(response.getStatus(), 404) + + def testLinks(self): + response = self.publish('/') + self.assertEquals(response.getStatus(), 200) + self.checkForBrokenLinks(response.consumeBody(), response.getPath()) + + +def sample_test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(SampleFunctionalTest)) + return suite + + +class HTTPCaller(CookieHandler): + """Execute an HTTP request string via the publisher""" + + def __call__(self, request_string, handle_errors=True, form=None): + # Commit work done by previous python code. + commit() + + # Discard leading white space to make call layout simpler + request_string = request_string.lstrip() + + # split off and parse the command line + l = request_string.find('\n') + command_line = request_string[:l].rstrip() + request_string = request_string[l+1:] + method, path, protocol = command_line.split() + + instream = StringIO(request_string) + environment = {"HTTP_COOKIE": self.httpCookie(path), + "HTTP_HOST": 'localhost', + "HTTP_REFERER": 'localhost', + "REQUEST_METHOD": method, + "SERVER_PROTOCOL": protocol, + } + + headers = [split_header(header) + for header in rfc822.Message(instream).headers] + for name, value in headers: + name = ('_'.join(name.upper().split('-'))) + if name not in ('CONTENT_TYPE', 'CONTENT_LENGTH'): + name = 'HTTP_' + name + environment[name] = value.rstrip() + + auth_key = 'HTTP_AUTHORIZATION' + if environment.has_key(auth_key): + environment[auth_key] = auth_header(environment[auth_key]) + + old_site = getSite() + setSite(None) + + request_cls, publication_cls = self.chooseRequestClass(method, path, + environment) + app = FunctionalTestSetup().getApplication() + + request = app._request( + path, instream, + environment=environment, + request=request_cls, publication=publication_cls) + if ISkinnable.providedBy(request): + # only ISkinnable requests have skins + setDefaultSkin(request) + + if form is not None: + if request.form: + raise ValueError("only one set of form values can be provided") + request.form = form + + request = publish(request, handle_errors=handle_errors) + + response = ResponseWrapper( + request.response, path, + omit=('x-content-type-warning', 'x-powered-by'), + ) + + self.saveCookies(response) + setSite(old_site) + + # sync Python connection: + getRootFolder()._p_jar.sync() + + return response + + def chooseRequestClass(self, method, path, environment): + """Choose and return a request class and a publication class""" + # note that `path` is unused by the default implementation (BBB) + return chooseClasses(method, environment) + + +def FunctionalDocFileSuite(*paths, **kw): + """Build a functional test suite from a text file.""" + kw['package'] = doctest._normalize_module(kw.get('package')) + _prepare_doctest_keywords(kw) + suite = doctest.DocFileSuite(*paths, **kw) + suite.layer = Functional + return suite + + +def FunctionalDocTestSuite(*paths, **kw): + """Build a functional test suite from docstrings in a module.""" + _prepare_doctest_keywords(kw) + suite = doctest.DocTestSuite(*paths, **kw) + suite.layer = Functional + return suite + + +def _prepare_doctest_keywords(kw): + globs = kw.setdefault('globs', {}) + globs['getRootFolder'] = getRootFolder + globs['sync'] = sync + + kwsetUp = kw.get('setUp') + def setUp(test): + test.globs['http'] = HTTPCaller() + FunctionalTestSetup().setUp() + if kwsetUp is not None: + kwsetUp(test) + kw['setUp'] = setUp + + kwtearDown = kw.get('tearDown') + def tearDown(test): + if kwtearDown is not None: + kwtearDown(test) + FunctionalTestSetup().tearDown() + kw['tearDown'] = tearDown + + if 'optionflags' not in kw: + old = doctest.set_unittest_reportflags(0) + doctest.set_unittest_reportflags(old) + kw['optionflags'] = (old + | doctest.ELLIPSIS + | doctest.REPORT_NDIFF + | doctest.NORMALIZE_WHITESPACE) + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/app/testing/__init__.py zope3-3.5~bzr18/src/zope/app/testing/__init__.py --- zope3-3.4.0/src/zope/app/testing/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Make directory a Python package. diff -Nru zope3-3.4.0/src/zope/app/testing/placelesssetup.py zope3-3.5~bzr18/src/zope/app/testing/placelesssetup.py --- zope3-3.4.0/src/zope/app/testing/placelesssetup.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/placelesssetup.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,64 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Unit test logic for setting up and tearing down basic infrastructure + +$Id: placelesssetup.py 110724 2010-04-11 00:04:11Z tseaver $ +""" +from zope.schema.vocabulary import setVocabularyRegistry +from zope.component.testing import PlacelessSetup as CAPlacelessSetup +from zope.component.eventtesting import PlacelessSetup as EventPlacelessSetup +from zope.i18n.testing import PlacelessSetup as I18nPlacelessSetup +from zope.password.testing import setUpPasswordManagers +from zope.traversing.browser.interfaces import IAbsoluteURL +from zope.traversing.browser.absoluteurl import AbsoluteURL + +from zope.app.testing import ztapi +from zope.container.testing import PlacelessSetup as ContainerPlacelessSetup + +class PlacelessSetup(CAPlacelessSetup, + EventPlacelessSetup, + I18nPlacelessSetup, + ContainerPlacelessSetup): + + def setUp(self, doctesttest=None): + CAPlacelessSetup.setUp(self) + EventPlacelessSetup.setUp(self) + ContainerPlacelessSetup.setUp(self) + I18nPlacelessSetup.setUp(self) + + setUpPasswordManagers() + ztapi.browserView(None, 'absolute_url', AbsoluteURL) + ztapi.browserViewProviding(None, AbsoluteURL, IAbsoluteURL) + + from zope.security.testing import addCheckerPublic + addCheckerPublic() + + from zope.security.management import newInteraction + newInteraction() + + setVocabularyRegistry(None) + + +ps = PlacelessSetup() +setUp = ps.setUp + +def tearDown(): + tearDown_ = ps.tearDown + def tearDown(doctesttest=None): + tearDown_() + return tearDown + +tearDown = tearDown() + +del ps diff -Nru zope3-3.4.0/src/zope/app/testing/recorded/test0001.request zope3-3.5~bzr18/src/zope/app/testing/recorded/test0001.request --- zope3-3.4.0/src/zope/app/testing/recorded/test0001.request 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/recorded/test0001.request 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,81 @@ +GET /@@contents.html HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive + +GET /@@contents.html HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/pl.gif HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/@@contents.html +If-Modified-Since: Thu, 19 Aug 2004 10:10:04 GMT +Authorization: Basic bWdyOm1ncnB3 + +GET /@@singleBranchTree.xml HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/mi.gif HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/ +Authorization: Basic bWdyOm1ncnB3 + +GET /++etc++site/@@manage HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/ +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/site_management.css HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/css,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/++etc++site/@@tasks.html +Authorization: Basic bWdyOm1ncnB3 + Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/app/testing/recorded/test0001.response and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/app/testing/recorded/test0001.response differ diff -Nru zope3-3.4.0/src/zope/app/testing/recorded/test0002.request zope3-3.5~bzr18/src/zope/app/testing/recorded/test0002.request --- zope3-3.4.0/src/zope/app/testing/recorded/test0002.request 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/recorded/test0002.request 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,145 @@ +GET /@@/zope3.css HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/css,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/@@contents.html +If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/onlinehelp.js HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: */* +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/@@contents.html +If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/xmltree.js HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: */* +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/@@contents.html +If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/favicon.png HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/zope3logo.gif HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/@@contents.html +If-Modified-Since: Thu, 19 Aug 2004 10:10:04 GMT +Authorization: Basic bWdyOm1ncnB3 + +GET /@@singleBranchTree.xml HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/zope-app-folder-interfaces-IFolder-zmi_icon.gif HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/@@contents.html +If-Modified-Since: Thu, 19 Aug 2004 10:10:08 GMT +Authorization: Basic bWdyOm1ncnB3 + +GET / HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Authorization: Basic bWdyOm1ncnB3 + +GET /@@children.xml HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Authorization: Basic bWdyOm1ncnB3 + +GET /@@/zope-app-site-interfaces-ISiteManager-zmi_icon.gif HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/ +Authorization: Basic bWdyOm1ncnB3 + +GET /++etc++site/@@tasks.html HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Referer: http://localhost:8081/ +Authorization: Basic bWdyOm1ncnB3 + +GET /++etc++site/@@singleBranchTree.xml HTTP/1.1 +Host: localhost:8081 +User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114 +Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 300 +Connection: keep-alive +Authorization: Basic bWdyOm1ncnB3 + Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/app/testing/recorded/test0002.response and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/app/testing/recorded/test0002.response differ diff -Nru zope3-3.4.0/src/zope/app/testing/setup.py zope3-3.5~bzr18/src/zope/app/testing/setup.py --- zope3-3.4.0/src/zope/app/testing/setup.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/setup.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,187 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Setting up an environment for testing context-dependent objects + +$Id: setup.py 110724 2010-04-11 00:04:11Z tseaver $ +""" +import zope.component +import zope.traversing.api + +#------------------------------------------------------------------------ +# Annotations +from zope.annotation.attribute import AttributeAnnotations +def setUpAnnotations(): + zope.component.provideAdapter(AttributeAnnotations) + +#------------------------------------------------------------------------ +# Dependencies +from zope.annotation.interfaces import IAttributeAnnotatable +from zope.app.dependable import Dependable +from zope.app.dependable.interfaces import IDependable +def setUpDependable(): + zope.component.provideAdapter(Dependable, (IAttributeAnnotatable,), + IDependable) + +#------------------------------------------------------------------------ +# Traversal +from zope.traversing.interfaces import ITraversable +from zope.container.interfaces import ISimpleReadContainer +from zope.container.traversal import ContainerTraversable +def setUpTraversal(): + from zope.traversing.testing import setUp + setUp() + zope.component.provideAdapter(ContainerTraversable, + (ISimpleReadContainer,), ITraversable) + +#------------------------------------------------------------------------ +# ISiteManager lookup +from zope.site.site import SiteManagerAdapter +from zope.component.interfaces import IComponentLookup +from zope.interface import Interface +def setUpSiteManagerLookup(): + zope.component.provideAdapter(SiteManagerAdapter, (Interface,), + IComponentLookup) + +#------------------------------------------------------------------------ +# Placeful setup +import zope.component.hooks +from zope.app.testing.placelesssetup import setUp as placelessSetUp +from zope.app.testing.placelesssetup import tearDown as placelessTearDown +def placefulSetUp(site=False): + placelessSetUp() + zope.component.hooks.setHooks() + setUpAnnotations() + setUpDependable() + setUpTraversal() + setUpSiteManagerLookup() + + if site: + site = rootFolder() + createSiteManager(site, setsite=True) + return site + +def placefulTearDown(): + placelessTearDown() + zope.component.hooks.resetHooks() + zope.component.hooks.setSite() + +#------------------------------------------------------------------------ +# Sample Folder Creation +from zope.site.folder import Folder, rootFolder +def buildSampleFolderTree(): + # set up a reasonably complex folder structure + # + # ____________ rootFolder ______________________________ + # / \ \ + # folder1 __________________ folder2 folder3 + # | \ | | + # folder1_1 ____ folder1_2 folder2_1 folder3_1 + # | \ | | + # folder1_1_1 folder1_1_2 folder1_2_1 folder2_1_1 + + root = rootFolder() + root[u'folder1'] = Folder() + root[u'folder1'][u'folder1_1'] = Folder() + root[u'folder1'][u'folder1_1'][u'folder1_1_1'] = Folder() + root[u'folder1'][u'folder1_1'][u'folder1_1_2'] = Folder() + root[u'folder1'][u'folder1_2'] = Folder() + root[u'folder1'][u'folder1_2'][u'folder1_2_1'] = Folder() + root[u'folder2'] = Folder() + root[u'folder2'][u'folder2_1'] = Folder() + root[u'folder2'][u'folder2_1'][u'folder2_1_1'] = Folder() + root[u"\N{CYRILLIC SMALL LETTER PE}" + u"\N{CYRILLIC SMALL LETTER A}" + u"\N{CYRILLIC SMALL LETTER PE}" + u"\N{CYRILLIC SMALL LETTER KA}" + u"\N{CYRILLIC SMALL LETTER A}3"] = Folder() + root[u"\N{CYRILLIC SMALL LETTER PE}" + u"\N{CYRILLIC SMALL LETTER A}" + u"\N{CYRILLIC SMALL LETTER PE}" + u"\N{CYRILLIC SMALL LETTER KA}" + u"\N{CYRILLIC SMALL LETTER A}3"][ + u"\N{CYRILLIC SMALL LETTER PE}" + u"\N{CYRILLIC SMALL LETTER A}" + u"\N{CYRILLIC SMALL LETTER PE}" + u"\N{CYRILLIC SMALL LETTER KA}" + u"\N{CYRILLIC SMALL LETTER A}3_1"] = Folder() + + return root + + +#------------------------------------------------------------------------ +# Sample Folder Creation +from zope.site.site import LocalSiteManager +import zope.component.interfaces + +def createSiteManager(folder, setsite=False): + if not zope.component.interfaces.ISite.providedBy(folder): + folder.setSiteManager(LocalSiteManager(folder)) + if setsite: + zope.component.hooks.setSite(folder) + return zope.traversing.api.traverse(folder, "++etc++site") + + +#------------------------------------------------------------------------ +# Local Utility Addition +def addUtility(sitemanager, name, iface, utility, suffix=''): + """Add a utility to a site manager + + This helper function is useful for tests that need to set up utilities. + """ + folder_name = (name or (iface.__name__ + 'Utility')) + suffix + default = sitemanager['default'] + default[folder_name] = utility + utility = default[folder_name] + sitemanager.registerUtility(utility, iface, name) + return utility + + +#------------------------------------------------------------------------ +# Setup of test text files as modules +import sys + +# Evil hack to make pickling work with classes defined in doc tests +class NoCopyDict(dict): + def copy(self): + return self + +class FakeModule: + """A fake module.""" + + def __init__(self, dict): + self.__dict = dict + + def __getattr__(self, name): + try: + return self.__dict[name] + except KeyError: + raise AttributeError(name) + + +def setUpTestAsModule(test, name=None): + if name is None: + if test.globs.has_key('__name__'): + name = test.globs['__name__'] + else: + name = test.globs.name + + test.globs['__name__'] = name + test.globs = NoCopyDict(test.globs) + sys.modules[name] = FakeModule(test.globs) + + +def tearDownTestAsModule(test): + del sys.modules[test.globs['__name__']] + test.globs.clear() + diff -Nru zope3-3.4.0/src/zope/app/testing/testing.py zope3-3.5~bzr18/src/zope/app/testing/testing.py --- zope3-3.4.0/src/zope/app/testing/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,55 @@ +############################################################################## +# +# Copyright (c) 2007 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.app.testing common test related classes/functions/objects. + +$Id: testing.py 110724 2010-04-11 00:04:11Z tseaver $ +""" + +__docformat__ = "reStructuredText" + +import os +from ZODB.POSException import ConflictError +from zope import interface +from zope import component +import zope.publisher.interfaces.browser +from zope.app.testing.functional import ZCMLLayer + +AppTestingLayer = ZCMLLayer( + os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'), + __name__, 'AppTestingLayer', allow_teardown=True) + + +class IFailingKlass(interface.Interface): + pass + +class FailingKlass(object): + interface.implements(IFailingKlass) + + +class ConflictRaisingView(object): + __used_for__ = IFailingKlass + + interface.implements(zope.publisher.interfaces.browser.IBrowserPublisher) + component.adapts(interface.Interface, + zope.publisher.interfaces.browser.IBrowserRequest) + + + def __init__(self, context, request): + pass + + def browserDefault(self, *_): + return self, () + + def __call__(self): + raise ConflictError diff -Nru zope3-3.4.0/src/zope/app/testing/tests.py zope3-3.5~bzr18/src/zope/app/testing/tests.py --- zope3-3.4.0/src/zope/app/testing/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,630 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 tcpdoc + +$Id: tests.py 110724 2010-04-11 00:04:11Z tseaver $ +""" +from doctest import DocTestSuite +import os +import re +import unittest +import StringIO + +from zope.testing.renormalizing import RENormalizing +from zope.component import getAllUtilitiesRegisteredFor +from ZODB.interfaces import IDatabase + +import zope.app.testing +from zope.app.publication.requestpublicationregistry import factoryRegistry +from zope.app.publication.requestpublicationfactories import BrowserFactory +from zope.app.testing import functional +from zope.app.testing.dochttp import dochttp +import transaction +from zope.app.testing.functional import SampleFunctionalTest, BrowserTestCase +from zope.app.testing.functional import FunctionalDocFileSuite +from zope.app.testing.functional import FunctionalTestCase +from zope.app.testing.functional import FunctionalTestSetup +from zope.app.testing.testing import AppTestingLayer + +from zope.app.testing.testing import FailingKlass + + + +HEADERS = """\ +HTTP/1.1 200 OK +Content-Type: text/plain +""" + +BODY = """\ +This is the response body. +""" + +here = os.path.dirname(zope.app.testing.__file__) +directory = os.path.join(here, 'recorded') + +expected = r''' + + >>> print http(r""" + ... GET /@@contents.html HTTP/1.1 + ... """) + HTTP/1.1 401 Unauthorized + Content-Length: 89 + Content-Type: text/html;charset=utf-8 + Www-Authenticate: basic realm="Zope" + + + + ... + + + + + + + >>> print http(r""" + ... GET /@@contents.html HTTP/1.1 + ... Authorization: Basic bWdyOm1ncnB3 + ... """) + HTTP/1.1 200 OK + Content-Length: 89 + Content-Type: text/html;charset=utf-8 + + + + ... + + + + + + + >>> print http(r""" + ... GET /++etc++site/@@manage HTTP/1.1 + ... Authorization: Basic bWdyOm1ncnB3 + ... Referer: http://localhost:8081/ + ... """) + HTTP/1.1 303 See Other + Content-Length: 0 + Content-Type: text/plain;charset=utf-8 + Location: @@tasks.html + + + + >>> print http(r""" + ... GET / HTTP/1.1 + ... Authorization: Basic bWdyOm1ncnB3 + ... """) + HTTP/1.1 200 OK + Content-Length: 89 + Content-Type: text/html;charset=utf-8 + + + + ... + + + + + + + >>> print http(r""" + ... GET /++etc++site/@@tasks.html HTTP/1.1 + ... Authorization: Basic bWdyOm1ncnB3 + ... Referer: http://localhost:8081/ + ... """) + HTTP/1.1 200 OK + Content-Length: 89 + Content-Type: text/html;charset=utf-8 + + + + ... + + + + +''' + + +class FunctionalHTTPDocTest(unittest.TestCase): + + def test_dochttp(self): + import sys + old = sys.stdout + sys.stdout = StringIO.StringIO() + dochttp(['-p', 'test', directory]) + got = sys.stdout.getvalue() + sys.stdout = old + self.assertEquals(expected, got) + + +class AuthHeaderTestCase(unittest.TestCase): + + def test_auth_encoded(self): + auth_header = functional.auth_header + header = 'Basic Z2xvYmFsbWdyOmdsb2JhbG1ncnB3' + self.assertEquals(auth_header(header), header) + + def test_auth_non_encoded(self): + auth_header = functional.auth_header + header = 'Basic globalmgr:globalmgrpw' + expected = 'Basic Z2xvYmFsbWdyOmdsb2JhbG1ncnB3' + self.assertEquals(auth_header(header), expected) + + def test_auth_non_encoded_empty(self): + auth_header = functional.auth_header + header = 'Basic globalmgr:' + expected = 'Basic Z2xvYmFsbWdyOg==' + self.assertEquals(auth_header(header), expected) + header = 'Basic :pass' + expected = 'Basic OnBhc3M=' + self.assertEquals(auth_header(header), expected) + + def test_auth_non_encoded_colon(self): + auth_header = zope.app.testing.functional.auth_header + header = 'Basic globalmgr:pass:pass' + expected = 'Basic Z2xvYmFsbWdyOnBhc3M6cGFzcw==' + self.assertEquals(auth_header(header), expected) + + +class HTTPCallerTestCase(unittest.TestCase): + + def test_chooseRequestClass(self): + from zope.publisher.interfaces import IRequest, IPublication + + factoryRegistry.register('GET', '*', 'browser', 0, BrowserFactory()) + + caller = functional.HTTPCaller() + request_class, publication_class = caller.chooseRequestClass( + method='GET', path='/', environment={}) + + self.assert_(IRequest.implementedBy(request_class)) + self.assert_(IPublication.implementedBy(publication_class)) + + +class DummyCookiesResponse(object): + # Ugh, this simulates the *internals* of a HTTPResponse object + # TODO: expand the IHTTPResponse interface to give access to all cookies + _cookies = None + + def __init__(self, cookies=None): + if not cookies: + cookies = {} + self._cookies = cookies + + +class CookieHandlerTestCase(unittest.TestCase): + def setUp(self): + self.handler = functional.CookieHandler() + + def test_saveCookies(self): + response = DummyCookiesResponse(dict( + spam=dict(value='eggs', path='/foo', comment='rest is ignored'), + monty=dict(value='python'))) + self.handler.saveCookies(response) + self.assertEqual(len(self.handler.cookies), 2) + self.assert_(self.handler.cookies['spam'].OutputString() in + ('spam=eggs; Path=/foo;','spam=eggs; Path=/foo')) + self.assert_(self.handler.cookies['monty'].OutputString() in + ('monty=python;','monty=python')) + + def test_httpCookie(self): + cookies = self.handler.cookies + cookies['spam'] = 'eggs' + cookies['spam']['path'] = '/foo' + cookies['bar'] = 'baz' + cookies['bar']['path'] = '/foo/baz' + cookies['monty'] = 'python' + + cookieHeader = self.handler.httpCookie('/foo/bar') + parts = cookieHeader.split('; ') + parts.sort() + self.assertEqual(parts, ['monty=python', 'spam=eggs']) + + cookieHeader = self.handler.httpCookie('/foo/baz') + parts = cookieHeader.split('; ') + parts.sort() + self.assertEqual(parts, ['bar=baz', 'monty=python', 'spam=eggs']) + + # There is no test for CookieHandler.loadCookies because it that method + # only passes the arguments on to Cookie.BaseCookie.load, which the + # standard library has tests for (we hope). + + +class CookieFunctionalTest(BrowserTestCase): + + """Functional tests should handle cookies like a web browser + + Multiple requests in the same test should acumulate cookies. + We also ensure that cookies with path values are only sent for + the correct URL's so we can test cookies don't 'leak'. Expiry, + secure and other cookie attributes are not being worried about + at the moment + + """ + + def setUp(self): + super(CookieFunctionalTest, self).setUp() + self.assertEqual( + len(self.cookies.keys()), 0, + 'cookies store should be empty' + ) + + root = self.getRootFolder() + + from zope.app.zptpage.zptpage import ZPTPage + + page = ZPTPage() + + page.source = u'''''' + + root['getcookies'] = page + + page = ZPTPage() + + page.source = u''' +

+ ''' + + root['setcookie'] = page + transaction.commit() + + def tearDown(self): + root = self.getRootFolder() + del root['getcookies'] + del root['setcookie'] + super(CookieFunctionalTest, self).tearDown() + + def testDefaultCookies(self): + # By default no cookies are set + response = self.publish('/') + self.assertEquals(response.getStatus(), 200) + self.assert_(not response._request._cookies) + + def testSimpleCookies(self): + self.cookies['aid'] = 'aval' + response = self.publish('/') + self.assertEquals(response.getStatus(), 200) + self.assertEquals(response._request._cookies['aid'], 'aval') + + def testCookiePaths(self): + # We only send cookies if the path is correct + self.cookies['aid'] = 'aval' + self.cookies['aid']['Path'] = '/sub/folder' + self.cookies['bid'] = 'bval' + response = self.publish('/') + + self.assertEquals(response.getStatus(), 200) + self.assert_(not response._request._cookies.has_key('aid')) + self.assertEquals(response._request._cookies['bid'], 'bval') + + def testHttpCookieHeader(self): + # Passing an HTTP_COOKIE header to publish adds cookies + response = self.publish('/', env={ + 'HTTP_COOKIE': '$Version=1, aid=aval; $Path=/sub/folder, bid=bval' + }) + self.assertEquals(response.getStatus(), 200) + self.failIf(response._request._cookies.has_key('aid')) + self.assertEquals(response._request._cookies['bid'], 'bval') + + def testStickyCookies(self): + # Cookies should acumulate during the test + response = self.publish('/', env={'HTTP_COOKIE': 'aid=aval;'}) + self.assertEquals(response.getStatus(), 200) + + # Cookies are implicity passed to further requests in this test + response = self.publish('/getcookies') + self.assertEquals(response.getStatus(), 200) + self.assertEquals(response.getBody().strip(), 'aid=aval') + + # And cookies set in responses also acumulate + response = self.publish('/setcookie') + self.assertEquals(response.getStatus(), 200) + response = self.publish('/getcookies') + self.assertEquals(response.getStatus(), 200) + self.assertEquals(response.getBody().strip(), 'aid=aval;bid=bval') + + +class SkinsAndHTTPCaller(FunctionalTestCase): + + def test_skins(self): + # Regression test for http://zope.org/Collectors/Zope3-dev/353 + from zope.app.testing.functional import HTTPCaller + http = HTTPCaller() + response = http("GET /++skin++Basic HTTP/1.1\n\n") + self.assert_("zopetopBasic.css" in str(response)) + +class RetryProblemFunctional(FunctionalTestCase): + + def setUp(self): + super(RetryProblemFunctional, self).setUp() + + root = self.getRootFolder() + + root['fail'] = FailingKlass() + + transaction.commit() + + def tearDown(self): + root = self.getRootFolder() + del root['fail'] + super(RetryProblemFunctional, self).tearDown() + + def test_retryOnConflictErrorFunctional(self): + from zope.app.testing.functional import HTTPCaller + + http = HTTPCaller() + response = http(r""" +GET /@@test-conflict-raise-view.html HTTP/1.1 +Authorization: Basic mgr:mgrpw +""") + + self.assertNotEqual(response.getStatus(), 599) + self.assertEqual(response.getStatus(), 500) + +class RetryProblemBrowser(BrowserTestCase): + def setUp(self): + super(RetryProblemBrowser, self).setUp() + + root = self.getRootFolder() + + root['fail'] = FailingKlass() + + transaction.commit() + + def tearDown(self): + root = self.getRootFolder() + del root['fail'] + super(RetryProblemBrowser, self).tearDown() + + def test_retryOnConflictErrorBrowser(self): + response = self.publish('/@@test-conflict-raise-view.html', + handle_errors=True) + self.assertNotEqual(response.getStatus(), 599) + self.assertEqual(response.getStatus(), 500) + + +ftesting_zcml = os.path.join(here, 'ftesting.zcml') + +def doctest_FunctionalTestSetup_clears_global_utilities(): + """Test that FunctionalTestSetup doesn't leave global utilities. + + Leaving global IDatabase utilities makes a nice juicy memory leak. + See https://bugs.launchpad.net/zope3/+bug/251273 + + This bug has now been fixed and this test exercises the fixed version. + + >>> setup = FunctionalTestSetup(ftesting_zcml) + + At this point, there are registrations for the base databases created by + the initialization: + + >>> base, = getAllUtilitiesRegisteredFor(IDatabase) + + Setting up for a test causes overriding registrations to be made: + + >>> setup.setUp() + >>> dbs = list(getAllUtilitiesRegisteredFor(IDatabase)) + >>> len(dbs) + 1 + >>> base in dbs + False + >>> override, = dbs + + Tearing down the test context causes the overriding database to be + removed: + + >>> setup.tearDown() + >>> list(getAllUtilitiesRegisteredFor(IDatabase)) + [] + + Tearing down completely: + + >>> setup.tearDownCompletely() + """ + + +empty_zcml = os.path.join(here, 'empty.zcml') + +def doctest_FunctionalTestSetup_supports_product_config(): + """Test that FunctionalTestSetup configures products. + + We want to apply the following product configuration before opening + databases: + + >>> product_config = ''' + ... + ... key1 value1 + ... key2 value2 + ... + ... ''' + + Since we expect the product configuration to be available when the layer + is initialized, we'll register a subscriber for the IDatabaseOpenedEvent + event, The normal CA-provided handling of the event is of no use to use, + since the functional layer controls the configuration of that, but a + low-level zoe.event subscriber will do the job: + + >>> import zope.event + + >>> def handle_database_open(event): + ... global config + ... IDbOE = zope.processlifetime.IDatabaseOpened + ... if IDbOE.providedBy(event): + ... config = zope.app.appsetup.product.getProductConfiguration( + ... 'abc') + + >>> zope.event.subscribers.append(handle_database_open) + + The product configuration is passed to the layer setup and installed by + the setUp method: + + >>> import pprint + >>> import zope.app.appsetup.product + + >>> setup = FunctionalTestSetup( + ... empty_zcml, product_config=product_config) + + The configuration was visible to our database-opened subscriber: + + >>> pprint.pprint(config, width=1) + {'key1': 'value1', + 'key2': 'value2'} + + >>> config = zope.app.appsetup.product.getProductConfiguration( + ... 'abc') + >>> pprint.pprint(config, width=1) + {'key1': 'value1', + 'key2': 'value2'} + + Let's run a test that mutates the product configuration: + + >>> setup.setUp() + >>> zope.app.appsetup.product.setProductConfiguration( + ... 'abc', {'another': 'value'}) + >>> zope.app.appsetup.product.getProductConfiguration('abc') + {'another': 'value'} + >>> setup.tearDown() + + A second test run in the layer sees the original product configuration: + + >>> setup.setUp() + >>> config = zope.app.appsetup.product.getProductConfiguration( + ... 'abc') + >>> pprint.pprint(config, width=1) + {'key1': 'value1', + 'key2': 'value2'} + >>> setup.tearDown() + + After the layer is cleaned up, there's no longer any product + configuration: + + >>> zope.event.subscribers.remove(handle_database_open) + >>> setup.tearDownCompletely() + + >>> zope.app.appsetup.product.saveConfiguration() + {} + + """ + + +def doctest_ZCMLLayer_carries_product_configuration(): + """Show that ``ZCMLLayer`` carries along product configuration. + + ZCML layers can carry be defined to work with specific product + configuration; this is useful when application code (early subscribers, + including generations) need configuration data. + + Let's define a couple of separate ZCML layers, and show that the + configuration data is properly associated with each, and applied at + appropriate times. + + We'll need two distinct product configurations: + + >>> product_config_one = ''' + ... + ... key1 a1 + ... key2 a2 + ... + ... ''' + + >>> product_config_two = ''' + ... + ... key1 b1 + ... key2 b2 + ... + ... ''' + + We can create two distinct layers that use these configurations: + + >>> LayerOne = functional.ZCMLLayer( + ... empty_zcml, 'zope.app.testing.tests', 'LayerOne', + ... product_config=product_config_one, + ... allow_teardown=True) + + >>> LayerTwo = functional.ZCMLLayer( + ... empty_zcml, 'zope.app.testing.tests', 'LayerTwo', + ... product_config=product_config_two, + ... allow_teardown=True) + + For each layer, we can see that the correct product configuration is + installed, and subsequent layer usages won't have problems because of the + previously installed layer. This checks that initialization and + deconstruction of the functional test setup is handled properly to allow + layers to be used in sequence. + + Let's use a helper function to show the configuration: + + >>> import pprint + + >>> def show_config(): + ... c = zope.app.appsetup.product.getProductConfiguration('abc') + ... pprint.pprint(c, width=1) + + >>> LayerOne.setUp() + >>> show_config() + {'key1': 'a1', + 'key2': 'a2'} + + >>> LayerOne.tearDown() + + >>> LayerTwo.setUp() + >>> show_config() + {'key1': 'b1', + 'key2': 'b2'} + + >>> LayerTwo.tearDown() + + """ + + +def test_suite(): + checker = RENormalizing([ + (re.compile(r'^HTTP/1.1 (\d{3}) .*?\n'), 'HTTP/1.1 \\1\n') + ]) + SampleFunctionalTest.layer = AppTestingLayer + CookieFunctionalTest.layer = AppTestingLayer + SkinsAndHTTPCaller.layer = AppTestingLayer + RetryProblemFunctional.layer = AppTestingLayer + RetryProblemBrowser.layer = AppTestingLayer + + doc_test = FunctionalDocFileSuite('doctest.txt', 'cookieTestOne.txt', + 'cookieTestTwo.txt', checker=checker) + doc_test.layer = AppTestingLayer + + return unittest.TestSuite(( + unittest.makeSuite(FunctionalHTTPDocTest), + unittest.makeSuite(AuthHeaderTestCase), + unittest.makeSuite(HTTPCallerTestCase), + unittest.makeSuite(CookieHandlerTestCase), + DocTestSuite(), + unittest.makeSuite(SampleFunctionalTest), + unittest.makeSuite(CookieFunctionalTest), + unittest.makeSuite(SkinsAndHTTPCaller), + unittest.makeSuite(RetryProblemFunctional), + unittest.makeSuite(RetryProblemBrowser), + doc_test, + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/app/testing/xmlrpc.py zope3-3.5~bzr18/src/zope/app/testing/xmlrpc.py --- zope3-3.4.0/src/zope/app/testing/xmlrpc.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/xmlrpc.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,76 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""XMLRPC testing helpers for Zope 3. + +$Id: xmlrpc.py 110724 2010-04-11 00:04:11Z tseaver $ +""" + +import StringIO +import xmlrpclib + +from zope.app.testing.functional import HTTPCaller + + +class ZopeTestTransport(xmlrpclib.Transport): + """xmlrpclib transport that delegates to + zope.app.testing.functional.HTTPCaller. + + It can be used like a normal transport, including support for basic + authentication. + """ + + verbose = False + handleErrors = True + + def request(self, host, handler, request_body, verbose=0): + request = "POST %s HTTP/1.0\n" % (handler,) + request += "Content-Length: %i\n" % len(request_body) + request += "Content-Type: text/xml\n" + + host, extra_headers, x509 = self.get_host_info(host) + if extra_headers: + request += "Authorization: %s\n" % ( + dict(extra_headers)["Authorization"],) + + request += "\n" + request_body + response = HTTPCaller()(request, handle_errors=self.handleErrors) + + errcode = response.getStatus() + errmsg = response.getStatusString() + # This is not the same way that the normal transport deals with the + # headers. + headers = response.getHeaders() + + if errcode != 200: + raise xmlrpclib.ProtocolError( + host + handler, + errcode, errmsg, + headers + ) + + return self._parse_response( + StringIO.StringIO(response.getBody()), sock=None) + + +def ServerProxy(uri, transport=None, encoding=None, + verbose=0, allow_none=0, handleErrors=True): + """A factory that creates a server proxy using the ZopeTestTransport + by default. + + """ + if transport is None: + transport = ZopeTestTransport() + if isinstance(transport, ZopeTestTransport): + transport.handleErrors = handleErrors + return xmlrpclib.ServerProxy(uri, transport, encoding, verbose, allow_none) diff -Nru zope3-3.4.0/src/zope/app/testing/ztapi.py zope3-3.5~bzr18/src/zope/app/testing/ztapi.py --- zope3-3.4.0/src/zope/app/testing/ztapi.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/testing/ztapi.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,98 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Testing helper functions + +$Id: ztapi.py 110724 2010-04-11 00:04:11Z tseaver $ +""" +import zope.interface +import zope.component +from zope.publisher.browser import IBrowserRequest +from zope.publisher.interfaces import IDefaultViewName +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.traversing.interfaces import ITraversable + +def provideView(for_, type, providing, name, factory, layer=None): + if layer is None: + layer = type + provideAdapter(for_, providing, factory, name, (layer,)) + +def provideMultiView(for_, type, providing, name, factory, layer=None): + if layer is None: + layer = type + provideAdapter(for_[0], providing, factory, name, tuple(for_[1:])+(layer,)) + +def browserView(for_, name, factory, layer=IDefaultBrowserLayer, + providing=zope.interface.Interface): + """Define a global browser view + """ + if isinstance(factory, (list, tuple)): + raise ValueError("Factory cannot be a list or tuple") + provideAdapter(for_, providing, factory, name, (layer,)) + +def browserViewProviding(for_, factory, providing, layer=IDefaultBrowserLayer): + """Define a view providing a particular interface.""" + if isinstance(factory, (list, tuple)): + raise ValueError("Factory cannot be a list or tuple") + return browserView(for_, '', factory, layer, providing) + +def browserResource(name, factory, layer=IDefaultBrowserLayer, + providing=zope.interface.Interface): + """Define a global browser view + """ + if isinstance(factory, (list, tuple)): + raise ValueError("Factory cannot be a list or tuple") + provideAdapter((layer,), providing, factory, name) + +def setDefaultViewName(for_, name, layer=IDefaultBrowserLayer, + type=IBrowserRequest): + if layer is None: + layer = type + gsm = zope.component.getGlobalSiteManager() + gsm.registerAdapter(name, (for_, layer), IDefaultViewName, '') + +stypes = list, tuple +def provideAdapter(required, provided, factory, name='', with_=(), **kw): + if with_ is None and kw.has_key('with'): + with_ = kw['with'] + if isinstance(factory, (list, tuple)): + raise ValueError("Factory cannot be a list or tuple") + gsm = zope.component.getGlobalSiteManager() + + if with_: + required = (required, ) + tuple(with_) + elif not isinstance(required, stypes): + required = (required,) + + gsm.registerAdapter(factory, required, provided, name, event=False) + +def subscribe(required, provided, factory): + gsm = zope.component.getGlobalSiteManager() + if provided is None: + gsm.registerHandler(factory, required, event=False) + else: + gsm.registerSubscriptionAdapter(factory, required, provided, + event=False) + + +def provideUtility(provided, component, name=''): + gsm = zope.component.getGlobalSiteManager() + gsm.registerUtility(component, provided, name, event=False) + +def unprovideUtility(provided, name=''): + gsm = zope.component.getGlobalSiteManager() + gsm.unregisterUtility(provided=provided, name=name) + +def provideNamespaceHandler(name, handler): + provideAdapter(None, ITraversable, handler, name=name) + provideView(None, None, ITraversable, name, handler) diff -Nru zope3-3.4.0/src/zope/app/wsgi/configure.zcml zope3-3.5~bzr18/src/zope/app/wsgi/configure.zcml --- zope3-3.4.0/src/zope/app/wsgi/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,5 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/app/wsgi/fileresult.py zope3-3.5~bzr18/src/zope/app/wsgi/fileresult.py --- zope3-3.4.0/src/zope/app/wsgi/fileresult.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/fileresult.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,74 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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. +# +############################################################################## +"""IResult adapters for files. + +$Id: fileresult.py 74219 2007-04-17 21:45:57Z gary $ +""" + +import tempfile +from zope import component, interface +import zope.publisher.interfaces.http +import zope.publisher.http +from zope.publisher.interfaces.http import IResult +from zope.security.proxy import removeSecurityProxy + +class FallbackWrapper: + interface.implements(IResult) + + def __init__(self, f): + self.close = f.close + self._file = f + + def __iter__(self): + f = self._file + while 1: + v = f.read(32768) + if v: + yield v + else: + break + +@component.adapter(file, zope.publisher.interfaces.http.IHTTPRequest) +@interface.implementer(zope.publisher.http.IResult) +def FileResult(f, request): + f = removeSecurityProxy(f) + if request.response.getHeader('content-length') is None: + f.seek(0, 2) + size = f.tell() + f.seek(0) + request.response.setHeader('Content-Length', str(size)) + + wrapper = request.environment.get('wsgi.file_wrapper') + if wrapper is not None: + f = wrapper(f) + else: + f = FallbackWrapper(f) + return f + +# We need to provide an adapter for temporary files *if* they are different +# than regular files. Whether they are is system dependent. Sigh. +# If temporary files are the same type, we'll create a fake type just +# to make the registration work. +_tfile = tempfile.TemporaryFile() +_tfile.close() +_tfile = _tfile.__class__ +if _tfile is file: + # need a fake one. Sigh + class _tfile: + pass + +@component.adapter(_tfile, zope.publisher.interfaces.http.IHTTPRequest) +@interface.implementer(zope.publisher.http.IResult) +def TemporaryFileResult(f, request): + return FileResult(f, request) diff -Nru zope3-3.4.0/src/zope/app/wsgi/fileresult.txt zope3-3.5~bzr18/src/zope/app/wsgi/fileresult.txt --- zope3-3.4.0/src/zope/app/wsgi/fileresult.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/fileresult.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,100 @@ +File results +============ + +The file results adapters provide adapters from Python file objects to +and from temporary file objects to zope.publisher.interfaces.http.IResult. +They also have the property that they can handle security proxied files +and unproxy them in the result. Finally, if the request has a +wsgi.file_wrapper environment variable, then that is used to wrap the +file in the result. + +Lets look at an example with a regular file object: + + >>> from zope import component + >>> import zope.app.wsgi.fileresult + >>> component.provideAdapter(zope.app.wsgi.fileresult.FileResult) + >>> component.provideAdapter(zope.app.wsgi.fileresult.TemporaryFileResult) + + >>> import tempfile + >>> dir = tempfile.mkdtemp() + >>> import os + >>> f = open(os.path.join(dir, 'f'), 'w+b') + >>> f.write('One\nTwo\nThree\nHa ha! I love to count!\n') + >>> from zope.security.checker import ProxyFactory + >>> from zope.publisher.interfaces.http import IResult + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + >>> result = component.getMultiAdapter((ProxyFactory(f), request), IResult) + >>> for line in result: + ... print line + One + Two + Three + Ha ha! I love to count! + + >>> request.response.getHeader('content-length') + '38' + + +We'll see something similar with a temporary file: + + >>> t = tempfile.TemporaryFile() + >>> t.write('Three\nTwo\nOne\nHa ha! I love to count down!\n') + >>> request = TestRequest() + >>> result = component.getMultiAdapter((ProxyFactory(t), request), IResult) + >>> for line in result: + ... print line + Three + Two + One + Ha ha! I love to count down! + + >>> request.response.getHeader('content-length') + '43' + + +If we provide a custom file wrapper: + + >>> class Wrapper: + ... def __init__(self, file): + ... self.file = file + + >>> request = TestRequest(environ={'wsgi.file_wrapper': Wrapper}) + >>> result = component.getMultiAdapter((ProxyFactory(f), request), IResult) + >>> result.__class__ is Wrapper + True + >>> result.file is f + True + + >>> request.response.getHeader('content-length') + '38' + + >>> request = TestRequest(environ={'wsgi.file_wrapper': Wrapper}) + >>> result = component.getMultiAdapter((ProxyFactory(t), request), IResult) + >>> result.__class__ is Wrapper + True + >>> result.file is t + True + + >>> request.response.getHeader('content-length') + '43' + +Normally, the file given to FileResult must be seekable and the entire +file is used. The adapters figure out the file size to determine a +content length and seek to the beginning of the file. + +You can suppress this behavior by setting the content length yourself: + + >>> request = TestRequest() + >>> request.response.setHeader('content-length', '10') + >>> f.seek(7) + >>> result = component.getMultiAdapter((ProxyFactory(t), request), IResult) + >>> print f.tell() + 7 + + >>> request.response.getHeader('content-length') + '10' + +Note that you should really only use file returns for large results. +Files use file descriptors which can be somewhat scarce resources on +some systems. Only use them when you need them. diff -Nru zope3-3.4.0/src/zope/app/wsgi/ftesting.zcml zope3-3.5~bzr18/src/zope/app/wsgi/ftesting.zcml --- zope3-3.4.0/src/zope/app/wsgi/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/app/wsgi/__init__.py zope3-3.5~bzr18/src/zope/app/wsgi/__init__.py --- zope3-3.4.0/src/zope/app/wsgi/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,168 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""A WSGI Application wrapper for zope + +$Id: __init__.py 111330 2010-04-24 08:04:00Z ctheune $ +""" +import os +import sys +import logging +import ZConfig +import zope.processlifetime +import zope.app.appsetup.product + +from zope.event import notify +from zope.interface import implements +from zope.publisher.publish import publish +from zope.publisher.interfaces.logginginfo import ILoggingInfo + +from zope.app.appsetup import appsetup +from zope.app.publication.httpfactory import HTTPPublicationRequestFactory +from zope.app.wsgi import interfaces + + +class WSGIPublisherApplication(object): + """A WSGI application implementation for the zope publisher + + Instances of this class can be used as a WSGI application object. + + The class relies on a properly initialized request factory. + """ + implements(interfaces.IWSGIApplication) + + def __init__(self, db=None, factory=HTTPPublicationRequestFactory, + handle_errors=True): + self.requestFactory = None + self.handleErrors = handle_errors + + if db is not None: + self.requestFactory = factory(db) + + def __call__(self, environ, start_response): + """See zope.app.wsgi.interfaces.IWSGIApplication""" + request = self.requestFactory(environ['wsgi.input'], environ) + + # Let's support post-mortem debugging + handle_errors = environ.get('wsgi.handleErrors', self.handleErrors) + + request = publish(request, handle_errors=handle_errors) + response = request.response + # Get logging info from principal for log use + logging_info = ILoggingInfo(request.principal, None) + if logging_info is None: + message = '-' + else: + message = logging_info.getLogMessage() + environ['wsgi.logging_info'] = message + + # Start the WSGI server response + start_response(response.getStatusString(), response.getHeaders()) + + # Return the result body iterable. + return response.consumeBodyIter() + + +class PMDBWSGIPublisherApplication(WSGIPublisherApplication): + + def __init__(self, db=None, factory=HTTPPublicationRequestFactory, + handle_errors=False): + super(PMDBWSGIPublisherApplication, self).__init__(db, factory, + handle_errors) + + def __call__(self, environ, start_response): + environ['wsgi.handleErrors'] = self.handleErrors + + # Call the application to handle the request and write a response + try: + app = super(PMDBWSGIPublisherApplication, self) + return app.__call__(environ, start_response) + except Exception, error: + import sys + import pdb + print "%s:" % sys.exc_info()[0] + print sys.exc_info()[1] + try: + pdb.post_mortem(sys.exc_info()[2]) + raise + finally: + pass + + +def config(configfile, schemafile=None, features=()): + # Load the configuration schema + if schemafile is None: + schemafile = os.path.join( + os.path.dirname(appsetup.__file__), 'schema', 'schema.xml') + + # Let's support both, an opened file and path + if isinstance(schemafile, basestring): + schema = ZConfig.loadSchema(schemafile) + else: + schema = ZConfig.loadSchemaFile(schemafile) + + # Load the configuration file + # Let's support both, an opened file and path + try: + if isinstance(configfile, basestring): + options, handlers = ZConfig.loadConfig(schema, configfile) + else: + options, handlers = ZConfig.loadConfigFile(schema, configfile) + except ZConfig.ConfigurationError, msg: + sys.stderr.write("Error: %s\n" % str(msg)) + sys.exit(2) + + # Insert all specified Python paths + if options.path: + sys.path[:0] = [os.path.abspath(p) for p in options.path] + + # Parse product configs + zope.app.appsetup.product.setProductConfigurations( + options.product_config) + + # Setup the event log + options.eventlog() + + # Setup other defined loggers + for logger in options.loggers: + logger() + + # Insert the devmode feature, if turned on + if options.devmode: + features += ('devmode',) + logging.warning("Developer mode is enabled: this is a security risk " + "and should NOT be enabled on production servers. Developer mode " + "can usually be turned off by setting the `devmode` option to " + "`off` or by removing it from the instance configuration file " + "completely.") + + # Execute the ZCML configuration. + appsetup.config(options.site_definition, features=features) + + # Connect to and open the database, notify subscribers. + db = appsetup.multi_database(options.databases)[0][0] + notify(zope.processlifetime.DatabaseOpened(db)) + + return db + + +def getWSGIApplication(configfile, schemafile=None, features=(), + requestFactory=HTTPPublicationRequestFactory, + handle_errors=True): + db = config(configfile, schemafile, features) + application = WSGIPublisherApplication(db, requestFactory, handle_errors) + + # Create the application, notify subscribers. + notify(interfaces.WSGIPublisherApplicationCreated(application)) + + return application diff -Nru zope3-3.4.0/src/zope/app/wsgi/interfaces.py zope3-3.5~bzr18/src/zope/app/wsgi/interfaces.py --- zope3-3.4.0/src/zope/app/wsgi/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,87 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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. +# +############################################################################## +"""WSGI-specific and compatible interfaces + +See PEP-0333 for details. + +$Id: interfaces.py 82663 2008-01-03 22:52:56Z yusei $ +""" +__docformat__ = "reStructuredText" + +import zope.interface +from zope.publisher.interfaces.http import IHeaderOutput + + +class IWSGIOutput(IHeaderOutput): + """This class handles the output generated by the publisher. It is used to + collect the headers and as an outstream to output the response body. + """ + + def getHeaders(): + """Return the response headers. + + The headers will be returned as a list of tuples according to the WSGI + specification. + """ + + def write(data): + """Write the response to the server. + + If the reponse has not begun, call the WSGI server's + ``start_response()`` callable to begin the response. + """ + +class IWSGIApplication(zope.interface.Interface): + """A WSGI application.""" + + def __call__(environ, start_response): + """Called by a WSGI server. + + The ``environ`` parameter is a dictionary object, containing CGI-style + environment variables. This object must be a builtin Python dictionary + (not a subclass, UserDict or other dictionary emulation), and the + application is allowed to modify the dictionary in any way it + desires. The dictionary must also include certain WSGI-required + variables (described in a later section), and may also include + server-specific extension variables, named according to a convention + that will be described below. + + The ``start_response`` parameter is a callable accepting two required + positional arguments, and one optional argument. For the sake of + illustration, we have named these arguments ``status``, + ``response_headers``, and ``exc_info``, but they are not required to + have these names, and the application must invoke the + ``start_response`` callable using positional arguments + (e.g. ``start_response(status, response_headers)``). + """ + + +class IWSGIServer(zope.interface.Interface): + """A WSGI server.""" + + def set_application(app): + """Tells the server about the application to use.""" + + +class IWSGIPublisherApplicationCreatedEvent(zope.interface.Interface): + """A WSGI application has been created.""" + + application = zope.interface.Attribute("The WSGI application.") + + +class WSGIPublisherApplicationCreated(object): + zope.interface.implements(IWSGIPublisherApplicationCreatedEvent) + + def __init__(self, application): + self.application = application diff -Nru zope3-3.4.0/src/zope/app/wsgi/paste.py zope3-3.5~bzr18/src/zope/app/wsgi/paste.py --- zope3-3.4.0/src/zope/app/wsgi/paste.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/paste.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,31 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""An application factory for Paste + +$Id: paste.py 96355 2009-02-10 00:16:42Z nadako $ +""" +from zope.app.wsgi import getWSGIApplication + +def asbool(obj): + if isinstance(obj, basestring): + obj = obj.lower() + if obj in ('1', 'true', 'yes', 't', 'y'): + return True + if obj in ('0', 'false', 'no', 'f', 'n'): + return False + return bool(obj) + +def ZopeApplication(global_config, config_file, handle_errors=True, **options): + handle_errors = asbool(handle_errors) + return getWSGIApplication(config_file, handle_errors=handle_errors) diff -Nru zope3-3.4.0/src/zope/app/wsgi/paste.txt zope3-3.5~bzr18/src/zope/app/wsgi/paste.txt --- zope3-3.4.0/src/zope/app/wsgi/paste.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/paste.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,87 @@ +================================================== +Zope WSGI application integration with PasteDeploy +================================================== + +PasteDeploy provides a framework for defining WSGI component factories +that are used for assembling an application using simple configuration +files. The ``zope.app.wsgi.paste`` package provides an paste application +factory for the Zope WSGI application server, created using the +configuration file, typically the ``zope.conf``. + +To use in Paste, you include a configuration section like this:: + + [app:main] + use = egg:zope.app.wsgi + config_file = %(here)s/zope.conf + +This example defines a "main" application using the zope.app.wsgi +factory. The only option that is required by zope.app.wsgi is the +path to a configuration file. That file typically defines a path to a +site definition file (the ``site.zcml``) and things like eventlog +configuration or enabling developer mode. + +The factory also accepts the ``handle_errors`` boolean argument. +It's useful, when you don't want Zope application to handle exceptions +and want it to propagate them to upper WSGI middlewares. + +The application factory only creates the WSGI application using the +``zope.app.wsgi.getWSGIApplication`` function. So we don't test it +here. Instead, we'll only examine the Paste application factory +provided by ``zope.app.wsgi.paste.ZopeApplication``. + +Let's create testing site.zcml and zope.conf file. + + >>> import os, tempfile + >>> temp_dir = tempfile.mkdtemp() + >>> sitezcml = os.path.join(temp_dir, 'site.zcml') + >>> open(sitezcml, 'w').write('') + >>> zopeconf = os.path.join(temp_dir, 'zope.conf') + >>> open(zopeconf, 'w').write(''' + ... site-definition %s + ... + ... + ... + ... + ... + ... + ... + ... path STDOUT + ... + ... + ... ''' % sitezcml) + +Let's end any security interaction we have, as application will try to +create new interaction for its tasks. + + >>> from zope.security import management + >>> if management.queryInteraction(): management.endInteraction() + +Now, let's create the application using our application factory as +Paste does. + + >>> from zope.app.wsgi.paste import ZopeApplication + >>> app = ZopeApplication({}, zopeconf) + + >>> app + + >>> app.handleErrors + True + +We can also specify handle_errors as false using boolean or strings: + + >>> app = ZopeApplication({}, zopeconf, handle_errors=False) + >>> app.handleErrors + False + + >>> for v in ('0', 'false', 'no', 'f', 'n'): + ... print ZopeApplication({}, zopeconf, handle_errors=v).handleErrors + False + False + False + False + False + +Okay, remove the temporary files. + + >>> import shutil + >>> shutil.rmtree(temp_dir) diff -Nru zope3-3.4.0/src/zope/app/wsgi/README.txt zope3-3.5~bzr18/src/zope/app/wsgi/README.txt --- zope3-3.4.0/src/zope/app/wsgi/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,189 @@ +===================== +Zope WSGI Application +===================== + +This package contains an interpretation of the WSGI specification (PEP-0333) +for the Zope application server by providing a WSGI application object. The +first step is to initialize the WSGI-compliant Zope application that is called +from the server. To do that, we first have to create and open a ZODB +connection: + + >>> from ZODB.MappingStorage import MappingStorage + >>> from ZODB.DB import DB + + >>> storage = MappingStorage('test.db') + >>> db = DB(storage, cache_size=4000) + +We can now initialize the application: + + >>> from zope.app import wsgi + >>> app = wsgi.WSGIPublisherApplication(db) + +The callable ``app`` object accepts two positional arguments, the environment +and the function that initializes the response and returns a function with +which the output data can be written. + +Even though this is commonly done by the server, we now have to create an +appropriate environment for the request. + + >>> import cStringIO + >>> environ = { + ... 'PATH_INFO': '/', + ... 'wsgi.input': cStringIO.StringIO('')} + +Next we create a WSGI-compliant ``start_response()`` method that accepts the +status of the response to the HTTP request and the headers that are part of +the response stream. The headers are expected to be a list of 2-tuples. The +``start_response()`` method must also return a ``write()`` function that +directly writes the output to the server. However, the Zope 3 implementation +will not utilize this function, since it is strongly discouraged by +PEP-0333. The second method of getting data to the server is by returning an +iteratable from the application call. Sp we simply ignore all the arguments +and return ``None`` as the write method. + + >>> def start_response(status, headers): + ... return None + +Now we can send the fabricated HTTP request to the application for processing: + + >>> print ''.join(app(environ, start_response)) + SystemError +

SystemError

+ A server error occurred. + + + +We can see that application really crashed and did not know what to do. This +is okay, since we have not setup anything. Getting a request successfully +processed would require us to bring up a lot of Zope 3's system, which would +be just a little bit too much for this demonstration. + +Now that we have seen the manual way of initializing and using the publisher +application, here is the way it is done using all of Zope 3's setup machinery:: + + from zope.app.server.main import setup, load_options + from zope.app.wsgi import PublisherApp + + # Read all configuration files and bring up the component architecture + args = ["-C/path/to/zope.conf"] + db = setup(load_options(args)) + + # Initialize the WSGI-compliant publisher application with the database + wsgiApplication = PublisherApp(db) + + # Here is an example on how the application could be registered with a + # WSGI-compliant server. Note that the ``setApplication()`` method is not + # part of the PEP 333 specification. + wsgiServer.setApplication(wsgiApplication) + +The code above assumes, that Zope is available on the ``PYTHONPATH``. Note +that you may have to edit ``zope.conf`` to provide an absolute path for +``site.zcml``. Unfortunately we do not have enough information about the +directory structure to make this code a doctest. + +In summary, to use Zope as a WSGI application, the following steps must be +taken: + +* configure and setup Zope + +* an instance of ``zope.app.wsgi.PublisherApp`` must be created with a + refernce to the opened database + +* this application instance must be somehow communicated to the WSGI server, + i.e. by calling a method on the server that sets the application. + + +Access logging +-------------- + +But let's test at least the user info logging feature. We can check the +environ after being sent to the app and also see that a key has been set to +store user names for use in access logs. + +This logging information is provided by an adapter registered for +`ILoggingInfo`. Out-of-the-box, `zope.publisher` registers a base +adapter that returns the principal id as value:: + + >>> print environ + {'wsgi.input': , + 'wsgi.logging_info': 'zope.anybody', 'PATH_INFO': '/'} + + +Creating A WSGI Application +--------------------------- + +We do not always want Zope to control the startup process. People want to be +able to start their favorite server and then register Zope simply as a WSGI +application. For those cases we provide a very high-level function called +``getWSGIApplication()`` that only requires the configuration file to set up +the Zope 3 application server and returns a WSGI application. Here is a simple +example: + + # We have to create our own site definition file -- which will simply be + # empty -- to provide a minimal test. + >>> import os, tempfile + >>> temp_dir = tempfile.mkdtemp() + >>> sitezcml = os.path.join(temp_dir, 'site.zcml') + >>> open(sitezcml, 'w').write('') + + >>> from cStringIO import StringIO + >>> configFile = StringIO(''' + ... site-definition %s + ... + ... + ... + ... + ... + ... + ... + ... path STDOUT + ... + ... + ... + ... + ... key1 val1 + ... + ... ''' %sitezcml) + + +Create an handler for the event. + + >>> import zope.component + >>> from zope.app.wsgi.interfaces import IWSGIPublisherApplicationCreatedEvent + >>> called = [] + + >>> @zope.component.adapter(IWSGIPublisherApplicationCreatedEvent) + ... def handler(event): + ... called.append(event) + + >>> zope.component.provideHandler(handler) + +Create an WSGI application. + + >>> app = wsgi.getWSGIApplication(configFile) + >>> app + + + >>> called[0].application is app + True + +The product configs were parsed: + + >>> import zope.app.appsetup.product as zapp + >>> print zapp.getProductConfiguration('sample') + {'key1': 'val1'} + + >>> import shutil + >>> shutil.rmtree(temp_dir) + + +About WSGI +---------- + +WSGI is the Python Web Server Gateway Interface, a PEP to standardize +the interface between web servers and python applications to promote +portability. + +For more information, refer to the WSGI specification: +http://www.python.org/peps/pep-0333.html + diff -Nru zope3-3.4.0/src/zope/app/wsgi/testing.py zope3-3.5~bzr18/src/zope/app/wsgi/testing.py --- zope3-3.4.0/src/zope/app/wsgi/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation 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.app.wsgi common test related classes/functions/objects. + +$Id: testing.py 110773 2010-04-13 11:49:56Z thefunny42 $ +""" + +__docformat__ = "reStructuredText" + +import zope.app.wsgi +from zope.app.wsgi.testlayer import BrowserLayer + +AppWSGILayer = BrowserLayer(zope.app.wsgi) + diff -Nru zope3-3.4.0/src/zope/app/wsgi/testlayer.py zope3-3.5~bzr18/src/zope/app/wsgi/testlayer.py --- zope3-3.4.0/src/zope/app/wsgi/testlayer.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/testlayer.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,273 @@ +############################################################################## +# +# Copyright (c) 2010 Zope Corporation 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. +# +############################################################################## +from StringIO import StringIO +import re +import base64 +import xmlrpclib + +from transaction import commit +from wsgi_intercept.mechanize_intercept import Browser as BaseInterceptBrowser +from zope.app.appsetup.testlayer import ZODBLayer +from zope.app.publication.httpfactory import HTTPPublicationRequestFactory +from zope.app.wsgi import WSGIPublisherApplication +from zope.testbrowser.browser import Browser as ZopeTestbrowser +import wsgi_intercept + +# List of hostname where the test browser/http function replies to +TEST_HOSTS = ['localhost', '127.0.0.1'] + + +class InterceptBrowser(BaseInterceptBrowser): + + default_schemes = ['http'] + default_others = ['_http_error', + '_http_default_error'] + default_features = ['_redirect', '_cookies', '_referer', '_refresh', + '_equiv', '_basicauth', '_digestauth'] + + +class Browser(ZopeTestbrowser): + """Override the zope.testbrowser.browser.Browser interface so that it + uses PatchedMechanizeBrowser + """ + + def __init__(self, *args, **kwargs): + kwargs['mech_browser'] = InterceptBrowser() + ZopeTestbrowser.__init__(self, *args, **kwargs) + + +# Compatibility helpers to behave like zope.app.testing + +basicre = re.compile('Basic (.+)?:(.+)?$') +def auth_header(header): + """This function takes an authorization HTTP header and encode the + couple user, password into base 64 like the HTTP protocol wants + it. + """ + match = basicre.match(header) + if match: + u, p = match.group(1, 2) + if u is None: + u = '' + if p is None: + p = '' + auth = base64.encodestring('%s:%s' % (u, p)) + return 'Basic %s' % auth[:-1] + return header + + +def is_wanted_header(header): + """Return True if the given HTTP header key is unwanted. + """ + key, value = header + return key.lower() not in ('x-content-type-warning', 'x-powered-by') + + +class TestBrowserMiddleware(object): + """This middleware makes the WSGI application compatible with the + HTTPCaller behavior defined in zope.app.testing.functional: + - It commits and synchronises the current transaction before and + after the test. + - It honors the X-zope-handle-errors header in order to support + zope.testbrowser Browser handleErrors flag. + - It modifies the HTTP Authorization header to encode user and + password into base 64 if it is Basic authentication. + """ + + def __init__(self, app, root, handle_errors): + assert isinstance(handle_errors, bool) + self.root = root + self.app = app + self.default_handle_errors = str(handle_errors) + + def __call__(self, environ, start_response): + # Handle debug mode + handle_errors = environ.get( + 'HTTP_X_ZOPE_HANDLE_ERRORS', self.default_handle_errors) + self.app.handleErrors = handle_errors == 'True' + + # Handle authorization + auth_key = 'HTTP_AUTHORIZATION' + if environ.has_key(auth_key): + environ[auth_key] = auth_header(environ[auth_key]) + + # Remove unwanted headers + def application_start_response(status, headers): + headers = filter(is_wanted_header, headers) + start_response(status, headers) + + commit() + for entry in self.app(environ, application_start_response): + yield entry + self.root._p_jar.sync() + + +class BrowserLayer(ZODBLayer): + """This create a test layer with a test database and register a wsgi + application to use that test database. + + A wsgi_intercept handler is installed as well, so you can use a + WSGI version of zope.testbrowser Browser instance to access the + application. + """ + + def testSetUp(self): + super(BrowserLayer, self).testSetUp() + wsgi_app = WSGIPublisherApplication( + self.db, HTTPPublicationRequestFactory, True) + + def factory(handle_errors=True): + return TestBrowserMiddleware( + wsgi_app, self.getRootFolder(), handle_errors) + + for host in TEST_HOSTS: + wsgi_intercept.add_wsgi_intercept(host, 80, factory) + + def testTearDown(self): + for host in TEST_HOSTS: + wsgi_intercept.remove_wsgi_intercept(host, 80) + super(BrowserLayer, self).testTearDown() + + +class NotInBrowserLayer(Exception): + """The current test is not running in a layer inheriting from + BrowserLayer. + """ + + +class FakeResponse(object): + """This behave like a Response object returned by HTTPCaller of + zope.app.testing.functional. + """ + + def __init__(self, response_text): + self.response_text = response_text + + def getStatus(self): + line = self.getStatusString() + status, rest = line.split(' ', 1) + return int(status) + + def getStatusString(self): + status_line = self.response_text.split('\n', 1)[0] + protocol, status_string = status_line.split(' ', 1) + return status_string + + def getHeader(self, name, default=None): + without_body = self.response_text.split('\n\n', 1)[0] + headers_text = without_body.split('\n', 1)[1] + headers = headers_text.split('\n') + result = [] + for header in headers: + header_name, header_value = header.split(': ', 1) + if name == header_name: + result.append(header_value) + if not result: + return default + elif len(result) == 1: + return result[0] + else: + return result + + def getHeaders(self): + without_body = self.response_text.split('\n\n', 1)[0] + headers_text = without_body.split('\n', 1)[1] + headers = headers_text.split('\n') + result = [] + for header in headers: + header_name, header_value = header.split(':', 1) + result.append((header_name, header_value)) + return result + + def getBody(self): + parts = self.response_text.split('\n\n', 1) + if len(parts) < 2: + return '' + return parts[1] + + def getOutput(self): + return self.response_text + + __str__ = getOutput + + + +def http(string, handle_errors=True): + """This function behave like the HTTPCaller of + zope.app.testing.functional. + """ + key = ('localhost', 80) + + if key not in wsgi_intercept._wsgi_intercept: + raise NotInBrowserLayer(NotInBrowserLayer.__doc__) + + (app_fn, script_name) = wsgi_intercept._wsgi_intercept[key] + app = app_fn(handle_errors=handle_errors) + + socket = wsgi_intercept.wsgi_fake_socket(app, 'localhost', 80, '') + socket.sendall(string.lstrip()) + result = socket.makefile() + return FakeResponse(result.getvalue()) + +class XMLRPCTestTransport(xmlrpclib.Transport): + """xmlrpclib transport that delegates to http(). + + It can be used like a normal transport, including support for basic + authentication. + """ + + verbose = False + handleErrors = True + + def request(self, host, handler, request_body, verbose=0): + request = "POST %s HTTP/1.0\n" % (handler,) + request += "Content-Length: %i\n" % len(request_body) + request += "Content-Type: text/xml\n" + + host, extra_headers, x509 = self.get_host_info(host) + if extra_headers: + request += "Authorization: %s\n" % ( + dict(extra_headers)["Authorization"],) + + request += "\n" + request_body + response = http(request, handle_errors=self.handleErrors) + + errcode = response.getStatus() + errmsg = response.getStatusString() + # This is not the same way that the normal transport deals with the + # headers. + headers = response.getHeaders() + + if errcode != 200: + raise xmlrpclib.ProtocolError( + host + handler, + errcode, errmsg, + headers + ) + + return self._parse_response( + StringIO(response.getBody()), sock=None) + + +def XMLRPCServerProxy(uri, transport=None, encoding=None, + verbose=0, allow_none=0, handleErrors=True): + """A factory that creates a server proxy using the XMLRPCTestTransport + by default. + + """ + if transport is None: + transport = XMLRPCTestTransport() + if isinstance(transport, XMLRPCTestTransport): + transport.handleErrors = handleErrors + return xmlrpclib.ServerProxy(uri, transport, encoding, verbose, allow_none) diff -Nru zope3-3.4.0/src/zope/app/wsgi/tests.py zope3-3.5~bzr18/src/zope/app/wsgi/tests.py --- zope3-3.4.0/src/zope/app/wsgi/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/app/wsgi/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,130 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""WSGI tests + +$Id: tests.py 110773 2010-04-13 11:49:56Z thefunny42 $ +""" +import tempfile +import unittest +import re +import zope.component + +from zope import component, interface +from zope.component.testlayer import ZCMLFileLayer +from zope.testing import doctest +from zope.testing import renormalizing + +import zope.event +import zope.app.wsgi +import zope.publisher.interfaces.browser +from zope.app.publication.requestpublicationregistry import factoryRegistry +from zope.app.publication.requestpublicationfactories import BrowserFactory +from zope.app.wsgi.testing import AppWSGILayer +from zope.authentication.interfaces import IAuthentication +from zope.securitypolicy.tests import principalRegistry + + +def cleanEvents(s): + zope.event.subscribers.pop() + + +class FileView: + + interface.implements(zope.publisher.interfaces.browser.IBrowserPublisher) + component.adapts(interface.Interface, + zope.publisher.interfaces.browser.IBrowserRequest) + + def __init__(self, _, request): + self.request = request + + def browserDefault(self, *_): + return self, () + + def __call__(self): + self.request.response.setHeader('content-type', 'text/plain') + f = tempfile.TemporaryFile() + f.write("Hello\nWorld!\n") + return f + + +def test_file_returns(): + """We want to make sure that file returns work + +Let's register a view that returns a temporary file and make sure that +nothing bad happens. :) + + >>> component.provideAdapter(FileView, name='test-file-view.html') + >>> from zope.security import checker + >>> checker.defineChecker( + ... FileView, + ... checker.NamesChecker(['browserDefault', '__call__']), + ... ) + + >>> from zope.app.wsgi.testlayer import Browser + >>> browser = Browser() + >>> browser.handleErrors = False + >>> browser.open('http://localhost/@@test-file-view.html') + >>> browser.headers['content-type'] + 'text/plain' + + >>> browser.headers['content-length'] + '13' + + >>> print browser.contents + Hello + World! + + +Clean up: + + >>> checker.undefineChecker(FileView) + >>> component.provideAdapter( + ... None, + ... (interface.Interface, + ... zope.publisher.interfaces.browser.IBrowserRequest), + ... zope.publisher.interfaces.browser.IBrowserPublisher, + ... 'test-file-view.html', + ... ) + + +""" + +def test_suite(): + + checker = renormalizing.RENormalizing([ + (re.compile(r"<class 'zope.component.interfaces.ComponentLookupError'>"), + r'ComponentLookupError'), + ]) + functional_suite = doctest.DocTestSuite() + functional_suite.layer = AppWSGILayer + + readme_test = doctest.DocFileSuite( + 'README.txt', + checker=checker, tearDown=cleanEvents, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS) + + doctest_suite = doctest.DocFileSuite( + 'fileresult.txt', 'paste.txt', + checker=checker, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS) + + readme_test.layer = ZCMLFileLayer(zope.app.wsgi) + doctest_suite.layer = ZCMLFileLayer(zope.app.wsgi) + + + return unittest.TestSuite(( + functional_suite, readme_test, doctest_suite)) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/applicationcontrol/applicationcontrol.py zope3-3.5~bzr18/src/zope/applicationcontrol/applicationcontrol.py --- zope3-3.4.0/src/zope/applicationcontrol/applicationcontrol.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/applicationcontrol/applicationcontrol.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Application Control + +$Id: applicationcontrol.py 107926 2010-01-09 14:12:52Z faassen $""" +__docformat__ = 'restructuredtext' + +import time +import zope.interface +import zope.traversing.interfaces +from zope.location import Location +from zope.security.checker import ProxyFactory, NamesChecker +from zope.applicationcontrol.interfaces import IApplicationControl + +class ApplicationControl(Location): + + zope.interface.implements(IApplicationControl) + + def __init__(self): + self.start_time = time.time() + + def getStartTime(self): + return self.start_time + + +applicationControllerRoot = Location() +zope.interface.directlyProvides( + applicationControllerRoot, + zope.traversing.interfaces.IContainmentRoot, + ) +applicationControllerRoot = ProxyFactory(applicationControllerRoot, + NamesChecker("__class__")) + +applicationController = ApplicationControl() +applicationController.__parent__ = applicationControllerRoot +applicationController.__name__ = '++etc++process' diff -Nru zope3-3.4.0/src/zope/applicationcontrol/configure.zcml zope3-3.5~bzr18/src/zope/applicationcontrol/configure.zcml --- zope3-3.4.0/src/zope/applicationcontrol/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/applicationcontrol/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/applicationcontrol/__init__.py zope3-3.5~bzr18/src/zope/applicationcontrol/__init__.py --- zope3-3.4.0/src/zope/applicationcontrol/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/applicationcontrol/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/applicationcontrol/interfaces.py zope3-3.5~bzr18/src/zope/applicationcontrol/interfaces.py --- zope3-3.4.0/src/zope/applicationcontrol/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/applicationcontrol/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,115 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Application Control Interface + +$Id: interfaces.py 107931 2010-01-09 14:42:28Z faassen $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface + +class IApplicationControl(Interface): + """The application control instance is usually generated upon startup and + can therefore record the startup time.""" + + def getStartTime(): + """Return time the application started in seconds since the epoch.""" + + +class IRuntimeInfo(Interface): + """Runtime Information Adapter for Application Control""" + + def getDeveloperMode(): + """Return the current developer mode setting""" + + def getPreferredEncoding(): + """Return the encoding used for text data, according + to user system preferences""" + + def getFileSystemEncoding(): + """Return the name of the encoding used to convert + Unicode filenames into system file names""" + + def getZopeVersion(): + """Return a string containing the descriptive version of the + current zope installation. + + Deprecated: the concept of a Zope version went away in the + Zope Toolkit. It is unlikely this gives sensible results in + many situations. + """ + + def getPythonVersion(): + """Return an unicode string containing verbose description + of the python interpreter""" + + def getPythonPath(): + """Return a tuple containing an unicode strings containing + the lookup paths of the python interpreter""" + + def getSystemPlatform(): + """Return an unicode string containing the system platform name + """ + + def getCommandLine(): + """Return the command line string Zope was invoked with""" + + def getProcessId(): + """Return the process id number currently serving the request""" + + def getUptime(): + """Return the Zope server uptime in seconds""" + +class IZopeVersion(Interface): + """Zope version + + Note: The notion of a zope version is deprecated to the Zope Toolkit. + """ + + def getZopeVersion(): + """Return a string containing the Zope version (possibly including + SVN information)""" + + +class IServerControl(Interface): + """Defines methods for shutting down and restarting the server. + + This utility also keeps a registry of things to call when shutting down + zope. You can register using this interface or the zcml on the global + ServerController instance. + """ + + def shutdown(time=0): + """Shutdown the server. + + The `time` should be greater-equal 0. + + If the `time` is 0, then we do a hard shutdown, i.e. closing + all sockets without waiting for tasks to complete. + + If the `time` is not 0, then we will give the tasks `time` seconds to + finish before shutting down. + """ + + def restart(time=0): + """Restart the server. + + The `time` should be greater-equal 0. + + If the `time` is 0, then we do a hard shutdown, i.e. closing + all sockets without waiting for tasks to complete. + + If the `time` is not 0, then we will give the tasks `time` seconds to + finish before shutting down. + """ diff -Nru zope3-3.4.0/src/zope/applicationcontrol/runtimeinfo.py zope3-3.5~bzr18/src/zope/applicationcontrol/runtimeinfo.py --- zope3-3.4.0/src/zope/applicationcontrol/runtimeinfo.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/applicationcontrol/runtimeinfo.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,120 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +""" Runtime Information + +$Id: runtimeinfo.py 107931 2010-01-09 14:42:28Z faassen $ +""" +__docformat__ = 'restructuredtext' + +import sys +import os +import time + +try: + import locale +except ImportError: + locale = None + +import platform + +from zope.component import getUtility, ComponentLookupError +from zope.interface import implements + +from zope.applicationcontrol.interfaces import IRuntimeInfo +from zope.applicationcontrol.interfaces import IApplicationControl +from zope.applicationcontrol.interfaces import IZopeVersion + +try: + from zope.app.appsetup import appsetup +except ImportError: + appsetup = None + + +class RuntimeInfo(object): + """Runtime information.""" + + implements(IRuntimeInfo) + __used_for__ = IApplicationControl + + def __init__(self, context): + self.context = context + + def getDeveloperMode(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + if appsetup is None: + return 'undefined' + + cc=appsetup.getConfigContext() + if cc == None: # make the test run + return 'undefined' + if cc.hasFeature('devmode'): + return 'On' + return 'Off' + + def getPreferredEncoding(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + if locale is not None: + try: + return locale.getpreferredencoding() + except locale.Error: + pass + return sys.getdefaultencoding() + + def getFileSystemEncoding(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + enc = sys.getfilesystemencoding() + if enc is None: + enc = self.getPreferredEncoding() + return enc + + def getZopeVersion(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + try: + version_utility = getUtility(IZopeVersion) + except ComponentLookupError: + return "Unavailable" + return version_utility.getZopeVersion() + + def getPythonVersion(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + return unicode(sys.version, self.getPreferredEncoding()) + + def getPythonPath(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + enc = self.getFileSystemEncoding() + return tuple([unicode(path, enc) for path in sys.path]) + + def getSystemPlatform(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + info = [] + enc = self.getPreferredEncoding() + for item in platform.uname(): + try: + t = unicode(item, enc) + except ValueError: + continue + info.append(t) + return u" ".join(info) + + def getCommandLine(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + return unicode(" ".join(sys.argv), self.getPreferredEncoding()) + + def getProcessId(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + return os.getpid() + + def getUptime(self): + """See zope.app.applicationcontrol.interfaces.IRuntimeInfo""" + return time.time() - self.context.getStartTime() diff -Nru zope3-3.4.0/src/zope/applicationcontrol/tests/__init__.py zope3-3.5~bzr18/src/zope/applicationcontrol/tests/__init__.py --- zope3-3.4.0/src/zope/applicationcontrol/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/applicationcontrol/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/applicationcontrol/tests/test_applicationcontrol.py zope3-3.5~bzr18/src/zope/applicationcontrol/tests/test_applicationcontrol.py --- zope3-3.4.0/src/zope/applicationcontrol/tests/test_applicationcontrol.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/applicationcontrol/tests/test_applicationcontrol.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +############################################################################## +"""Application Control Tests + +$Id: test_applicationcontrol.py 107926 2010-01-09 14:12:52Z faassen $ +""" +import unittest +from zope.interface.verify import verifyObject + +import time +from zope.applicationcontrol.applicationcontrol import ApplicationControl +from zope.applicationcontrol.interfaces import IApplicationControl + +# seconds, time values may differ in order to be assumed equal +time_tolerance = 2 + +class Test(unittest.TestCase): + + def _Test__new(self): + return ApplicationControl() + + def test_IVerify(self): + verifyObject(IApplicationControl, self._Test__new()) + + def test_startTime(self): + assert_time = time.time() + test_time = self._Test__new().getStartTime() + + self.failUnless(abs(assert_time - test_time) < time_tolerance) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/applicationcontrol/tests/test_runtimeinfo.py zope3-3.5~bzr18/src/zope/applicationcontrol/tests/test_runtimeinfo.py --- zope3-3.4.0/src/zope/applicationcontrol/tests/test_runtimeinfo.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/applicationcontrol/tests/test_runtimeinfo.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,124 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +############################################################################## +"""Runtime Info Tests + +$Id: test_runtimeinfo.py 107931 2010-01-09 14:42:28Z faassen $ +""" +import unittest +import os, sys, time + +try: + import locale +except ImportError: + locale = None + +from zope import component +from zope.interface import implements +from zope.interface.verify import verifyObject +from zope.applicationcontrol.applicationcontrol import applicationController +from zope.applicationcontrol.interfaces import IRuntimeInfo, IZopeVersion + +# seconds, time values may differ in order to be assumed equal +time_tolerance = 2 +stupid_version_string = "3085t0klvn93850voids" + +class TestZopeVersion(object): + """A fallback implementation for the ZopeVersion utility.""" + + implements(IZopeVersion) + + def getZopeVersion(self): + return stupid_version_string + +class Test(unittest.TestCase): + + def _Test__new(self): + from zope.applicationcontrol.runtimeinfo import RuntimeInfo + return RuntimeInfo(applicationController) + + def _getPreferredEncoding(self): + if locale is not None: + try: + return locale.getpreferredencoding() + except locale.Error: + pass + return sys.getdefaultencoding() + + def _getFileSystemEncoding(self): + enc = sys.getfilesystemencoding() + if enc is None: + enc = self._getPreferredEncoding() + return enc + + def testIRuntimeInfoVerify(self): + verifyObject(IRuntimeInfo, self._Test__new()) + + def test_PreferredEncoding(self): + runtime_info = self._Test__new() + enc = self._getPreferredEncoding() + self.assertEqual(runtime_info.getPreferredEncoding(), enc) + + def test_FileSystemEncoding(self): + runtime_info = self._Test__new() + enc = self._getFileSystemEncoding() + self.assertEqual(runtime_info.getFileSystemEncoding(), enc) + + def test_ZopeVersion(self): + runtime_info = self._Test__new() + + # we expect that there is no utility + self.assertEqual(runtime_info.getZopeVersion(), u"Unavailable") + + siteManager = component.getSiteManager() + siteManager.registerUtility(TestZopeVersion(), IZopeVersion) + + self.assertEqual(runtime_info.getZopeVersion(), stupid_version_string) + + def test_PythonVersion(self): + runtime_info = self._Test__new() + enc = self._getPreferredEncoding() + self.assertEqual(runtime_info.getPythonVersion(), + unicode(sys.version, enc)) + + def test_SystemPlatform(self): + runtime_info = self._Test__new() + self.failUnless(runtime_info.getSystemPlatform()) + + def test_CommandLine(self): + runtime_info = self._Test__new() + self.assertEqual(runtime_info.getCommandLine(), " ".join(sys.argv)) + + def test_ProcessId(self): + runtime_info = self._Test__new() + self.assertEqual(runtime_info.getProcessId(), os.getpid()) + + def test_Uptime(self): + runtime_info = self._Test__new() + # whats the uptime we expect? + + start_time = applicationController.getStartTime() + asserted_uptime = time.time() - start_time + + # get the uptime the current implementation calculates + test_uptime = runtime_info.getUptime() + + self.failUnless(abs(asserted_uptime - test_uptime) < time_tolerance) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/authentication/configure.zcml zope3-3.5~bzr18/src/zope/authentication/configure.zcml --- zope3-3.4.0/src/zope/authentication/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/authentication/__init__.py zope3-3.5~bzr18/src/zope/authentication/__init__.py --- zope3-3.4.0/src/zope/authentication/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# i am a package \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/authentication/interfaces.py zope3-3.5~bzr18/src/zope/authentication/interfaces.py --- zope3-3.4.0/src/zope/authentication/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,179 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""Authentication interfaces + +$Id: interfaces.py 97931 2009-03-11 22:31:33Z nadako $ +""" +from zope.interface import Interface +from zope.security.interfaces import IPrincipal, IGroup +from zope.schema.interfaces import ISource + + +class PrincipalLookupError(LookupError): + """No principal for given principal id""" + + +class IUnauthenticatedPrincipal(IPrincipal): + """A principal that hasn't been authenticated. + + Authenticated principals are preferable to UnauthenticatedPrincipals. + """ + + +class IFallbackUnauthenticatedPrincipal(IUnauthenticatedPrincipal): + """Marker interface for the fallback unauthenticated principal. + + This principal can be used by publications to set on a request if + no principal, not even an unauthenticated principal, was returned + by any authentication utility to fulfill the contract of IApplicationRequest. + """ + + +class IUnauthenticatedGroup(IGroup): + """A group containing unauthenticated users""" + + +class IAuthenticatedGroup(IGroup): + """A group containing authenticated users""" + + +class IEveryoneGroup(IGroup): + """A group containing all users""" + + +class IAuthentication(Interface): + """Provide support for establishing principals for requests. + + This is implemented by performing protocol-specific actions, such as + issuing challenges or providing login interfaces. + + `IAuthentication` objects are used to implement authentication + utilities. Because they implement utilities, they are expected to + collaborate with utilities in other contexts. Client code doesn't search a + context and call multiple utilities. Instead, client code will call the + most specific utility in a place and rely on the utility to delegate to + other utilities as necessary. + + The interface doesn't include methods for data management. Utilities may + use external data and not allow management in Zope. Simularly, the data to + be managed may vary with different implementations of a utility. + """ + + def authenticate(request): + """Identify a principal for a request. + + If a principal can be identified, then return the + principal. Otherwise, return None. + + The request object is fairly opaque. We may decide + that it implements some generic request interface. + + Implementation note + + It is likely that the component will dispatch + to another component based on the actual + request interface. This will allow different + kinds of requests to be handled correctly. + + For example, a component that authenticates + based on user names and passwords might request + an adapter for the request as in:: + + getpw = getAdapter(request, context=self) + + The `context` keyword argument is used to control + where the ILoginPassword component is searched for. + This is necessary because requests are placeless. + """ + + def unauthenticatedPrincipal(): + """Return the unauthenticated principal, if one is defined. + + Return None if no unauthenticated principal is defined. + + The unauthenticated principal must provide IUnauthenticatedPrincipal. + """ + + def unauthorized(id, request): + """Signal an authorization failure. + + This method is called when an auhorization problem + occurs. It can perform a variety of actions, such + as issuing an HTTP authentication challenge or + displaying a login interface. + + Note that the authentication utility nearest to the + requested resource is called. It is up to + authentication utility implementations to + collaborate with utilities higher in the object + hierarchy. + + If no principal has been identified, id will be + None. + """ + + def getPrincipal(id): + """Get principal meta-data. + + Returns an object of type IPrincipal for the given principal + id. A PrincipalLookupError is raised if the principal cannot be + found. + + Note that the authentication utility nearest to the requested + resource is called. It is up to authentication utility + implementations to collaborate with utilities higher in the + object hierarchy. + """ + + +class ILoginPassword(Interface): + """A password based login. + + An `IAuthentication` utility may use this (adapting a request), + to discover the login/password passed from the user, or to + indicate that a login is required. + """ + + def getLogin(): + """Return login name, or None if no login name found.""" + + def getPassword(): + """Return password, or None if no login name found. + + If there's a login but no password, return empty string. + """ + + def needLogin(realm): + """Indicate that a login is needed. + + The realm argument is the name of the principal registry. + """ + +class IPrincipalSource(ISource): + """A Source of Principal Ids""" + + +class ILogout(Interface): + """Provides support for logging out.""" + + def logout(request): + """Perform a logout.""" + + +class ILogoutSupported(Interface): + """A marker indicating that the security configuration supports logout. + + Provide an adapter to this interface to signal that the security system + supports logout. + """ diff -Nru zope3-3.4.0/src/zope/authentication/loginpassword.py zope3-3.5~bzr18/src/zope/authentication/loginpassword.py --- zope3-3.4.0/src/zope/authentication/loginpassword.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/loginpassword.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""Login/Password provider. + +$Id: loginpassword.py 97931 2009-03-11 22:31:33Z nadako $ +""" +from zope.interface import implements +from zope.authentication.interfaces import ILoginPassword + + +class LoginPassword(object): + """Basic ILoginPassword implementation. + + This class can be used as a base for implementing ILoginPassword adapters. + """ + + implements(ILoginPassword) + + def __init__(self, login, password): + self.__login = login + if login is None: + self.__password = None + else: + self.__password = password or '' + + def getLogin(self): + return self.__login + + def getPassword(self): + return self.__password + + def needLogin(self, realm): + pass diff -Nru zope3-3.4.0/src/zope/authentication/logout.py zope3-3.5~bzr18/src/zope/authentication/logout.py --- zope3-3.4.0/src/zope/authentication/logout.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/logout.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,45 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""ILogout implementations + +$Id: logout.py 97931 2009-03-11 22:31:33Z nadako $ +""" +from zope.component import adapts +from zope.interface import implements, Interface + +from zope.authentication.interfaces import IAuthentication +from zope.authentication.interfaces import ILogout, ILogoutSupported + + +class NoLogout(object): + """An adapter for IAuthentication utilities that don't implement ILogout.""" + + adapts(IAuthentication) + implements(ILogout) + + def __init__(self, auth): + pass + + def logout(self, request): + pass + + +class LogoutSupported(object): + """A class that can be registered as an adapter to flag logout support.""" + + adapts(Interface) + implements(ILogoutSupported) + + def __init__(self, dummy): + pass diff -Nru zope3-3.4.0/src/zope/authentication/logout.txt zope3-3.5~bzr18/src/zope/authentication/logout.txt --- zope3-3.4.0/src/zope/authentication/logout.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/logout.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,77 @@ +============== +Logout Support +============== + +Logout support is defined by a simple interface ILogout: + + >>> from zope.authentication.interfaces import ILogout + +that has a single 'logout' method. + +The current use of ILogout is to adapt an IAuthentication component to ILogout +To illustrate, we'll create a simple logout implementation that adapts +IAuthentication: + + >>> class SimpleLogout(object): + ... + ... adapts(IAuthentication) + ... implements(ILogout) + ... + ... def __init__(self, auth): + ... pass + ... + ... def logout(self, request): + ... print 'User has logged out' + + >>> provideAdapter(SimpleLogout) + +and something to represent an authentication utility: + + >>> class Authentication(object): + ... + ... implements(IAuthentication) + + >>> auth = Authentication() + +To perform a logout, we adapt auth to ILogout and call 'logout': + + >>> logout = ILogout(auth) + >>> logout.logout(TestRequest()) + User has logged out + + +The 'NoLogout' Adapter +---------------------- + +The class: + + >>> from zope.authentication.logout import NoLogout + +can be registered as a fallback provider of ILogout for IAuthentication +components that are not otherwise adaptable to ILogout. NoLogout's logout +method is a no-op: + + >>> NoLogout(auth).logout(TestRequest()) + + +Logout User Interface +--------------------- + +Because some authentication protocols do not formally support logout, it may +not be possible for a user to logout once he or she has logged in. In such +cases, it would be inappropriate to present a user interface for logging out. + +Because logout support is site-configurable, Zope provides an adapter that, +when registered, indicates that the site is configured for logout: + + >>> from zope.authentication.logout import LogoutSupported + +This class merely serves as a flag as it implements ILogoutSupported: + + >>> from zope.authentication.interfaces import ILogoutSupported + >>> ILogoutSupported.implementedBy(LogoutSupported) + True + + >>> request = object() + >>> ILogoutSupported.providedBy(LogoutSupported(request)) + True diff -Nru zope3-3.4.0/src/zope/authentication/principal.py zope3-3.5~bzr18/src/zope/authentication/principal.py --- zope3-3.4.0/src/zope/authentication/principal.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/principal.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,198 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""Principal source and helper function + +$Id: principal.py 97941 2009-03-12 00:09:21Z nadako $ +""" +from zope.browser.interfaces import ITerms +from zope.component import getUtility, queryNextUtility, adapts +from zope.interface import implements, Interface +from zope.schema.interfaces import ISourceQueriables + +from zope.authentication.interfaces import IAuthentication, IPrincipalSource +from zope.authentication.interfaces import PrincipalLookupError + + +def checkPrincipal(context, principal_id): + """An utility function to check if there's a principal for given principal id. + + Raises ValueError when principal doesn't exists for given context and + principal id. + + To test it, let's create and register a dummy authentication utility. + + >>> class DummyUtility: + ... + ... implements(IAuthentication) + ... + ... def getPrincipal(self, id): + ... if id == 'bob': + ... return id + ... raise PrincipalLookupError(id) + + >>> from zope.component import provideUtility + >>> provideUtility(DummyUtility()) + + Now, let's the behaviour of this function. + + >>> checkPrincipal(None, 'bob') + >>> checkPrincipal(None, 'dan') + Traceback (most recent call last): + ... + ValueError: ('Undefined principal id', 'dan') + + """ + auth = getUtility(IAuthentication, context=context) + try: + if auth.getPrincipal(principal_id): + return + except PrincipalLookupError: + pass + raise ValueError("Undefined principal id", principal_id) + + +class PrincipalSource(object): + """Generic Principal Source""" + + implements(IPrincipalSource, ISourceQueriables) + + def __contains__(self, id): + """Test for the existence of a user. + + We want to check whether the system knows about a particular + principal, which is referenced via its id. The source will go through + the most local authentication utility to look for the + principal. Whether the utility consults other utilities to give an + answer is up to the utility itself. + + First we need to create a dummy utility that will return a user, if + the id is 'bob'. + + >>> class DummyUtility: + ... implements(IAuthentication) + ... def getPrincipal(self, id): + ... if id == 'bob': + ... return id + ... raise PrincipalLookupError(id) + + Let's register our dummy auth utility. + + >>> from zope.component import provideUtility + >>> provideUtility(DummyUtility()) + + Now initialize the principal source and test the method + + >>> source = PrincipalSource() + >>> 'jim' in source + False + >>> 'bob' in source + True + """ + auth = getUtility(IAuthentication) + try: + auth.getPrincipal(id) + except PrincipalLookupError: + return False + else: + return True + + def getQueriables(self): + """Returns an iteratable of queriables. + + Queriables are responsible for providing interfaces to search for + principals by a set of given parameters (can be different for the + various queriables). This method will walk up through all of the + authentication utilities to look for queriables. + + >>> class DummyUtility1: + ... implements(IAuthentication) + ... __parent__ = None + ... def __repr__(self): return 'dummy1' + >>> dummy1 = DummyUtility1() + + >>> class DummyUtility2: + ... implements(ISourceQueriables, IAuthentication) + ... __parent__ = None + ... def getQueriables(self): + ... return ('1', 1), ('2', 2), ('3', 3) + >>> dummy2 = DummyUtility2() + + >>> class DummyUtility3(DummyUtility2): + ... implements(IAuthentication) + ... def getQueriables(self): + ... return ('4', 4), + >>> dummy3 = DummyUtility3() + + >>> from zope.component.nexttesting import testingNextUtility + >>> testingNextUtility(dummy1, dummy2, IAuthentication) + >>> testingNextUtility(dummy2, dummy3, IAuthentication) + + >>> from zope.component import provideUtility + >>> provideUtility(dummy1) + + >>> source = PrincipalSource() + >>> list(source.getQueriables()) + [(u'0', dummy1), (u'1.1', 1), (u'1.2', 2), (u'1.3', 3), (u'2.4', 4)] + """ + i = 0 + auth = getUtility(IAuthentication) + yielded = [] + while True: + queriables = ISourceQueriables(auth, None) + if queriables is None: + yield unicode(i), auth + else: + for qid, queriable in queriables.getQueriables(): + # ensure that we dont return same yielded utility more + # then once + if queriable not in yielded: + yield unicode(i)+'.'+unicode(qid), queriable + yielded.append(queriable) + auth = queryNextUtility(auth, IAuthentication) + if auth is None: + break + i += 1 + + +class PrincipalTerms(object): + + implements(ITerms) + adapts(IPrincipalSource, Interface) + + def __init__(self, context, request): + self.context = context + + def getTerm(self, principal_id): + if principal_id not in self.context: + raise LookupError(principal_id) + + auth = getUtility(IAuthentication) + principal = auth.getPrincipal(principal_id) + + if principal is None: + # TODO: is this a possible case? + raise LookupError(principal_id) + + return PrincipalTerm(principal_id.encode('base64').strip().replace('=', '_'), + principal.title) + + def getValue(self, token): + return token.replace('_', '=').decode('base64') + + +class PrincipalTerm(object): + + def __init__(self, token, title): + self.token = token + self.title = title diff -Nru zope3-3.4.0/src/zope/authentication/principalterms.txt zope3-3.5~bzr18/src/zope/authentication/principalterms.txt --- zope3-3.4.0/src/zope/authentication/principalterms.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/principalterms.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,68 @@ +=============== +Principal Terms +=============== + +Principal Terms are used to support browser interfaces for searching principal +sources. They provide access to tokens and titles for values. The principal +terms view uses an authentication utility to get principal titles. Let's +create an authentication utility to demonstrate how this works: + + >>> class Principal: + ... def __init__(self, id, title): + ... self.id, self.title = id, title + + >>> from zope.interface import implements + >>> from zope.authentication.interfaces import IAuthentication + >>> from zope.authentication.interfaces import PrincipalLookupError + >>> class AuthUtility: + ... implements(IAuthentication) + ... data = {'jim': 'Jim Fulton', 'stephan': 'Stephan Richter'} + ... + ... def getPrincipal(self, id): + ... title = self.data.get(id) + ... if title is not None: + ... return Principal(id, title) + ... raise PrincipalLookupError + +Now we need to install the authentication utility: + + >>> from zope.component import provideUtility + >>> provideUtility(AuthUtility(), IAuthentication) + +We need a principal source so that we can create a view from it. + + >>> from zope.component import getUtility + >>> class PrincipalSource: + ... def __contains__(self, id): + ... auth = getUtility(IAuthentication) + ... try: + ... auth.getPrincipal(id) + ... except PrincipalLookupError: + ... return False + ... else: + ... return True + +Now we can create an terms view: + + >>> from zope.authentication.principal import PrincipalTerms + >>> terms = PrincipalTerms(PrincipalSource(), None) + +Now we can ask the terms view for terms: + + >>> term = terms.getTerm('stephan') + >>> term.title + 'Stephan Richter' + >>> term.token + 'c3RlcGhhbg__' + +If we ask for a term that does not exist, we get a lookup error: + + >>> terms.getTerm('bob') + Traceback (most recent call last): + ... + LookupError: bob + +If we have a token, we can get the principal id for it. + + >>> terms.getValue('c3RlcGhhbg__') + 'stephan' diff -Nru zope3-3.4.0/src/zope/authentication/tests/test_loginpassword.py zope3-3.5~bzr18/src/zope/authentication/tests/test_loginpassword.py --- zope3-3.4.0/src/zope/authentication/tests/test_loginpassword.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/tests/test_loginpassword.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2001-2009 Zope Corporation 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 Login and Password + +$Id: test_loginpassword.py 98048 2009-03-13 20:14:26Z nadako $ +""" +import unittest + +from zope.authentication.loginpassword import LoginPassword + +class Test(unittest.TestCase): + + def testLoginPassword(self): + lp = LoginPassword("tim", "123") + self.assertEqual(lp.getLogin(), "tim") + self.assertEqual(lp.getPassword(), "123") + lp = LoginPassword(None, None) + self.assertEqual(lp.getLogin(), None) + self.assertEqual(lp.getPassword(), None) + lp = LoginPassword(None, "123") + self.assertEqual(lp.getLogin(), None) + self.assertEqual(lp.getPassword(), None) + lp = LoginPassword("tim", None) + self.assertEqual(lp.getLogin(), "tim") + self.assertEqual(lp.getPassword(), "") + lp.needLogin("tim") # This method should exist + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/authentication/tests/test_logout.py zope3-3.5~bzr18/src/zope/authentication/tests/test_logout.py --- zope3-3.4.0/src/zope/authentication/tests/test_logout.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/tests/test_logout.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,37 @@ +############################################################################## +# +# Copyright (c) 2004-2009 Zope Corporation 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. +# +############################################################################## +""" +$Id: test_logout.py 111660 2010-04-30 17:27:53Z hannosch $ +""" +import doctest +import unittest + +from zope.component import provideAdapter, adapts +from zope.interface import implements + +from zope.authentication.interfaces import IAuthentication + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite( + '../logout.txt', + globs={'provideAdapter': provideAdapter, + 'TestRequest': object, + 'implements': implements, + 'adapts': adapts, + 'IAuthentication': IAuthentication + }, + ), + )) diff -Nru zope3-3.4.0/src/zope/authentication/tests/test_principal.py zope3-3.5~bzr18/src/zope/authentication/tests/test_principal.py --- zope3-3.4.0/src/zope/authentication/tests/test_principal.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/authentication/tests/test_principal.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,26 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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 for principal lookup related functionality + +$Id: test_principal.py 111660 2010-04-30 17:27:53Z hannosch $ +""" +import doctest +import unittest + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite('zope.authentication.principal'), + doctest.DocFileSuite('../principalterms.txt'), + )) diff -Nru zope3-3.4.0/src/zope/broken/__init__.py zope3-3.5~bzr18/src/zope/broken/__init__.py --- zope3-3.4.0/src/zope/broken/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/broken/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Make this directory a package. diff -Nru zope3-3.4.0/src/zope/broken/interfaces.py zope3-3.5~bzr18/src/zope/broken/interfaces.py --- zope3-3.4.0/src/zope/broken/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/broken/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,28 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation 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.broken interfaces. + +$Id: interfaces.py 72735 2007-02-21 05:02:33Z baijum $ +""" + +__docformat__ = "reStructuredText" + +import zope.interface + +try: + from ZODB.interfaces import IBroken +except ImportError: + class IBroken(zope.interface.Interface): + """Marker interface for broken objects + """ diff -Nru zope3-3.4.0/src/zope/browser/__init__.py zope3-3.5~bzr18/src/zope/browser/__init__.py --- zope3-3.4.0/src/zope/browser/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browser/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# make a package diff -Nru zope3-3.4.0/src/zope/browser/interfaces.py zope3-3.5~bzr18/src/zope/browser/interfaces.py --- zope3-3.4.0/src/zope/browser/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browser/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,109 @@ +############################################################################## +# +# Copyright (c) 2004-2009 Zope Corporation 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. +# +############################################################################## +"""Shared dependency less Zope3 brwoser components. +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Attribute +from zope.interface import Interface + +class IView(Interface): + """ Views are multi-adapters for context and request objects. + """ + context = Attribute("The context object the view renders") + request = Attribute("The request object driving the view") + +class IBrowserView(IView): + """ Views which are specialized for requests from a browser + + o Such views are distinct from those geerated via WebDAV, FTP, XML-RPC, + etc.. + """ + +class IAdding(IBrowserView): + """ Multi-adapter interface for views which add items to containers. + + o The 'context' of the view must implement ``zope.container.IContainer``. + """ + def add(content): + """Add content object to context. + + Add using the name in `contentName`. + + Return the added object in the context of its container. + + If `contentName` is already used in container, raise + ``zope.container.interfaces.DuplicateIDError``. + """ + + contentName = Attribute( + """The content name, usually set by the Adder traverser. + + If the content name hasn't been defined yet, returns ``None``. + + Some creation views might use this to optionally display the + name on forms. + """ + ) + + def nextURL(): + """Return the URL that the creation view should redirect to. + + This is called by the creation view after calling add. + + It is the adder's responsibility, not the creation view's to + decide what page to display after content is added. + """ + + def nameAllowed(): + """Return whether names can be input by the user. + """ + + def addingInfo(): + """Return add menu data as a sequence of mappings. + + Each mapping contains 'action', 'title', and possibly other keys. + + The result is sorted by title. + """ + + def isSingleMenuItem(): + """Return whether there is single menu item or not.""" + + def hasCustomAddView(): + "This should be called only if there is `singleMenuItem` else return 0" + + +class ITerms(Interface): + """ Adapter providing lookups for vocabulary terms. + """ + def getTerm(value): + """Return an ITitledTokenizedTerm object for the given value + + LookupError is raised if the value isn't in the source + """ + + def getValue(token): + """Return a value for a given identifier token + + LookupError is raised if there isn't a value in the source. + """ + +class ISystemErrorView(Interface): + """Error views that can classify their contexts as system errors + """ + + def isSystemError(): + """Return a boolean indicating whether the error is a system errror + """ diff -Nru zope3-3.4.0/src/zope/browser/README.txt zope3-3.5~bzr18/src/zope/browser/README.txt --- zope3-3.4.0/src/zope/browser/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browser/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,78 @@ +IView +----- + +Views adapt both a context and a request. + +There is not much we can test except that ``IView`` is importable +and an interface: + + >>> from zope.interface import Interface + >>> from zope.browser.interfaces import IView + >>> Interface.providedBy(IView) + True + +IBrowserView +------------- + +Browser views are views specialized for requests from a browser (e.g., +as distinct from WebDAV, FTP, XML-RPC, etc.). + +There is not much we can test except that ``IBrowserView`` is importable +and an interface derived from ``IView``: + + >>> from zope.interface import Interface + >>> from zope.browser.interfaces import IBrowserView + >>> Interface.providedBy(IBrowserView) + True + >>> IBrowserView.extends(IView) + True + +IAdding +------- + +Adding views manage how newly-created items get added to containers. + +There is not much we can test except that ``IAdding`` is importable +and an interface derived from ``IBrowserView``: + + >>> from zope.interface import Interface + >>> from zope.browser.interfaces import IAdding + >>> Interface.providedBy(IBrowserView) + True + >>> IAdding.extends(IBrowserView) + True + +ITerms +------ + +The ``ITerms`` interface is used as a base for ``ISource`` widget +implementations. This interfaces get used by ``zope.app.form`` and was +initially defined in ``zope.app.form.browser.interfaces``, which made it +impossible to use for other packages like ``z3c.form`` wihtout depending on +``zope.app.form``. + +Moving such base components / interfaces to ``zope.browser`` makes it +possible to share them without undesirable dependencies. + +There is not much we can test except that ITerms is importable +and an interface: + + >>> from zope.interface import Interface + >>> from zope.browser.interfaces import ITerms + >>> Interface.providedBy(ITerms) + True + +ISystemErrorView +---------------- + +Views providing this interface can classify their contexts as system +errors. These errors can be handled in a special way (e. g. more +detailed logging). + +There is not much we can test except that ISystemErrorView is importable +and an interface: + + >>> from zope.interface import Interface + >>> from zope.browser.interfaces import ISystemErrorView + >>> Interface.providedBy(ISystemErrorView) + True diff -Nru zope3-3.4.0/src/zope/browser/tests.py zope3-3.5~bzr18/src/zope/browser/tests.py --- zope3-3.4.0/src/zope/browser/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browser/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,32 @@ +############################################################################## +# +# Copyright (c) 2007 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. +# +############################################################################## +""" +$Id:$ +""" +__docformat__ = "reStructuredText" + +import doctest +import unittest + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('README.txt', + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, + ), + )) + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/browsermenu/configure.zcml zope3-3.5~bzr18/src/zope/browsermenu/configure.zcml --- zope3-3.4.0/src/zope/browsermenu/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,15 @@ + + + + + + + + diff -Nru zope3-3.4.0/src/zope/browsermenu/field.py zope3-3.5~bzr18/src/zope/browsermenu/field.py --- zope3-3.4.0/src/zope/browsermenu/field.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/field.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,109 @@ +############################################################################# +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Menu field + +$Id: field.py 103270 2009-08-27 13:56:02Z nadako $ +""" +__docformat__ = 'restructuredtext' + +from zope.component import queryUtility +from zope.component.interfaces import ComponentLookupError +from zope.configuration.exceptions import ConfigurationError +from zope.configuration.fields import GlobalObject +from zope.schema import ValidationError + +from zope.browsermenu.interfaces import IMenuItemType + + +class MenuField(GlobalObject): + r"""This fields represents a menu (item type). + + Besides being able to look up the menu by importing it, we also try + to look up the name in the site manager. + + >>> from zope.interface import directlyProvides + >>> from zope.interface.interface import InterfaceClass + + >>> menu1 = InterfaceClass('menu1', (), + ... __doc__='Menu Item Type: menu1', + ... __module__='zope.app.menus') + >>> directlyProvides(menu1, IMenuItemType) + + >>> menus = None + >>> class Resolver(object): + ... def resolve(self, path): + ... if path.startswith('zope.app.menus') and \ + ... hasattr(menus, 'menu1') or \ + ... path == 'zope.browsermenu.menus.menu1': + ... return menu1 + ... raise ConfigurationError('menu1') + + >>> field = MenuField() + >>> field = field.bind(Resolver()) + + Test 1: Import the menu + ----------------------- + + >>> field.fromUnicode('zope.browsermenu.menus.menu1') is menu1 + True + + Test 2: We have a shortcut name. Import the menu from `zope.app.menus1`. + ------------------------------------------------------------------------ + + >>> from types import ModuleType as module + >>> import sys + >>> menus = module('menus') + >>> old = sys.modules.get('zope.app.menus', None) + >>> sys.modules['zope.app.menus'] = menus + >>> setattr(menus, 'menu1', menu1) + + >>> field.fromUnicode('menu1') is menu1 + True + + >>> if old is not None: + ... sys.modules['zope.app.menus'] = old + + Test 3: Get the menu from the Site Manager + ------------------------------------------ + + >>> from zope.component import provideUtility + >>> provideUtility(menu1, IMenuItemType, 'menu1') + + >>> field.fromUnicode('menu1') is menu1 + True + """ + + def fromUnicode(self, u): + name = str(u.strip()) + + try: + value = queryUtility(IMenuItemType, name) + except ComponentLookupError: + # The component architecture is not up and running. + pass + else: + if value is not None: + self.validate(value) + return value + + try: + value = self.context.resolve('zope.app.menus.'+name) + except ConfigurationError, v: + try: + value = self.context.resolve(name) + except ConfigurationError, v: + raise ValidationError(v) + + self.validate(value) + return value diff -Nru zope3-3.4.0/src/zope/browsermenu/interfaces.py zope3-3.5~bzr18/src/zope/browsermenu/interfaces.py --- zope3-3.4.0/src/zope/browsermenu/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,161 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Menu-specific interfaces + +$Id: interfaces.py 103270 2009-08-27 13:56:02Z nadako $ +""" +from zope.i18nmessageid import ZopeMessageFactory as _ +from zope.interface import Interface, directlyProvides +from zope.interface.interfaces import IInterface +from zope.schema import TextLine, Text, URI, Int + + +class IMenuItemType(IInterface): + """Menu item type + + Menu item types are interfaces that define classes of + menu items. + """ + +class AddMenu(Interface): + """Special menu for providing a list of addable objects.""" + +directlyProvides(AddMenu, IMenuItemType) + + +class IBrowserMenu(Interface): + """Menu + + Menus are objects that can return a list of menu items they contain. How + they generate this list is up to them. Commonly, however, they will look + up adapters that provide the ``IBrowserMenuItem`` interface. + """ + + id = TextLine( + title=_("Menu Id"), + description=_("The id uniquely identifies this menu."), + required=True + ) + + title = TextLine( + title=_("Menu title"), + description=_("The title provides the basic label for the menu."), + required=False + ) + + description = Text( + title=_("Menu description"), + description=_("A description of the menu. This might be shown " + "on menu pages or in pop-up help for menus."), + required=False + ) + + def getMenuItems(object, request): + """Return a TAL-friendly list of menu items. + + The object (acts like the context) and request can be used to select + the items that are available. + """ + + +class IBrowserMenuItem(Interface): + """Menu type + + An interface that defines a menu. + """ + + title = TextLine( + title=_("Menu item title"), + description=_("The title provides the basic label for the menu item."), + required=True + ) + + description = Text( + title=_("Menu item description"), + description=_("A description of the menu item. This might be shown " + "on menu pages or in pop-up help for menu items."), + required=False + ) + + action = TextLine( + title=_("The URL to display if the item is selected"), + description=_("When a user selects a browser menu item, the URL" + "given in the action is displayed. The action is " + "usually given as a relative URL, relative to the " + "object the menu item is for."), + required=True + ) + + order = Int( + title=_("Menu item ordering hint"), + description=_("This attribute provides a hint for menu item ordering." + "Menu items will generally be sorted by the `for_`" + "attribute and then by the order.") + ) + + filter_string = TextLine( + title=_("A condition for displaying the menu item"), + description=_("The condition is given as a TALES expression. The " + "expression has access to the variables:\n" + "\n" + "context -- The object the menu is being displayed " + "for\n" + "\n" + "request -- The browser request\n" + "\n" + "nothing -- None\n" + "\n" + "The menu item will not be displayed if there is a \n" + "filter and the filter evaluates to a false value."), + required=False) + + icon = URI( + title=_("Icon URI"), + description=_("URI of the icon representing this menu item")) + + def available(): + """Test whether the menu item should be displayed + + A menu item might not be available for an object, for example + due to security limitations or constraints. + """ + +class IBrowserSubMenuItem(IBrowserMenuItem): + """A menu item that points to a sub-menu.""" + + submenuId = TextLine( + title=_("Sub-Menu Id"), + description=_("The menu id of the menu that describes the " + "sub-menu below this item."), + required=True) + + action = TextLine( + title=_("The URL to display if the item is selected"), + description=_("When a user selects a browser menu item, the URL " + "given in the action is displayed. The action is " + "usually given as a relative URL, relative to the " + "object the menu item is for."), + required=False + ) + + +class IMenuAccessView(Interface): + """View that provides access to menus""" + + def __getitem__(menu_id): + """Get menu information + + Return a sequence of dictionaries with labels and + actions, where actions are relative URLs. + """ diff -Nru zope3-3.4.0/src/zope/browsermenu/menu.py zope3-3.5~bzr18/src/zope/browsermenu/menu.py --- zope3-3.4.0/src/zope/browsermenu/menu.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/menu.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,199 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Menu implementation code + +$Id: menu.py 103270 2009-08-27 13:56:02Z nadako $ +""" +__docformat__ = "reStructuredText" +import sys + +from zope.component import getAdapters, getUtility +from zope.interface import Interface, implements, providedBy +from zope.interface.interfaces import IInterface +from zope.pagetemplate.engine import Engine +from zope.publisher.browser import BrowserView +from zope.security import canAccess, checkPermission +from zope.security.interfaces import Forbidden, Unauthorized +from zope.security.proxy import removeSecurityProxy +from zope.traversing.publicationtraverse import PublicationTraverser + +from zope.browsermenu.interfaces import IBrowserMenu, IMenuItemType +from zope.browsermenu.interfaces import IBrowserMenuItem, IBrowserSubMenuItem +from zope.browsermenu.interfaces import IMenuAccessView + +class BrowserMenu(object): + """Browser Menu""" + implements(IBrowserMenu) + + def __init__(self, id, title=u'', description=u''): + self.id = id + self.title = title + self.description = description + + def getMenuItemType(self): + return getUtility(IMenuItemType, self.id) + + def getMenuItems(self, object, request): + """Return menu item entries in a TAL-friendly form.""" + + result = [] + for name, item in getAdapters((object, request), + self.getMenuItemType()): + if item.available(): + result.append(item) + + # Now order the result. This is not as easy as it seems. + # + # (1) Look at the interfaces and put the more specific menu entries + # to the front. + # (2) Sort unambigious entries by order and then by title. + ifaces = list(providedBy(removeSecurityProxy(object)).__iro__) + max_key = len(ifaces) + def iface_index(item): + iface = item._for + if not iface: + iface = Interface + if IInterface.providedBy(iface): + return ifaces.index(iface) + if isinstance(removeSecurityProxy(object), item._for): + # directly specified for class, this goes first. + return -1 + # no idea. This goes last. + return max_key + result = [(iface_index(item), item.order, item.title, item) + for item in result] + result.sort() + + result = [ + {'title': title, + 'description': item.description, + 'action': item.action, + 'selected': (item.selected() and u'selected') or u'', + 'icon': item.icon, + 'extra': item.extra, + 'submenu': (IBrowserSubMenuItem.providedBy(item) and + getMenu(item.submenuId, object, request)) or None} + for index, order, title, item in result] + + return result + + +class BrowserMenuItem(BrowserView): + """Browser Menu Item Class""" + implements(IBrowserMenuItem) + + title = u'' + description = u'' + action = u'' + extra = None + order = 0 + permission = None + filter = None + icon = None + _for = Interface + + def available(self): + # Make sure we have the permission needed to access the menu's action + if self.permission is not None: + # If we have an explicit permission, check that we + # can access it. + if not checkPermission(self.permission, self.context): + return False + + elif self.action != u'': + # Otherwise, test access by attempting access + path = self.action + l = self.action.find('?') + if l >= 0: + path = self.action[:l] + + traverser = PublicationTraverser() + try: + view = traverser.traverseRelativeURL( + self.request, self.context, path) + except (Unauthorized, Forbidden, LookupError): + return False + else: + # we're assuming that view pages are callable + # this is a pretty sound assumption + if not canAccess(view, '__call__'): + return False + + # Make sure that we really want to see this menu item + if self.filter is not None: + + try: + include = self.filter(Engine.getContext( + context = self.context, + nothing = None, + request = self.request, + modules = sys.modules, + )) + except Unauthorized: + return False + else: + if not include: + return False + + return True + + def selected(self): + request_url = self.request.getURL() + + normalized_action = self.action + if self.action.startswith('@@'): + normalized_action = self.action[2:] + + if request_url.endswith('/'+normalized_action): + return True + if request_url.endswith('/++view++'+normalized_action): + return True + if request_url.endswith('/@@'+normalized_action): + return True + + return False + + +class BrowserSubMenuItem(BrowserMenuItem): + """Browser Menu Item Base Class""" + implements(IBrowserSubMenuItem) + + submenuId = None + + def selected(self): + if self.action is u'': + return False + return super(BrowserSubMenuItem, self).selected() + + +def getMenu(id, object, request): + """Return menu item entries in a TAL-friendly form.""" + menu = getUtility(IBrowserMenu, id) + return menu.getMenuItems(object, request) + + +def getFirstMenuItem(id, object, request): + """Get the first item of a menu.""" + items = getMenu(id, object, request) + if items: + return items[0] + return None + + +class MenuAccessView(BrowserView): + """A view allowing easy access to menus.""" + implements(IMenuAccessView) + + def __getitem__(self, menuId): + return getMenu(menuId, self.context, self.request) diff -Nru zope3-3.4.0/src/zope/browsermenu/metaconfigure.py zope3-3.5~bzr18/src/zope/browsermenu/metaconfigure.py --- zope3-3.4.0/src/zope/browsermenu/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,306 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Menu Directives Configuration Handlers + +$Id: metaconfigure.py 103270 2009-08-27 13:56:02Z nadako $ +""" +from zope.browser.interfaces import IAdding +from zope.component import getGlobalSiteManager, getUtility +from zope.component.interface import provideInterface +from zope.component.zcml import adapter, proxify, utility +from zope.configuration.exceptions import ConfigurationError +from zope.interface import Interface +from zope.interface.interface import InterfaceClass +from zope.pagetemplate.engine import Engine +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.security.checker import InterfaceChecker, CheckerPublic +from zope.security.metaconfigure import ClassDirective + +from zope.browsermenu.menu import BrowserMenu, BrowserMenuItem, BrowserSubMenuItem +from zope.browsermenu.interfaces import IBrowserMenu, IMenuItemType +from zope.browsermenu.interfaces import IBrowserMenuItem, IBrowserSubMenuItem +from zope.browsermenu.interfaces import AddMenu + +# Create special modules that contain all menu item types +from types import ModuleType as module +import sys +try: + import zope.app +except ImportError: # we doesn't always have zope.app now + sys.modules['zope.app'] = module('app') +menus = module('menus') +sys.modules['zope.app.menus'] = menus + + +_order_counter = {} + + +def menuDirective(_context, id=None, class_=BrowserMenu, interface=None, + title=u'', description=u''): + """Registers a new browser menu.""" + if id is None and interface is None: + raise ConfigurationError( + "You must specify the 'id' or 'interface' attribute.") + + if interface is None: + if id in dir(menus): + # reuse existing interfaces for the id, without this we are not + # able to override menus. + interface = getattr(menus, id) + else: + interface = InterfaceClass(id, (), + __doc__='Menu Item Type: %s' %id, + __module__='zope.app.menus') + # Add the menu item type to the `menus` module. + # Note: We have to do this immediately, so that directives using the + # MenuField can find the menu item type. + setattr(menus, id, interface) + path = 'zope.app.menus.' + id + else: + path = interface.__module__ + '.' + interface.getName() + + # If an id was specified, make this menu available under this id. + # Note that the menu will be still available under its path, since it + # is an adapter, and the `MenuField` can resolve paths as well. + if id is None: + id = path + else: + # Make the interface available in the `zope.app.menus` module, so + # that other directives can find the interface under the name + # before the CA is setup. + _context.action( + discriminator = ('browser', 'MenuItemType', path), + callable = provideInterface, + args = (path, interface, IMenuItemType, _context.info) + ) + setattr(menus, id, interface) + + # Register the layer interface as an interface + _context.action( + discriminator = ('interface', path), + callable = provideInterface, + args = (path, interface), + kw = {'info': _context.info} + ) + + # Register the menu item type interface as an IMenuItemType + _context.action( + discriminator = ('browser', 'MenuItemType', id), + callable = provideInterface, + args = (id, interface, IMenuItemType, _context.info) + ) + + # Register the menu as a utility + utility(_context, IBrowserMenu, class_(id, title, description), name=id) + + +def menuItemDirective(_context, menu, for_, + action, title, description=u'', icon=None, filter=None, + permission=None, layer=IDefaultBrowserLayer, extra=None, + order=0, item_class=None): + """Register a single menu item.""" + return menuItemsDirective(_context, menu, for_, layer).menuItem( + _context, action, title, description, icon, filter, + permission, extra, order, item_class) + + +def subMenuItemDirective(_context, menu, for_, title, submenu, + action=u'', description=u'', icon=None, filter=None, + permission=None, layer=IDefaultBrowserLayer, + extra=None, order=0, item_class=None): + """Register a single sub-menu menu item.""" + return menuItemsDirective(_context, menu, for_, layer).subMenuItem( + _context, submenu, title, description, action, icon, filter, + permission, extra, order, item_class) + + +class MenuItemFactory(object): + """generic factory for menu items.""" + + def __init__(self, factory, **kwargs): + self.factory = factory + if 'permission' in kwargs and kwargs['permission'] == 'zope.Public': + kwargs['permission'] = CheckerPublic + self.kwargs = kwargs + + def __call__(self, context, request): + item = self.factory(context, request) + + for key, value in self.kwargs.items(): + setattr(item, key, value) + + if item.permission is not None: + checker = InterfaceChecker(IBrowserMenuItem, item.permission) + item = proxify(item, checker) + + return item + + +class menuItemsDirective(object): + """Register several menu items for a particular menu.""" + + menuItemClass = BrowserMenuItem + subMenuItemClass = BrowserSubMenuItem + + def __init__(self, _context, menu, for_, layer=IDefaultBrowserLayer, + permission=None): + self.for_ = for_ + self.menuItemType = menu + self.layer = layer + self.permission = permission + + def menuItem(self, _context, action, title, description=u'', + icon=None, filter=None, permission=None, extra=None, + order=0, item_class=None): + + if filter is not None: + filter = Engine.compile(filter) + + if permission is None: + permission = self.permission + + if order == 0: + order = _order_counter.get(self.for_, 1) + _order_counter[self.for_] = order + 1 + + if item_class is None: + item_class = self.menuItemClass + + if not IBrowserMenuItem.implementedBy(item_class): + raise ValueError("Item class (%s) must implement IBrowserMenuItem" % item_class) + + factory = MenuItemFactory( + item_class, + title=title, description=description, icon=icon, action=action, + filter=filter, permission=permission, extra=extra, order=order, + _for=self.for_) + adapter(_context, (factory,), self.menuItemType, + (self.for_, self.layer), name=title) + + def subMenuItem(self, _context, submenu, title, description=u'', + action=u'', icon=None, filter=None, permission=None, + extra=None, order=0, item_class=None): + + if filter is not None: + filter = Engine.compile(filter) + + if permission is None: + permission = self.permission + + if order == 0: + order = _order_counter.get(self.for_, 1) + _order_counter[self.for_] = order + 1 + + if item_class is None: + item_class = self.subMenuItemClass + + if not IBrowserSubMenuItem.implementedBy(item_class): + raise ValueError("Item class (%s) must implement IBrowserSubMenuItem" % item_class) + + factory = MenuItemFactory( + item_class, + title=title, description=description, icon=icon, action=action, + filter=filter, permission=permission, extra=extra, order=order, + _for=self.for_, submenuId=submenu) + adapter(_context, (factory,), self.menuItemType, + (self.for_, self.layer), name=title) + + def __call__(self, _context): + # Nothing to do. + pass + +def _checkViewFor(for_=None, layer=None, view_name=None): + """Check if there is a view of that name registered for IAdding + and IBrowserRequest. If not raise a ConfigurationError + + It will raise a ConfigurationError if : + o view="" + o if view_name is not registred + """ + + if view_name is None: + raise ConfigurationError( + "Within a addMenuItem directive the view attribut" + " is optional but can\'t be empty" + ) + + gsm = getGlobalSiteManager() + if gsm.adapters.lookup((for_, layer), + Interface, view_name) is None: + raise ConfigurationError( + "view name %s not found " %view_name + ) + +def addMenuItem(_context, title, description='', menu=None, for_=None, + class_=None, factory=None, view=None, icon=None, filter=None, + permission=None, layer=IDefaultBrowserLayer, extra=None, + order=0, item_class=None): + """Create an add menu item for a given class or factory + + As a convenience, a class can be provided, in which case, a + factory is automatically defined based on the class. In this + case, the factory id is based on the class name. + + """ + + if for_ is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', for_) + ) + forname = 'For' + for_.getName() + else: + for_ = IAdding + forname = '' + + if menu is not None: + if isinstance(menu, (str, unicode)): + menu = getUtility(IMenuItemType, menu) + if menu is None: + raise ValueError("Missing menu id '%s'" % menu) + + if class_ is None: + if factory is None: + raise ValueError("Must specify either class or factory") + else: + if factory is not None: + raise ValueError("Can't specify both class and factory") + if permission is None: + raise ValueError( + "A permission must be specified when a class is used") + factory = "BrowserAdd%s__%s.%s" % ( + forname, class_.__module__, class_.__name__) + ClassDirective(_context, class_).factory(_context, id=factory) + + extra = {'factory': factory} + + if view: + action = view + # This action will check if the view exists + _context.action( + discriminator = None, + callable = _checkViewFor, + args = (for_, layer, view), + order=999999 + ) + else: + action = factory + + if menu == None: + menu = AddMenu + + return menuItemsDirective(_context, menu, for_, layer=layer).menuItem( + _context, action, title, description, icon, filter, + permission, extra, order, item_class) diff -Nru zope3-3.4.0/src/zope/browsermenu/metadirectives.py zope3-3.5~bzr18/src/zope/browsermenu/metadirectives.py --- zope3-3.4.0/src/zope/browsermenu/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,242 @@ +############################################################################# +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Menu ZCML directives + +$Id: metadirectives.py 103270 2009-08-27 13:56:02Z nadako $ +""" +from zope.interface import Interface +from zope.configuration.fields import GlobalObject, GlobalInterface +from zope.configuration.fields import Tokens, Path, PythonIdentifier, MessageID +from zope.schema import TextLine, Id, Int, Bool +from zope.security.zcml import Permission + +from zope.component.zcml import IBasicViewInformation +from zope.browsermenu.field import MenuField + + +class IMenuDirective(Interface): + """Define a browser menu""" + + id = TextLine( + title=u"The name of the menu.", + description=u"This is, effectively, an id.", + required=False + ) + + title = MessageID( + title=u"Title", + description=u"A descriptive title for documentation purposes", + required=False + ) + + description = MessageID( + title=u"Description", + description=u"A description title of the menu.", + required=False + ) + + class_ = GlobalObject( + title=u"Menu Class", + description=u"The menu class used to generate the menu.", + required=False + ) + + interface = GlobalInterface( + title=u"The menu's interface.", + required=False + ) + + +class IMenuItemsDirective(Interface): + """ + Define a group of browser menu items + + This directive is useful when many menu items are defined for the + same interface and menu. + """ + + menu = MenuField( + title=u"Menu name", + description=u"The (name of the) menu the items are defined for", + required=True, + ) + + for_ = GlobalObject( + title=u"Interface", + description=u"The interface the menu items are defined for", + required=True + ) + + layer = GlobalInterface( + title=u"Layer", + description=u"The Layer for which the item is declared.", + required=False + ) + + permission = Permission( + title=u"The permission needed access the item", + description=u""" + This can usually be inferred by the system, however, doing so + may be expensive. When displaying a menu, the system tries to + traverse to the URLs given in each action to determine whether + the url is accessible to the current user. This can be + avoided if the permission is given explicitly.""", + required=False + ) + + +class IMenuItem(Interface): + """Common menu item configuration + """ + + title = MessageID( + title=u"Title", + description=u"The text to be displayed for the menu item", + required=True + ) + + description = MessageID( + title=u"A longer explanation of the menu item", + description=u""" + A UI may display this with the item or display it when the + user requests more assistance.""", + required=False + ) + + icon = TextLine( + title=u"Icon Path", + description=u"Path to the icon resource representing this menu item.", + required=False + ) + + permission = Permission( + title=u"The permission needed access the item", + description=u""" + This can usually be inferred by the system, however, doing so + may be expensive. When displaying a menu, the system tries to + traverse to the URLs given in each action to determine whether + the url is accessible to the current user. This can be + avoided if the permission is given explicitly.""", + required=False + ) + + filter = TextLine( + title=u"A condition for displaying the menu item", + description=u""" + The condition is given as a TALES expression. The expression + has access to the variables: + + context -- The object the menu is being displayed for + + request -- The browser request + + nothing -- None + + The menu item will not be displayed if there is a filter and + the filter evaluates to a false value.""", + required=False + ) + + order = Int( + title=u"Order", + description=u"A relative position of the menu item in the menu.", + required=False, + default=0 + ) + + item_class = GlobalObject( + title=u"Menu item class", + description=u""" + A class to be used as a factory for creating menu item""", + required=False + ) + +class IMenuItemSubdirective(IMenuItem): + """Define a menu item within a group of menu items""" + + action = TextLine( + title=u"The relative url to use if the item is selected", + description=u""" + The url is relative to the object the menu is being displayed + for.""", + required=True + ) + +class IMenuItemDirective(IMenuItemsDirective, IMenuItemSubdirective): + """Define one menu item""" + +class ISubMenuItemSubdirective(IMenuItem): + """Define a menu item that represents a a sub menu. + + For a sub-menu menu item, the action is optional, this the item itself + might not represent a destination, but just an entry point to the sub menu. + """ + + action = TextLine( + title=u"The relative url to use if the item is selected", + description=u""" + The url is relative to the object the menu is being displayed + for.""", + required=False + ) + + submenu = TextLine( + title=u"Sub-Menu Id", + description=u"The menu that will be used to provide the sub-entries.", + required=True, + ) + +class ISubMenuItemDirective(IMenuItemsDirective, ISubMenuItemSubdirective): + """Define one menu item""" + +class IAddMenuItemDirective(IMenuItem): + """Define an add-menu item""" + + for_ = GlobalInterface( + title=u"Interface", + description=u"The interface the menu items are defined for", + required=False + ) + + class_ = GlobalObject( + title=u"Class", + description=u""" + A class to be used as a factory for creating new objects""", + required=False + ) + + factory = Id( + title=u"Factory", + description=u"A factory id for creating new objects", + required = False, + ) + + view = TextLine( + title=u"Custom view name", + description=u"The name of a custom add view", + required = False, + ) + + menu = MenuField( + title=u"Menu name", + description=u"The (name of the) menu the items are defined for", + required=False, + ) + + layer = GlobalInterface( + title=u"The layer the custom view is declared for", + description=u"The default layer for which the custom view is " + u"applicable. By default it is applied to all layers.", + required=False + ) diff -Nru zope3-3.4.0/src/zope/browsermenu/meta.zcml zope3-3.5~bzr18/src/zope/browsermenu/meta.zcml --- zope3-3.4.0/src/zope/browsermenu/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/browsermenu/README.txt zope3-3.5~bzr18/src/zope/browsermenu/README.txt --- zope3-3.4.0/src/zope/browsermenu/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,633 @@ +============= +Browser Menus +============= + +Browser menus are used to categorize browser actions, such as the views of a +content component or the addable components of a container. In essence they +provide the same functionality as menu bars in desktop application. + + >>> from zope.browsermenu import menu, metaconfigure + +Menus are simple components that have an id, title and description. They also +must provide a method called ``getMenuItems(object, request)`` that returns a +TAL-friendly list of information dictionaries. We will see this in detail +later. The default menu implementation, however, makes the menu be very +transparent by identifying the menu through an interface. So let's define and +register a simple edit menu: + + >>> import zope.interface + >>> class EditMenu(zope.interface.Interface): + ... """This is an edit menu.""" + + >>> from zope.browsermenu.interfaces import IMenuItemType + >>> zope.interface.directlyProvides(EditMenu, IMenuItemType) + + >>> from zope.component import provideUtility + >>> provideUtility(EditMenu, IMenuItemType, 'edit') + +Now we have to create and register the menu itself: + + >>> from zope.browsermenu.interfaces import IBrowserMenu + >>> provideUtility( + ... menu.BrowserMenu('edit', u'Edit', u'Edit Menu'), IBrowserMenu, 'edit') + +Note that these steps seem like a lot of boilerplate, but all this work is +commonly done for you via ZCML. An item in a menu is simply an adapter that +provides. In the following section we will have a closer look at the browser +menu item: + +``BrowserMenuItem`` class +------------------------- + +The browser menu item represents an entry in the menu. Essentially, the menu +item is a browser view of a content component. Thus we have to create a +content component first: + + >>> class IContent(zope.interface.Interface): + ... pass + + >>> from zope.publisher.interfaces.browser import IBrowserPublisher + >>> from zope.security.interfaces import Unauthorized, Forbidden + + >>> class Content(object): + ... zope.interface.implements(IContent, IBrowserPublisher) + ... + ... def foo(self): + ... pass + ... + ... def browserDefault(self, r): + ... return self, () + ... + ... def publishTraverse(self, request, name): + ... if name.startswith('fb'): + ... raise Forbidden, name + ... if name.startswith('ua'): + ... raise Unauthorized, name + ... if name.startswith('le'): + ... raise LookupError, name + ... return self.foo + +We also implemented the ``IBrowserPublisher`` interface, because we want to +make the object traversable, so that we can make availability checks later. + +Since the ``BrowserMenuItem`` is just a view, we can initiate it with an +object and a request. + + >>> from zope.publisher.browser import TestRequest + >>> item = menu.BrowserMenuItem(Content(), TestRequest()) + +Note that the menu item knows *nothing* about the menu itself. It purely +depends on the adapter registration to determine in which menu it will +appear. The advantage is that a menu item can be reused in several menus. + +Now we add a title, description, order and icon and see whether we can then +access the value. Note that these assignments are always automatically done by +the framework. + + >>> item.title = u'Item 1' + >>> item.title + u'Item 1' + + >>> item.description = u'This is Item 1.' + >>> item.description + u'This is Item 1.' + + >>> item.order + 0 + >>> item.order = 1 + >>> item.order + 1 + + >>> item.icon is None + True + >>> item.icon = u'/@@/icon.png' + >>> item.icon + u'/@@/icon.png' + +Since there is no permission or view specified yet, the menu item should +be available and not selected. + + >>> item.available() + True + >>> item.selected() + False + +There are two ways to deny availability of a menu item: (1) the current +user does not have the correct permission to access the action or the menu +item itself, or (2) the filter returns ``False``, in which case the menu +item should also not be shown. + + >>> from zope.security.interfaces import IPermission + >>> from zope.security.permission import Permission + >>> perm = Permission('perm', 'Permission') + >>> provideUtility(perm, IPermission, 'perm') + + >>> class ParticipationStub(object): + ... principal = 'principal' + ... interaction = None + + +In the first case, the permission of the menu item was explicitely +specified. Make sure that the user needs this permission to make the menu +item available. + + >>> item.permission = perm + +Now, we are not setting any user. This means that the menu item should be +available. + + >>> from zope.security.management import newInteraction, endInteraction + >>> endInteraction() + >>> newInteraction() + >>> item.available() + True + +Now we specify a principal that does not have the specified permission. + + >>> endInteraction() + >>> newInteraction(ParticipationStub()) + >>> item.available() + False + +In the second case, the permission is not explicitely defined and the +availability is determined by the permission required to access the +action. + + >>> item.permission = None + + All views starting with 'fb' are forbidden, the ones with 'ua' are + unauthorized and all others are allowed. + + >>> item.action = u'fb' + >>> item.available() + False + >>> item.action = u'ua' + >>> item.available() + False + >>> item.action = u'a' + >>> item.available() + True + +Also, sometimes a menu item might be registered for a view that does not +exist. In those cases the traversal mechanism raises a `TraversalError`, which +is a special type of `LookupError`. All actions starting with `le` should +raise this error: + + >>> item.action = u'le' + >>> item.available() + False + +Now let's test filtering. If the filter is specified, it is assumed to be +a TALES obejct. + + >>> from zope.pagetemplate.engine import Engine + >>> item.action = u'a' + >>> item.filter = Engine.compile('not:context') + >>> item.available() + False + >>> item.filter = Engine.compile('context') + >>> item.available() + True + +Finally, make sure that the menu item can be selected. + + >>> item.request = TestRequest(SERVER_URL='http://127.0.0.1/@@view.html', + ... PATH_INFO='/@@view.html') + + >>> item.selected() + False + >>> item.action = u'view.html' + >>> item.selected() + True + >>> item.action = u'@@view.html' + >>> item.selected() + True + >>> item.request = TestRequest( + ... SERVER_URL='http://127.0.0.1/++view++view.html', + ... PATH_INFO='/++view++view.html') + >>> item.selected() + True + >>> item.action = u'otherview.html' + >>> item.selected() + False + + +``BrowserSubMenuItem`` class +---------------------------- + +The menu framework also allows for submenus. Submenus can be inserted by +creating a special menu item that simply points to another menu to be +inserted: + + >>> item = menu.BrowserSubMenuItem(Content(), TestRequest()) + +The framework will always set the sub-menu automatically (we do it +manually here): + + >>> class SaveOptions(zope.interface.Interface): + ... "A sub-menu that describes available save options for the content." + + >>> zope.interface.directlyProvides(SaveOptions, IMenuItemType) + + >>> provideUtility(SaveOptions, IMenuItemType, 'save') + >>> provideUtility(menu.BrowserMenu('save', u'Save', u'Save Menu'), + ... IBrowserMenu, 'save') + +Now we can assign the sub-menu id to the menu item: + + >>> item.submenuId = 'save' + +Also, the ``action`` attribute for the browser sub-menu item is optional, +because you often do not want the item itself to represent something. The rest +of the class is identical to the ``BrowserMenuItem`` class. + + +Getting a Menu +-------------- + +Now that we know how the single menu item works, let's have a look at how menu +items get put together to a menu. But let's first create some menu items and +register them as adapters with the component architecture. + +Register the edit menu entries first. We use the menu item factory to create +the items: + + >>> from zope.component import provideAdapter + >>> from zope.publisher.interfaces.browser import IBrowserRequest + + >>> undo = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="Undo", + ... action="undo.html") + >>> provideAdapter(undo, (IContent, IBrowserRequest), EditMenu, 'undo') + + >>> redo = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="Redo", + ... action="redo.html", icon="/@@/redo.png") + >>> provideAdapter(redo, (IContent, IBrowserRequest), EditMenu, 'redo') + + >>> save = metaconfigure.MenuItemFactory(menu.BrowserSubMenuItem, title="Save", + ... submenuId='save', order=2) + >>> provideAdapter(save, (IContent, IBrowserRequest), EditMenu, 'save') + +And now the save options: + + >>> saveas = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="Save as", + ... action="saveas.html") + >>> provideAdapter(saveas, (IContent, IBrowserRequest), + ... SaveOptions, 'saveas') + + >>> saveall = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="Save all", + ... action="saveall.html") + >>> provideAdapter(saveall, (IContent, IBrowserRequest), + ... SaveOptions, 'saveall') + +Note that we can also register menu items for classes: + + + >>> new = metaconfigure.MenuItemFactory(menu.BrowserMenuItem, title="New", + ... action="new.html", _for=Content) + >>> provideAdapter(new, (Content, IBrowserRequest), EditMenu, 'new') + + +The utility that is used to generate the menu into a TAL-friendly +data-structure is ``getMenu()``:: + + getMenu(menuId, object, request) + +where ``menuId`` is the id originally specified for the menu. Let's look up the +menu now: + + >>> pprint(menu.getMenu('edit', Content(), TestRequest())) + [{'action': 'new.html', + 'description': u'', + 'extra': None, + 'icon': None, + 'selected': u'', + 'submenu': None, + 'title': 'New'}, + {'action': 'redo.html', + 'description': u'', + 'extra': None, + 'icon': '/@@/redo.png', + 'selected': u'', + 'submenu': None, + 'title': 'Redo'}, + {'action': 'undo.html', + 'description': u'', + 'extra': None, + 'icon': None, + 'selected': u'', + 'submenu': None, + 'title': 'Undo'}, + {'action': u'', + 'description': u'', + 'extra': None, + 'icon': None, + 'selected': u'', + 'submenu': [{'action': 'saveall.html', + 'description': u'', + 'extra': None, + 'icon': None, + 'selected': u'', + 'submenu': None, + 'title': 'Save all'}, + {'action': 'saveas.html', + 'description': u'', + 'extra': None, + 'icon': None, + 'selected': u'', + 'submenu': None, + 'title': 'Save as'}], + 'title': 'Save'}] + + +Custom ``IBrowserMenu`` Implementations +--------------------------------------- + +Until now we have only seen how to use the default menu implementation. Much +of the above boilerplate was necessary just to support custom menus. But what +could custom menus do? Sometimes menu items are dynamically generated based on +a certain state of the object the menu is for. For example, you might want to +show all items in a folder-like component. So first let's create this +folder-like component: + + >>> class Folderish(Content): + ... names = ['README.txt', 'logo.png', 'script.py'] + +Now we create a menu using the names to create a menu: + + >>> from zope.browsermenu.interfaces import IBrowserMenu + + >>> class Items(object): + ... zope.interface.implements(IBrowserMenu) + ... + ... def __init__(self, id, title=u'', description=u''): + ... self.id = id + ... self.title = title + ... self.description = description + ... + ... def getMenuItems(self, object, request): + ... return [{'title': name, + ... 'description': None, + ... 'action': name + '/manage', + ... 'selected': u'', + ... 'icon': None, + ... 'extra': {}, + ... 'submenu': None} + ... for name in object.names] + +and register it: + + >>> provideUtility(Items('items', u'Items', u'Items Menu'), + ... IBrowserMenu, 'items') + +We can now get the menu items using the previously introduced API: + + >>> pprint(menu.getMenu('items', Folderish(), TestRequest())) + [{'action': 'README.txt/manage', + 'description': None, + 'extra': {}, + 'icon': None, + 'selected': u'', + 'submenu': None, + 'title': 'README.txt'}, + {'action': 'logo.png/manage', + 'description': None, + 'extra': {}, + 'icon': None, + 'selected': u'', + 'submenu': None, + 'title': 'logo.png'}, + {'action': 'script.py/manage', + 'description': None, + 'extra': {}, + 'icon': None, + 'selected': u'', + 'submenu': None, + 'title': 'script.py'}] + + +``MenuItemFactory`` class +------------------------- + +As you have seen above already, we have used the menu item factory to generate +adapter factories for menu items. The factory needs a particular +``IBrowserMenuItem`` class to instantiate. Here is an example using a dummy +menu item class: + + >>> class DummyBrowserMenuItem(object): + ... "a dummy factory for menu items" + ... def __init__(self, context, request): + ... self.context = context + ... self.request = request + +To instantiate this class, pass the factory and the other arguments as keyword +arguments (every key in the arguments should map to an attribute of the menu +item class). We use dummy values for this example. + + >>> factory = metaconfigure.MenuItemFactory( + ... DummyBrowserMenuItem, title='Title', description='Description', + ... icon='Icon', action='Action', filter='Filter', + ... permission='zope.Public', extra='Extra', order='Order', _for='For') + >>> factory.factory is DummyBrowserMenuItem + True + +The "zope.Public" permission needs to be translated to ``CheckerPublic``. + + >>> from zope.security.checker import CheckerPublic + >>> factory.kwargs['permission'] is CheckerPublic + True + +Call the factory with context and request to return the instance. We continue +to use dummy values. + + >>> item = factory('Context', 'Request') + +The returned value should be an instance of the ``DummyBrowserMenuItem``, and +have all of the values we initially set on the factory. + + >>> isinstance(item, DummyBrowserMenuItem) + True + >>> item.context + 'Context' + >>> item.request + 'Request' + >>> item.title + 'Title' + >>> item.description + 'Description' + >>> item.icon + 'Icon' + >>> item.action + 'Action' + >>> item.filter + 'Filter' + >>> item.permission is CheckerPublic + True + >>> item.extra + 'Extra' + >>> item.order + 'Order' + >>> item._for + 'For' + +If you pass a permission other than ``zope.Public`` to the +``MenuItemFactory``, it should pass through unmodified. + + >>> factory = metaconfigure.MenuItemFactory( + ... DummyBrowserMenuItem, title='Title', description='Description', + ... icon='Icon', action='Action', filter='Filter', + ... permission='another.Permission', extra='Extra', order='Order', + ... _for='For_') + >>> factory.kwargs['permission'] + 'another.Permission' + + +Directive Handlers +------------------ + +``menu`` Directive Handler +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Provides a new menu (item type). + + >>> class Context(object): + ... info = u'doc' + ... def __init__(self): + ... self.actions = [] + ... + ... def action(self, **kw): + ... self.actions.append(kw) + +Possibility 1: The Old Way +++++++++++++++++++++++++++ + + >>> context = Context() + >>> metaconfigure.menuDirective(context, u'menu1', title=u'Menu 1') + >>> iface = context.actions[0]['args'][1] + >>> iface.getName() + u'menu1' + + >>> import sys + >>> hasattr(sys.modules['zope.app.menus'], 'menu1') + True + + >>> del sys.modules['zope.app.menus'].menu1 + +Possibility 2: Just specify an interface +++++++++++++++++++++++++++++++++++++++++ + + >>> class menu1(zope.interface.Interface): + ... pass + + >>> context = Context() + >>> metaconfigure.menuDirective(context, interface=menu1) + >>> context.actions[0]['args'][1] is menu1 + True + +Possibility 3: Specify an interface and an id ++++++++++++++++++++++++++++++++++++++++++++++ + + >>> context = Context() + >>> metaconfigure.menuDirective(context, id='menu1', interface=menu1) + + >>> pprint([action['discriminator'] for action in context.actions]) + [('browser', 'MenuItemType', '__builtin__.menu1'), + ('interface', '__builtin__.menu1'), + ('browser', 'MenuItemType', 'menu1'), + ('utility', + , + 'menu1'), + None] + +Here are some disallowed configurations. + + >>> context = Context() + >>> metaconfigure.menuDirective(context) + Traceback (most recent call last): + ... + ConfigurationError: You must specify the 'id' or 'interface' attribute. + + >>> metaconfigure.menuDirective(context, title='Menu 1') + Traceback (most recent call last): + ... + ConfigurationError: You must specify the 'id' or 'interface' attribute. + + +``menuItems`` Directive Handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Register several menu items for a particular menu. + + >>> class TestMenuItemType(zope.interface.Interface): + ... pass + + >>> class ITest(zope.interface.Interface): + ... pass + + >>> context = Context() + >>> items = metaconfigure.menuItemsDirective(context, TestMenuItemType, ITest) + >>> context.actions + [] + >>> items.menuItem(context, u'view.html', 'View') + >>> items.subMenuItem(context, SaveOptions, 'Save') + + >>> disc = [action['discriminator'] for action in context.actions] + >>> disc.sort() + >>> pprint(disc[-2:]) + [('adapter', + (, + ), + , + 'Save'), + ('adapter', + (, + ), + , + 'View')] + +Custom menu item classes +~~~~~~~~~~~~~~~~~~~~~~~~ + +We can register menu items and sub menu items with custom classes instead +of ones used by default. For that, we need to create an implementation +of IBrowserMenuItem or IBrowserSubMenuItem. + + >>> context = Context() + >>> items = metaconfigure.menuItemsDirective(context, TestMenuItemType, ITest) + >>> context.actions + [] + +Let's create a custom menu item class that inherits standard BrowserMenuItem: + + >>> class MyMenuItem(menu.BrowserMenuItem): + ... pass + + >>> items.menuItem(context, u'view.html', 'View', item_class=MyMenuItem) + +Also create a custom sub menu item class inheriting standard BrowserSubMenuItem: + + >>> class MySubMenuItem(menu.BrowserSubMenuItem): + ... pass + + >>> items.subMenuItem(context, SaveOptions, 'Save', item_class=MySubMenuItem) + + >>> actions = sorted(context.actions, key=lambda a:a['discriminator']) + >>> factories = [action['args'][1] for action in actions][-2:] + + >>> factories[0].factory is MySubMenuItem + True + + >>> factories[1].factory is MyMenuItem + True + +These directive will fail if you provide an item_class that does not +implement IBrowserMenuItem/IBrowserSubMenuItem: + + >>> items.menuItem(context, u'fail', 'Failed', item_class=object) + Traceback (most recent call last): + ... + ValueError: Item class () must implement IBrowserMenuItem + + >>> items.subMenuItem(context, SaveOptions, 'Failed', item_class=object) + Traceback (most recent call last): + ... + ValueError: Item class () must implement IBrowserSubMenuItem diff -Nru zope3-3.4.0/src/zope/browsermenu/tests/addmenuitems.zcml zope3-3.5~bzr18/src/zope/browsermenu/tests/addmenuitems.zcml --- zope3-3.4.0/src/zope/browsermenu/tests/addmenuitems.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/tests/addmenuitems.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,23 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/browsermenu/tests/menus-permissions.zcml zope3-3.5~bzr18/src/zope/browsermenu/tests/menus-permissions.zcml --- zope3-3.4.0/src/zope/browsermenu/tests/menus-permissions.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/tests/menus-permissions.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,19 @@ + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/browsermenu/tests/menus.zcml zope3-3.5~bzr18/src/zope/browsermenu/tests/menus.zcml --- zope3-3.4.0/src/zope/browsermenu/tests/menus.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/tests/menus.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/browsermenu/tests/test_addMenuItem.py zope3-3.5~bzr18/src/zope/browsermenu/tests/test_addMenuItem.py --- zope3-3.4.0/src/zope/browsermenu/tests/test_addMenuItem.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/tests/test_addMenuItem.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,427 @@ +############################################################################# +# +# Copyright (c) 2003 Zope Corporation 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 the addMenuItem directive + +>>> context = Context() +>>> addMenuItem(context, class_=X, title="Add an X", +... permission="zope.ManageContent") +>>> context +((('utility', + , + 'BrowserAdd__zope.browsermenu.tests.test_addMenuItem.X'), + , + ('registerUtility', + >, + , + 'BrowserAdd__zope.browsermenu.tests.test_addMenuItem.X'), + {'factory': None}), + (None, + , + ('zope.component.interfaces.IFactory', + ), + {}), + (('adapter', + (, + ), + , + 'Add an X'), + , + ('registerAdapter', + , + (, + ), + , + 'Add an X', + ''), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', ), + {}), + (None, + , + ('', + ), + {})) + +$Id: test_addMenuItem.py 111004 2010-04-16 20:13:36Z tseaver $ +""" + +import unittest +from doctest import DocTestSuite +import re +import pprint +import cStringIO +from zope.interface import Interface +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.browsermenu.metaconfigure import addMenuItem + +atre = re.compile(' at [0-9a-fA-Fx]+') + +class IX(Interface): + pass + +class X(object): + pass + +class ILayerStub(IBrowserRequest): + pass + +class MenuStub(object): + pass + + +class Context(object): + actions = () + info = '' + + def action(self, discriminator, callable, args=(), kw={}, order=0): + self.actions += ((discriminator, callable, args, kw), ) + + def __repr__(self): + stream = cStringIO.StringIO() + pprinter = pprint.PrettyPrinter(stream=stream, width=60) + pprinter.pprint(self.actions) + r = stream.getvalue() + return (''.join(atre.split(r))).strip() + + +def test_w_factory(): + """ + >>> context = Context() + >>> addMenuItem(context, factory="x.y.z", title="Add an X", + ... permission="zope.ManageContent", description="blah blah", + ... filter="context/foo") + >>> context + ((('adapter', + (, + ), + , + 'Add an X'), + , + ('registerAdapter', + , + (, + ), + , + 'Add an X', + ''), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', ), + {}), + (None, + , + ('', + ), + {})) + """ + +def test_w_factory_and_view(): + """ + >>> context = Context() + >>> addMenuItem(context, factory="x.y.z", title="Add an X", + ... permission="zope.ManageContent", description="blah blah", + ... filter="context/foo", view="AddX") + >>> context + ((None, + , + (, + , + 'AddX'), + {}), + (('adapter', + (, + ), + , + 'Add an X'), + , + ('registerAdapter', + , + (, + ), + , + 'Add an X', + ''), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', ), + {}), + (None, + , + ('', + ), + {})) + """ + +def test_w_factory_class_view(): + """ + >>> context = Context() + >>> addMenuItem(context, class_=X, title="Add an X", + ... permission="zope.ManageContent", description="blah blah", + ... filter="context/foo", view="AddX") + >>> import pprint + >>> context + ((('utility', + , + 'BrowserAdd__zope.browsermenu.tests.test_addMenuItem.X'), + , + ('registerUtility', + >, + , + 'BrowserAdd__zope.browsermenu.tests.test_addMenuItem.X'), + {'factory': None}), + (None, + , + ('zope.component.interfaces.IFactory', + ), + {}), + (None, + , + (, + , + 'AddX'), + {}), + (('adapter', + (, + ), + , + 'Add an X'), + , + ('registerAdapter', + , + (, + ), + , + 'Add an X', + ''), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', ), + {}), + (None, + , + ('', + ), + {})) + """ + +def test_w_for_factory(): + """ + >>> context = Context() + >>> addMenuItem(context, for_=IX, factory="x.y.z", title="Add an X", + ... permission="zope.ManageContent", description="blah blah", + ... filter="context/foo") + >>> context + ((None, + , + ('', + ), + {}), + (('adapter', + (, + ), + , + 'Add an X'), + , + ('registerAdapter', + , + (, + ), + , + 'Add an X', + ''), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', + ), + {})) + """ + +def test_w_factory_layer(): + """ + >>> context = Context() + >>> addMenuItem(context, factory="x.y.z", title="Add an X", layer=ILayerStub, + ... permission="zope.ManageContent", description="blah blah", + ... filter="context/foo") + >>> context + ((('adapter', + (, + ), + , + 'Add an X'), + , + ('registerAdapter', + , + (, + ), + , + 'Add an X', + ''), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', ), + {}), + (None, + , + ('', + ), + {})) + """ + +def test_w_for_menu_factory(): + """ + >>> context = Context() + >>> addMenuItem(context, for_=IX, menu=MenuStub, factory="x.y.z", title="Add an X", + ... permission="zope.ManageContent", description="blah blah", + ... filter="context/foo") + >>> context + ((None, + , + ('', + ), + {}), + (('adapter', + (, + ), + , + 'Add an X'), + , + ('registerAdapter', + , + (, + ), + , + 'Add an X', + ''), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', + ), + {})) + """ + +def test_w_factory_icon_extra_order(): + """ + >>> context = Context() + >>> addMenuItem(context, factory="x.y.z", title="Add an X", + ... permission="zope.ManageContent", description="blah blah", + ... filter="context/foo", icon=u'/@@/icon.png', extra='Extra', + ... order=99) + >>> context + ((('adapter', + (, + ), + , + 'Add an X'), + , + ('registerAdapter', + , + (, + ), + , + 'Add an X', + ''), + {}), + (None, + , + ('', + ), + {}), + (None, + , + ('', ), + {}), + (None, + , + ('', + ), + {})) + """ + +from zope.configuration.xmlconfig import XMLConfig + +import zope.browsermenu +import zope.component +from zope.testing import cleanup + +class TestAddMenuItem(cleanup.CleanUp, unittest.TestCase): + + def setUp(self): + super(TestAddMenuItem, self).setUp() + XMLConfig('meta.zcml', zope.component)() + XMLConfig('meta.zcml', zope.browsermenu)() + + def test_addMenuItemDirectives(self): + XMLConfig('tests/addmenuitems.zcml', zope.browsermenu)() + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite(), + unittest.makeSuite(TestAddMenuItem), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/browsermenu/tests/test_directives.py zope3-3.5~bzr18/src/zope/browsermenu/tests/test_directives.py --- zope3-3.4.0/src/zope/browsermenu/tests/test_directives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/tests/test_directives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,194 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""'browser' namespace directive tests + +$Id: test_directives.py 111004 2010-04-16 20:13:36Z tseaver $ +""" + +import sys +import os +import unittest +from cStringIO import StringIO +from doctest import DocTestSuite + +from zope import component +from zope.interface import Interface, implements, directlyProvides, providedBy + +import zope.security.management +from zope.configuration.xmlconfig import xmlconfig, XMLConfig +from zope.configuration.exceptions import ConfigurationError +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.publisher.interfaces.browser import IBrowserSkinType +from zope.security.proxy import removeSecurityProxy, ProxyFactory +from zope.security.permission import Permission +from zope.security.interfaces import IPermission +from zope.traversing.adapters import DefaultTraversable +from zope.traversing.interfaces import ITraversable + +import zope.browsermenu +from zope.component.testfiles.views import IC, V1, VZMI, R1, IV +from zope.browsermenu.menu import getFirstMenuItem, BrowserMenu +from zope.browsermenu.interfaces import IMenuItemType, IBrowserMenu +from zope.testing import cleanup + +tests_path = os.path.join( + os.path.dirname(zope.browsermenu.__file__), + 'tests') + +template = """ + %s + """ + + +request = TestRequest() + +class M1(BrowserMenu): + pass + +class V2(V1, object): + + def action(self): + return self.action2() + + def action2(self): + return "done" + +class VT(V1, object): + def publishTraverse(self, request, name): + try: + return int(name) + except: + return super(VT, self).publishTraverse(request, name) + +class Ob(object): + implements(IC) + +ob = Ob() + +class NCV(object): + "non callable view" + + def __init__(self, context, request): + pass + +class CV(NCV): + "callable view" + def __call__(self): + pass + + +class C_w_implements(NCV): + implements(Interface) + + def index(self): + return self + +class ITestMenu(Interface): + """Test menu.""" + +directlyProvides(ITestMenu, IMenuItemType) + + +class ITestLayer(IBrowserRequest): + """Test Layer.""" + +class ITestSkin(ITestLayer): + """Test Skin.""" + + +class MyResource(object): + + def __init__(self, request): + self.request = request + + +class Test(cleanup.CleanUp, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + XMLConfig('meta.zcml', zope.browsermenu)() + component.provideAdapter(DefaultTraversable, (None,), ITraversable) + + def tearDown(self): + if 'test_menu' in dir(sys.modules['zope.app.menus']): + delattr(sys.modules['zope.app.menus'], 'test_menu') + super(Test, self).tearDown() + + def testMenuOverride(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % ( + ''' + + + ''' + ))) + menu1 = component.getUtility(IBrowserMenu, 'test_menu') + menuItem1 = getFirstMenuItem('test_menu', ob, TestRequest()) + xmlconfig(StringIO(template % ( + ''' + + ''' + ))) + menu2 = component.getUtility(IBrowserMenu, 'test_menu') + menuItem2 = getFirstMenuItem('test_menu', ob, TestRequest()) + self.assertNotEqual(menu1, menu2) + self.assertEqual(menuItem1, menuItem2) + + + def testMenuItemNeedsFor(self): + # directive fails if no 'for' argument was provided + from zope.configuration.exceptions import ConfigurationError + self.assertRaises(ConfigurationError, xmlconfig, StringIO(template % + ''' + + + ''' + )) + + # it works, when the argument is there and a valid interface + xmlconfig(StringIO(template % + ''' + + ''' + )) + +def test_suite(): + return unittest.makeSuite(Test) diff -Nru zope3-3.4.0/src/zope/browsermenu/tests/test_fields.py zope3-3.5~bzr18/src/zope/browsermenu/tests/test_fields.py --- zope3-3.4.0/src/zope/browsermenu/tests/test_fields.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/tests/test_fields.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 fields. + +$Id: test_fields.py 111004 2010-04-16 20:13:36Z tseaver $ +""" +import unittest +import doctest +from zope.testing import cleanup + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite('zope.browsermenu.field', + setUp=lambda test:cleanup.setUp(), + tearDown=lambda test:cleanup.tearDown()), + )) diff -Nru zope3-3.4.0/src/zope/browsermenu/tests/test_menudirectives.py zope3-3.5~bzr18/src/zope/browsermenu/tests/test_menudirectives.py --- zope3-3.4.0/src/zope/browsermenu/tests/test_menudirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/tests/test_menudirectives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,163 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Browser Menu Directives Tests + +$Id: test_menudirectives.py 103270 2009-08-27 13:56:02Z nadako $ +""" +import unittest + +from zope.configuration.xmlconfig import XMLConfig +from zope.interface import Interface, implements +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.browsermenu.interfaces import IBrowserMenu +from zope.security.interfaces import Unauthorized, Forbidden +import zope.component + +import zope.security + +from zope.testing import cleanup + +import zope.browsermenu + + +template = """ + %s + """ + +class I1(Interface): pass +class I11(I1): pass +class I12(I1): pass +class I111(I11): pass + + +class C1(object): + implements(I1) + +class I2(Interface): pass + +class C2(object): + implements(I2) + +class TestObject(object): + implements(IBrowserPublisher, I111) + + def f(self): + pass + + def browserDefault(self, r): + return self, () + + def publishTraverse(self, request, name): + if name[:1] == 'f': + raise Forbidden(name) + if name[:1] == 'u': + raise Unauthorized(name) + return self.f + +class IMyLayer(Interface): + pass + +class IMySkin(IMyLayer, IDefaultBrowserLayer): + pass + + +class TestPermissions(cleanup.CleanUp, unittest.TestCase): + + def setUp(self): + super(TestPermissions, self).setUp() + XMLConfig('meta.zcml', zope.browsermenu)() + XMLConfig('meta.zcml', zope.security)() + + def testMenuItemsPermission(self): + XMLConfig('tests/menus-permissions.zcml', zope.browsermenu)() + + menu = zope.component.getUtility(IBrowserMenu, 'test_id') + # This is a bit icky, but the menu hides too much stuff from us. + items = zope.component.getAdapters((C2(), TestRequest()), + menu.getMenuItemType()) + item = list(items)[0][1] + self.assertEquals("zope.View", item.permission) + + +class Test(cleanup.CleanUp, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + XMLConfig('meta.zcml', zope.browsermenu)() + + def testMenusAndMenuItems(self): + XMLConfig('tests/menus.zcml', zope.browsermenu)() + + menu = zope.browsermenu.menu.getMenu( + 'test_id', TestObject(), TestRequest()) + + def d(n): + return {'action': "a%s" % n, + 'title': "t%s" % n, + 'description': u'', + 'selected': '', + 'submenu': None, + 'icon': None, + 'extra': None} + + self.assertEqual(menu[:-1], [d(5), d(6), d(3), d(2), d(1)]) + self.assertEqual( + menu[-1], + {'submenu': [{'submenu': None, + 'description': u'', + 'extra': None, + 'selected': u'', + 'action': u'a10', + 'title': u't10', + 'icon': None}], + 'description': u'', + 'extra': None, + 'selected': u'', + 'action': u'', + 'title': u's1', + 'icon': None}) + + first = zope.browsermenu.menu.getFirstMenuItem( + 'test_id', TestObject(), TestRequest()) + + self.assertEqual(first, d(5)) + + def testMenuItemWithLayer(self): + XMLConfig('tests/menus.zcml', zope.browsermenu)() + + menu = zope.browsermenu.menu.getMenu( + 'test_id', TestObject(), TestRequest()) + self.assertEqual(len(menu), 6) + + menu = zope.browsermenu.menu.getMenu( + 'test_id', TestObject(), TestRequest(skin=IMyLayer)) + self.assertEqual(len(menu), 2) + + menu = zope.browsermenu.menu.getMenu( + 'test_id', TestObject(), TestRequest(skin=IMySkin)) + self.assertEqual(len(menu), 8) + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test), + unittest.makeSuite(TestPermissions), + )) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/browsermenu/tests/test_menu.py zope3-3.5~bzr18/src/zope/browsermenu/tests/test_menu.py --- zope3-3.4.0/src/zope/browsermenu/tests/test_menu.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browsermenu/tests/test_menu.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,30 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Browser Menu Item Tests + +$Id: test_menu.py 111004 2010-04-16 20:13:36Z tseaver $ +""" +import unittest +import doctest +import pprint +from zope.testing import cleanup + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('../README.txt', + setUp=lambda test:cleanup.setUp(), + tearDown=lambda test:cleanup.tearDown(), + globs={'pprint': pprint.pprint}, + optionflags=doctest.NORMALIZE_WHITESPACE), + )) diff -Nru zope3-3.4.0/src/zope/browserpage/configure.zcml zope3-3.5~bzr18/src/zope/browserpage/configure.zcml --- zope3-3.4.0/src/zope/browserpage/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/browserpage/__init__.py zope3-3.5~bzr18/src/zope/browserpage/__init__.py --- zope3-3.4.0/src/zope/browserpage/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/browserpage/metaconfigure.py zope3-3.5~bzr18/src/zope/browserpage/metaconfigure.py --- zope3-3.4.0/src/zope/browserpage/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,462 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Browser page ZCML configuration code + +$Id: metaconfigure.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +import os + +from zope.component import queryMultiAdapter +from zope.component.interface import provideInterface +from zope.component.zcml import handler +from zope.interface import implements, classImplements, Interface +from zope.publisher.interfaces import NotFound +from zope.security.checker import CheckerPublic, Checker, defineChecker +from zope.configuration.exceptions import ConfigurationError +from zope.pagetemplate.engine import Engine +from zope.pagetemplate.engine import _Engine +from zope.pagetemplate.engine import TrustedEngine +from zope.pagetemplate.engine import _TrustedEngine +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.browser import BrowserView + +from zope.browserpage.simpleviewclass import SimpleViewClass +from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile + +try: + from zope.browsermenu.metaconfigure import menuItemDirective +except ImportError: + menuItemDirective = None + +# There are three cases we want to suport: +# +# Named view without pages (single-page view) +# +# +# +# Unnamed view with pages (multi-page view) +# +# +# +# +# +# +# +# Named view with pages (add view is a special case of this) +# +# +# +# +# +# + +# We'll also provide a convenience directive for add views: +# +# +# +# +# +# + +# page + +def page(_context, name, permission, for_=Interface, + layer=IDefaultBrowserLayer, template=None, class_=None, + allowed_interface=None, allowed_attributes=None, + attribute='__call__', menu=None, title=None, + ): + _handle_menu(_context, menu, title, [for_], name, permission, layer) + required = {} + + permission = _handle_permission(_context, permission) + + if not (class_ or template): + raise ConfigurationError("Must specify a class or template") + + if attribute != '__call__': + if template: + raise ConfigurationError( + "Attribute and template cannot be used together.") + + if not class_: + raise ConfigurationError( + "A class must be provided if attribute is used") + + if template: + template = os.path.abspath(str(_context.path(template))) + if not os.path.isfile(template): + raise ConfigurationError("No such file", template) + required['__getitem__'] = permission + + # TODO: new __name__ attribute must be tested + if class_: + if attribute != '__call__': + if not hasattr(class_, attribute): + raise ConfigurationError( + "The provided class doesn't have the specified attribute " + ) + if template: + # class and template + new_class = SimpleViewClass(template, bases=(class_, ), name=name) + else: + if not hasattr(class_, 'browserDefault'): + cdict = { + 'browserDefault': + lambda self, request: (getattr(self, attribute), ()) + } + else: + cdict = {} + + cdict['__name__'] = name + cdict['__page_attribute__'] = attribute + new_class = type(class_.__name__, (class_, simple,), cdict) + + if hasattr(class_, '__implements__'): + classImplements(new_class, IBrowserPublisher) + + else: + # template + new_class = SimpleViewClass(template, name=name) + + for n in (attribute, 'browserDefault', '__call__', 'publishTraverse'): + required[n] = permission + + _handle_allowed_interface(_context, allowed_interface, permission, + required) + _handle_allowed_attributes(_context, allowed_attributes, permission, + required) + + _handle_for(_context, for_) + + defineChecker(new_class, Checker(required)) + + _context.action( + discriminator = ('view', for_, name, IBrowserRequest, layer), + callable = handler, + args = ('registerAdapter', + new_class, (for_, layer), Interface, name, _context.info), + ) + + +# pages, which are just a short-hand for multiple page directives. + +# Note that a class might want to access one of the defined +# templates. If it does though, it should use getMultiAdapter. + +class pages(object): + + def __init__(self, _context, permission, for_=Interface, + layer=IDefaultBrowserLayer, class_=None, + allowed_interface=None, allowed_attributes=None, + ): + self.opts = dict(for_=for_, permission=permission, + layer=layer, class_=class_, + allowed_interface=allowed_interface, + allowed_attributes=allowed_attributes, + ) + + def page(self, _context, name, attribute='__call__', template=None, + menu=None, title=None): + return page(_context, + name=name, + attribute=attribute, + template=template, + menu=menu, title=title, + **(self.opts)) + + def __call__(self): + return () + +# view (named view with pages) + +# This is a different case. We actually build a class with attributes +# for all of the given pages. + +class view(object): + + default = None + + def __init__(self, _context, permission, for_=Interface, + name='', layer=IDefaultBrowserLayer, class_=None, + allowed_interface=None, allowed_attributes=None, + menu=None, title=None, provides=Interface, + ): + + _handle_menu(_context, menu, title, [for_], name, permission, layer) + + permission = _handle_permission(_context, permission) + + self.args = (_context, name, for_, permission, layer, class_, + allowed_interface, allowed_attributes) + + self.pages = [] + self.menu = menu + self.provides = provides + + def page(self, _context, name, attribute=None, template=None): + if template: + template = os.path.abspath(_context.path(template)) + if not os.path.isfile(template): + raise ConfigurationError("No such file", template) + else: + if not attribute: + raise ConfigurationError( + "Must specify either a template or an attribute name") + + self.pages.append((name, attribute, template)) + return () + + def defaultPage(self, _context, name): + self.default = name + return () + + def __call__(self): + (_context, name, for_, permission, layer, class_, + allowed_interface, allowed_attributes) = self.args + + required = {} + + cdict = {} + pages = {} + + for pname, attribute, template in self.pages: + + if template: + cdict[pname] = ViewPageTemplateFile(template) + if attribute and attribute != name: + cdict[attribute] = cdict[pname] + else: + if not hasattr(class_, attribute): + raise ConfigurationError("Undefined attribute", + attribute) + + attribute = attribute or pname + required[pname] = permission + + pages[pname] = attribute + + # This should go away, but noone seems to remember what to do. :-( + if hasattr(class_, 'publishTraverse'): + + def publishTraverse(self, request, name, + pages=pages, getattr=getattr): + + if name in pages: + return getattr(self, pages[name]) + view = queryMultiAdapter((self, request), name=name) + if view is not None: + return view + + m = class_.publishTraverse.__get__(self) + return m(request, name) + + else: + def publishTraverse(self, request, name, + pages=pages, getattr=getattr): + + if name in pages: + return getattr(self, pages[name]) + view = queryMultiAdapter((self, request), name=name) + if view is not None: + return view + + raise NotFound(self, name, request) + + cdict['publishTraverse'] = publishTraverse + + if not hasattr(class_, 'browserDefault'): + if self.default or self.pages: + default = self.default or self.pages[0][0] + cdict['browserDefault'] = ( + lambda self, request, default=default: + (self, (default, )) + ) + elif providesCallable(class_): + cdict['browserDefault'] = ( + lambda self, request: (self, ()) + ) + + if class_ is not None: + bases = (class_, simple) + else: + bases = (simple,) + + try: + cname = str(name) + except: + cname = "GeneratedClass" + + cdict['__name__'] = name + newclass = type(cname, bases, cdict) + + for n in ('publishTraverse', 'browserDefault', '__call__'): + required[n] = permission + + _handle_allowed_interface(_context, allowed_interface, permission, + required) + _handle_allowed_attributes(_context, allowed_attributes, permission, + required) + _handle_for(_context, for_) + + defineChecker(newclass, Checker(required)) + + if self.provides is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', self.provides) + ) + + _context.action( + discriminator = ('view', (for_, layer), name, self.provides), + callable = handler, + args = ('registerAdapter', + newclass, (for_, layer), self.provides, name, + _context.info), + ) + +def _handle_menu(_context, menu, title, for_, name, permission, + layer=IDefaultBrowserLayer): + + if menu or title: + if not (menu and title): + raise ConfigurationError( + "If either menu or title are specified, they must " + "both be specified.") + if len(for_) != 1: + raise ConfigurationError( + "Menus can be specified only for single-view, not for " + "multi-views.") + + if menuItemDirective is None: + import warnings + warnings.warn_explicit( + 'Page directive used with "menu" argument, while "zope.browsermenu" ' + 'package is not installed. Doing nothing.', + UserWarning, + _context.info.file, _context.info.line) + return [] + + return menuItemDirective( + _context, menu, for_[0], '@@' + name, title, + permission=permission, layer=layer) + + return [] + +def _handle_permission(_context, permission): + if permission == 'zope.Public': + permission = CheckerPublic + + return permission + +def _handle_allowed_interface(_context, allowed_interface, permission, + required): + # Allow access for all names defined by named interfaces + if allowed_interface: + for i in allowed_interface: + _context.action( + discriminator = None, + callable = provideInterface, + args = (None, i) + ) + + for name in i: + required[name] = permission + +def _handle_allowed_attributes(_context, allowed_attributes, permission, + required): + # Allow access for all named attributes + if allowed_attributes: + for name in allowed_attributes: + required[name] = permission + +def _handle_for(_context, for_): + if for_ is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', for_) + ) + +class simple(BrowserView): + implements(IBrowserPublisher) + + def publishTraverse(self, request, name): + raise NotFound(self, name, request) + + def __call__(self, *a, **k): + # If a class doesn't provide it's own call, then get the attribute + # given by the browser default. + + attr = self.__page_attribute__ + if attr == '__call__': + raise AttributeError("__call__") + + meth = getattr(self, attr) + return meth(*a, **k) + +def providesCallable(class_): + if hasattr(class_, '__call__'): + for c in class_.__mro__: + if '__call__' in c.__dict__: + return True + return False + + +def expressiontype(_context, name, handler): + _context.action( + discriminator = ("tales:expressiontype", name), + callable = registerType, + args = (name, handler) + ) + +def registerType(name, handler): + Engine.registerType(name, handler) + TrustedEngine.registerType(name, handler) + + +def clear(): + Engine.__init__() + _Engine(Engine) + TrustedEngine.__init__() + _TrustedEngine(TrustedEngine) + + +try: + from zope.testing.cleanup import addCleanUp +except ImportError: + pass +else: + addCleanUp(clear) diff -Nru zope3-3.4.0/src/zope/browserpage/metadirectives.py zope3-3.5~bzr18/src/zope/browserpage/metadirectives.py --- zope3-3.4.0/src/zope/browserpage/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,206 @@ +############################################################################# +# +# 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. +# +############################################################################## +"""ZCML directives for defining browser pages + +$Id: metadirectives.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +from zope.interface import Interface +from zope.configuration.fields import GlobalObject, GlobalInterface +from zope.configuration.fields import Tokens, Path, PythonIdentifier, MessageID +from zope.schema import TextLine, Id, Int, Bool +from zope.security.zcml import Permission + +from zope.component.zcml import IBasicViewInformation + +try: + from zope.browsermenu.field import MenuField +except ImportError: # avoid hard dependency on zope.browsermenu + MenuField = TextLine + + +class IPagesDirective(IBasicViewInformation): + """ + Define multiple pages without repeating all of the parameters. + + The pages directive allows multiple page views to be defined + without repeating the 'for', 'permission', 'class', 'layer', + 'allowed_attributes', and 'allowed_interface' attributes. + """ + + for_ = GlobalObject( + title=u"The interface or class this view is for.", + required=False + ) + + permission = Permission( + title=u"Permission", + description=u"The permission needed to use the view.", + required=True + ) + +class IViewDirective(IPagesDirective): + """ + The view directive defines a view that has subpages. + + The pages provided by the defined view are accessed by first + traversing to the view name and then traversing to the page name. + """ + + for_ = GlobalInterface( + title=u"The interface this view is for.", + required=False + ) + + name = TextLine( + title=u"The name of the view.", + description=u"The name shows up in URLs/paths. For example 'foo'.", + required=False, + default=u'', + ) + + menu = MenuField( + title=u"The browser menu to include the page (view) in.", + description=u""" + Many views are included in menus. It's convenient to name + the menu in the page directive, rather than having to give a + separate menuItem directive. 'zmi_views' is the menu most often + used in the Zope management interface. + + This attribute will only work if zope.browsermenu is installed. + """, + required=False + ) + + title = MessageID( + title=u"The browser menu label for the page (view)", + description=u""" + This attribute must be supplied if a menu attribute is + supplied. + + This attribute will only work if zope.browsermenu is installed. + """, + required=False + ) + + provides = GlobalInterface( + title=u"The interface this view provides.", + description=u""" + A view can provide an interface. This would be used for + views that support other views.""", + required=False, + default=Interface, + ) + +class IViewPageSubdirective(Interface): + """ + Subdirective to IViewDirective. + """ + + name = TextLine( + title=u"The name of the page (view)", + description=u""" + The name shows up in URLs/paths. For example 'foo' or + 'foo.html'. This attribute is required unless you use the + subdirective 'page' to create sub views. If you do not have + sub pages, it is common to use an extension for the view name + such as '.html'. If you do have sub pages and you want to + provide a view name, you shouldn't use extensions.""", + required=True + ) + + attribute = PythonIdentifier( + title=u"The name of the view attribute implementing the page.", + description=u""" + This refers to the attribute (method) on the view that is + implementing a specific sub page.""", + required=False + ) + + template = Path( + title=u"The name of a template that implements the page.", + description=u""" + Refers to a file containing a page template (should end in + extension '.pt' or '.html').""", + required=False + ) + +class IViewDefaultPageSubdirective(Interface): + """ + Subdirective to IViewDirective. + """ + + name = TextLine( + title=u"The name of the page that is the default.", + description=u""" + The named page will be used as the default if no name is + specified explicitly in the path. If no defaultPage directive + is supplied, the default page will be the first page + listed.""", + required=True + ) + + +class IPagesPageSubdirective(IViewPageSubdirective): + """ + Subdirective to IPagesDirective + """ + + menu = MenuField( + title=u"The browser menu to include the page (view) in.", + description=u""" + Many views are included in menus. It's convenient to name the + menu in the page directive, rather than having to give a + separate menuItem directive. + + This attribute will only work if zope.browsermenu is installed. + """, + required=False + ) + + title = MessageID( + title=u"The browser menu label for the page (view)", + description=u""" + This attribute must be supplied if a menu attribute is + supplied. + + This attribute will only work if zope.browsermenu is installed. + """, + required=False + ) + +class IPageDirective(IPagesDirective, IPagesPageSubdirective): + """ + The page directive is used to create views that provide a single + url or page. + + The page directive creates a new view class from a given template + and/or class and registers it. + """ + +class IExpressionTypeDirective(Interface): + """Register a new TALES expression type""" + + name = TextLine( + title=u"Name", + description=u"""Name of the expression. This will also be used + as the prefix in actual TALES expressions.""", + required=True + ) + + handler = GlobalObject( + title=u"Handler", + description=u"""Handler is class that implements + zope.tales.interfaces.ITALESExpression.""", + required=True + ) diff -Nru zope3-3.4.0/src/zope/browserpage/meta.zcml zope3-3.5~bzr18/src/zope/browserpage/meta.zcml --- zope3-3.4.0/src/zope/browserpage/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/browserpage/namedtemplate.py zope3-3.5~bzr18/src/zope/browserpage/namedtemplate.py --- zope3-3.4.0/src/zope/browserpage/namedtemplate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/namedtemplate.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,75 @@ +############################################################################## +# +# Copyright (c) 2005-2009 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. +# +############################################################################## +""" +""" + +from zope import component, interface +import zope.traversing.interfaces + +class INamedTemplate(interface.Interface): + """A template that is looked up by name + """ + +class NamedTemplateImplementation: + + def __init__(self, descriptor, view_type=None): + try: + descriptor.__get__ + except AttributeError: + raise TypeError( + "NamedTemplateImplementation must be passed a descriptor." + ) + self.descriptor = descriptor + interface.implementer(INamedTemplate)(self) + + if view_type is not None: + component.adapter(view_type)(self) + + def __call__(self, instance): + return self.descriptor.__get__(instance, instance.__class__) + + +class implementation: + + def __init__(self, view_type=None): + self.view_type = view_type + + def __call__(self, descriptor): + return NamedTemplateImplementation(descriptor, self.view_type) + + +class NamedTemplate(object): + + def __init__(self, name): + self.__name__ = name + + def __get__(self, instance, type=None): + if instance is None: + return self + return component.getAdapter(instance, INamedTemplate, self.__name__) + + def __call__(self, instance, *args, **kw): + self.__get__(instance)(*args, **kw) + + +# TODO need test +class NamedTemplatePathAdapter(object): + + interface.implements(zope.traversing.interfaces.IPathAdapter) + + def __init__(self, context): + self.context = context + + def __getitem__(self, name): + return component.getAdapter(self.context, INamedTemplate, name) diff -Nru zope3-3.4.0/src/zope/browserpage/namedtemplate.txt zope3-3.5~bzr18/src/zope/browserpage/namedtemplate.txt --- zope3-3.4.0/src/zope/browserpage/namedtemplate.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/namedtemplate.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,73 @@ +=============== +Named Templates +=============== + +We often want to be able to define view logic and view templates +independently. We'd like to be able to change the template used by a +form without being forced to modify the form. + +Named templates provide templates that are registered as named view +adapters. To define a named template, use the `NamedTemplateImplementation` +constructor: + + >>> from zope.browserpage import ViewPageTemplateFile + >>> from zope.browserpage.namedtemplate import ( + ... NamedTemplateImplementation) + >>> sample = ViewPageTemplateFile('tests/namedtemplate.pt') + >>> sample = NamedTemplateImplementation(sample) + +Let's define a view that uses the named template. To use a named +template, use the NamedTemplate constructor, and give a template name: + + >>> from zope.browserpage.namedtemplate import NamedTemplate + >>> class MyView: + ... def __init__(self, context, request): + ... self.context = context + ... self.request = request + ... + ... __call__ = NamedTemplate('sample') + +Normally, we'd register a named template for a view interface, to +allow it to be registered for multiple views. We'll just register it +for our view class. + + >>> from zope import component + >>> component.provideAdapter(sample, [MyView], name='sample') + +Now, with this in place, we should be able to use our view: + + >>> class MyContent: + ... def __init__(self, name): + ... self.name = name + + >>> from zope.publisher.browser import TestRequest + >>> print MyView(MyContent('bob'), TestRequest())(x=42) + + Hello bob + The URL is http://127.0.0.1 + The positional arguments were () + The keyword argument x is 42 + + + +The view type that a named template is to be used for can be supplied +when the named template is created: + + >>> class MyView: + ... def __init__(self, context, request): + ... self.context = context + ... self.request = request + ... + ... __call__ = NamedTemplate('sample2') + + >>> sample = ViewPageTemplateFile('tests/namedtemplate.pt') + >>> sample = NamedTemplateImplementation(sample, MyView) + >>> component.provideAdapter(sample, name='sample2') + >>> print MyView(MyContent('bob'), TestRequest())(x=42) + + Hello bob + The URL is http://127.0.0.1 + The positional arguments were () + The keyword argument x is 42 + + diff -Nru zope3-3.4.0/src/zope/browserpage/simpleviewclass.py zope3-3.5~bzr18/src/zope/browserpage/simpleviewclass.py --- zope3-3.4.0/src/zope/browserpage/simpleviewclass.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/simpleviewclass.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,60 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Simple View Class + +$Id: simpleviewclass.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import sys +from zope.interface import implements +from zope.publisher.browser import BrowserView +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.interfaces import NotFound +from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile + +class simple(BrowserView): + + implements(IBrowserPublisher) + + def browserDefault(self, request): + return self, () + + def publishTraverse(self, request, name): + if name == 'index.html': + return self.index + + raise NotFound(self, name, request) + + def __getitem__(self, name): + return self.index.macros[name] + + def __call__(self, *args, **kw): + return self.index(*args, **kw) + + +def SimpleViewClass(src, offering=None, used_for=None, bases=(), name=u''): + if offering is None: + offering = sys._getframe(1).f_globals + + bases += (simple, ) + + class_ = type("SimpleViewClass from %s" % src, bases, + {'index': ViewPageTemplateFile(src, offering), + '__name__': name}) + + if used_for is not None: + class_.__used_for__ = used_for + + return class_ diff -Nru zope3-3.4.0/src/zope/browserpage/tests/__init__.py zope3-3.5~bzr18/src/zope/browserpage/tests/__init__.py --- zope3-3.4.0/src/zope/browserpage/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/browserpage/tests/namedtemplate.pt zope3-3.5~bzr18/src/zope/browserpage/tests/namedtemplate.pt --- zope3-3.4.0/src/zope/browserpage/tests/namedtemplate.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/namedtemplate.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ + +Hello +The URL is +The positional arguments were +The keyword argument x is + diff -Nru zope3-3.4.0/src/zope/browserpage/tests/sample.py zope3-3.5~bzr18/src/zope/browserpage/tests/sample.py --- zope3-3.4.0/src/zope/browserpage/tests/sample.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/sample.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Sample Component + +$Id: sample.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +from zope.browserpage import ViewPageTemplateFile + +class C(object): + index = ViewPageTemplateFile('test.pt') diff -Nru zope3-3.4.0/src/zope/browserpage/tests/simpletestview.py zope3-3.5~bzr18/src/zope/browserpage/tests/simpletestview.py --- zope3-3.4.0/src/zope/browserpage/tests/simpletestview.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/simpletestview.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Simple Test View + +$Id: simpletestview.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +from zope.browserpage.simpleviewclass import SimpleViewClass + +SimpleTestView = SimpleViewClass('testsimpleviewclass.pt') diff -Nru zope3-3.4.0/src/zope/browserpage/tests/test_boundpagetemplate.py zope3-3.5~bzr18/src/zope/browserpage/tests/test_boundpagetemplate.py --- zope3-3.4.0/src/zope/browserpage/tests/test_boundpagetemplate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/test_boundpagetemplate.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,37 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Bound Page Template Tests + +$Id: test_boundpagetemplate.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +import unittest + +class Test(unittest.TestCase): + + def testAttributes(self): + + from zope.browserpage.tests.sample import C + + C.index.im_func.foo = 1 + self.assertEqual(C.index.macros, C.index.im_func.macros) + self.assertEqual(C.index.filename, C.index.im_func.filename) + + + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/browserpage/tests/test_expressiontype.py zope3-3.5~bzr18/src/zope/browserpage/tests/test_expressiontype.py --- zope3-3.4.0/src/zope/browserpage/tests/test_expressiontype.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/test_expressiontype.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,60 @@ +############################################################################## +# +# 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 to check talesapi zcml configuration + +$Id: test_expressiontype.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +import unittest + +from cStringIO import StringIO +from zope.configuration.xmlconfig import xmlconfig, XMLConfig +from zope.pagetemplate.engine import Engine +import zope.browserpage + +from zope.component.testing import PlacelessSetup + +template = """ + %s + """ + + +class Handler(object): + pass + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + XMLConfig('meta.zcml', zope.browserpage)() + + def testExpressionType(self): + xmlconfig(StringIO(template % ( + """ + + """ + ))) + self.assert_("test" in Engine.getTypes()) + self.assert_(Handler is Engine.getTypes()['test']) + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/browserpage/tests/testfiles/test2.pt zope3-3.5~bzr18/src/zope/browserpage/tests/testfiles/test2.pt --- zope3-3.4.0/src/zope/browserpage/tests/testfiles/test2.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/testfiles/test2.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

test

diff -Nru zope3-3.4.0/src/zope/browserpage/tests/testfiles/test3.pt zope3-3.5~bzr18/src/zope/browserpage/tests/testfiles/test3.pt --- zope3-3.4.0/src/zope/browserpage/tests/testfiles/test3.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/testfiles/test3.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

test

diff -Nru zope3-3.4.0/src/zope/browserpage/tests/testfiles/test.pt zope3-3.5~bzr18/src/zope/browserpage/tests/testfiles/test.pt --- zope3-3.4.0/src/zope/browserpage/tests/testfiles/test.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/testfiles/test.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

test

diff -Nru zope3-3.4.0/src/zope/browserpage/tests/test_namedtemplate.py zope3-3.5~bzr18/src/zope/browserpage/tests/test_namedtemplate.py --- zope3-3.4.0/src/zope/browserpage/tests/test_namedtemplate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/test_namedtemplate.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,39 @@ +############################################################################## +# +# Copyright (c) 2005-2009 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. +# +############################################################################## +""" + +$Id: test_namedtemplate.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +import os +import os.path +import zope.component.testing +import zope.traversing.adapters + + +def pageSetUp(test): + zope.component.testing.setUp(test) + zope.component.provideAdapter( + zope.traversing.adapters.DefaultTraversable, + [None], + ) + + +def test_suite(): + import doctest + filename = os.path.join(os.pardir, 'namedtemplate.txt') + return doctest.DocFileSuite( + filename, + setUp=pageSetUp, tearDown=zope.component.testing.tearDown, + globs={'__file__': os.path.abspath(os.path.join(os.path.dirname(__file__), filename))} + ) diff -Nru zope3-3.4.0/src/zope/browserpage/tests/test_page.py zope3-3.5~bzr18/src/zope/browserpage/tests/test_page.py --- zope3-3.4.0/src/zope/browserpage/tests/test_page.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/test_page.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,961 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Tests for browser:page directive and friends + +$Id: test_page.py 111996 2010-05-05 17:34:04Z tseaver $ +""" + +import sys +import os +import unittest +from doctest import DocTestSuite +from cStringIO import StringIO + +from zope import component +from zope.interface import Interface, implements, directlyProvides, providedBy + +import zope.security.management +from zope.configuration.xmlconfig import xmlconfig, XMLConfig +from zope.configuration.exceptions import ConfigurationError +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces import IDefaultViewName +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.publisher.interfaces.browser import IBrowserSkinType, IDefaultSkin +from zope.security.proxy import removeSecurityProxy, ProxyFactory +from zope.security.permission import Permission +from zope.security.interfaces import IPermission +from zope.testing import cleanup +from zope.traversing.adapters import DefaultTraversable +from zope.traversing.interfaces import ITraversable + +import zope.publisher.defaultview +import zope.browserpage +import zope.browsermenu +from zope.browsermenu.menu import getFirstMenuItem +from zope.browsermenu.interfaces import IMenuItemType +from zope.component.testfiles.views import IC, V1, VZMI, R1, IV + +tests_path = os.path.dirname(__file__) + +template = """ + %s + """ + +class templateclass(object): + def data(self): return 42 + +request = TestRequest() + +class V2(V1, object): + + def action(self): + return self.action2() + + def action2(self): + return "done" + +class VT(V1, object): + def publishTraverse(self, request, name): + try: + return int(name) + except: + return super(VT, self).publishTraverse(request, name) + +class Ob(object): + implements(IC) + +ob = Ob() + +class NCV(object): + "non callable view" + + def __init__(self, context, request): + pass + +class CV(NCV): + "callable view" + def __call__(self): + pass + + +class C_w_implements(NCV): + implements(Interface) + + def index(self): + return self + +class ITestLayer(IBrowserRequest): + """Test Layer.""" + +class ITestSkin(ITestLayer): + """Test Skin.""" + + +class ITestMenu(Interface): + """Test menu.""" + +directlyProvides(ITestMenu, IMenuItemType) + +class Test(cleanup.CleanUp, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + XMLConfig('meta.zcml', zope.browserpage)() + XMLConfig('meta.zcml', zope.browsermenu)() + component.provideAdapter(DefaultTraversable, (None,), ITraversable, ) + zope.security.management.newInteraction() + + def testPage(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % ( + ''' + + ''' + ))) + + v = component.queryMultiAdapter((ob, request), name='test') + self.assert_(issubclass(v.__class__, V1)) + + + def testPageWithClassWithMenu(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + testtemplate = os.path.join(tests_path, 'testfiles', 'test.pt') + + + xmlconfig(StringIO(template % ( + ''' + + + ''' % testtemplate + ))) + menuItem = getFirstMenuItem('test_menu', ob, TestRequest()) + self.assertEqual(menuItem["title"], "Test View") + self.assertEqual(menuItem["action"], "@@test") + v = component.queryMultiAdapter((ob, request), name='test') + self.assertEqual(v(), "

test

\n") + + + def testPageWithTemplateWithMenu(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + testtemplate = os.path.join(tests_path, 'testfiles', 'test.pt') + + xmlconfig(StringIO(template % ( + ''' + + + ''' % testtemplate + ))) + + menuItem = getFirstMenuItem('test_menu', ob, TestRequest()) + self.assertEqual(menuItem["title"], "Test View") + self.assertEqual(menuItem["action"], "@@test") + v = component.queryMultiAdapter((ob, request), name='test') + self.assertEqual(v(), "

test

\n") + + + def testPageInPagesWithTemplateWithMenu(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + testtemplate = os.path.join(tests_path, 'testfiles', 'test.pt') + + xmlconfig(StringIO(template % ( + ''' + + + + + ''' % testtemplate + ))) + + menuItem = getFirstMenuItem('test_menu', ob, TestRequest()) + self.assertEqual(menuItem["title"], "Test View") + self.assertEqual(menuItem["action"], "@@test") + v = component.queryMultiAdapter((ob, request), name='test') + self.assertEqual(v(), "

test

\n") + + + def testPageInPagesWithClassWithMenu(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + testtemplate = os.path.join(tests_path, 'testfiles', 'test.pt') + + xmlconfig(StringIO(template % ( + ''' + + + + + ''' % testtemplate + ))) + + menuItem = getFirstMenuItem('test_menu', ob, TestRequest()) + self.assertEqual(menuItem["title"], "Test View") + self.assertEqual(menuItem["action"], "@@test") + v = component.queryMultiAdapter((ob, request), name='test') + self.assertEqual(v(), "

test

\n") + + def testSkinPage(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % ( + ''' + + + ''' + ))) + + v = component.queryMultiAdapter((ob, request), name='test') + self.assert_(issubclass(v.__class__, V1)) + v = component.queryMultiAdapter( + (ob, TestRequest(skin=ITestSkin)), name='test') + self.assert_(issubclass(v.__class__, VZMI)) + + + def testInterfaceProtectedPage(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.getMultiAdapter((ob, request), name='test') + v = ProxyFactory(v) + self.assertEqual(v.index(), 'V1 here') + self.assertRaises(Exception, getattr, v, 'action') + + def testAttributeProtectedPage(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.getMultiAdapter((ob, request), name='test') + v = ProxyFactory(v) + self.assertEqual(v.action(), 'done') + self.assertEqual(v.action2(), 'done') + self.assertRaises(Exception, getattr, v, 'index') + + def testAttributeProtectedView(self): + xmlconfig(StringIO(template % + ''' + + + + ''' + )) + + v = component.getMultiAdapter((ob, request), name='test') + v = ProxyFactory(v) + page = v.publishTraverse(request, 'index.html') + self.assertEqual(page(), 'done') + self.assertEqual(v.action2(), 'done') + self.assertRaises(Exception, getattr, page, 'index') + + def testInterfaceAndAttributeProtectedPage(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.getMultiAdapter((ob, request), name='test') + self.assertEqual(v.index(), 'V1 here') + self.assertEqual(v.action(), 'done') + + def testDuplicatedInterfaceAndAttributeProtectedPage(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.getMultiAdapter((ob, request), name='test') + self.assertEqual(v.index(), 'V1 here') + self.assertEqual(v.action(), 'done') + + def test_class_w_implements(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.getMultiAdapter((ob, request), name='test') + self.assertEqual(v.index(), v) + self.assert_(IBrowserPublisher.providedBy(v)) + + def testIncompleteProtectedPageNoPermission(self): + self.assertRaises( + ConfigurationError, + xmlconfig, + StringIO(template % + ''' + + ''' + )) + + + def testPageViews(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + test3 = os.path.join(tests_path, 'testfiles', 'test3.pt') + + xmlconfig(StringIO(template % + ''' + + + + + + + ''' % test3 + )) + + v = component.getMultiAdapter((ob, request), name='index.html') + self.assertEqual(v(), 'V1 here') + v = component.getMultiAdapter((ob, request), name='action.html') + self.assertEqual(v(), 'done') + v = component.getMultiAdapter((ob, request), name='test.html') + self.assertEqual(str(v()), '

done

\n') + + def testNamedViewPageViewsCustomTraversr(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % + ''' + + + + + + ''' + )) + + view = component.getMultiAdapter((ob, request), name='test') + view = removeSecurityProxy(view) + self.assertEqual(view.browserDefault(request)[1], (u'index.html', )) + + + v = view.publishTraverse(request, 'index.html') + v = removeSecurityProxy(v) + self.assertEqual(v(), 'V1 here') + v = view.publishTraverse(request, 'action.html') + v = removeSecurityProxy(v) + self.assertEqual(v(), 'done') + + + def testNamedViewNoPagesForCallable(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + view = component.getMultiAdapter((ob, request), name='test') + view = removeSecurityProxy(view) + self.assertEqual(view.browserDefault(request), (view, ())) + + def testNamedViewNoPagesForNonCallable(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + view = component.getMultiAdapter((ob, request), name='test') + view = removeSecurityProxy(view) + self.assertEqual(getattr(view, 'browserDefault', None), None) + + def testNamedViewPageViewsNoDefault(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + test3 = os.path.join(tests_path, 'testfiles', 'test3.pt') + + xmlconfig(StringIO(template % + ''' + + + + + + + ''' % test3 + )) + + view = component.getMultiAdapter((ob, request), name='test') + view = removeSecurityProxy(view) + self.assertEqual(view.browserDefault(request)[1], (u'index.html', )) + + + v = view.publishTraverse(request, 'index.html') + v = removeSecurityProxy(v) + self.assertEqual(v(), 'V1 here') + v = view.publishTraverse(request, 'action.html') + v = removeSecurityProxy(v) + self.assertEqual(v(), 'done') + v = view.publishTraverse(request, 'test.html') + v = removeSecurityProxy(v) + self.assertEqual(str(v()), '

done

\n') + + def testNamedViewPageViewsWithDefault(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + test3 = os.path.join(tests_path, 'testfiles', 'test3.pt') + + xmlconfig(StringIO(template % + ''' + + + + + + + + ''' % test3 + )) + + view = component.getMultiAdapter((ob, request), name='test') + view = removeSecurityProxy(view) + self.assertEqual(view.browserDefault(request)[1], (u'test.html', )) + + + v = view.publishTraverse(request, 'index.html') + v = removeSecurityProxy(v) + self.assertEqual(v(), 'V1 here') + v = view.publishTraverse(request, 'action.html') + v = removeSecurityProxy(v) + self.assertEqual(v(), 'done') + v = view.publishTraverse(request, 'test.html') + v = removeSecurityProxy(v) + self.assertEqual(str(v()), '

done

\n') + + def testTraversalOfPageForView(self): + """Tests proper traversal of a page defined for a view.""" + + xmlconfig(StringIO(template % + ''' + + + + ''' + )) + + view = component.getMultiAdapter((ob, request), name='test') + view = removeSecurityProxy(view) + view.publishTraverse(request, 'index.html') + + def testTraversalOfPageForViewWithPublishTraverse(self): + """Tests proper traversal of a page defined for a view. + + This test is different from testTraversalOfPageForView in that it + tests the behavior on a view that has a publishTraverse method -- + the implementation of the lookup is slightly different in such a + case. + """ + xmlconfig(StringIO(template % + ''' + + + + ''' + )) + + view = component.getMultiAdapter((ob, request), name='test') + view = removeSecurityProxy(view) + view.publishTraverse(request, 'index.html') + + def testProtectedPageViews(self): + component.provideUtility(Permission('p', 'P'), IPermission, 'p') + + request = TestRequest() + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % + ''' + + + + + + + + + + ''' + )) + + v = component.getMultiAdapter((ob, request), name='index.html') + v = ProxyFactory(v) + zope.security.management.getInteraction().add(request) + self.assertRaises(Exception, v) + v = component.getMultiAdapter((ob, request), name='action.html') + v = ProxyFactory(v) + self.assertRaises(Exception, v) + + def testProtectedNamedViewPageViews(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % + ''' + + + + + + + + + + ''' + )) + + view = component.getMultiAdapter((ob, request), name='test') + self.assertEqual(view.browserDefault(request)[1], (u'index.html', )) + + v = view.publishTraverse(request, 'index.html') + self.assertEqual(v(), 'V1 here') + + def testSkinnedPageView(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % + ''' + + + + + + + + ''' + )) + + v = component.getMultiAdapter((ob, request), name='index.html') + self.assertEqual(v(), 'V1 here') + v = component.getMultiAdapter((ob, TestRequest(skin=ITestSkin)), + name='index.html') + self.assertEqual(v(), 'done') + + + def test_template_page(self): + path = os.path.join(tests_path, 'testfiles', 'test.pt') + + self.assertEqual( + component.queryMultiAdapter((ob, request), name='index.html'), + None) + + xmlconfig(StringIO(template % + ''' + + ''' % path + )) + + v = component.getMultiAdapter((ob, request), name='index.html') + self.assertEqual(v().strip(), '

test

') + + def test_page_menu_within_different_layers(self): + path = os.path.join(tests_path, 'testfiles', 'test.pt') + self.assertEqual( + component.queryMultiAdapter((ob, request), name='index.html'), + None) + + xmlconfig(StringIO(template % + ''' + + + + + + ''' % (path, path) + )) + + v = component.getMultiAdapter((ob, request), name='index.html') + self.assertEqual(v().strip(), '

test

') + + def testtemplateWClass(self): + path = os.path.join(tests_path, 'testfiles', 'test2.pt') + + self.assertEqual( + component.queryMultiAdapter((ob, request), name='index.html'), + None) + + xmlconfig(StringIO(template % + ''' + + ''' % path + )) + + v = component.getMultiAdapter((ob, request), name='index.html') + self.assertEqual(v().strip(), '

42

') + + def testProtectedtemplate(self): + + path = os.path.join(tests_path, 'testfiles', 'test.pt') + + request = TestRequest() + self.assertEqual( + component.queryMultiAdapter((ob, request), name='test'), + None) + + xmlconfig(StringIO(template % + ''' + + + + + + ''' % path + )) + + xmlconfig(StringIO(template % + ''' + + ''' % path + )) + + v = component.getMultiAdapter((ob, request), name='xxx.html') + v = ProxyFactory(v) + zope.security.management.getInteraction().add(request) + self.assertRaises(Exception, v) + + v = component.getMultiAdapter((ob, request), name='index.html') + v = ProxyFactory(v) + self.assertEqual(v().strip(), '

test

') + + + def testtemplateNoName(self): + path = os.path.join(tests_path, 'testfiles', 'test.pt') + self.assertRaises( + ConfigurationError, + xmlconfig, + StringIO(template % + ''' + + ''' % path + )) + + def testtemplateAndPage(self): + path = os.path.join(tests_path, 'testfiles', 'test.pt') + self.assertRaises( + ConfigurationError, + xmlconfig, + StringIO(template % + ''' + + + + ''' % path + )) + + def testViewThatProvidesAnInterface(self): + request = TestRequest() + self.assertEqual( + component.queryMultiAdapter((ob, request), IV, name='test'), None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.queryMultiAdapter((ob, request), IV, name='test') + self.assertEqual(v, None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.queryMultiAdapter((ob, request), IV, name='test') + self.assert_(isinstance(v, V1)) + + def testUnnamedViewThatProvidesAnInterface(self): + request = TestRequest() + self.assertEqual(component.queryMultiAdapter((ob, request), IV), + None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.queryMultiAdapter((ob, request), IV) + self.assertEqual(v, None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = component.queryMultiAdapter((ob, request), IV) + + self.assert_(isinstance(v, V1)) + + +def test_suite(): + return unittest.makeSuite(Test) diff -Nru zope3-3.4.0/src/zope/browserpage/tests/test.pt zope3-3.5~bzr18/src/zope/browserpage/tests/test.pt --- zope3-3.4.0/src/zope/browserpage/tests/test.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/test.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/browserpage/tests/testsimpleviewclass.pt zope3-3.5~bzr18/src/zope/browserpage/tests/testsimpleviewclass.pt --- zope3-3.4.0/src/zope/browserpage/tests/testsimpleviewclass.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/testsimpleviewclass.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ + + +

hello world

+ + diff -Nru zope3-3.4.0/src/zope/browserpage/tests/test_simpleviewclass.py zope3-3.5~bzr18/src/zope/browserpage/tests/test_simpleviewclass.py --- zope3-3.4.0/src/zope/browserpage/tests/test_simpleviewclass.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/test_simpleviewclass.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,173 @@ +############################################################################## +# +# Copyright (c) 2001-2009 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. +# +############################################################################## +"""Simple View Class Tests + +$Id: test_simpleviewclass.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +import unittest + +class Test_SimpleTestView(unittest.TestCase): + + def _getTargetClass(self): + from zope.browserpage.tests.simpletestview import SimpleTestView + return SimpleTestView + + def _makeOne(self, context, request): + return self._getTargetClass()(context, request) + + def test_simple(self): + from zope.publisher.browser import TestRequest + context = DummyContext() + request = TestRequest() + view = self._makeOne(context, request) + macro = view['test'] + out = view() + self.assertEqual(out, + '\n' + ' \n' + '

hello world

\n' + ' \n\n') + +class Test_SimpleViewClass(unittest.TestCase): + + def _getTargetClass(self): + from zope.browserpage.simpleviewclass import SimpleViewClass + return SimpleViewClass + + def _makeKlass(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def test___name__(self): + klass = self._makeKlass('testsimpleviewclass.pt', name='test.html') + view = klass(None, None) + self.assertEqual(view.__name__, 'test.html') + + def test___getitem___(self): + klass = self._makeKlass('testsimpleviewclass.pt', name='test.html') + view = klass(None, None) + self.assert_(view['test'] is not None) + self.assertRaises(KeyError, view.__getitem__, 'foo') + + def test_w_base_classes(self): + from zope.publisher.browser import TestRequest + class BaseClass(object): + pass + + klass = self._makeKlass('testsimpleviewclass.pt', bases=(BaseClass, )) + + self.failUnless(issubclass(klass, BaseClass)) + + ob = DummyContext() + request = TestRequest() + view = klass(ob, request) + macro = view['test'] + out = view() + self.assertEqual(out, + '\n' + ' \n' + '

hello world

\n' + ' \n\n') + +class Test_simple(unittest.TestCase): + + def _getTargetClass(self): + from zope.browserpage.simpleviewclass import simple + return simple + + def _makeOne(self, context=None, request=None): + if context is None: + context = DummyContext() + if request is None: + request = DummyRequest() + return self._getTargetClass()(context, request) + + def test_class_conforms_to_IBrowserPublisher(self): + from zope.interface.verify import verifyClass + from zope.publisher.interfaces.browser import IBrowserPublisher + verifyClass(IBrowserPublisher, self._getTargetClass()) + + def test_browserDefault(self): + request = DummyRequest() + view = self._makeOne(request=request) + self.assertEqual(view.browserDefault(request), (view, ())) + + def test_publishTraverse_not_index_raises_NotFound(self): + from zope.publisher.interfaces import NotFound + request = DummyRequest() + view = self._makeOne(request=request) + self.assertRaises(NotFound, view.publishTraverse, request, 'nonesuch') + + def test_publishTraverse_w_index_returns_index(self): + request = DummyRequest() + view = self._makeOne(request=request) + index = view.index = DummyTemplate() + self.failUnless(view.publishTraverse(request, 'index.html') is index) + + def test___getitem___uses_index_macros(self): + view = self._makeOne() + view.index = index = DummyTemplate() + index.macros = {} + index.macros['aaa'] = aaa = object() + self.failUnless(view['aaa'] is aaa) + + def test___call___no_args_no_kw(self): + view = self._makeOne() + view.index = index = DummyTemplate() + result = view() + self.failUnless(result is index) + self.assertEqual(index._called_with, ((), {})) + + def test___call___w_args_no_kw(self): + view = self._makeOne() + view.index = index = DummyTemplate() + result = view('abc') + self.failUnless(result is index) + self.assertEqual(index._called_with, (('abc',), {})) + + def test___call___no_args_w_kw(self): + view = self._makeOne() + view.index = index = DummyTemplate() + result = view(foo='bar') + self.failUnless(result is index) + self.assertEqual(index._called_with, ((), {'foo': 'bar'})) + + def test___call___no_args_no_kw(self): + view = self._makeOne() + view.index = index = DummyTemplate() + result = view('abc', foo='bar') + self.failUnless(result is index) + self.assertEqual(index._called_with, (('abc',), {'foo': 'bar'})) + + +class DummyContext: + pass + +class DummyResponse: + pass + +class DummyRequest: + debug = False + response = DummyResponse() + +class DummyTemplate: + def __call__(self, *args, **kw): + self._called_with = (args, kw) + return self + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_SimpleTestView), + unittest.makeSuite(Test_SimpleViewClass), + unittest.makeSuite(Test_simple), + )) diff -Nru zope3-3.4.0/src/zope/browserpage/tests/test_viewzpt.py zope3-3.5~bzr18/src/zope/browserpage/tests/test_viewzpt.py --- zope3-3.4.0/src/zope/browserpage/tests/test_viewzpt.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/test_viewzpt.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,144 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""View ZPT Tests + +$Id: test_viewzpt.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +import unittest + +from zope.component import getGlobalSiteManager +from zope.component.testing import PlacelessSetup +from zope.interface import Interface, implements + +from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile + + +class I1(Interface): + pass + +class C1(object): + implements(I1) + +class InstanceWithContext(object): + def __init__(self, context): + self.context = context + +class InstanceWithoutContext(object): + pass + + +class TestViewZPT(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(TestViewZPT, self).setUp() + self.t = ViewPageTemplateFile('test.pt') + self.context = C1() + + def testNamespaceContextAvailable(self): + context = self.context + request = None + + namespace = self.t.pt_getContext(InstanceWithContext(context), request) + self.failUnless(namespace['context'] is context) + self.failUnless('views' in namespace) + + def testNamespaceHereNotAvailable(self): + request = None + self.assertRaises(AttributeError, self.t.pt_getContext, + InstanceWithoutContext(), request) + + def testViewMapper(self): + the_view = "This is the view" + the_view_name = "some view name" + def ViewMaker(*args, **kw): + return the_view + + from zope.publisher.interfaces import IRequest + + gsm = getGlobalSiteManager() + gsm.registerAdapter( + ViewMaker, (I1, IRequest), Interface, the_view_name, event=False) + + class MyRequest(object): + implements(IRequest) + + request = MyRequest() + + namespace = self.t.pt_getContext(InstanceWithContext(self.context), + request) + views = namespace['views'] + self.failUnless(the_view is views[the_view_name]) + + def test_debug_flags(self): + from zope.publisher.browser import TestRequest + self.request = TestRequest() + self.request.debug.sourceAnnotations = False + self.assert_('test.pt' not in self.t(self)) + self.request.debug.sourceAnnotations = True + self.assert_('test.pt' in self.t(self)) + + t = ViewPageTemplateFile('testsimpleviewclass.pt') + self.request.debug.showTAL = False + self.assert_('metal:' not in t(self)) + self.request.debug.showTAL = True + self.assert_('metal:' in t(self)) + + def test_render_sets_content_type_unless_set(self): + from zope.publisher.browser import TestRequest + t = ViewPageTemplateFile('test.pt') + + self.request = TestRequest() + response = self.request.response + self.assert_(not response.getHeader('Content-Type')) + t(self) + self.assertEquals(response.getHeader('Content-Type'), 'text/html') + + self.request = TestRequest() + response = self.request.response + response.setHeader('Content-Type', 'application/x-test-junk') + t(self) + self.assertEquals(response.getHeader('Content-Type'), + 'application/x-test-junk') + + +class TestViewZPTContentType(unittest.TestCase): + + def testInitWithoutType(self): + t = ViewPageTemplateFile('test.pt') + t._cook_check() + self.assertEquals(t.content_type, "text/html") + + t = ViewPageTemplateFile('testxml.pt') + t._cook_check() + self.assertEquals(t.content_type, "text/xml") + + def testInitWithType(self): + t = ViewPageTemplateFile('test.pt', content_type="text/plain") + t._cook_check() + self.assertEquals(t.content_type, "text/plain") + + t = ViewPageTemplateFile('testxml.pt', content_type="text/plain") + t._cook_check() + self.assertEquals(t.content_type, "text/xml") + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestViewZPT)) + suite.addTest(unittest.makeSuite(TestViewZPTContentType)) + return suite + + +if __name__ == '__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/browserpage/tests/testxml.pt zope3-3.5~bzr18/src/zope/browserpage/tests/testxml.pt --- zope3-3.4.0/src/zope/browserpage/tests/testxml.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/tests/testxml.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ + + diff -Nru zope3-3.4.0/src/zope/browserpage/viewpagetemplatefile.py zope3-3.5~bzr18/src/zope/browserpage/viewpagetemplatefile.py --- zope3-3.4.0/src/zope/browserpage/viewpagetemplatefile.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserpage/viewpagetemplatefile.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,93 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""File-based page templates that can be used as methods on views. + +$Id: viewpagetemplatefile.py 111996 2010-05-05 17:34:04Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +from zope.component import getMultiAdapter +from zope.pagetemplate.pagetemplatefile import PageTemplateFile +from zope.pagetemplate.engine import TrustedAppPT + +class ViewPageTemplateFile(TrustedAppPT, PageTemplateFile): + """Page Templates used as methods of views defined as Python classes. + """ + + def __init__(self, filename, _prefix=None, content_type=None): + _prefix = self.get_path_from_prefix(_prefix) + super(ViewPageTemplateFile, self).__init__(filename, _prefix) + if content_type is not None: + self.content_type = content_type + + def pt_getContext(self, instance, request, **_kw): + # instance is a View component + namespace = super(ViewPageTemplateFile, self).pt_getContext(**_kw) + namespace['request'] = request + namespace['view'] = instance + namespace['context'] = context = instance.context + namespace['views'] = ViewMapper(context, request) + return namespace + + def __call__(self, instance, *args, **keywords): + namespace = self.pt_getContext( + request=instance.request, + instance=instance, args=args, options=keywords) + debug_flags = instance.request.debug + s = self.pt_render( + namespace, + showtal=getattr(debug_flags, 'showTAL', 0), + sourceAnnotations=getattr(debug_flags, 'sourceAnnotations', 0), + ) + response = instance.request.response + if not response.getHeader("Content-Type"): + response.setHeader("Content-Type", self.content_type) + return s + + def __get__(self, instance, type): + return BoundPageTemplate(self, instance) + +class ViewMapper(object): + def __init__(self, ob, request): + self.ob = ob + self.request = request + + def __getitem__(self, name): + return getMultiAdapter((self.ob, self.request), name=name) + + +class BoundPageTemplate(object): + def __init__(self, pt, ob): + object.__setattr__(self, 'im_func', pt) + object.__setattr__(self, 'im_self', ob) + + macros = property(lambda self: self.im_func.macros) + filename = property(lambda self: self.im_func.filename) + + def __call__(self, *args, **kw): + if self.im_self is None: + im_self, args = args[0], args[1:] + else: + im_self = self.im_self + return self.im_func(im_self, *args, **kw) + + def __setattr__(self, name, v): + raise AttributeError("Can't set attribute", name) + + def __repr__(self): + return "" % self.im_self + + +def NoTraverser(ob, request): + return None diff -Nru zope3-3.4.0/src/zope/browserresource/configure.zcml zope3-3.5~bzr18/src/zope/browserresource/configure.zcml --- zope3-3.4.0/src/zope/browserresource/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/browserresource/directory.py zope3-3.5~bzr18/src/zope/browserresource/directory.py --- zope3-3.4.0/src/zope/browserresource/directory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/directory.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,125 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Resource Directory + +A 'resource directory' is an on-disk directory which is registered as +a resource using the ZCML directive. The +directory is treated as a source for individual resources; it can be +traversed to retrieve resources represented by contained files, which +can in turn be treated as resources. The contained files have +__name__ values which include a '/' separating the __name__ of the +resource directory from the name of the file within the directory. + +$Id: directory.py 104477 2009-09-24 09:41:25Z nadako $ +""" +import fnmatch +import os + +from zope.component import queryUtility +from zope.interface import implements, classProvides +from zope.publisher.browser import BrowserView +from zope.publisher.interfaces import NotFound +from zope.publisher.interfaces.browser import IBrowserPublisher + +from zope.browserresource.file import FileResourceFactory +from zope.browserresource.resource import Resource +from zope.browserresource.interfaces import IResourceFactory +from zope.browserresource.interfaces import IResourceFactoryFactory + +_marker = object() + +def empty(): + return '' + +# we only need this class as a context for DirectoryResource +class Directory(object): + + def __init__(self, path, checker, name): + self.path = path + self.checker = checker + self.__name__ = name + +class DirectoryResource(BrowserView, Resource): + + implements(IBrowserPublisher) + + default_factory = FileResourceFactory + directory_factory = None # this will be assigned later in the module + forbidden_names = ('.svn', ) + + def publishTraverse(self, request, name): + '''See interface IBrowserPublisher''' + return self.get(name) + + def browserDefault(self, request): + '''See interface IBrowserPublisher''' + return empty, () + + def __getitem__(self, name): + res = self.get(name, None) + if res is None: + raise KeyError(name) + return res + + def get(self, name, default=_marker): + + for pat in self.forbidden_names: + if fnmatch.fnmatch(name, pat): + if default is _marker: + raise NotFound(None, name) + else: + return default + + path = self.context.path + filename = os.path.join(path, name) + isfile = os.path.isfile(filename) + isdir = os.path.isdir(filename) + + if not (isfile or isdir): + if default is _marker: + raise NotFound(None, name) + return default + + if isfile: + ext = os.path.splitext(os.path.normcase(name))[1][1:] + factory = queryUtility(IResourceFactoryFactory, ext, + self.default_factory) + else: + factory = self.directory_factory + + rname = self.__name__ + '/' + name + resource = factory(filename, self.context.checker, rname)(self.request) + resource.__parent__ = self + return resource + + +class DirectoryResourceFactory(object): + + implements(IResourceFactory) + classProvides(IResourceFactoryFactory) + + factoryClass = DirectoryResource + + def __init__(self, path, checker, name): + self.__dir = Directory(path, checker, name) + self.__checker = checker + self.__name = name + + def __call__(self, request): + resource = self.factoryClass(self.__dir, request) + resource.__Security_checker__ = self.__checker + resource.__name__ = self.__name + return resource + +DirectoryResource.directory_factory = DirectoryResourceFactory diff -Nru zope3-3.4.0/src/zope/browserresource/file.py zope3-3.5~bzr18/src/zope/browserresource/file.py --- zope3-3.4.0/src/zope/browserresource/file.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/file.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,238 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""File-based browser resources. + +$Id: file.py 103197 2009-08-25 11:54:46Z nadako $ +""" + +import os +import time +try: + from email.utils import formatdate, parsedate_tz, mktime_tz +except ImportError: # python 2.4 + from email.Utils import formatdate, parsedate_tz, mktime_tz + +from zope.contenttype import guess_content_type +from zope.interface import implements, classProvides +from zope.publisher.browser import BrowserView +from zope.publisher.interfaces import NotFound +from zope.publisher.interfaces.browser import IBrowserPublisher + +from zope.browserresource.resource import Resource +from zope.browserresource.interfaces import IResourceFactory +from zope.browserresource.interfaces import IResourceFactoryFactory + + +class File(object): + + def __init__(self, path, name): + self.path = path + self.__name__ = name + + f = open(path, 'rb') + data = f.read() + f.close() + self.content_type = guess_content_type(path, data)[0] + + self.lmt = float(os.path.getmtime(path)) or time.time() + self.lmh = formatdate(self.lmt, usegmt=True) + + +class FileResource(BrowserView, Resource): + + implements(IBrowserPublisher) + + cacheTimeout = 86400 + + def publishTraverse(self, request, name): + '''File resources can't be traversed further, so raise NotFound if + someone tries to traverse it. + + >>> factory = FileResourceFactory(testFilePath, nullChecker, 'test.txt') + >>> request = TestRequest() + >>> resource = factory(request) + >>> resource.publishTraverse(request, '_testData') + Traceback (most recent call last): + ... + NotFound: Object: None, name: '_testData' + + ''' + raise NotFound(None, name) + + def browserDefault(self, request): + '''Return a callable for processing browser requests. + + >>> factory = FileResourceFactory(testFilePath, nullChecker, 'test.txt') + >>> request = TestRequest(REQUEST_METHOD='GET') + >>> resource = factory(request) + >>> view, next = resource.browserDefault(request) + >>> view() == open(testFilePath, 'rb').read() + True + >>> next == () + True + + >>> request = TestRequest(REQUEST_METHOD='HEAD') + >>> resource = factory(request) + >>> view, next = resource.browserDefault(request) + >>> view() == '' + True + >>> next == () + True + + ''' + return getattr(self, request.method), () + + def chooseContext(self): + '''Choose the appropriate context. + + This method can be overriden in subclasses, that need to choose + appropriate file, based on current request or other condition, + like, for example, i18n files. + + ''' + return self.context + + def GET(self): + '''Return a file data for downloading with GET requests + + >>> factory = FileResourceFactory(testFilePath, nullChecker, 'test.txt') + >>> request = TestRequest() + >>> resource = factory(request) + >>> resource.GET() == open(testFilePath, 'rb').read() + True + >>> request.response.getHeader('Content-Type') == 'text/plain' + True + + Let's test If-Modified-Since header support. + + >>> timestamp = time.time() + + >>> file = factory._FileResourceFactory__file # get mangled file + >>> file.lmt = timestamp + >>> file.lmh = formatdate(timestamp, usegmt=True) + + >>> before = timestamp - 1000 + >>> request = TestRequest(HTTP_IF_MODIFIED_SINCE=formatdate(before, usegmt=True)) + >>> resource = factory(request) + >>> bool(resource.GET()) + True + + >>> after = timestamp + 1000 + >>> request = TestRequest(HTTP_IF_MODIFIED_SINCE=formatdate(after, usegmt=True)) + >>> resource = factory(request) + >>> bool(resource.GET()) + False + >>> request.response.getStatus() + 304 + + It won't fail on bad If-Modified-Since headers. + + >>> request = TestRequest(HTTP_IF_MODIFIED_SINCE='bad header') + >>> resource = factory(request) + >>> bool(resource.GET()) + True + + ''' + + file = self.chooseContext() + request = self.request + response = request.response + + setCacheControl(response, self.cacheTimeout) + + # HTTP If-Modified-Since header handling. This is duplicated + # from OFS.Image.Image - it really should be consolidated + # somewhere... + header = request.getHeader('If-Modified-Since', None) + if header is not None: + header = header.split(';')[0] + # Some proxies seem to send invalid date strings for this + # header. If the date string is not valid, we ignore it + # rather than raise an error to be generally consistent + # with common servers such as Apache (which can usually + # understand the screwy date string as a lucky side effect + # of the way they parse it). + try: + mod_since = long(mktime_tz(parsedate_tz(header))) + except: + mod_since = None + if mod_since is not None: + if getattr(file, 'lmt', None): + last_mod = long(file.lmt) + else: + last_mod = 0L + if last_mod > 0 and last_mod <= mod_since: + response.setStatus(304) + return '' + + response.setHeader('Content-Type', file.content_type) + response.setHeader('Last-Modified', file.lmh) + + f = open(file.path,'rb') + data = f.read() + f.close() + + return data + + def HEAD(self): + '''Return proper headers and no content for HEAD requests + + >>> factory = FileResourceFactory(testFilePath, nullChecker, 'test.txt') + >>> request = TestRequest() + >>> resource = factory(request) + >>> resource.HEAD() == '' + True + >>> request.response.getHeader('Content-Type') == 'text/plain' + True + + ''' + file = self.chooseContext() + response = self.request.response + response.setHeader('Content-Type', file.content_type) + response.setHeader('Last-Modified', file.lmh) + setCacheControl(response, self.cacheTimeout) + return '' + + # for unit tests + def _testData(self): + f = open(self.context.path, 'rb') + data = f.read() + f.close() + return data + + +def setCacheControl(response, secs=86400): + # Cache for one day by default + response.setHeader('Cache-Control', 'public,max-age=%s' % secs) + t = time.time() + secs + response.setHeader('Expires', formatdate(t, usegmt=True)) + + +class FileResourceFactory(object): + + resourceClass = FileResource + + implements(IResourceFactory) + classProvides(IResourceFactoryFactory) + + def __init__(self, path, checker, name): + self.__file = File(path, name) + self.__checker = checker + self.__name = name + + def __call__(self, request): + resource = self.resourceClass(self.__file, request) + resource.__Security_checker__ = self.__checker + resource.__name__ = self.__name + return resource diff -Nru zope3-3.4.0/src/zope/browserresource/i18nfile.py zope3-3.5~bzr18/src/zope/browserresource/i18nfile.py --- zope3-3.4.0/src/zope/browserresource/i18nfile.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/i18nfile.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,82 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Internationalized file resource. + +$Id: i18nfile.py 103196 2009-08-25 11:48:42Z nadako $ +""" +from zope.i18n.interfaces import II18nAware +from zope.i18n.negotiator import negotiator +from zope.interface import implements, classProvides + +from zope.browserresource.file import FileResource +from zope.browserresource.interfaces import IResourceFactory +from zope.browserresource.interfaces import IResourceFactoryFactory + + +class I18nFileResource(FileResource): + + implements(II18nAware) + + def __init__(self, data, request, defaultLanguage='en'): + """Creates an internationalized file resource. data should be + a mapping from languages to File objects. + """ + self._data = data + self.request = request + self.defaultLanguage = defaultLanguage + + def chooseContext(self): + """Choose the appropriate context according to language""" + langs = self.getAvailableLanguages() + language = negotiator.getLanguage(langs, self.request) + try: + return self._data[language] + except KeyError: + return self._data[self.defaultLanguage] + + def getDefaultLanguage(self): + 'See II18nAware' + return self.defaultLanguage + + def setDefaultLanguage(self, language): + 'See II18nAware' + if language not in self._data: + raise ValueError( + 'cannot set nonexistent language (%s) as default' % language) + self.defaultLanguage = language + + def getAvailableLanguages(self): + 'See II18nAware' + return self._data.keys() + + # for unit tests + def _testData(self, language): + file = self._data[language] + f=open(file.path,'rb') + data=f.read() + f.close() + return data + + +class I18nFileResourceFactory(object): + + implements(IResourceFactory) + classProvides(IResourceFactoryFactory) + + def __init__(self, data, defaultLanguage): + self.__data = data + self.__defaultLanguage = defaultLanguage + + def __call__(self, request): + return I18nFileResource(self.__data, request, self.__defaultLanguage) diff -Nru zope3-3.4.0/src/zope/browserresource/icon.py zope3-3.5~bzr18/src/zope/browserresource/icon.py --- zope3-3.4.0/src/zope/browserresource/icon.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/icon.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,52 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Icon support + +$Id: icon.py 105850 2009-11-19 07:05:29Z tlotze $ +""" +import zope.component.hooks +from zope.component import getAdapter +from zope.location import locate + + +class IconView(object): + + def __init__(self, context, request, rname, alt, width, height): + self.context = context + self.request = request + self.rname = rname + self.alt = alt + self.width = width + self.height = height + + def __call__(self): + return ('%s' + % (self.url(), self.alt, self.width, self.height)) + + def url(self): + resource = getAdapter(self.request, name=self.rname) + locate(resource, zope.component.hooks.getSite(), self.rname) + return resource() + +class IconViewFactory(object): + + def __init__(self, rname, alt, width, height): + self.rname = rname + self.alt = alt + self.width = width + self.height = height + + def __call__(self, context, request): + return IconView(context, request, self.rname, self.alt, + self.width, self.height) diff -Nru zope3-3.4.0/src/zope/browserresource/interfaces.py zope3-3.5~bzr18/src/zope/browserresource/interfaces.py --- zope3-3.4.0/src/zope/browserresource/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""Resource interfaces + +$Id: interfaces.py 103276 2009-08-27 14:31:59Z nadako $ +""" +from zope.interface import Interface, Attribute + + +class IResource(Interface): + + request = Attribute('Request object that is requesting the resource') + + def __call__(): + """return the absolute URL of this resource.""" + +class IResourceFactory(Interface): + + def __call__(request): + """Return an IResource object""" + +class IResourceFactoryFactory(Interface): + """A factory for IResourceFactory objects + + These factories are registered as named utilities that can be selected + for creating resource factories in a pluggable way. + + Resource directories and browser:resource directive use these utilities + to choose what resource to create, depending on the file extension, so + third-party packages could easily plug-in additional resource types. + + """ + + def __call__(path, checker, name): + """Return an IResourceFactory""" diff -Nru zope3-3.4.0/src/zope/browserresource/metaconfigure.py zope3-3.5~bzr18/src/zope/browserresource/metaconfigure.py --- zope3-3.4.0/src/zope/browserresource/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,264 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""ZCML directive handlers for browser resources + +$Id: metaconfigure.py 103276 2009-08-27 14:31:59Z nadako $ +""" +import os + +from zope.component import queryUtility +from zope.component.interface import provideInterface +from zope.component.zcml import handler +from zope.configuration.exceptions import ConfigurationError +from zope.interface import Interface, implements, classProvides +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.security.checker import CheckerPublic, NamesChecker, Checker +from zope.security.proxy import Proxy + +from zope.browserresource.directory import DirectoryResourceFactory +from zope.browserresource.file import File, FileResourceFactory +from zope.browserresource.i18nfile import I18nFileResourceFactory +from zope.browserresource.icon import IconViewFactory +from zope.browserresource.interfaces import IResourceFactory +from zope.browserresource.interfaces import IResourceFactoryFactory + +allowed_names = ('GET', 'HEAD', 'publishTraverse', 'browserDefault', + 'request', '__call__') + +class ResourceFactoryWrapper(object): + + implements(IResourceFactory) + classProvides(IResourceFactoryFactory) + + def __init__(self, factory, checker, name): + self.__factory = factory + self.__checker = checker + self.__name = name + + def __call__(self, request): + resource = self.__factory(request) + resource.__Security_checker__ = self.__checker + resource.__name__ = self.__name + return resource + + +def resource(_context, name, layer=IDefaultBrowserLayer, + permission='zope.Public', factory=None, + file=None, image=None, template=None): + + if permission == 'zope.Public': + permission = CheckerPublic + + checker = NamesChecker(allowed_names, permission) + + if (factory and (file or image or template)) or \ + (file and (factory or image or template)) or \ + (image and (factory or file or template)) or \ + (template and (factory or file or image)): + raise ConfigurationError( + "Must use exactly one of factory or file or image or template" + " attributes for resource directives" + ) + + if image or template: + import warnings + warnings.warn_explicit( + 'The "template" and "image" attributes of resource ' + 'directive are deprecated in favor of pluggable ' + 'file resource factories based on file extensions. ' + 'Use the "file" attribute instead.', + DeprecationWarning, + _context.info.file, _context.info.line) + if image: + file = image + elif template: + file = template + + _context.action( + discriminator = ('resource', name, IBrowserRequest, layer), + callable = resourceHandler, + args = (name, layer, checker, factory, file, _context.info), + ) + + +def resourceHandler(name, layer, checker, factory, file, context_info): + if factory is not None: + factory = ResourceFactoryWrapper(factory, checker, name) + else: + ext = os.path.splitext(os.path.normcase(file))[1][1:] + factory_factory = queryUtility(IResourceFactoryFactory, ext, + FileResourceFactory) + factory = factory_factory(file, checker, name) + handler('registerAdapter', factory, (layer,), Interface, name, context_info) + + +def resourceDirectory(_context, name, directory, layer=IDefaultBrowserLayer, + permission='zope.Public'): + if permission == 'zope.Public': + permission = CheckerPublic + + checker = NamesChecker(allowed_names + ('__getitem__', 'get'), + permission) + + if not os.path.isdir(directory): + raise ConfigurationError( + "Directory %s does not exist" % directory + ) + + factory = DirectoryResourceFactory(directory, checker, name) + _context.action( + discriminator = ('resource', name, IBrowserRequest, layer), + callable = handler, + args = ('registerAdapter', + factory, (layer,), Interface, name, _context.info), + ) + + +def icon(_context, name, for_, file=None, resource=None, + layer=IDefaultBrowserLayer, title=None, + width=16, height=16): + + iname = for_.getName() + + if title is None: + title = iname + if title.startswith('I'): + title = title[1:] # Remove leading 'I' + + if file is not None and resource is not None: + raise ConfigurationError( + "Can't use more than one of file, and resource " + "attributes for icon directives" + ) + elif file is not None: + resource = '-'.join(for_.__module__.split('.')) + resource = "%s-%s-%s" % (resource, iname, name) + ext = os.path.splitext(file)[1] + if ext: + resource += ext + + # give this module another name, so we can use the "resource" directive + # in it that won't conflict with our local variable with the same name. + from zope.browserresource import metaconfigure + metaconfigure.resource(_context, file=file, name=resource, layer=layer) + elif resource is None: + raise ConfigurationError( + "At least one of the file, and resource " + "attributes for resource directives must be specified" + ) + + vfactory = IconViewFactory(resource, title, width, height) + + _context.action( + discriminator = ('view', name, vfactory, layer), + callable = handler, + args = ('registerAdapter', + vfactory, (for_, layer), Interface, name, _context.info) + ) + + _context.action( + discriminator = None, + callable = provideInterface, + args = (for_.__module__+'.'+for_.getName(), + for_) + ) + + +class I18nResource(object): + + type = IBrowserRequest + default_allowed_attributes = '__call__' + + def __init__(self, _context, name=None, defaultLanguage='en', + layer=IDefaultBrowserLayer, permission=None): + self._context = _context + self.name = name + self.defaultLanguage = defaultLanguage + self.layer = layer + self.permission = permission + self.__data = {} + + def translation(self, _context, language, file=None, image=None): + + if file is not None and image is not None: + raise ConfigurationError( + "Can't use more than one of file, and image " + "attributes for resource directives" + ) + elif file is None and image is None: + raise ConfigurationError( + "At least one of the file, and image " + "attributes for resource directives must be specified" + ) + + if image is not None: + import warnings + warnings.warn_explicit( + 'The "image" attribute of i18n-resource directive is ' + 'deprecated in favor of simple files. Use the "file" ' + 'attribute instead.', + DeprecationWarning, + _context.info.file, _context.info.line) + file = image + + self.__data[language] = File(_context.path(file), self.name) + + + def __call__(self, require = None): + if self.name is None: + return + + if self.defaultLanguage not in self.__data: + raise ConfigurationError( + "A translation for the default language (%s) " + "must be specified" % self.defaultLanguage + ) + + permission = self.permission + factory = I18nFileResourceFactory(self.__data, self.defaultLanguage) + + if permission: + if require is None: + require = {} + + if permission == 'zope.Public': + permission = CheckerPublic + + if require: + checker = Checker(require) + + factory = self._proxyFactory(factory, checker) + + self._context.action( + discriminator = ('i18n-resource', self.name, self.type, self.layer), + callable = handler, + args = ('registerAdapter', + factory, (self.layer,), Interface, self.name, + self._context.info) + ) + + + def _proxyFactory(self, factory, checker): + def proxyView(request, + factory=factory, checker=checker): + resource = factory(request) + + # We need this in case the resource gets unwrapped and + # needs to be rewrapped + resource.__Security_checker__ = checker + + return Proxy(resource, checker) + + return proxyView diff -Nru zope3-3.4.0/src/zope/browserresource/metadirectives.py zope3-3.5~bzr18/src/zope/browserresource/metadirectives.py --- zope3-3.4.0/src/zope/browserresource/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,245 @@ +############################################################################# +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""ZCML directives for defining browser resources + +$Id: metadirectives.py 103276 2009-08-27 14:31:59Z nadako $ +""" +from zope.configuration.fields import GlobalObject, GlobalInterface +from zope.configuration.fields import Path, MessageID +from zope.interface import Interface +from zope.schema import TextLine, Int +from zope.security.zcml import Permission + + +class IBasicResourceInformation(Interface): + """ + This is the basic information for all browser resources. + """ + + layer = GlobalInterface( + title=u"The layer the resource should be found in", + description=u""" + For information on layers, see the documentation for the skin + directive. Defaults to "default".""", + required=False + ) + + permission = Permission( + title=u"The permission needed to access the resource.", + description=u""" + If a permission isn't specified, the resource will always be + accessible.""", + required=False + ) + +class IResourceDirective(IBasicResourceInformation): + """ + Defines a browser resource + """ + + name = TextLine( + title=u"The name of the resource", + description=u""" + This is the name used in resource urls. Resource urls are of + the form site/@@/resourcename, where site is the url of + "site", a folder with a site manager. + + We make resource urls site-relative (as opposed to + content-relative) so as not to defeat caches.""", + required=True + ) + + factory = GlobalObject( + title=u"Resource Factory", + description=u"The factory used to create the resource. The factory " + u"should only expect to get the request passed when " + u"called.", + required=False + ) + + file = Path( + title=u"File", + description=u"The file containing the resource data. The resource " + u"type that will be created depends on file extension. " + u"The named IResourceFactoryFactory utilities are " + u"registered per extension. If no factory is registered " + u"for given file extension, the default FileResource " + u"factory will be used.", + required=False + ) + + image = Path( + title=u"Image", + description=u""" + If the image attribute is used, then an image resource, rather + than a file resource will be created. + + This attribute is deprecated in favor of pluggable resource types, + registered per extension. Use the "file" attribute instead. + """, + required=False + ) + + template = Path( + title=u"Template", + description=u""" + If the template attribute is used, then a page template resource, + rather than a file resource will be created. + + This attribute is deprecated in favor of pluggable resource types, + registered per extension. Use the "file" attribute instead. To + use page template resources, you need to instal zope.ptresource + package. + """, + required=False + ) + +class II18nResourceDirective(IBasicResourceInformation): + """ + Defines an i18n'd resource. + """ + + name = TextLine( + title=u"The name of the resource", + description=u""" + This is the name used in resource urls. Resource urls are of + the form site/@@/resourcename, where site is the url of + "site", a folder with a site manager. + + We make resource urls site-relative (as opposed to + content-relative) so as not to defeat caches.""", + required=True + ) + + defaultLanguage = TextLine( + title=u"Default language", + description=u"Defines the default language", + required=False + ) + +class II18nResourceTranslationSubdirective(IBasicResourceInformation): + """ + Subdirective to II18nResourceDirective. + """ + + language = TextLine( + title=u"Language", + description=u"Language of this translation of the resource", + required=True + ) + + file = Path( + title=u"File", + description=u"The file containing the resource data.", + required=False + ) + + image = Path( + title=u"Image", + description=u""" + If the image attribute is used, then an image resource, rather + than a file resource will be created. + + This attribute is deprecated, as images are now simply files. + Use the "file" attribute instead. + """, + required=False + ) + +class IResourceDirectoryDirective(IBasicResourceInformation): + """ + Defines a directory containing browser resource + """ + + name = TextLine( + title=u"The name of the resource", + description=u""" + This is the name used in resource urls. Resource urls are of + the form site/@@/resourcename, where site is the url of + "site", a folder with a site manager. + + We make resource urls site-relative (as opposed to + content-relative) so as not to defeat caches.""", + required=True + ) + + directory = Path( + title=u"Directory", + description=u"The directory containing the resource data.", + required=True + ) + + +class IIconDirective(Interface): + """ + Define an icon for an interface + """ + + name = TextLine( + title=u"The name of the icon.", + description=u"The name shows up in URLs/paths. For example 'foo'.", + required=True + ) + + for_ = GlobalInterface( + title=u"The interface this icon is for.", + description=u""" + The icon will be for all objects that implement this + interface.""", + required=True + ) + + file = Path( + title=u"File", + description=u"The file containing the icon.", + required=False + ) + + resource = TextLine( + title=u"Resource", + description=u"A resource containing the icon.", + required=False + ) + + title = MessageID( + title=u"Title", + description=u"Descriptive title", + required=False + ) + + layer = GlobalInterface( + title=u"The layer the icon should be found in", + description=u""" + For information on layers, see the documentation for the skin + directive. Defaults to "default".""", + required=False + ) + + width = Int( + title=u"The width of the icon.", + description=u""" + The width will be used for the + attribute. Defaults to 16.""", + required=False, + default=16 + ) + + height = Int( + title=u"The height of the icon.", + description=u""" + The height will be used for the + attribute. Defaults to 16.""", + required=False, + default=16 + ) diff -Nru zope3-3.4.0/src/zope/browserresource/meta.zcml zope3-3.5~bzr18/src/zope/browserresource/meta.zcml --- zope3-3.4.0/src/zope/browserresource/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/browserresource/resource.py zope3-3.5~bzr18/src/zope/browserresource/resource.py --- zope3-3.4.0/src/zope/browserresource/resource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/resource.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,65 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Resource base class and AbsoluteURL adapter + +$Id: resource.py 105850 2009-11-19 07:05:29Z tlotze $ +""" +import zope.component.hooks +from zope.component import adapts, getMultiAdapter, queryMultiAdapter +from zope.interface import implements, implementsOnly +from zope.location import Location +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.traversing.browser.interfaces import IAbsoluteURL +import zope.traversing.browser.absoluteurl + +from zope.browserresource.interfaces import IResource + + +class Resource(Location): + + implements(IResource) + + def __init__(self, request): + self.request = request + + def __call__(self): + return str(getMultiAdapter((self, self.request), IAbsoluteURL)) + + +class AbsoluteURL(zope.traversing.browser.absoluteurl.AbsoluteURL): + + implementsOnly(IAbsoluteURL) + adapts(IResource, IBrowserRequest) + + def __init__(self, context, request): + self.context = context + self.request = request + + def _createUrl(self, baseUrl, name): + return "%s/@@/%s" % (baseUrl, name) + + def __str__(self): + name = self.context.__name__ + if name.startswith('++resource++'): + name = name[12:] + + site = zope.component.hooks.getSite() + base = queryMultiAdapter((site, self.request), IAbsoluteURL, + name="resource") + if base is None: + url = str(getMultiAdapter((site, self.request), IAbsoluteURL)) + else: + url = str(base) + + return self._createUrl(url, name) diff -Nru zope3-3.4.0/src/zope/browserresource/resources.py zope3-3.5~bzr18/src/zope/browserresource/resources.py --- zope3-3.4.0/src/zope/browserresource/resources.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/resources.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,119 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Resource URL access + +$Id: resources.py 105850 2009-11-19 07:05:29Z tlotze $ +""" +from zope.component import queryAdapter +from zope.interface import implements +from zope.location import locate +from zope.publisher.browser import BrowserView +from zope.publisher.interfaces import NotFound +from zope.publisher.interfaces.browser import IBrowserPublisher + +class Resources(BrowserView): + """A view that can be traversed further to access browser resources + + This view is usually registered for zope.component.interfaces.ISite objects + with no name, so resources will be available at /@@/. + + Let's test how it's traversed to get registered resources. Let's create + a sample resource class and register it. + + >>> from zope.component import provideAdapter + >>> from zope.interface import Interface + >>> from zope.publisher.interfaces import NotFound + >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer + >>> from zope.publisher.browser import TestRequest + + >>> class Resource(object): + ... def __init__(self,request): + ... self.request = request + ... def __call__(self): + ... return 'http://localhost/testresource' + + >>> provideAdapter(Resource, (IDefaultBrowserLayer,), Interface, 'test') + + Now, create a site and request objects and get the Resources object to + work with. + + >>> site = object() + >>> request = TestRequest() + >>> resources = Resources(site, request) + + Okay, let's test the publishTraverse method. It should traverse to our + registered resource. + + >>> resource = resources.publishTraverse(request, 'test') + >>> resource.__parent__ is site + True + >>> resource.__name__ == 'test' + True + >>> resource() + 'http://localhost/testresource' + + However, it will raise NotFound exception if we try to traverse to an + unregistered resource. + + >>> resources.publishTraverse(request, 'does-not-exist') + Traceback (most recent call last): + ... + NotFound: Object: , + name: 'does-not-exist' + + When accessed without further traversing, it returns an empty page and no + futher traversing steps. + + >>> view, path = resources.browserDefault(request) + >>> view() == '' + True + >>> path == () + True + + The Resources view also provides __getitem__ method for use in templates. + + >>> resource = resources['test'] + >>> resource.__parent__ is site + True + >>> resource.__name__ == 'test' + True + >>> resource() + 'http://localhost/testresource' + + """ + + implements(IBrowserPublisher) + + def publishTraverse(self, request, name): + '''See zope.publisher.interfaces.browser.IBrowserPublisher interface''' + resource = queryAdapter(request, name=name) + if resource is None: + raise NotFound(self, name) + + locate(resource, self.context, name) + return resource + + def browserDefault(self, request): + '''See zope.publisher.interfaces.browser.IBrowserPublisher interface''' + return empty, () + + def __getitem__(self, name): + '''A helper method to make this view usable from templates, + so resources can be acessed in template like context/@@/. + ''' + return self.publishTraverse(self.request, name) + + +def empty(): + return '' diff -Nru zope3-3.4.0/src/zope/browserresource/tests/support.py zope3-3.5~bzr18/src/zope/browserresource/tests/support.py --- zope3-3.4.0/src/zope/browserresource/tests/support.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/support.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 tests that need a simple site to be provided. + +$Id: support.py 105850 2009-11-19 07:05:29Z tlotze $ +""" + +import zope.component +import zope.component.hooks +import zope.component.interfaces +from zope.interface import implements +from zope.traversing.interfaces import IContainmentRoot + +import zope.browserresource.resource + +class Site: + + implements(zope.component.interfaces.ISite, IContainmentRoot) + + def getSiteManager(self): + return zope.component.getGlobalSiteManager() + +site = Site() + + +class SiteHandler(object): + + def setUp(self): + super(SiteHandler, self).setUp() + zope.component.hooks.setSite(site) + zope.component.provideAdapter( + zope.browserresource.resource.AbsoluteURL) + + def tearDown(self): + zope.component.hooks.setSite() + super(SiteHandler, self).tearDown() diff -Nru zope3-3.4.0/src/zope/browserresource/tests/test_directives.py zope3-3.5~bzr18/src/zope/browserresource/tests/test_directives.py --- zope3-3.4.0/src/zope/browserresource/tests/test_directives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/test_directives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,253 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""'browser' namespace directive tests + +$Id: test_directives.py 103138 2009-08-24 11:37:52Z nadako $ +""" + +import os +import unittest +from cStringIO import StringIO + +from zope import component +from zope.interface import Interface, implements, directlyProvides, providedBy + +import zope.security.management +from zope.configuration.xmlconfig import xmlconfig, XMLConfig +from zope.configuration.exceptions import ConfigurationError +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces import IDefaultViewName +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.publisher.interfaces.browser import IBrowserSkinType, IDefaultSkin +from zope.security.proxy import removeSecurityProxy, ProxyFactory +from zope.security.permission import Permission +from zope.security.interfaces import IPermission +from zope.traversing.adapters import DefaultTraversable +from zope.traversing.interfaces import ITraversable + +import zope.publisher.defaultview +import zope.browserresource +from zope.component import provideAdapter, provideUtility +from zope.component.testfiles.views import R1, IV +from zope.browserresource.file import FileResource +from zope.browserresource.i18nfile import I18nFileResource +from zope.browserresource.directory import DirectoryResource +from zope.testing import cleanup + +tests_path = os.path.join( + os.path.dirname(zope.browserresource.__file__), + 'tests') + +template = """ + %s + """ + + +request = TestRequest() + +class ITestLayer(IBrowserRequest): + """Test Layer.""" + +class ITestSkin(ITestLayer): + """Test Skin.""" + + +class MyResource(object): + + def __init__(self, request): + self.request = request + + +class Test(cleanup.CleanUp, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + XMLConfig('meta.zcml', zope.browserresource)() + provideAdapter(DefaultTraversable, (None,), ITraversable) + + def tearDown(self): + super(Test, self).tearDown() + + def testI18nResource(self): + self.assertEqual(component.queryAdapter(request, name='test'), None) + + path1 = os.path.join(tests_path, 'testfiles', 'test.pt') + path2 = os.path.join(tests_path, 'testfiles', 'test2.pt') + + xmlconfig(StringIO(template % ( + ''' + + + + + ''' % (path1, path2) + ))) + + v = component.getAdapter(request, name='test') + self.assertEqual( + component.queryAdapter(request, name='test').__class__, + I18nFileResource) + self.assertEqual(v._testData('en'), open(path1, 'rb').read()) + self.assertEqual(v._testData('fr'), open(path2, 'rb').read()) + + # translation must be provided for the default language + config = StringIO(template % ( + ''' + + + + + ''' % (path1, path2) + )) + self.assertRaises(ConfigurationError, xmlconfig, config) + + def testFactory(self): + self.assertEqual( + component.queryAdapter(request, name='index.html'), None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + r = component.getAdapter(request, name='index.html') + self.assertEquals(r.__class__, MyResource) + r = ProxyFactory(r) + self.assertEqual(r.__name__, "index.html") + + def testFile(self): + path = os.path.join(tests_path, 'testfiles', 'test.pt') + + self.assertEqual(component.queryAdapter(request, name='test'), None) + + xmlconfig(StringIO(template % + ''' + + ''' % path + )) + + r = component.getAdapter(request, name='index.html') + self.assertTrue(isinstance(r, FileResource)) + r = ProxyFactory(r) + self.assertEqual(r.__name__, "index.html") + + # Make sure we can access available attrs and not others + for n in ('GET', 'HEAD', 'publishTraverse', 'request', '__call__'): + getattr(r, n) + + self.assertRaises(Exception, getattr, r, '_testData') + + r = removeSecurityProxy(r) + self.assertEqual(r._testData(), open(path, 'rb').read()) + + + def testPluggableFactory(self): + + class ImageResource(object): + def __init__(self, image, request): + pass + + class ImageResourceFactory(object): + def __init__(self, path, checker, name): + pass + def __call__(self, request): + return ImageResource(None, request) + + from zope.browserresource.interfaces import IResourceFactoryFactory + component.provideUtility(ImageResourceFactory, IResourceFactoryFactory, + name='gif') + + xmlconfig(StringIO(template % + ''' + + ''' % os.path.join(tests_path, 'testfiles', 'test.gif') + )) + + r = component.getAdapter(request, name='test.gif') + self.assertTrue(isinstance(r, ImageResource)) + + def testDirectory(self): + path = os.path.join(tests_path, 'testfiles', 'subdir') + + self.assertEqual(component.queryAdapter(request, name='dir'), None) + + xmlconfig(StringIO(template % + ''' + + ''' % path + )) + + r = component.getAdapter(request, name='dir') + self.assertTrue(isinstance(r, DirectoryResource)) + r = ProxyFactory(r) + self.assertEqual(r.__name__, "dir") + + # Make sure we can access available attrs and not others + for n in ('publishTraverse', 'browserDefault', 'request', '__call__', + 'get', '__getitem__'): + getattr(r, n) + + self.assertRaises(Exception, getattr, r, 'directory_factory') + + inexistent_dir = StringIO(template % + ''' + + ''') + + self.assertRaises(ConfigurationError, xmlconfig, inexistent_dir) + + def test_SkinResource(self): + self.assertEqual(component.queryAdapter(request, name='test'), None) + + path = os.path.join(tests_path, 'testfiles', 'test.pt') + xmlconfig(StringIO(template % ( + ''' + + ''' % path + ))) + + self.assertEqual(component.queryAdapter(request, name='test'), None) + + r = component.getAdapter(TestRequest(skin=ITestSkin), name='test') + self.assertEqual(r._testData(), open(path, 'rb').read()) + + +def test_suite(): + return unittest.makeSuite(Test) diff -Nru zope3-3.4.0/src/zope/browserresource/tests/test_directory.py zope3-3.5~bzr18/src/zope/browserresource/tests/test_directory.py --- zope3-3.4.0/src/zope/browserresource/tests/test_directory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/test_directory.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,176 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Directory-based resources test + +$Id: test_directory.py 104477 2009-09-24 09:41:25Z nadako $ +""" +import os +import tempfile +import shutil +from unittest import TestCase, main, makeSuite + +from zope.publisher.interfaces import NotFound +from zope.proxy import isProxy +from zope.publisher.browser import TestRequest +from zope.security import proxy +from zope.security.checker import NamesChecker, ProxyFactory +from zope.interface import implements +from zope.location.interfaces import IContained +from zope.traversing.browser.absoluteurl import AbsoluteURL +from zope.traversing.browser.interfaces import IAbsoluteURL +from zope.component import provideAdapter, provideUtility + +from zope.testing import cleanup + +from zope.browserresource.directory import \ + DirectoryResourceFactory, DirectoryResource +from zope.browserresource.file import FileResource +import zope.browserresource.tests as p +from zope.browserresource.tests import support + +test_directory = os.path.dirname(p.__file__) + +checker = NamesChecker( + ('get', '__getitem__', 'request', 'publishTraverse') + ) + +class Ob(object): + implements(IContained) + __parent__ = __name__ = None + +ob = Ob() + +class Test(support.SiteHandler, cleanup.CleanUp, TestCase): + + def setUp(self): + super(Test, self).setUp() + provideAdapter(AbsoluteURL, (None, None), IAbsoluteURL) + + def testNotFound(self): + path = os.path.join(test_directory, 'testfiles') + request = TestRequest() + factory = DirectoryResourceFactory(path, checker, 'testfiles') + resource = factory(request) + self.assertRaises(NotFound, resource.publishTraverse, + resource.request, 'doesnotexist') + self.assertRaises(NotFound, resource.get, 'doesnotexist') + + def testBrowserDefault(self): + path = os.path.join(test_directory, 'testfiles') + request = TestRequest() + factory = DirectoryResourceFactory(path, checker, 'testfiles') + resource = factory(request) + view, next = resource.browserDefault(request) + self.assertEquals(view(), '') + self.assertEquals(next, ()) + + def testGetitem(self): + path = os.path.join(test_directory, 'testfiles') + request = TestRequest() + factory = DirectoryResourceFactory(path, checker, 'testfiles') + resource = factory(request) + self.assertRaises(KeyError, resource.__getitem__, 'doesnotexist') + file = resource['test.txt'] + + def testForbiddenNames(self): + request = TestRequest() + old_forbidden_names = DirectoryResource.forbidden_names + path = tempfile.mkdtemp() + try: + os.mkdir(os.path.join(path, '.svn')) + open(os.path.join(path, 'test.txt'), 'w').write('') + + factory = DirectoryResourceFactory(path, checker, 'testfiles') + resource = factory(request) + + self.assertEquals(resource.get('.svn', None), None) + self.assertNotEquals(resource.get('test.txt', None), None) + + DirectoryResource.forbidden_names = ('*.txt', ) + self.assertEquals(resource.get('test.txt', None), None) + self.assertNotEquals(resource.get('.svn', None), None) + finally: + shutil.rmtree(path) + DirectoryResource.forbidden_names = old_forbidden_names + + def testProxy(self): + path = os.path.join(test_directory, 'testfiles') + request = TestRequest() + factory = DirectoryResourceFactory(path, checker, 'testfiles') + resource = factory(request) + file = ProxyFactory(resource['test.txt']) + self.assert_(isProxy(file)) + + def testURL(self): + request = TestRequest() + request._vh_root = support.site + path = os.path.join(test_directory, 'testfiles') + files = DirectoryResourceFactory(path, checker, 'test_files')(request) + files.__parent__ = support.site + file = files['test.gif'] + self.assertEquals(file(), 'http://127.0.0.1/@@/test_files/test.gif') + + def testURL2Level(self): + request = TestRequest() + request._vh_root = support.site + ob.__parent__ = support.site + ob.__name__ = 'ob' + path = os.path.join(test_directory, 'testfiles') + files = DirectoryResourceFactory(path, checker, 'test_files')(request) + files.__parent__ = ob + file = files['test.gif'] + self.assertEquals(file(), 'http://127.0.0.1/@@/test_files/test.gif') + + def testURL3Level(self): + request = TestRequest() + request._vh_root = support.site + ob.__parent__ = support.site + ob.__name__ = 'ob' + path = os.path.join(test_directory, 'testfiles') + files = DirectoryResourceFactory(path, checker, 'test_files')(request) + files.__parent__ = ob + file = files['test.gif'] + self.assertEquals(file(), 'http://127.0.0.1/@@/test_files/test.gif') + subdir = files['subdir'] + self.assert_(proxy.isinstance(subdir, DirectoryResource)) + file = subdir['test.gif'] + self.assertEquals(file(), + 'http://127.0.0.1/@@/test_files/subdir/test.gif') + + def testPluggableFactories(self): + path = os.path.join(test_directory, 'testfiles') + request = TestRequest() + resource = DirectoryResourceFactory(path, checker, 'files')(request) + + class ImageResource(object): + def __init__(self, image, request): + pass + + class ImageResourceFactory(object): + def __init__(self, path, checker, name): + pass + def __call__(self, request): + return ImageResource(None, request) + + from zope.browserresource.interfaces import IResourceFactoryFactory + provideUtility(ImageResourceFactory, IResourceFactoryFactory, 'gif') + + image = resource['test.gif'] + self.assert_(proxy.isinstance(image, ImageResource)) + + file = resource['test.txt'] + self.assert_(proxy.isinstance(file, FileResource)) + +def test_suite(): + return makeSuite(Test) diff -Nru zope3-3.4.0/src/zope/browserresource/tests/test_file.py zope3-3.5~bzr18/src/zope/browserresource/tests/test_file.py --- zope3-3.4.0/src/zope/browserresource/tests/test_file.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/test_file.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,46 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""File-based browser resource tests. + +$Id: test_file.py 111698 2010-04-30 20:23:50Z hannosch $ +""" + +import doctest +import os +import unittest +from zope.testing import cleanup + +from zope.publisher.browser import TestRequest +from zope.security.checker import NamesChecker + + +def setUp(test): + cleanup.setUp() + data_dir = os.path.join(os.path.dirname(__file__), 'testfiles') + + test.globs['testFilePath'] = os.path.join(data_dir, 'test.txt') + test.globs['nullChecker'] = NamesChecker() + test.globs['TestRequest'] = TestRequest + + +def tearDown(test): + cleanup.tearDown() + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite( + 'zope.browserresource.file', + setUp=setUp, tearDown=tearDown, + optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE), + )) Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/browserresource/tests/testfiles/subdir/test.gif and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/browserresource/tests/testfiles/subdir/test.gif differ diff -Nru zope3-3.4.0/src/zope/browserresource/tests/testfiles/test2.pt zope3-3.5~bzr18/src/zope/browserresource/tests/testfiles/test2.pt --- zope3-3.4.0/src/zope/browserresource/tests/testfiles/test2.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/testfiles/test2.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

test

Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/browserresource/tests/testfiles/test.gif and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/browserresource/tests/testfiles/test.gif differ diff -Nru zope3-3.4.0/src/zope/browserresource/tests/testfiles/test.pt zope3-3.5~bzr18/src/zope/browserresource/tests/testfiles/test.pt --- zope3-3.4.0/src/zope/browserresource/tests/testfiles/test.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/testfiles/test.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

test

diff -Nru zope3-3.4.0/src/zope/browserresource/tests/testfiles/test.txt zope3-3.5~bzr18/src/zope/browserresource/tests/testfiles/test.txt --- zope3-3.4.0/src/zope/browserresource/tests/testfiles/test.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/testfiles/test.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ +test +data diff -Nru zope3-3.4.0/src/zope/browserresource/tests/test_i18nfile.py zope3-3.5~bzr18/src/zope/browserresource/tests/test_i18nfile.py --- zope3-3.4.0/src/zope/browserresource/tests/test_i18nfile.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/test_i18nfile.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,150 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""I18n File-Resource Tests + +$Id: test_i18nfile.py 103196 2009-08-25 11:48:42Z nadako $ +""" +from unittest import main, makeSuite +import os + +from zope.publisher.interfaces import NotFound + +from zope.component import provideAdapter, provideUtility +from zope.testing import cleanup + +from zope.i18n.interfaces import IUserPreferredCharsets, IUserPreferredLanguages + +from zope.publisher.http import IHTTPRequest, HTTPCharsets +from zope.publisher.browser import BrowserLanguages, TestRequest + +from zope.browserresource.i18nfile import I18nFileResource +from zope.browserresource.i18nfile import I18nFileResourceFactory +from zope.browserresource.file import File +import zope.browserresource.tests as p + +from zope.i18n.interfaces import INegotiator +from zope.i18n.negotiator import negotiator + +from zope.i18n.tests.testii18naware import TestII18nAware + +test_directory = os.path.dirname(p.__file__) + + +class Test(cleanup.CleanUp, TestII18nAware): + + def setUp(self): + super(Test, self).setUp() + TestII18nAware.setUp(self) + provideAdapter(HTTPCharsets, (IHTTPRequest,), IUserPreferredCharsets) + provideAdapter(BrowserLanguages, (IHTTPRequest,), IUserPreferredLanguages) + # Setup the negotiator utility + provideUtility(negotiator, INegotiator) + + + def _createObject(self): + obj = I18nFileResource({'en':None, 'lt':None, 'fr':None}, + TestRequest(), 'fr') + return obj + + def test_setDefaultLanguage(self): + ob = self._createObject() + self.assertRaises(ValueError, ob.setDefaultLanguage, 'ru') + + def _createDict(self, filename1='test.pt', filename2='test2.pt'): + path1 = os.path.join(test_directory, 'testfiles', filename1) + path2 = os.path.join(test_directory, 'testfiles', filename2) + return { 'en': File(path1, filename1), + 'fr': File(path2, filename2) } + + + def testNoTraversal(self): + + resource = I18nFileResourceFactory(self._createDict(), 'en')\ + (TestRequest()) + + self.assertRaises(NotFound, + resource.publishTraverse, + resource.request, + '_testData') + + def testFileGET(self): + + # case 1: no language preference, should get en + path = os.path.join(test_directory, 'testfiles', 'test.txt') + + resource = I18nFileResourceFactory(self._createDict('test.txt'), 'en')\ + (TestRequest()) + + + self.assertEqual(resource.GET(), open(path, 'rb').read()) + + response = resource.request.response + self.assertEqual(response.getHeader('Content-Type'), 'text/plain') + + # case 2: prefer lt, have only en and fr, should get en + resource = I18nFileResourceFactory( + self._createDict('test.txt'), 'en')\ + (TestRequest(HTTP_ACCEPT_LANGUAGE='lt')) + + self.assertEqual(resource.GET(), open(path, 'rb').read()) + + response = resource.request.response + self.assertEqual(response.getHeader('Content-Type'), 'text/plain') + + # case 3: prefer fr, have it, should get fr + path = os.path.join(test_directory, 'testfiles', 'test2.pt') + resource = I18nFileResourceFactory( + self._createDict('test.pt', 'test2.pt'), 'en')\ + (TestRequest(HTTP_ACCEPT_LANGUAGE='fr')) + + self.assertEqual(resource.GET(), open(path, 'rb').read()) + + response = resource.request.response + self.assertEqual(response.getHeader('Content-Type'), 'text/html') + + + def testFileHEAD(self): + + # case 1: no language preference, should get en + resource = I18nFileResourceFactory(self._createDict('test.txt'), 'en')\ + (TestRequest()) + + self.assertEqual(resource.HEAD(), '') + + response = resource.request.response + self.assertEqual(response.getHeader('Content-Type'), 'text/plain') + + # case 2: prefer lt, have only en and fr, should get en + resource = I18nFileResourceFactory( + self._createDict('test.txt'), 'en')\ + (TestRequest(HTTP_ACCEPT_LANGUAGE='lt')) + + self.assertEqual(resource.HEAD(), '') + + response = resource.request.response + self.assertEqual(response.getHeader('Content-Type'), 'text/plain') + + # case 3: prefer fr, have it, should get fr + resource = I18nFileResourceFactory( + self._createDict('test.pt', 'test2.pt'), 'en')\ + (TestRequest(HTTP_ACCEPT_LANGUAGE='fr')) + + self.assertEqual(resource.HEAD(), '') + + response = resource.request.response + self.assertEqual(response.getHeader('Content-Type'), 'text/html') + + +def test_suite(): + return makeSuite(Test) diff -Nru zope3-3.4.0/src/zope/browserresource/tests/test_icondirective.py zope3-3.5~bzr18/src/zope/browserresource/tests/test_icondirective.py --- zope3-3.4.0/src/zope/browserresource/tests/test_icondirective.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/test_icondirective.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,208 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 Icon-Directive + +$Id: test_icondirective.py 103136 2009-08-24 11:20:44Z nadako $ +""" +import os +from StringIO import StringIO +from unittest import TestCase, main, makeSuite + +from zope import component +from zope.configuration.exceptions import ConfigurationError +from zope.configuration.xmlconfig import xmlconfig, XMLConfig +from zope.interface import implements +from zope.publisher.browser import TestRequest +from zope.security.checker import ProxyFactory, CheckerPublic +from zope.security.interfaces import Forbidden +from zope.security.proxy import removeSecurityProxy +from zope.traversing.interfaces import IContainmentRoot +from zope.traversing.browser.absoluteurl import AbsoluteURL +from zope.traversing.browser.interfaces import IAbsoluteURL + +import zope.location.interfaces +import zope.browserresource +from zope.component.testfiles.views import IC +from zope.browserresource.tests import support +from zope.testing import cleanup + + +template = """ + %s + """ + + +request = TestRequest() + +class Ob(object): + implements(IC) + +ob = Ob() +request._vh_root = support.site + +def defineCheckers(): + # define the appropriate checker for a FileResource for these tests + from zope.security.protectclass import protectName + from zope.browserresource.file import FileResource + protectName(FileResource, '__call__', 'zope.Public') + + +class Test(support.SiteHandler, cleanup.CleanUp, TestCase): + + def setUp(self): + super(Test, self).setUp() + XMLConfig('meta.zcml', zope.browserresource)() + defineCheckers() + component.provideAdapter(AbsoluteURL, (None, None), IAbsoluteURL) + + def test(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='zmi_icon'), + None) + + import zope.browserresource.tests as p + path = os.path.dirname(p.__file__) + path = os.path.join(path, 'testfiles', 'test.gif') + + # Configure the icon and make sure we can render the resulting view: + xmlconfig(StringIO(template % ( + ''' + + ''' % path + ))) + + view = component.getMultiAdapter((ob, request), name='zmi_icon') + rname = 'zope-component-testfiles-views-IC-zmi_icon.gif' + self.assertEqual( + view(), + 'C' + % rname) + + self.assertEqual(view.url(), 'http://127.0.0.1/@@/' + rname) + + # Make sure that the title attribute works + xmlconfig(StringIO(template % ( + ''' + + ''' % path + ))) + + view = component.getMultiAdapter( + (ob, request), name='zmi_icon_w_title') + rname = 'zope-component-testfiles-views-IC-zmi_icon_w_title.gif' + self.assertEqual( + view(), + 'click this!' + % rname) + + # Make sure that the width and height attributes work + xmlconfig(StringIO(template % ( + ''' + + ''' % path + ))) + + view = component.getMultiAdapter((ob, request), + name='zmi_icon_w_width_and_height') + rname = ('zope-component-testfiles-views-IC-' + 'zmi_icon_w_width_and_height.gif') + self.assertEqual( + view(), + 'C' + % rname) + + # Make sure that the image was installed as a resource: + resource = ProxyFactory(component.getAdapter(request, name=rname)) + self.assertRaises(Forbidden, getattr, resource, '_testData') + resource = removeSecurityProxy(resource) + self.assertEqual(resource._testData(), open(path, 'rb').read()) + + def testResource(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='zmi_icon'), None) + + import zope.browserresource.tests as p + path = os.path.dirname(p.__file__) + path = os.path.join(path, 'testfiles', 'test.gif') + + xmlconfig(StringIO(template % ( + ''' + + + ''' % path + ))) + + view = component.getMultiAdapter((ob, request), name='zmi_icon') + rname = "zmi_icon_res" + self.assertEqual( + view(), + 'C' + % rname) + + resource = ProxyFactory(component.getAdapter(request, name=rname)) + + self.assertRaises(Forbidden, getattr, resource, '_testData') + resource = removeSecurityProxy(resource) + self.assertEqual(resource._testData(), open(path, 'rb').read()) + + def testResourceErrors(self): + self.assertEqual( + component.queryMultiAdapter((ob, request), name='zmi_icon'), None) + + import zope.browserresource.tests as p + path = os.path.dirname(p.__file__) + path = os.path.join(path, 'testfiles', 'test.gif') + + config = StringIO(template % ( + ''' + + + ''' % (path, path) + )) + self.assertRaises(ConfigurationError, xmlconfig, config) + + config = StringIO(template % ( + """ + + """ + )) + self.assertRaises(ConfigurationError, xmlconfig, config) + + +def test_suite(): + return makeSuite(Test) diff -Nru zope3-3.4.0/src/zope/browserresource/tests/test_resource.py zope3-3.5~bzr18/src/zope/browserresource/tests/test_resource.py --- zope3-3.4.0/src/zope/browserresource/tests/test_resource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/test_resource.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,75 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Unit tests for Resource + +$Id: test_resource.py 105850 2009-11-19 07:05:29Z tlotze $ +""" +import unittest + +from zope import component + +from zope.publisher.browser import TestRequest + +import zope.component.interfaces +from zope.browserresource.resource import Resource +from zope.browserresource.tests import support +from zope.traversing.browser.interfaces import IAbsoluteURL +from zope.traversing.browser.absoluteurl import AbsoluteURL +from zope.testing import cleanup + + +class TestResource(support.SiteHandler, cleanup.CleanUp, unittest.TestCase): + + def setUp(self): + super(TestResource, self).setUp() + component.provideAdapter(AbsoluteURL, (None, None), IAbsoluteURL) + + def testGlobal(self): + req = TestRequest() + r = Resource(req) + req._vh_root = support.site + r.__parent__ = support.site + r.__name__ = 'foo' + self.assertEquals(r(), 'http://127.0.0.1/@@/foo') + r.__name__ = '++resource++foo' + self.assertEquals(r(), 'http://127.0.0.1/@@/foo') + + def testGlobalInVirtualHost(self): + req = TestRequest() + req.setVirtualHostRoot(['x', 'y']) + r = Resource(req) + req._vh_root = support.site + r.__parent__ = support.site + r.__name__ = 'foo' + self.assertEquals(r(), 'http://127.0.0.1/x/y/@@/foo') + + def testResourceUrl(self): + # fake IAbsoluteURL adapter + def resourceBase(site, request): + return 'http://cdn.example.com' + component.provideAdapter( + resourceBase, + (zope.component.interfaces.ISite, TestRequest), + IAbsoluteURL, 'resource') + + req = TestRequest() + r = Resource(req) + req._vh_root = support.site + r.__parent__ = support.site + r.__name__ = 'foo' + self.assertEquals(r(), 'http://cdn.example.com/@@/foo') + + +def test_suite(): + return unittest.makeSuite(TestResource) diff -Nru zope3-3.4.0/src/zope/browserresource/tests/test_resources.py zope3-3.5~bzr18/src/zope/browserresource/tests/test_resources.py --- zope3-3.4.0/src/zope/browserresource/tests/test_resources.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/browserresource/tests/test_resources.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,35 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 Browser Resources + +$Id: test_resources.py 111698 2010-04-30 20:23:50Z hannosch $ +""" + +import doctest +import unittest +from zope.testing import cleanup + +def setUp(test): + cleanup.setUp() + +def tearDown(test): + cleanup.tearDown() + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite( + 'zope.browserresource.resources', + setUp=setUp, tearDown=tearDown, + optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE), + )) diff -Nru zope3-3.4.0/src/zope/cachedescriptors/__init__.py zope3-3.5~bzr18/src/zope/cachedescriptors/__init__.py --- zope3-3.4.0/src/zope/cachedescriptors/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/cachedescriptors/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# diff -Nru zope3-3.4.0/src/zope/cachedescriptors/method.py zope3-3.5~bzr18/src/zope/cachedescriptors/method.py --- zope3-3.4.0/src/zope/cachedescriptors/method.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/cachedescriptors/method.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,59 @@ +############################################################################## +# Copyright (c) 2007 Zope Corporation 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. +############################################################################## +"""Cached Methods + +$Id: method.py 79733 2007-09-18 09:36:00Z zagy $ +""" + +class cachedIn(object): + """Cached method with given cache attribute.""" + + def __init__(self, attribute_name, factory=dict): + self.attribute_name = attribute_name + self.factory = factory + + def __call__(self, func): + + def decorated(instance, *args, **kwargs): + cache = self.cache(instance) + key = self._get_cache_key(*args, **kwargs) + try: + v = cache[key] + except KeyError: + v = cache[key] = func(instance, *args, **kwargs) + return v + + decorated.invalidate = self.invalidate + return decorated + + def invalidate(self, instance, *args, **kwargs): + cache = self.cache(instance) + key = self._get_cache_key(*args, **kwargs) + try: + del cache[key] + except KeyError: + pass + + def cache(self, instance): + try: + cache = getattr(instance, self.attribute_name) + except AttributeError: + cache = self.factory() + setattr(instance, self.attribute_name, cache) + return cache + + @staticmethod + def _get_cache_key(*args, **kwargs): + kw = kwargs.items() + kw.sort() + key = (args, tuple(kw)) + return key diff -Nru zope3-3.4.0/src/zope/cachedescriptors/method.txt zope3-3.5~bzr18/src/zope/cachedescriptors/method.txt --- zope3-3.4.0/src/zope/cachedescriptors/method.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/cachedescriptors/method.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,88 @@ +Method Cache +------------ + +cachedIn +~~~~~~~~ + +The `cachedIn` property allows to specify the attribute where to store the +computed value: + + >>> import math + >>> from zope.cachedescriptors import method + + >>> class Point(object): + ... + ... def __init__(self, x, y): + ... self.x, self.y = x, y + ... + ... @method.cachedIn('_cache') + ... def distance(self, x, y): + ... print 'computing distance' + ... return math.sqrt((self.x - x)**2 + (self.y - y)**2) + ... + >>> point = Point(1.0, 2.0) + +The value is computed once: + + >>> point.distance(2, 2) + computing distance + 1.0 + >>> point.distance(2, 2) + 1.0 + + +Using different arguments calculates a new distance: + + >>> point.distance(5, 2) + computing distance + 4.0 + >>> point.distance(5, 2) + 4.0 + + +The data is stored at the given `_cache` attribute: + + >>> isinstance(point._cache, dict) + True + + >>> sorted(point._cache.items()) + [(((2, 2), ()), 1.0), (((5, 2), ()), 4.0)] + + +It is possible to exlicitly invalidate the data: + + >>> point.distance.invalidate(point, 5, 2) + >>> point.distance(5, 2) + computing distance + 4.0 + +Invalidating keys which are not in the cache, does not result in an error: + + >>> point.distance.invalidate(point, 47, 11) + + +It is possible to pass in a factory for the cache attribute. Create another +Point class: + + + >>> class MyDict(dict): + ... pass + >>> class Point(object): + ... + ... def __init__(self, x, y): + ... self.x, self.y = x, y + ... + ... @method.cachedIn('_cache', MyDict) + ... def distance(self, x, y): + ... print 'computing distance' + ... return math.sqrt((self.x - x)**2 + (self.y - y)**2) + ... + >>> point = Point(1.0, 2.0) + >>> point.distance(2, 2) + computing distance + 1.0 + +Now the cache is a MyDict instance: + + >>> isinstance(point._cache, MyDict) + True diff -Nru zope3-3.4.0/src/zope/cachedescriptors/property.py zope3-3.5~bzr18/src/zope/cachedescriptors/property.py --- zope3-3.4.0/src/zope/cachedescriptors/property.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/cachedescriptors/property.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,106 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""Cached properties + +See the CachedProperty class. + +$Id: property.py 75652 2007-05-09 13:11:30Z zagy $ +""" + +ncaches = 0l + + +class CachedProperty(object): + """Cached Properties. + """ + + def __init__(self, func, *names): + global ncaches + ncaches += 1 + self.data = (func, names, + "_v_cached_property_key_%s" % ncaches, + "_v_cached_property_value_%s" % ncaches) + + def __get__(self, inst, class_): + if inst is None: + return self + + func, names, key_name, value_name = self.data + + key = names and [getattr(inst, name) for name in names] + value = getattr(inst, value_name, self) + + if value is not self: + # We have a cached value + if key == getattr(inst, key_name, self): + # Cache is still good! + return value + + # We need to compute and cache the value + + value = func(inst) + setattr(inst, key_name, key) + setattr(inst, value_name, value) + + return value + + +class Lazy(object): + """Lazy Attributes. + """ + + def __init__(self, func, name=None): + if name is None: + name = func.__name__ + self.data = (func, name) + + def __get__(self, inst, class_): + if inst is None: + return self + + func, name = self.data + value = func(inst) + inst.__dict__[name] = value + + return value + + +class readproperty(object): + + def __init__(self, func): + self.func = func + + def __get__(self, inst, class_): + if inst is None: + return self + + func = self.func + return func(inst) + + +class cachedIn(object): + """Cached property with given cache attribute.""" + + def __init__(self, attribute_name): + self.attribute_name = attribute_name + + def __call__(self, func): + + def get(instance): + try: + value = getattr(instance, self.attribute_name) + except AttributeError: + value = func(instance) + setattr(instance, self.attribute_name, value) + return value + + return property(get) diff -Nru zope3-3.4.0/src/zope/cachedescriptors/property.txt zope3-3.5~bzr18/src/zope/cachedescriptors/property.txt --- zope3-3.4.0/src/zope/cachedescriptors/property.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/cachedescriptors/property.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,211 @@ +Cached Properties +----------------- + +Cached properties are computed properties that cache their computed +values. They take into account instance attributes that they depend +on, so when the instance attributes change, the properties will change +the values they return. + +CachedProperty +~~~~~~~~~~~~~~ + +Cached properties cache their data in _v_ attributes, so they are +also useful for managing the computation of volatile attributes for +persistent objects. Let's look at an example: + + >>> from zope.cachedescriptors import property + >>> import math + + >>> class Point: + ... + ... def __init__(self, x, y): + ... self.x, self.y = x, y + ... + ... def radius(self): + ... print 'computing radius' + ... return math.sqrt(self.x**2 + self.y**2) + ... radius = property.CachedProperty(radius, 'x', 'y') + + >>> point = Point(1.0, 2.0) + +If we ask for the radius the first time: + + >>> '%.2f' % point.radius + computing radius + '2.24' + +We see that the radius function is called, but if we ask for it again: + + >>> '%.2f' % point.radius + '2.24' + +The function isn't called. If we change one of the attribute the +radius depends on, it will be recomputed: + + >>> point.x = 2.0 + >>> '%.2f' % point.radius + computing radius + '2.83' + +But changing other attributes doesn't cause recomputation: + + >>> point.q = 1 + >>> '%.2f' % point.radius + '2.83' + +Note that we don't have any non-volitile attributes added: + + >>> names = [name for name in point.__dict__ if not name.startswith('_v_')] + >>> names.sort() + >>> names + ['q', 'x', 'y'] + + +Lazy Computed Attributes +~~~~~~~~~~~~~~~~~~~~~~~~ + +The `property` module provides another descriptor that supports a +slightly different caching model: lazy attributes. Like cached +proprties, they are computed the first time they are used. however, +they aren't stored in volatile attributes and they aren't +automatically updated when other attributes change. Furthermore, the +store their data using their attribute name, thus overriding +themselves. This provides much faster attribute access after the +attribute has been computed. Let's look at the previous example using +lazy attributes: + + >>> class Point: + ... + ... def __init__(self, x, y): + ... self.x, self.y = x, y + ... + ... @property.Lazy + ... def radius(self): + ... print 'computing radius' + ... return math.sqrt(self.x**2 + self.y**2) + + >>> point = Point(1.0, 2.0) + +If we ask for the radius the first time: + + >>> '%.2f' % point.radius + computing radius + '2.24' + +We see that the radius function is called, but if we ask for it again: + + >>> '%.2f' % point.radius + '2.24' + +The function isn't called. If we change one of the attribute the +radius depends on, it still isn't called: + + >>> point.x = 2.0 + >>> '%.2f' % point.radius + '2.24' + +If we want the radius to be recomputed, we have to manually delete it: + + >>> del point.radius + + >>> point.x = 2.0 + >>> '%.2f' % point.radius + computing radius + '2.83' + +Note that the radius is stored in the instance dictionary: + + >>> '%.2f' % point.__dict__['radius'] + '2.83' + +The lazy attribute needs to know the attribute name. It normally +deduces the attribute name from the name of the function passed. If we +want to use a different name, we need to pass it: + + >>> def d(point): + ... print 'computing diameter' + ... return 2*point.radius + + >>> Point.diameter = property.Lazy(d, 'diameter') + >>> '%.2f' % point.diameter + computing diameter + '5.66' + + +readproperty +~~~~~~~~~~~~ + +readproperties are like lazy computed attributes except that the +attribute isn't set by the property: + + + >>> class Point: + ... + ... def __init__(self, x, y): + ... self.x, self.y = x, y + ... + ... @property.readproperty + ... def radius(self): + ... print 'computing radius' + ... return math.sqrt(self.x**2 + self.y**2) + + >>> point = Point(1.0, 2.0) + + >>> '%.2f' % point.radius + computing radius + '2.24' + + >>> '%.2f' % point.radius + computing radius + '2.24' + +But you *can* replace the property by setting a value. This is the major +difference to the builtin `property`: + + >>> point.radius = 5 + >>> point.radius + 5 + + +cachedIn +~~~~~~~~ + +The `cachedIn` property allows to specify the attribute where to store the +computed value: + + >>> class Point: + ... + ... def __init__(self, x, y): + ... self.x, self.y = x, y + ... + ... @property.cachedIn('_radius_attribute') + ... def radius(self): + ... print 'computing radius' + ... return math.sqrt(self.x**2 + self.y**2) + + >>> point = Point(1.0, 2.0) + + >>> '%.2f' % point.radius + computing radius + '2.24' + + >>> '%.2f' % point.radius + '2.24' + +The radius is cached in the attribute with the given name, `_radius_attribute` +in this case: + + >>> '%.2f' % point._radius_attribute + '2.24' + +When the attribute is removed the radius is re-calculated once. This allows +invalidation: + + >>> del point._radius_attribute + + >>> '%.2f' % point.radius + computing radius + '2.24' + + >>> '%.2f' % point.radius + '2.24' diff -Nru zope3-3.4.0/src/zope/cachedescriptors/README.txt zope3-3.5~bzr18/src/zope/cachedescriptors/README.txt --- zope3-3.4.0/src/zope/cachedescriptors/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/cachedescriptors/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,28 @@ +================== +Cached descriptors +================== + +Cached descriptors cache their output. They take into account +instance attributes that they depend on, so when the instance +attributes change, the descriptors will change the values they +return. + +Cached descriptors cache their data in _v_ attributes, so they are +also useful for managing the computation of volatile attributes for +persistent objects. + +Persistent descriptors: + + property + + A simple computed property. See property.txt. + + method + + Idempotent method. The return values are cached based on method + arguments and on any instance attributes that the methods are + defined to depend on. + + **Note**, only a cache based on arguments has been implemented so far. + + See method.txt. diff -Nru zope3-3.4.0/src/zope/cachedescriptors/tests.py zope3-3.5~bzr18/src/zope/cachedescriptors/tests.py --- zope3-3.4.0/src/zope/cachedescriptors/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/cachedescriptors/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 hookup + +$Id: tests.py 111678 2010-04-30 17:59:46Z hannosch $ +""" + +import doctest + + +def test_suite(): + return doctest.DocFileSuite( + 'property.txt', 'method.txt', + optionflags=doctest.ELLIPSIS) diff -Nru zope3-3.4.0/src/zope/component/_api.py zope3-3.5~bzr18/src/zope/component/_api.py --- zope3-3.4.0/src/zope/component/_api.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/_api.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,238 @@ +############################################################################## +# +# 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 3 Component Architecture + +$Id: _api.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +import sys +import types + +from zope.interface import Interface +from zope.interface import implementedBy +from zope.interface import providedBy + +from zope.component.interfaces import IComponentArchitecture +from zope.component.interfaces import IComponentRegistrationConvenience +from zope.component.interfaces import IFactory +from zope.component.interfaces import ComponentLookupError +from zope.component.interfaces import IComponentLookup +from zope.component._declaration import adaptedBy +from zope.component._declaration import adapter +from zope.component._declaration import adapts + +# Use the C implementation in zope.hookable, if available; fall back +# to our Python version if not. +try: + from zope.hookable import hookable +except ImportError: + from zope.component.hookable import hookable + +# getSiteManager() returns a component registry. Although the term +# "site manager" is deprecated in favor of "component registry", +# the old term is kept around to maintain a stable API. +base = None +@hookable +def getSiteManager(context=None): + global base + if context is None: + if base is None: + from zope.component.globalregistry import base + return base + else: + # Use the global site manager to adapt context to `IComponentLookup` + # to avoid the recursion implied by using a local `getAdapter()` call. + try: + return IComponentLookup(context) + except TypeError, error: + raise ComponentLookupError(*error.args) + +# Adapter API + +def getAdapterInContext(object, interface, context): + adapter = queryAdapterInContext(object, interface, context) + if adapter is None: + raise ComponentLookupError(object, interface) + return adapter + +def queryAdapterInContext(object, interface, context, default=None): + conform = getattr(object, '__conform__', None) + if conform is not None: + try: + adapter = conform(interface) + except TypeError: + # We got a TypeError. It might be an error raised by + # the __conform__ implementation, or *we* may have + # made the TypeError by calling an unbound method + # (object is a class). In the later case, we behave + # as though there is no __conform__ method. We can + # detect this case by checking whether there is more + # than one traceback object in the traceback chain: + if sys.exc_info()[2].tb_next is not None: + # There is more than one entry in the chain, so + # reraise the error: + raise + # This clever trick is from Phillip Eby + else: + if adapter is not None: + return adapter + + if interface.providedBy(object): + return object + + return getSiteManager(context).queryAdapter(object, interface, '', default) + +def getAdapter(object, interface=Interface, name=u'', context=None): + adapter = queryAdapter(object, interface, name, None, context) + if adapter is None: + raise ComponentLookupError(object, interface, name) + return adapter + +def queryAdapter(object, interface=Interface, name=u'', default=None, + context=None): + if context is None: + return adapter_hook(interface, object, name, default) + return getSiteManager(context).queryAdapter(object, interface, name, + default) + +def getMultiAdapter(objects, interface=Interface, name=u'', context=None): + adapter = queryMultiAdapter(objects, interface, name, context=context) + if adapter is None: + raise ComponentLookupError(objects, interface, name) + return adapter + +def queryMultiAdapter(objects, interface=Interface, name=u'', default=None, + context=None): + try: + sitemanager = getSiteManager(context) + except ComponentLookupError: + # Oh blast, no site manager. This should *never* happen! + return default + + return sitemanager.queryMultiAdapter(objects, interface, name, default) + +def getAdapters(objects, provided, context=None): + try: + sitemanager = getSiteManager(context) + except ComponentLookupError: + # Oh blast, no site manager. This should *never* happen! + return [] + return sitemanager.getAdapters(objects, provided) + +def subscribers(objects, interface, context=None): + try: + sitemanager = getSiteManager(context) + except ComponentLookupError: + # Oh blast, no site manager. This should *never* happen! + return [] + return sitemanager.subscribers(objects, interface) + +def handle(*objects): + sitemanager = getSiteManager(None) + # iterating over subscribers assures they get executed + for ignored in sitemanager.subscribers(objects, None): + pass + +############################################################################# +# Register the component architectures adapter hook, with the adapter hook +# registry of the `zope.inteface` package. This way we will be able to call +# interfaces to create adapters for objects. For example, `I1(ob)` is +# equvalent to `getAdapterInContext(I1, ob, '')`. +@hookable +def adapter_hook(interface, object, name='', default=None): + try: + sitemanager = getSiteManager() + except ComponentLookupError: + # Oh blast, no site manager. This should *never* happen! + return None + return sitemanager.queryAdapter(object, interface, name, default) + +import zope.interface.interface +zope.interface.interface.adapter_hooks.append(adapter_hook) +############################################################################# + + +# Utility API + +def getUtility(interface, name='', context=None): + utility = queryUtility(interface, name, context=context) + if utility is not None: + return utility + raise ComponentLookupError(interface, name) + +def queryUtility(interface, name='', default=None, context=None): + return getSiteManager(context).queryUtility(interface, name, default) + +def getUtilitiesFor(interface, context=None): + return getSiteManager(context).getUtilitiesFor(interface) + + +def getAllUtilitiesRegisteredFor(interface, context=None): + return getSiteManager(context).getAllUtilitiesRegisteredFor(interface) + + +_marker = object() + +def queryNextUtility(context, interface, name='', default=None): + """Query for the next available utility. + + Find the next available utility providing `interface` and having the + specified name. If no utility was found, return the specified `default` + value. + """ + try: + sm = getSiteManager(context) + except ComponentLookupError: + return default + bases = sm.__bases__ + for base in bases: + util = base.queryUtility(interface, name, _marker) + if util is not _marker: + return util + return default + + +def getNextUtility(context, interface, name=''): + """Get the next available utility. + + If no utility was found, a `ComponentLookupError` is raised. + """ + util = queryNextUtility(context, interface, name, _marker) + if util is _marker: + raise zope.component.interfaces.ComponentLookupError( + "No more utilities for %s, '%s' have been found." % ( + interface, name)) + return util + + +# Factories + +def createObject(__factory_name, *args, **kwargs): + context = kwargs.pop('context', None) + return getUtility(IFactory, __factory_name, context)(*args, **kwargs) + +def getFactoryInterfaces(name, context=None): + return getUtility(IFactory, name, context).getInterfaces() + +def getFactoriesFor(interface, context=None): + utils = getSiteManager(context) + for (name, factory) in utils.getUtilitiesFor(IFactory): + interfaces = factory.getInterfaces() + try: + if interfaces.isOrExtends(interface): + yield name, factory + except AttributeError: + for iface in interfaces: + if iface.isOrExtends(interface): + yield name, factory + break diff -Nru zope3-3.4.0/src/zope/component/configure.zcml zope3-3.5~bzr18/src/zope/component/configure.zcml --- zope3-3.4.0/src/zope/component/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/component/_declaration.py zope3-3.5~bzr18/src/zope/component/_declaration.py --- zope3-3.4.0/src/zope/component/_declaration.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/_declaration.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,62 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Adapter declarations + +$Id: _declaration.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +import types +import sys + +class adapter: + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + if isinstance(ob, _class_types): + ob.__component_adapts__ = _adapts_descr(self.interfaces) + else: + ob.__component_adapts__ = self.interfaces + + return ob + +def adapts(*interfaces): + frame = sys._getframe(1) + locals = frame.f_locals + + # Try to make sure we were called from a class def. In 2.2.0 we can't + # check for __module__ since it doesn't seem to be added to the locals + # until later on. + if (locals is frame.f_globals) or ( + ('__module__' not in locals) and sys.version_info[:3] > (2, 2, 0)): + raise TypeError("adapts can be used only from a class definition.") + + if '__component_adapts__' in locals: + raise TypeError("adapts can be used only once in a class definition.") + + locals['__component_adapts__'] = _adapts_descr(interfaces) + +def adaptedBy(ob): + return getattr(ob, '__component_adapts__', None) + +_class_types = type, types.ClassType + +class _adapts_descr(object): + def __init__(self, interfaces): + self.interfaces = interfaces + + def __get__(self, inst, cls): + if inst is None: + return self.interfaces + raise AttributeError('__component_adapts__') diff -Nru zope3-3.4.0/src/zope/component/event.py zope3-3.5~bzr18/src/zope/component/event.py --- zope3-3.4.0/src/zope/component/event.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/event.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Implement Component Architecture-specific event dispatching, based +on subscription adapters / handlers. + +$Id: event.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import zope.component.interfaces +import zope.event + + +def dispatch(*event): + zope.component.subscribers(event, None) + +zope.event.subscribers.append(dispatch) + + +@zope.component.adapter(zope.component.interfaces.IObjectEvent) +def objectEventNotify(event): + """Event subscriber to dispatch ObjectEvents to interested adapters.""" + zope.component.subscribers((event.object, event), None) diff -Nru zope3-3.4.0/src/zope/component/eventtesting.py zope3-3.5~bzr18/src/zope/component/eventtesting.py --- zope3-3.4.0/src/zope/component/eventtesting.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/eventtesting.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,54 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Placeless Test Setup + +$Id: eventtesting.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +from zope.component import provideHandler +from zope.component.event import objectEventNotify +from zope.component.registry import dispatchUtilityRegistrationEvent +from zope.component.registry import dispatchAdapterRegistrationEvent +from zope.component.registry import ( + dispatchSubscriptionAdapterRegistrationEvent) +from zope.component.registry import dispatchHandlerRegistrationEvent +from zope.testing import cleanup + +events = [] +def getEvents(event_type=None, filter=None): + r = [] + for event in events: + if event_type is not None and not event_type.providedBy(event): + continue + if filter is not None and not filter(event): + continue + r.append(event) + + return r + +def clearEvents(): + del events[:] +cleanup.addCleanUp(clearEvents) + +class PlacelessSetup: + + def setUp(self): + provideHandler(objectEventNotify) + provideHandler(dispatchUtilityRegistrationEvent) + provideHandler(dispatchAdapterRegistrationEvent) + provideHandler(dispatchSubscriptionAdapterRegistrationEvent) + provideHandler(dispatchHandlerRegistrationEvent) + provideHandler(events.append, (None,)) + +def setUp(test=None): + PlacelessSetup().setUp() diff -Nru zope3-3.4.0/src/zope/component/event.txt zope3-3.5~bzr18/src/zope/component/event.txt --- zope3-3.4.0/src/zope/component/event.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/event.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,142 @@ +Events +====== + +The Component Architecture provides a way to dispatch events to event +handlers. Event handlers are registered as *subscribers* +a.k.a. *handlers*. + +Before we can start we need to import ``zope.component.event`` to make +the dispatching effective: + + >>> import zope.component.event + +Consider two event classes: + + >>> class Event1(object): + ... pass + + >>> class Event2(Event1): + ... pass + +Now consider two handlers for these event classes: + + >>> called = [] + + >>> import zope.component + >>> @zope.component.adapter(Event1) + ... def handler1(event): + ... called.append(1) + + >>> @zope.component.adapter(Event2) + ... def handler2(event): + ... called.append(2) + +We can register them with the Component Architecture: + + >>> zope.component.provideHandler(handler1) + >>> zope.component.provideHandler(handler2) + +Now let's go through the events. We'll see that the handlers have been +called accordingly: + + >>> from zope.event import notify + >>> notify(Event1()) + >>> called + [1] + + >>> del called[:] + >>> notify(Event2()) + >>> called.sort() + >>> called + [1, 2] + + + +Object events +------------- + + +The ``objectEventNotify`` function is a subscriber to dispatch +ObjectEvents to interested adapters. + +First create an object class: + + >>> class IUseless(zope.interface.Interface): + ... """Useless object""" + + >>> class UselessObject(object): + ... """Useless object""" + ... zope.interface.implements(IUseless) + +Then create an event class: + + >>> class IObjectThrownEvent(zope.component.interfaces.IObjectEvent): + ... """An object has been thrown away""" + + >>> class ObjectThrownEvent(zope.component.interfaces.ObjectEvent): + ... """An object has been thrown away""" + ... zope.interface.implements(IObjectThrownEvent) + +Create an object and an event: + + >>> hammer = UselessObject() + >>> event = ObjectThrownEvent(hammer) + +Then notify the event to the subscribers. +Since the subscribers list is empty, nothing happens. + + >>> zope.component.event.objectEventNotify(event) + +Now create an handler for the event: + + >>> events = [] + >>> def record(*args): + ... events.append(args) + + >>> zope.component.provideHandler(record, [IUseless, IObjectThrownEvent]) + +The event is notified to the subscriber: + + >>> zope.component.event.objectEventNotify(event) + >>> events == [(hammer, event)] + True + +Following test demonstrates how a subscriber can raise an exception +to prevent an action. + + >>> zope.component.provideHandler(zope.component.event.objectEventNotify) + +Let's create a container: + + >>> class ToolBox(dict): + ... def __delitem__(self, key): + ... notify(ObjectThrownEvent(self[key])) + ... return super(ToolBox,self).__delitem__(key) + + >>> container = ToolBox() + +And put the object into the container: + + >>> container['Red Hammer'] = hammer + +Create an handler function that will raise an error when called: + + >>> class Veto(Exception): + ... pass + + >>> def callback(item, event): + ... assert(item == event.object) + ... raise Veto + +Register the handler: + + >>> zope.component.provideHandler(callback, [IUseless, IObjectThrownEvent]) + +Then if we try to remove the object, an ObjectThrownEvent is fired: + + >>> del container['Red Hammer'] + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + raise Veto + Veto diff -Nru zope3-3.4.0/src/zope/component/factory.py zope3-3.5~bzr18/src/zope/component/factory.py --- zope3-3.4.0/src/zope/component/factory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/factory.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Factory object + +$Id: factory.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +from zope.interface import implements, implementedBy +from zope.interface.declarations import Implements +from zope.component.interfaces import IFactory + +class Factory(object): + """Generic factory implementation. + + The purpose of this implementation is to provide a quick way of creating + factories for classes, functions and other objects. + """ + implements(IFactory) + + def __init__(self, callable, title='', description='', interfaces=None): + self._callable = callable + self.title = title + self.description = description + self._interfaces = interfaces + + def __call__(self, *args, **kw): + return self._callable(*args, **kw) + + def getInterfaces(self): + if self._interfaces is not None: + spec = Implements(*self._interfaces) + spec.__name__ = getattr(self._callable, '__name__', '[callable]') + return spec + return implementedBy(self._callable) + + def __repr__(self): + return '<%s for %s>' % (self.__class__.__name__, repr(self._callable)) diff -Nru zope3-3.4.0/src/zope/component/factory.txt zope3-3.5~bzr18/src/zope/component/factory.txt --- zope3-3.4.0/src/zope/component/factory.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/factory.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,131 @@ +Factories +========= + + +The Factory Class +----------------- + + >>> from zope.interface import Interface + >>> class IFunction(Interface): + ... pass + + >>> class IKlass(Interface): + ... pass + + >>> from zope.interface import implements + >>> class Klass(object): + ... implements(IKlass) + ... + ... def __init__(self, *args, **kw): + ... self.args = args + ... self.kw = kw + + >>> from zope.component.factory import Factory + >>> factory = Factory(Klass, 'Klass', 'Klassier') + >>> factory2 = Factory(lambda x: x, 'Func', 'Function') + >>> factory3 = Factory(lambda x: x, 'Func', 'Function', (IFunction,)) + +Calling a Factory +~~~~~~~~~~~~~~~~~ + +Here we test whether the factory correctly creates the objects and +including the correct handling of constructor elements. + +First we create a factory that creates instanace of the `Klass` class: + + >>> factory = Factory(Klass, 'Klass', 'Klassier') + +Now we use the factory to create the instance + + >>> kl = factory(1, 2, foo=3, bar=4) + +and make sure that the correct class was used to create the object: + + >>> kl.__class__ + + +Since we passed in a couple positional and keyword arguments + + >>> kl.args + (1, 2) + >>> kl.kw + {'foo': 3, 'bar': 4} + + >>> factory2(3) + 3 + >>> factory3(3) + 3 + + +Title and Description +~~~~~~~~~~~~~~~~~~~~~ + + >>> factory.title + 'Klass' + >>> factory.description + 'Klassier' + >>> factory2.title + 'Func' + >>> factory2.description + 'Function' + >>> factory3.title + 'Func' + >>> factory3.description + 'Function' + + +Provided Interfaces +~~~~~~~~~~~~~~~~~~~ + + >>> implemented = factory.getInterfaces() + >>> implemented.isOrExtends(IKlass) + True + >>> list(implemented) + [] + + >>> implemented2 = factory2.getInterfaces() + >>> list(implemented2) + [] + + >>> implemented3 = factory3.getInterfaces() + >>> list(implemented3) + [] + + +The Component Architecture Factory API +-------------------------------------- + + >>> import zope.component + >>> factory = Factory(Klass, 'Klass', 'Klassier') + >>> gsm = zope.component.getGlobalSiteManager() + + >>> from zope.component.interfaces import IFactory + >>> gsm.registerUtility(factory, IFactory, 'klass') + +Creating an Object +~~~~~~~~~~~~~~~~~~ + + >>> kl = zope.component.createObject('klass', 1, 2, foo=3, bar=4) + >>> isinstance(kl, Klass) + True + >>> kl.args + (1, 2) + >>> kl.kw + {'foo': 3, 'bar': 4} + +Accessing Provided Interfaces +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + >>> implemented = zope.component.getFactoryInterfaces('klass') + >>> implemented.isOrExtends(IKlass) + True + >>> [iface for iface in implemented] + [] + +List of All Factories +~~~~~~~~~~~~~~~~~~~~~ + + >>> [(name, fac.__class__) for name, fac in + ... zope.component.getFactoriesFor(IKlass)] + [(u'klass', )] + diff -Nru zope3-3.4.0/src/zope/component/globalregistry.py zope3-3.5~bzr18/src/zope/component/globalregistry.py --- zope3-3.4.0/src/zope/component/globalregistry.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/globalregistry.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,89 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Global components support + +$Id: globalregistry.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +from zope.interface import implements +from zope.interface.adapter import AdapterRegistry +from zope.component.registry import Components +from zope.component.interfaces import Invalid, IComponentLookup, IRegistry +from zope.component.interfaces import ComponentLookupError + +def GAR(components, registryName): + return getattr(components, registryName) + +class GlobalAdapterRegistry(AdapterRegistry): + """A global adapter registry + + This adapter registry's main purpose is to be picklable in combination + with a site manager.""" + + def __init__(self, parent, name): + self.__parent__ = parent + self.__name__ = name + super(GlobalAdapterRegistry, self).__init__() + + def __reduce__(self): + return GAR, (self.__parent__, self.__name__) + +class BaseGlobalComponents(Components): + + def _init_registries(self): + self.adapters = GlobalAdapterRegistry(self, 'adapters') + self.utilities = GlobalAdapterRegistry(self, 'utilities') + + def __reduce__(self): + # Global site managers are pickled as global objects + return self.__name__ + +base = BaseGlobalComponents('base') + +try: + from zope.testing.cleanup import addCleanUp +except ImportError: + pass +else: + addCleanUp(lambda: base.__init__('base')) + del addCleanUp + +globalSiteManager = base +def getGlobalSiteManager(): + return globalSiteManager + +# The following APIs provide global registration support for Python code. +# We eventually want to deprecate these in favor of using the global +# component registry directly. + +def provideUtility(component, provides=None, name=u''): + base.registerUtility(component, provides, name, event=False) + + +def provideAdapter(factory, adapts=None, provides=None, name=''): + base.registerAdapter(factory, adapts, provides, name, event=False) + +def provideSubscriptionAdapter(factory, adapts=None, provides=None): + base.registerSubscriptionAdapter(factory, adapts, provides, event=False) + +def provideHandler(factory, adapts=None): + base.registerHandler(factory, adapts, event=False) + +import zope.component._api # see http://www.zope.org/Collectors/Zope3-dev/674 +# Ideally, we will switch to an explicit adapter hook registration. For now, +# if you provide an adapter, we want to make sure that the adapter hook is +# registered, and that registration depends on code in _api, which itself +# depends on code in this module. So, for now, we do another of these nasty +# circular import workarounds. See also standalonetests.py, as run by +# tests.py in StandaloneTests, for a test that fails without this hack, and +# succeeds with it. diff -Nru zope3-3.4.0/src/zope/component/hookable.py zope3-3.5~bzr18/src/zope/component/hookable.py --- zope3-3.4.0/src/zope/component/hookable.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/hookable.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,33 @@ +############################################################################## +# +# Copyright (c) 2009 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 module supplies a pure-Python version of zope.hookable.hookable. +""" +class hookable(object): + __slots__ = ('__original', '__implementation') + + original = property(lambda self: self.__original,) + implementation = property(lambda self: self.__implementation,) + + def __init__(self, implementation): + self.__original = self.__implementation = implementation + + def sethook(self, newimplementation): + old, self.__implementation = self.__implementation, newimplementation + return old + + def reset(self): + self.__implementation = self.__original + + def __call__(self, *args, **kw): + return self.__implementation(*args, **kw) diff -Nru zope3-3.4.0/src/zope/component/hooks.py zope3-3.5~bzr18/src/zope/component/hooks.py --- zope3-3.4.0/src/zope/component/hooks.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/hooks.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,127 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Hooks for getting and setting a site in the thread global namespace. + +$Id$ +""" +__docformat__ = 'restructuredtext' + +import threading +import zope.component + +try: + import zope.security.proxy +except ImportError: + SECURITY_SUPPORT = False +else: + SECURITY_SUPPORT = True + + +class read_property(object): + def __init__(self, func): + self.func = func + + def __get__(self, inst, cls): + if inst is None: + return self + + return self.func(inst) + +class SiteInfo(threading.local): + site = None + sm = zope.component.getGlobalSiteManager() + + def adapter_hook(self): + adapter_hook = self.sm.adapters.adapter_hook + self.adapter_hook = adapter_hook + return adapter_hook + + adapter_hook = read_property(adapter_hook) + +siteinfo = SiteInfo() + +def setSite(site=None): + if site is None: + sm = zope.component.getGlobalSiteManager() + else: + + # We remove the security proxy because there's no way for + # untrusted code to get at it without it being proxied again. + + # We should really look look at this again though, especially + # once site managers do less. There's probably no good reason why + # they can't be proxied. Well, except maybe for performance. + + if SECURITY_SUPPORT: + site = zope.security.proxy.removeSecurityProxy(site) + # The getSiteManager method is defined by IPossibleSite. + sm = site.getSiteManager() + + siteinfo.site = site + siteinfo.sm = sm + try: + del siteinfo.adapter_hook + except AttributeError: + pass + +def getSite(): + return siteinfo.site + + +def getSiteManager(context=None): + """A special hook for getting the site manager. + + Here we take the currently set site into account to find the appropriate + site manager. + """ + if context is None: + return siteinfo.sm + + # We remove the security proxy because there's no way for + # untrusted code to get at it without it being proxied again. + + # We should really look look at this again though, especially + # once site managers do less. There's probably no good reason why + # they can't be proxied. Well, except maybe for performance. + sm = zope.component.interfaces.IComponentLookup( + context, zope.component.getGlobalSiteManager()) + if SECURITY_SUPPORT: + sm = zope.security.proxy.removeSecurityProxy(sm) + return sm + + +def adapter_hook(interface, object, name='', default=None): + try: + return siteinfo.adapter_hook(interface, object, name, default) + except zope.component.interfaces.ComponentLookupError: + return default + + +def setHooks(): + zope.component.adapter_hook.sethook(adapter_hook) + zope.component.getSiteManager.sethook(getSiteManager) + +def resetHooks(): + # Reset hookable functions to original implementation. + zope.component.adapter_hook.reset() + zope.component.getSiteManager.reset() + +# Clear the site thread global +clearSite = setSite +try: + from zope.testing.cleanup import addCleanUp +except ImportError: + pass +else: + addCleanUp(resetHooks) diff -Nru zope3-3.4.0/src/zope/component/hooks.txt zope3-3.5~bzr18/src/zope/component/hooks.txt --- zope3-3.4.0/src/zope/component/hooks.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/hooks.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,65 @@ +============================== +The current component registry +============================== + +There can be any number of component registries in an application. One of them +is the global component registry, and there is also the concept of a currently +used component registry. Component registries other than the global one are +associated with objects called sites. The ``zope.component.hooks`` module +provides an API to set and access the current site as well as manipulate the +adapter hook associated with it. + +As long as we haven't set a site, none is being considered current: + +>>> from zope.component.hooks import getSite +>>> print getSite() +None + +We can also ask for the current component registry (aka site manager +historically); it will return the global one if no current site is set: + +>>> from zope.component.hooks import getSiteManager +>>> getSiteManager() + + +Let's set a site now. A site has to be an object that provides the +``getSiteManager`` method, which is specified by +``zope.component.interfaces.IPossibleSite``: + +>>> from zope.component.registry import Components +>>> class Site(object): +... def __init__(self): +... self.registry = Components('components') +... def getSiteManager(self): +... return self.registry + +>>> from zope.component.hooks import setSite +>>> site1 = Site() +>>> setSite(site1) + +After this, the newly set site is considered the currently active one: + +>>> getSite() is site1 +True +>>> getSiteManager() is site1.registry +True + +If we set another site, that one will be considered current: + +>>> site2 = Site() +>>> site2.registry is not site1.registry +True +>>> setSite(site2) + +>>> getSite() is site2 +True +>>> getSiteManager() is site2.registry +True + +Finally we can unset the site and the global component registry is used again: + +>>> setSite() +>>> print getSite() +None +>>> getSiteManager() + diff -Nru zope3-3.4.0/src/zope/component/index.txt zope3-3.5~bzr18/src/zope/component/index.txt --- zope3-3.4.0/src/zope/component/index.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/index.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +Welcome to zope.component's documentation! +========================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + README + socketexample + event + factory + registry + persistentregistry + zcml + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff -Nru zope3-3.4.0/src/zope/component/__init__.py zope3-3.5~bzr18/src/zope/component/__init__.py --- zope3-3.4.0/src/zope/component/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,62 @@ +############################################################################## +# +# 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 3 Component Architecture + +$Id: __init__.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +from zope.interface import Interface +from zope.interface import implementedBy +from zope.interface import moduleProvides +from zope.interface import providedBy + +from zope.component.interfaces import ComponentLookupError +from zope.component.interfaces import IComponentArchitecture +from zope.component.interfaces import IComponentLookup +from zope.component.interfaces import IComponentRegistrationConvenience +from zope.component.interfaces import IFactory + +from zope.component.globalregistry import getGlobalSiteManager +from zope.component.globalregistry import globalSiteManager +from zope.component.globalregistry import provideAdapter +from zope.component.globalregistry import provideHandler +from zope.component.globalregistry import provideSubscriptionAdapter +from zope.component.globalregistry import provideUtility + +from zope.component._api import adapter_hook +from zope.component._api import createObject +from zope.component._api import getAdapter +from zope.component._api import getAdapterInContext +from zope.component._api import getAdapters +from zope.component._api import getAllUtilitiesRegisteredFor +from zope.component._api import getFactoriesFor +from zope.component._api import getFactoryInterfaces +from zope.component._api import getMultiAdapter +from zope.component._api import getSiteManager +from zope.component._api import getUtilitiesFor +from zope.component._api import getUtility +from zope.component._api import getNextUtility +from zope.component._api import handle +from zope.component._api import queryAdapter +from zope.component._api import queryAdapterInContext +from zope.component._api import queryMultiAdapter +from zope.component._api import queryUtility +from zope.component._api import queryNextUtility +from zope.component._api import subscribers + +from zope.component._declaration import adaptedBy +from zope.component._declaration import adapter +from zope.component._declaration import adapts + +moduleProvides(IComponentArchitecture, IComponentRegistrationConvenience) +__all__ = tuple(IComponentArchitecture) diff -Nru zope3-3.4.0/src/zope/component/interface.py zope3-3.5~bzr18/src/zope/component/interface.py --- zope3-3.4.0/src/zope/component/interface.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/interface.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,262 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Interface utility functions + +$Id: interface.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +from types import ClassType + +import zope.component +from zope.component.interfaces import ComponentLookupError +from zope.interface import alsoProvides +from zope.interface.interfaces import IInterface + +def provideInterface(id, interface, iface_type=None, info=''): + """register Interface with global site manager as utility + + >>> gsm = zope.component.getGlobalSiteManager() + + >>> from zope.interface import Interface + >>> from zope.interface.interfaces import IInterface + >>> from zope.component.tests import ITestType + + >>> class I(Interface): + ... pass + >>> IInterface.providedBy(I) + True + >>> ITestType.providedBy(I) + False + >>> interfaces = gsm.getUtilitiesFor(ITestType) + >>> list(interfaces) + [] + + # provide first interface type + >>> provideInterface('', I, ITestType) + >>> ITestType.providedBy(I) + True + >>> interfaces = list(gsm.getUtilitiesFor(ITestType)) + >>> [name for (name, iface) in interfaces] + [u'zope.component.interface.I'] + >>> [iface.__name__ for (name, iface) in interfaces] + ['I'] + + # provide second interface type + >>> class IOtherType(IInterface): + ... pass + >>> provideInterface('', I, IOtherType) + + >>> ITestType.providedBy(I) + True + >>> IOtherType.providedBy(I) + True + >>> interfaces = list(gsm.getUtilitiesFor(ITestType)) + >>> [name for (name, iface) in interfaces] + [u'zope.component.interface.I'] + >>> interfaces = list(gsm.getUtilitiesFor(IOtherType)) + >>> [name for (name, iface) in interfaces] + [u'zope.component.interface.I'] + + >>> class I1(Interface): + ... pass + >>> provideInterface('', I1) + >>> IInterface.providedBy(I1) + True + >>> ITestType.providedBy(I1) + False + >>> interfaces = list(gsm.getUtilitiesFor(ITestType)) + >>> [name for (name, iface) in interfaces] + [u'zope.component.interface.I'] + >>> [iface.__name__ for (name, iface) in interfaces] + ['I'] + """ + if not id: + id = "%s.%s" % (interface.__module__, interface.__name__) + + if not IInterface.providedBy(interface): + if not isinstance(interface, (type, ClassType)): + raise TypeError(id, "is not an interface or class") + return + + if iface_type is not None: + if not iface_type.extends(IInterface): + raise TypeError(iface_type, "is not an interface type") + alsoProvides(interface, iface_type) + else: + iface_type = IInterface + + gsm = zope.component.getGlobalSiteManager() + gsm.registerUtility(interface, iface_type, id, info) + + +def getInterface(context, id): + """Return interface or raise ComponentLookupError + + >>> from zope.interface import Interface + >>> from zope.component.tests import ITestType + + >>> class I4(Interface): + ... pass + >>> IInterface.providedBy(I4) + True + >>> ITestType.providedBy(I4) + False + >>> getInterface(None, 'zope.component.interface.I4') + Traceback (most recent call last): + ... + ComponentLookupError: zope.component.interface.I4 + >>> provideInterface('', I4, ITestType) + >>> ITestType.providedBy(I4) + True + >>> iface = queryInterface( """\ + """ 'zope.component.interface.I4') + >>> iface.__name__ + 'I4' + """ + iface = queryInterface(id, None) + if iface is None: + raise ComponentLookupError(id) + return iface + + +def queryInterface(id, default=None): + """return interface or ``None`` + + >>> from zope.interface import Interface + >>> from zope.interface.interfaces import IInterface + >>> from zope.component.tests import ITestType + + >>> class I3(Interface): + ... pass + >>> IInterface.providedBy(I3) + True + >>> ITestType.providedBy(I3) + False + >>> queryInterface('zope.component.interface.I3') + + >>> provideInterface('', I3, ITestType) + >>> ITestType.providedBy(I3) + True + >>> iface = queryInterface('zope.component.interface.I3') + >>> iface.__name__ + 'I3' + """ + return zope.component.queryUtility(IInterface, id, default) + + +def searchInterface(context, search_string=None, base=None): + """Interfaces search + + >>> from zope.interface import Interface + >>> from zope.interface.interfaces import IInterface + >>> from zope.component.tests import ITestType + + >>> class I5(Interface): + ... pass + >>> IInterface.providedBy(I5) + True + >>> ITestType.providedBy(I5) + False + >>> searchInterface(None, 'zope.component.interface.I5') + [] + >>> provideInterface('', I5, ITestType) + >>> ITestType.providedBy(I5) + True + >>> iface = searchInterface(None, 'zope.component.interface.I5') + >>> iface[0].__name__ + 'I5' + """ + return [iface_util[1] for iface_util in + searchInterfaceUtilities(context, search_string, base)] + + +def searchInterfaceIds(context, search_string=None, base=None): + """Interfaces search + + >>> from zope.interface import Interface + >>> from zope.interface.interfaces import IInterface + >>> from zope.component.tests import ITestType + + >>> class I5(Interface): + ... pass + >>> IInterface.providedBy(I5) + True + >>> ITestType.providedBy(I5) + False + >>> searchInterface(None, 'zope.component.interface.I5') + [] + >>> provideInterface('', I5, ITestType) + >>> ITestType.providedBy(I5) + True + >>> iface = searchInterfaceIds(None, 'zope.component.interface.I5') + >>> iface + [u'zope.component.interface.I5'] + """ + return [iface_util[0] for iface_util in + searchInterfaceUtilities(context, search_string, base)] + + +def searchInterfaceUtilities(context, search_string=None, base=None): + gsm = zope.component.getGlobalSiteManager() + iface_utilities = gsm.getUtilitiesFor(IInterface) + + if search_string: + search_string = search_string.lower() + iface_utilities = [iface_util for iface_util in iface_utilities + if (getInterfaceAllDocs(iface_util[1]).\ + find(search_string) >= 0)] + if base: + res = [iface_util for iface_util in iface_utilities + if iface_util[1].extends(base)] + else: + res = [iface_util for iface_util in iface_utilities] + return res + + +def getInterfaceAllDocs(interface): + iface_id = '%s.%s' %(interface.__module__, interface.__name__) + docs = [str(iface_id).lower(), + str(interface.__doc__).lower()] + + if IInterface.providedBy(interface): + for name in interface: + docs.append( + str(interface.getDescriptionFor(name).__doc__).lower()) + + return '\n'.join(docs) + + +def nameToInterface(context, id): + if id == 'None': + return None + iface = getInterface(context, id) + return iface + +def interfaceToName(context, interface): + if interface is None: + return 'None' + items = searchInterface(context, base=interface) + ids = [('%s.%s' %(iface.__module__, iface.__name__)) + for iface in items + if iface == interface] + + if not ids: + # Do not fail badly, instead resort to the standard + # way of getting the interface name, cause not all interfaces + # may be registered as utilities. + return interface.__module__ + '.' + interface.__name__ + + assert len(ids) == 1, "Ambiguous interface names: %s" % ids + return ids[0] diff -Nru zope3-3.4.0/src/zope/component/interfaces.py zope3-3.5~bzr18/src/zope/component/interfaces.py --- zope3-3.4.0/src/zope/component/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,944 @@ +############################################################################ +# +# 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. +# +############################################################################ +"""Component and Component Architecture Interfaces + +$Id: interfaces.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +__docformat__ = "reStructuredText" + +from zope.interface import Attribute +from zope.interface import Interface +from zope.interface import implements + +class ComponentLookupError(LookupError): + """A component could not be found.""" + +class Invalid(Exception): + """A component doesn't satisfy a promise.""" + +class Misused(Exception): + """A component is being used (registered) for the wrong interface.""" + + +class IObjectEvent(Interface): + """An event related to an object. + + The object that generated this event is not necessarily the object + refered to by location. + """ + + object = Attribute("The subject of the event.") + + +class ObjectEvent(object): + implements(IObjectEvent) + + def __init__(self, object): + self.object = object + +class IComponentArchitecture(Interface): + """The Component Architecture is defined by two key components: Adapters + and Utiltities. Both are managed by site managers. All other components + build on top of them. + """ + # Site Manager API + + def getGlobalSiteManager(): + """Return the global site manager. + + This function should never fail and always return an object that + provides `IGlobalSiteManager`. + """ + + def getSiteManager(context=None): + """Get the nearest site manager in the given context. + + If `context` is `None`, return the global site manager. + + If the `context` is not `None`, it is expected that an adapter + from the `context` to `IComponentLookup` can be found. If no + adapter is found, a `ComponentLookupError` is raised. + + """ + + # Utility API + + def getUtility(interface, name='', context=None): + """Get the utility that provides interface + + Returns the nearest utility to the context that implements the + specified interface. If one is not found, raises + ComponentLookupError. + """ + + def queryUtility(interface, name='', default=None, context=None): + """Look for the utility that provides interface + + Returns the nearest utility to the context that implements + the specified interface. If one is not found, returns default. + """ + + def queryNextUtility(context, interface, name='', default=None): + """Query for the next available utility. + + Find the next available utility providing `interface` and having the + specified name. If no utility was found, return the specified `default` + value. + """ + + def getNextUtility(context, interface, name=''): + """Get the next available utility. + + If no utility was found, a `ComponentLookupError` is raised. + """ + + def getUtilitiesFor(interface, context=None): + """Return the utilities that provide an interface + + An iterable of utility name-value pairs is returned. + """ + + def getAllUtilitiesRegisteredFor(interface, context=None): + """Return all registered utilities for an interface + + This includes overridden utilities. + + An iterable of utility instances is returned. No names are + returned. + """ + + # Adapter API + + def getAdapter(object, + interface=Interface, name=u'', + context=None): + """Get a named adapter to an interface for an object + + Returns an adapter that can adapt object to interface. If a matching + adapter cannot be found, raises ComponentLookupError. + + If context is None, an application-defined policy is used to choose + an appropriate service manager from which to get an 'Adapters' service. + + If 'context' is not None, context is adapted to IServiceService, + and this adapter's 'Adapters' service is used. + """ + + def getAdapterInContext(object, interface, context): + """Get a special adapter to an interface for an object + + NOTE: This method should only be used if a custom context + needs to be provided to provide custom component + lookup. Otherwise, call the interface, as in:: + + interface(object) + + Returns an adapter that can adapt object to interface. If a matching + adapter cannot be found, raises ComponentLookupError. + + Context is adapted to IServiceService, and this adapter's + 'Adapters' service is used. + + If the object has a __conform__ method, this method will be + called with the requested interface. If the method returns a + non-None value, that value will be returned. Otherwise, if the + object already implements the interface, the object will be + returned. + """ + + def getMultiAdapter(objects, + interface=Interface, name='', + context=None): + """Look for a multi-adapter to an interface for an objects + + Returns a multi-adapter that can adapt objects to interface. If a + matching adapter cannot be found, raises ComponentLookupError. + + If context is None, an application-defined policy is used to choose + an appropriate service manager from which to get an 'Adapters' service. + + If 'context' is not None, context is adapted to IServiceService, + and this adapter's 'Adapters' service is used. + + The name consisting of an empty string is reserved for unnamed + adapters. The unnamed adapter methods will often call the + named adapter methods with an empty string for a name. + """ + + def queryAdapter(object, interface=Interface, name=u'', + default=None, context=None): + """Look for a named adapter to an interface for an object + + Returns an adapter that can adapt object to interface. If a matching + adapter cannot be found, returns the default. + + If context is None, an application-defined policy is used to choose + an appropriate service manager from which to get an 'Adapters' service. + + If 'context' is not None, context is adapted to IServiceService, + and this adapter's 'Adapters' service is used. + """ + + def queryAdapterInContext(object, interface, context, default=None): + """Look for a special adapter to an interface for an object + + NOTE: This method should only be used if a custom context + needs to be provided to provide custom component + lookup. Otherwise, call the interface, as in:: + + interface(object, default) + + Returns an adapter that can adapt object to interface. If a matching + adapter cannot be found, returns the default. + + Context is adapted to IServiceService, and this adapter's + 'Adapters' service is used. + + If the object has a __conform__ method, this method will be + called with the requested interface. If the method returns a + non-None value, that value will be returned. Otherwise, if the + object already implements the interface, the object will be + returned. + """ + + def queryMultiAdapter(objects, + interface=Interface, name=u'', + default=None, + context=None): + """Look for a multi-adapter to an interface for objects + + Returns a multi-adapter that can adapt objects to interface. If a + matching adapter cannot be found, returns the default. + + If context is None, an application-defined policy is used to choose + an appropriate service manager from which to get an 'Adapters' service. + + If 'context' is not None, context is adapted to IServiceService, + and this adapter's 'Adapters' service is used. + + The name consisting of an empty string is reserved for unnamed + adapters. The unnamed adapter methods will often call the + named adapter methods with an empty string for a name. + """ + + def getAdapters(objects, provided, context=None): + """Look for all matching adapters to a provided interface for objects + + Return a list of adapters that match. If an adapter is named, only the + most specific adapter of a given name is returned. + + If context is None, an application-defined policy is used to choose + an appropriate service manager from which to get an 'Adapters' + service. + + If 'context' is not None, context is adapted to IServiceService, + and this adapter's 'Adapters' service is used. + """ + + def subscribers(required, provided, context=None): + """Get subscribers + + Subscribers are returned that provide the provided interface + and that depend on and are computed from the sequence of + required objects. + + If context is None, an application-defined policy is used to choose + an appropriate service manager from which to get an 'Adapters' + service. + + If 'context' is not None, context is adapted to IServiceService, + and this adapter's 'Adapters' service is used. + """ + + def handle(*objects): + """Call all of the handlers for the given objects + + Handlers are subscription adapter factories that don't produce + anything. They do all of their work when called. Handlers + are typically used to handle events. + + """ + + + def adapts(*interfaces): + """Declare that a class adapts the given interfaces. + + This function can only be used in a class definition. + + (TODO, allow classes to be passed as well as interfaces.) + """ + + # Factory service + + def createObject(factory_name, *args, **kwargs): + """Create an object using a factory + + Finds the named factory in the current site and calls it with + the given arguments. If a matching factory cannot be found + raises ComponentLookupError. Returns the created object. + + A context keyword argument can be provided to cause the + factory to be looked up in a location other than the current + site. (Of course, this means that it is impossible to pass a + keyword argument named "context" to the factory. + """ + + def getFactoryInterfaces(name, context=None): + """Get interfaces implemented by a factory + + Finds the factory of the given name that is nearest to the + context, and returns the interface or interface tuple that + object instances created by the named factory will implement. + """ + + def getFactoriesFor(interface, context=None): + """Return a tuple (name, factory) of registered factories that + create objects which implement the given interface. + """ + +class IComponentLookup(Interface): + """Component Manager for a Site + + This object manages the components registered at a particular site. The + definition of a site is intentionally vague. + """ + + adapters = Attribute( + "Adapter Registry to manage all registered adapters.") + + utilities = Attribute( + "Adapter Registry to manage all registered utilities.") + + def queryAdapter(object, interface, name=u'', default=None): + """Look for a named adapter to an interface for an object + + If a matching adapter cannot be found, returns the default. + """ + + def getAdapter(object, interface, name=u''): + """Look for a named adapter to an interface for an object + + If a matching adapter cannot be found, a ComponentLookupError + is raised. + """ + + def queryMultiAdapter(objects, interface, name=u'', default=None): + """Look for a multi-adapter to an interface for multiple objects + + If a matching adapter cannot be found, returns the default. + """ + + def getMultiAdapter(objects, interface, name=u''): + """Look for a multi-adapter to an interface for multiple objects + + If a matching adapter cannot be found, a ComponentLookupError + is raised. + """ + + def getAdapters(objects, provided): + """Look for all matching adapters to a provided interface for objects + + Return an iterable of name-adapter pairs for adapters that + provide the given interface. + """ + + def subscribers(objects, provided): + """Get subscribers + + Subscribers are returned that provide the provided interface + and that depend on and are comuted from the sequence of + required objects. + """ + + def handle(*objects): + """Call handlers for the given objects + + Handlers registered for the given objects are called. + """ + + def queryUtility(interface, name='', default=None): + """Look up a utility that provides an interface. + + If one is not found, returns default. + """ + + def getUtilitiesFor(interface): + """Look up the registered utilities that provide an interface. + + Returns an iterable of name-utility pairs. + """ + + def getAllUtilitiesRegisteredFor(interface): + """Return all registered utilities for an interface + + This includes overridden utilities. + + An iterable of utility instances is returned. No names are + returned. + """ + +class IComponentRegistrationConvenience(Interface): + """API for registering components. + + CAUTION: This API should only be used from test or + application-setup code. This api shouldn't be used by regular + library modules, as component registration is a configuration + activity. + """ + + def provideUtility(component, provides=None, name=u''): + """Register a utility globally + + A utility is registered to provide an interface with a + name. If a component provides only one interface, then the + provides argument can be omitted and the provided interface + will be used. (In this case, provides argument can still be + provided to provide a less specific interface.) + + CAUTION: This API should only be used from test or + application-setup code. This API shouldn't be used by regular + library modules, as component registration is a configuration + activity. + + """ + + def provideAdapter(factory, adapts=None, provides=None, name=u''): + """Register an adapter globally + + An adapter is registered to provide an interface with a name + for some number of object types. If a factory implements only + one interface, then the provides argument can be omitted and + the provided interface will be used. (In this case, a provides + argument can still be provided to provide a less specific + interface.) + + If the factory has an adapts declaration, then the adapts + argument can be omitted and the declaration will be used. (An + adapts argument can be provided to override the declaration.) + + CAUTION: This API should only be used from test or + application-setup code. This API shouldn't be used by regular + library modules, as component registration is a configuration + activity. + """ + + def provideSubscriptionAdapter(factory, adapts=None, provides=None): + """Register a subscription adapter + + A subscription adapter is registered to provide an interface + for some number of object types. If a factory implements only + one interface, then the provides argument can be omitted and + the provided interface will be used. (In this case, a provides + argument can still be provided to provide a less specific + interface.) + + If the factory has an adapts declaration, then the adapts + argument can be omitted and the declaration will be used. (An + adapts argument can be provided to override the declaration.) + + CAUTION: This API should only be used from test or + application-setup code. This API shouldn't be used by regular + library modules, as component registration is a configuration + activity. + """ + + def provideHandler(handler, adapts=None): + """Register a handler + + Handlers are subscription adapter factories that don't produce + anything. They do all of their work when called. Handlers + are typically used to handle events. + + If the handler has an adapts declaration, then the adapts + argument can be omitted and the declaration will be used. (An + adapts argument can be provided to override the declaration.) + + CAUTION: This API should only be used from test or + application-setup code. This API shouldn't be used by regular + library modules, as component registration is a configuration + activity. + """ + +class IRegistry(Interface): + """Object that supports component registry + """ + + def registrations(): + """Return an iterable of component registrations + """ + +class IFactory(Interface): + """A factory is responsible for creating other components.""" + + title = Attribute("The factory title.") + + description = Attribute("A brief description of the factory.") + + def __call__(*args, **kw): + """Return an instance of the objects we're a factory for.""" + + + def getInterfaces(): + """Get the interfaces implemented by the factory + + Return the interface(s), as an instance of Implements, that objects + created by this factory will implement. If the callable's Implements + instance cannot be created, an empty Implements instance is returned. + """ + +class IRegistration(Interface): + """A registration-information object + """ + + registry = Attribute("The registry having the registration") + + name = Attribute("The registration name") + + info = Attribute("""Information about the registration + + This is information deemed useful to people browsing the + configuration of a system. It could, for example, include + commentary or information about the source of the configuration. + """) + +class IUtilityRegistration(IRegistration): + """Information about the registration of a utility + """ + + factory = Attribute("The factory used to create the utility. Optional.") + component = Attribute("The object registered") + provided = Attribute("The interface provided by the component") + +class _IBaseAdapterRegistration(IRegistration): + """Information about the registration of an adapter + """ + + factory = Attribute("The factory used to create adapters") + + required = Attribute("""The adapted interfaces + + This is a sequence of interfaces adapters by the registered + factory. The factory will be caled with a sequence of objects, as + positional arguments, that provide these interfaces. + """) + + provided = Attribute("""The interface provided by the adapters. + + This interface is implemented by the factory + """) + +class IAdapterRegistration(_IBaseAdapterRegistration): + """Information about the registration of an adapter + """ + +class ISubscriptionAdapterRegistration(_IBaseAdapterRegistration): + """Information about the registration of a subscription adapter + """ + +class IHandlerRegistration(IRegistration): + + handler = Attribute("An object called used to handle an event") + + required = Attribute("""The handled interfaces + + This is a sequence of interfaces handled by the registered + handler. The handler will be caled with a sequence of objects, as + positional arguments, that provide these interfaces. + """) + +class IRegistrationEvent(IObjectEvent): + """An event that involves a registration""" + +class RegistrationEvent(ObjectEvent): + """There has been a change in a registration + """ + implements(IRegistrationEvent) + + def __repr__(self): + return "%s event:\n%r" % (self.__class__.__name__, self.object) + +class IRegistered(IRegistrationEvent): + """A component or factory was registered + """ + +class Registered(RegistrationEvent): + implements(IRegistered) + +class IUnregistered(IRegistrationEvent): + """A component or factory was unregistered + """ + +class Unregistered(RegistrationEvent): + """A component or factory was unregistered + """ + implements(IUnregistered) + +class IComponentRegistry(Interface): + """Register components + """ + + def registerUtility(component=None, provided=None, name=u'', info=u'', factory=None): + """Register a utility + + factory + Factory for the component to be registerd. + + component + The registered component + + provided + This is the interface provided by the utility. If the + component provides a single interface, then this + argument is optional and the component-implemented + interface will be used. + + name + The utility name. + + info + An object that can be converted to a string to provide + information about the registration. + + Only one of component and factory can be used. + A Registered event is generated with an IUtilityRegistration. + """ + + def unregisterUtility(component=None, provided=None, name=u'', factory=None): + """Unregister a utility + + A boolean is returned indicating whether the registry was + changed. If the given component is None and there is no + component registered, or if the given component is not + None and is not registered, then the function returns + False, otherwise it returns True. + + factory + Factory for the component to be unregisterd. + + component + The registered component The given component can be + None, in which case any component registered to provide + the given provided interface with the given name is + unregistered. + + provided + This is the interface provided by the utility. If the + component is not None and provides a single interface, + then this argument is optional and the + component-implemented interface will be used. + + name + The utility name. + + Only one of component and factory can be used. + An UnRegistered event is generated with an IUtilityRegistration. + """ + + def registeredUtilities(): + """Return an iterable of IUtilityRegistration instances. + + These registrations describe the current utility registrations + in the object. + """ + + def registerAdapter(factory, required=None, provided=None, name=u'', + info=u''): + """Register an adapter factory + + Parameters: + + factory + The object used to compute the adapter + + required + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + __component_adapts__ attribute will be used. The + __component_adapts__ attribute is usually attribute is + normally set in class definitions using adapts + function, or for callables using the adapter + decorator. If the factory doesn't have a + __component_adapts__ adapts attribute, then this + argument is required. + + provided + This is the interface provided by the adapter and + implemented by the factory. If the factory + implements a single interface, then this argument is + optional and the factory-implemented interface will be + used. + + name + The adapter name. + + info + An object that can be converted to a string to provide + information about the registration. + + A Registered event is generated with an IAdapterRegistration. + """ + + def unregisterAdapter(factory=None, required=None, + provided=None, name=u''): + """Register an adapter factory + + A boolean is returned indicating whether the registry was + changed. If the given component is None and there is no + component registered, or if the given component is not + None and is not registered, then the function returns + False, otherwise it returns True. + + Parameters: + + factory + This is the object used to compute the adapter. The + factory can be None, in which case any factory + registered to implement the given provided interface + for the given required specifications with the given + name is unregistered. + + required + This is a sequence of specifications for objects to be + adapted. If the factory is not None and the required + arguments is omitted, then the value of the factory's + __component_adapts__ attribute will be used. The + __component_adapts__ attribute attribute is normally + set in class definitions using adapts function, or for + callables using the adapter decorator. If the factory + is None or doesn't have a __component_adapts__ adapts + attribute, then this argument is required. + + provided + This is the interface provided by the adapter and + implemented by the factory. If the factory is not + None and implements a single interface, then this + argument is optional and the factory-implemented + interface will be used. + + name + The adapter name. + + An Unregistered event is generated with an IAdapterRegistration. + """ + + def registeredAdapters(): + """Return an iterable of IAdapterRegistration instances. + + These registrations describe the current adapter registrations + in the object. + """ + + def registerSubscriptionAdapter(factory, required=None, provides=None, + name=u'', info=''): + """Register a subscriber factory + + Parameters: + + factory + The object used to compute the adapter + + required + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + __component_adapts__ attribute will be used. The + __component_adapts__ attribute is usually attribute is + normally set in class definitions using adapts + function, or for callables using the adapter + decorator. If the factory doesn't have a + __component_adapts__ adapts attribute, then this + argument is required. + + provided + This is the interface provided by the adapter and + implemented by the factory. If the factory implements + a single interface, then this argument is optional and + the factory-implemented interface will be used. + + name + The adapter name. + + Currently, only the empty string is accepted. Other + strings will be accepted in the future when support for + named subscribers is added. + + info + An object that can be converted to a string to provide + information about the registration. + + A Registered event is generated with an + ISubscriptionAdapterRegistration. + """ + + def unregisterSubscriptionAdapter(factory=None, required=None, + provides=None, name=u''): + """Unregister a subscriber factory. + + A boolean is returned indicating whether the registry was + changed. If the given component is None and there is no + component registered, or if the given component is not + None and is not registered, then the function returns + False, otherwise it returns True. + + Parameters: + + factory + This is the object used to compute the adapter. The + factory can be None, in which case any factories + registered to implement the given provided interface + for the given required specifications with the given + name are unregistered. + + required + This is a sequence of specifications for objects to be + adapted. If the factory is not None and the required + arguments is omitted, then the value of the factory's + __component_adapts__ attribute will be used. The + __component_adapts__ attribute attribute is normally + set in class definitions using adapts function, or for + callables using the adapter decorator. If the factory + is None or doesn't have a __component_adapts__ adapts + attribute, then this argument is required. + + provided + This is the interface provided by the adapter and + implemented by the factory. If the factory is not + None implements a single interface, then this argument + is optional and the factory-implemented interface will + be used. + + name + The adapter name. + + Currently, only the empty string is accepted. Other + strings will be accepted in the future when support for + named subscribers is added. + + An Unregistered event is generated with an + ISubscriptionAdapterRegistration. + """ + + def registeredSubscriptionAdapters(): + """Return an iterable of ISubscriptionAdapterRegistration instances. + + These registrations describe the current subscription adapter + registrations in the object. + """ + + def registerHandler(handler, required=None, name=u'', info=''): + """Register a handler. + + A handler is a subscriber that doesn't compute an adapter + but performs some function when called. + + Parameters: + + handler + The object used to handle some event represented by + the objects passed to it. + + required + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + __component_adapts__ attribute will be used. The + __component_adapts__ attribute is usually attribute is + normally set in class definitions using adapts + function, or for callables using the adapter + decorator. If the factory doesn't have a + __component_adapts__ adapts attribute, then this + argument is required. + + name + The handler name. + + Currently, only the empty string is accepted. Other + strings will be accepted in the future when support for + named handlers is added. + + info + An object that can be converted to a string to provide + information about the registration. + + + A Registered event is generated with an IHandlerRegistration. + """ + + def unregisterHandler(handler=None, required=None, name=u''): + """Unregister a handler. + + A handler is a subscriber that doesn't compute an adapter + but performs some function when called. + + A boolean is returned indicating whether the registry was + changed. + + Parameters: + + handler + This is the object used to handle some event + represented by the objects passed to it. The handler + can be None, in which case any handlers registered for + the given required specifications with the given are + unregistered. + + required + This is a sequence of specifications for objects to be + adapted. If omitted, then the value of the factory's + __component_adapts__ attribute will be used. The + __component_adapts__ attribute is usually attribute is + normally set in class definitions using adapts + function, or for callables using the adapter + decorator. If the factory doesn't have a + __component_adapts__ adapts attribute, then this + argument is required. + + name + The handler name. + + Currently, only the empty string is accepted. Other + strings will be accepted in the future when support for + named handlers is added. + + An Unregistered event is generated with an IHandlerRegistration. + """ + + def registeredHandlers(): + """Return an iterable of IHandlerRegistration instances. + + These registrations describe the current handler registrations + in the object. + """ + + +class IComponents(IComponentLookup, IComponentRegistry): + """Component registration and access + """ + + +class IPossibleSite(Interface): + """An object that could be a site. + """ + + def setSiteManager(sitemanager): + """Sets the site manager for this object. + """ + + def getSiteManager(): + """Returns the site manager contained in this object. + + If there isn't a site manager, raise a component lookup. + """ + + +class ISite(IPossibleSite): + """Marker interface to indicate that we have a site""" diff -Nru zope3-3.4.0/src/zope/component/meta.zcml zope3-3.5~bzr18/src/zope/component/meta.zcml --- zope3-3.4.0/src/zope/component/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/component/nexttesting.py zope3-3.5~bzr18/src/zope/component/nexttesting.py --- zope3-3.4.0/src/zope/component/nexttesting.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/nexttesting.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,106 @@ +############################################################################## +# +# Copyright (c) 2009 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 functions for testing utilities that use get/queryNextUtility. + +$Id: nexttesting.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +import zope.interface +from zope.component.interfaces import IComponentLookup, IComponents + + +class SiteManagerStub(object): + zope.interface.implements(IComponents) + + __bases__ = () + + def __init__(self): + self._utils = {} + + def setNext(self, next): + self.__bases__ = (next, ) + + def provideUtility(self, iface, util, name=''): + self._utils[(iface, name)] = util + + def queryUtility(self, iface, name='', default=None): + return self._utils.get((iface, name), default) + + +def testingNextUtility(utility, nextutility, interface, name='', + sitemanager=None, nextsitemanager=None): + """Provide a next utility for testing. + + This function sets up two utilities, so the get/queryNextUtility functions + will see the second one as the "next" to the first one. + + To test it, we need to create a utility interface and implementation: + + >>> from zope.interface import Interface, implements + >>> class IAnyUtility(Interface): + ... pass + + >>> class AnyUtility(object): + ... implements(IAnyUtility) + ... def __init__(self, id): + ... self.id = id + + >>> any1 = AnyUtility(1) + >>> any1next = AnyUtility(2) + + Now, we can make the "any1next" be next to "any1". + + >>> testingNextUtility(any1, any1next, IAnyUtility) + + >>> from zope.component import getNextUtility + >>> getNextUtility(any1, IAnyUtility) is any1next + True + + It will work for named utilities as well. + + >>> testingNextUtility(any1, any1next, IAnyUtility, 'any') + >>> getNextUtility(any1, IAnyUtility, 'any') is any1next + True + + We can also provide our custom component registries: + + >>> sm = SiteManagerStub() + >>> nextsm = SiteManagerStub() + + >>> testingNextUtility(any1, any1next, IAnyUtility, + ... sitemanager=sm, nextsitemanager=nextsm) + + >>> IComponentLookup(any1) is sm + True + >>> IComponentLookup(any1next) is nextsm + True + >>> getNextUtility(any1, IAnyUtility) is any1next + True + + """ + if sitemanager is None: + sitemanager = SiteManagerStub() + if nextsitemanager is None: + nextsitemanager = SiteManagerStub() + sitemanager.setNext(nextsitemanager) + + sitemanager.provideUtility(interface, utility, name) + utility.__conform__ = ( + lambda iface: + iface.isOrExtends(IComponentLookup) and sitemanager or None + ) + nextsitemanager.provideUtility(interface, nextutility, name) + nextutility.__conform__ = ( + lambda iface: + iface.isOrExtends(IComponentLookup) and nextsitemanager or None + ) diff -Nru zope3-3.4.0/src/zope/component/persistentregistry.py zope3-3.5~bzr18/src/zope/component/persistentregistry.py --- zope3-3.4.0/src/zope/component/persistentregistry.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/persistentregistry.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,56 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Persistent component managers + +$Id: persistentregistry.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +import persistent.mapping +import persistent.list +import zope.interface.adapter + +import zope.component.registry + +class PersistentAdapterRegistry( + zope.interface.adapter.VerifyingAdapterRegistry, + persistent.Persistent, + ): + + def changed(self, originally_changed): + if originally_changed is self: + self._p_changed = True + super(PersistentAdapterRegistry, self).changed(originally_changed) + + def __getstate__(self): + state = super(PersistentAdapterRegistry, self).__getstate__().copy() + for name in self._delegated: + state.pop(name, 0) + return state + + def __setstate__(self, state): + super(PersistentAdapterRegistry, self).__setstate__(state) + self._createLookup() + self._v_lookup.changed(self) + + +class PersistentComponents(zope.component.registry.Components): + + def _init_registries(self): + self.adapters = PersistentAdapterRegistry() + self.utilities = PersistentAdapterRegistry() + + def _init_registrations(self): + self._utility_registrations = persistent.mapping.PersistentMapping() + self._adapter_registrations = persistent.mapping.PersistentMapping() + self._subscription_registrations = persistent.list.PersistentList() + self._handler_registrations = persistent.list.PersistentList() diff -Nru zope3-3.4.0/src/zope/component/persistentregistry.txt zope3-3.5~bzr18/src/zope/component/persistentregistry.txt --- zope3-3.4.0/src/zope/component/persistentregistry.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/persistentregistry.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ +Persistent Component Management +=============================== + +Persistent component management allows persistent management of +components. From a usage point of view, there shouldn't be any new +behavior beyond what's described in registry.txt. diff -Nru zope3-3.4.0/src/zope/component/README.txt zope3-3.5~bzr18/src/zope/component/README.txt --- zope3-3.4.0/src/zope/component/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,396 @@ +Zope Component Architecture +=========================== + +This package, together with `zope.interface`, provides facilities for +defining, registering and looking up components. There are two basic +kinds of components: adapters and utilities. + +Utilities +--------- + +Utilities are just components that provide an interface and that are +looked up by an interface and a name. Let's look at a trivial utility +definition: + + >>> from zope import interface + + >>> class IGreeter(interface.Interface): + ... def greet(): + ... "say hello" + + >>> class Greeter: + ... interface.implements(IGreeter) + ... + ... def __init__(self, other="world"): + ... self.other = other + ... + ... def greet(self): + ... print "Hello", self.other + +We can register an instance this class using `provideUtility` [1]_: + + >>> from zope import component + >>> greet = Greeter('bob') + >>> component.provideUtility(greet, IGreeter, 'robert') + +In this example we registered the utility as providing the `IGreeter` +interface with a name of 'bob'. We can look the interface up with +either `queryUtility` or `getUtility`: + + >>> component.queryUtility(IGreeter, 'robert').greet() + Hello bob + + >>> component.getUtility(IGreeter, 'robert').greet() + Hello bob + +`queryUtility` and `getUtility` differ in how failed lookups are handled: + + >>> component.queryUtility(IGreeter, 'ted') + >>> component.queryUtility(IGreeter, 'ted', 42) + 42 + >>> component.getUtility(IGreeter, 'ted') + ... # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ComponentLookupError: (, 'ted') + +If a component provides only one interface, as in the example above, +then we can omit the provided interface from the call to `provideUtility`: + + >>> ted = Greeter('ted') + >>> component.provideUtility(ted, name='ted') + >>> component.queryUtility(IGreeter, 'ted').greet() + Hello ted + +The name defaults to an empty string: + + >>> world = Greeter() + >>> component.provideUtility(world) + >>> component.queryUtility(IGreeter).greet() + Hello world + +Adapters +-------- + +Adapters are components that are computed from other components to +adapt them to some interface. Because they are computed from other +objects, they are provided as factories, usually classes. Here, we'll +create a greeter for persons, so we can provide personalized greetings +for different people: + + >>> class IPerson(interface.Interface): + ... name = interface.Attribute("Name") + + >>> class PersonGreeter: + ... + ... component.adapts(IPerson) + ... interface.implements(IGreeter) + ... + ... def __init__(self, person): + ... self.person = person + ... + ... def greet(self): + ... print "Hello", self.person.name + +The class defines a constructor that takes an argument for every +object adapted. + +We used `component.adapts` to declare what we adapt. We can find +out if an object declares that it adapts anything using adaptedBy: + + >>> list(component.adaptedBy(PersonGreeter)) == [IPerson] + True + +If an object makes no declaration, then None is returned: + + >>> component.adaptedBy(Greeter()) is None + True + + +If we declare the interfaces adapted and if we provide only one +interface, as in the example above, then we can provide the adapter +very simply [1]_: + + >>> component.provideAdapter(PersonGreeter) + +For adapters that adapt a single interface to a single interface +without a name, we can get the adapter by simply calling the +interface: + + >>> class Person: + ... interface.implements(IPerson) + ... + ... def __init__(self, name): + ... self.name = name + + >>> IGreeter(Person("Sally")).greet() + Hello Sally + +We can also provide arguments to be very specific about what +how to register the adapter. + + >>> class BobPersonGreeter(PersonGreeter): + ... name = 'Bob' + ... def greet(self): + ... print "Hello", self.person.name, "my name is", self.name + + >>> component.provideAdapter( + ... BobPersonGreeter, [IPerson], IGreeter, 'bob') + +The arguments can also be provided as keyword arguments: + + >>> class TedPersonGreeter(BobPersonGreeter): + ... name = "Ted" + + >>> component.provideAdapter( + ... factory=TedPersonGreeter, adapts=[IPerson], + ... provides=IGreeter, name='ted') + +For named adapters, use `queryAdapter`, or `getAdapter`: + + >>> component.queryAdapter(Person("Sally"), IGreeter, 'bob').greet() + Hello Sally my name is Bob + + >>> component.getAdapter(Person("Sally"), IGreeter, 'ted').greet() + Hello Sally my name is Ted + +If an adapter can't be found, `queryAdapter` returns a default value +and `getAdapter` raises an error: + + >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank') + >>> component.queryAdapter(Person("Sally"), IGreeter, 'frank', 42) + 42 + >>> component.getAdapter(Person("Sally"), IGreeter, 'frank') + ... # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ComponentLookupError: (...Person...>, <...IGreeter>, 'frank') + +Adapters can adapt multiple objects: + + >>> class TwoPersonGreeter: + ... + ... component.adapts(IPerson, IPerson) + ... interface.implements(IGreeter) + ... + ... def __init__(self, person, greeter): + ... self.person = person + ... self.greeter = greeter + ... + ... def greet(self): + ... print "Hello", self.person.name + ... print "my name is", self.greeter.name + + >>> component.provideAdapter(TwoPersonGreeter) + +To look up a multi-adapter, use either `queryMultiAdapter` or +`getMultiAdapter`: + + >>> component.queryMultiAdapter((Person("Sally"), Person("Bob")), + ... IGreeter).greet() + Hello Sally + my name is Bob + +Adapters need not be classes. Any callable will do. We use the +adapter decorator (in the Python 2.4 decorator sense) to declare that +a callable object adapts some interfaces (or classes): + + >>> class IJob(interface.Interface): + ... "A job" + + >>> class Job: + ... interface.implements(IJob) + + >>> def personJob(person): + ... return getattr(person, 'job', None) + >>> personJob = interface.implementer(IJob)(personJob) + >>> personJob = component.adapter(IPerson)(personJob) + +In Python 2.4, the example can be written: + + >>> @interface.implementer(IJob) + ... @component.adapter(IPerson) + ... def personJob(person): + ... return getattr(person, 'job', None) + +which looks a bit nicer. + +In this example, the personJob function simply returns the person's +`job` attribute if present, or None if it's not present. An adapter +factory can return None to indicate that adaptation wasn't possible. +Let's register this adapter and try it out: + + >>> component.provideAdapter(personJob) + >>> sally = Person("Sally") + >>> IJob(sally) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: ('Could not adapt', ... + +The adaptation failed because sally didn't have a job. Let's give her +one: + + >>> job = Job() + >>> sally.job = job + >>> IJob(sally) is job + True + +Subscription Adapters +--------------------- + +Unlike regular adapters, subscription adapters are used when we want +all of the adapters that adapt an object to a particular adapter. + +Consider a validation problem. We have objects and we want to assess +whether they meet some sort of standards. We define a validation +interface: + + >>> class IValidate(interface.Interface): + ... def validate(ob): + ... """Determine whether the object is valid + ... + ... Return a string describing a validation problem. + ... An empty string is returned to indicate that the + ... object is valid. + ... """ + +Perhaps we have documents: + + >>> class IDocument(interface.Interface): + ... summary = interface.Attribute("Document summary") + ... body = interface.Attribute("Document text") + + >>> class Document: + ... interface.implements(IDocument) + ... def __init__(self, summary, body): + ... self.summary, self.body = summary, body + +Now, we may want to specify various validation rules for +documents. For example, we might require that the summary be a single +line: + + >>> class SingleLineSummary: + ... component.adapts(IDocument) + ... interface.implements(IValidate) + ... + ... def __init__(self, doc): + ... self.doc = doc + ... + ... def validate(self): + ... if '\n' in self.doc.summary: + ... return 'Summary should only have one line' + ... else: + ... return '' + +Or we might require the body to be at least 1000 characters in length: + + >>> class AdequateLength: + ... component.adapts(IDocument) + ... interface.implements(IValidate) + ... + ... def __init__(self, doc): + ... self.doc = doc + ... + ... def validate(self): + ... if len(self.doc.body) < 1000: + ... return 'too short' + ... else: + ... return '' + +We can register these as subscription adapters [1]_: + + >>> component.provideSubscriptionAdapter(SingleLineSummary) + >>> component.provideSubscriptionAdapter(AdequateLength) + +We can then use the subscribers to validate objects: + + >>> doc = Document("A\nDocument", "blah") + >>> [adapter.validate() + ... for adapter in component.subscribers([doc], IValidate) + ... if adapter.validate()] + ['Summary should only have one line', 'too short'] + + >>> doc = Document("A\nDocument", "blah" * 1000) + >>> [adapter.validate() + ... for adapter in component.subscribers([doc], IValidate) + ... if adapter.validate()] + ['Summary should only have one line'] + + >>> doc = Document("A Document", "blah") + >>> [adapter.validate() + ... for adapter in component.subscribers([doc], IValidate) + ... if adapter.validate()] + ['too short'] + +Handlers +-------- + +Handlers are subscription adapter factories that don't produce +anything. They do all of their work when called. Handlers +are typically used to handle events. + +Event subscribers are different from other subscription adapters in +that the caller of event subscribers doesn't expect to interact with +them in any direct way. For example, an event publisher doesn't +expect to get any return value. Because subscribers don't need to +provide an API to their callers, it is more natural to define them +with functions, rather than classes. For example, in a +document-management system, we might want to record creation times for +documents: + + >>> import datetime + + >>> def documentCreated(event): + ... event.doc.created = datetime.datetime.utcnow() + +In this example, we have a function that takes an event and performs +some processing. It doesn't actually return anything. This is a +special case of a subscription adapter that adapts an event to +nothing. All of the work is done when the adapter "factory" is +called. We call subscribers that don't actually create anything +"handlers". There are special APIs for registering and calling +them. + +To register the subscriber above, we define a document-created event: + + >>> class IDocumentCreated(interface.Interface): + ... doc = interface.Attribute("The document that was created") + + >>> class DocumentCreated: + ... interface.implements(IDocumentCreated) + ... + ... def __init__(self, doc): + ... self.doc = doc + +We'll also change our handler definition to: + + >>> def documentCreated(event): + ... event.doc.created = datetime.datetime.utcnow() + + >>> documentCreated = component.adapter(IDocumentCreated)(documentCreated) + +Note that in Python 2.4, this can be written: + + >>> @component.adapter(IDocumentCreated) + ... def documentCreated(event): + ... event.doc.created = datetime.datetime.utcnow() + +This marks the handler as an adapter of `IDocumentCreated` events. + +Now we'll register the handler [1]_: + + >>> component.provideHandler(documentCreated) + +Now, if we can create an event and use the `handle` function to call +handlers registered for the event: + + >>> component.handle(DocumentCreated(doc)) + >>> doc.created.__class__.__name__ + 'datetime' + + + +.. [1] CAUTION: This API should only be used from test or + application-setup code. This API shouldn't be used by regular + library modules, as component registration is a configuration + activity. diff -Nru zope3-3.4.0/src/zope/component/registry.py zope3-3.5~bzr18/src/zope/component/registry.py --- zope3-3.4.0/src/zope/component/registry.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/registry.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,514 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Basic components support + +$Id: registry.py 110537 2010-04-06 03:01:29Z tseaver $ +""" + +import types + +from zope.interface import Interface +from zope.interface import implementedBy +from zope.interface import implements +from zope.interface import implementsOnly +from zope.interface import providedBy +from zope.interface.adapter import AdapterRegistry +from zope.interface.interfaces import ISpecification + +from zope.component.interfaces import ComponentLookupError +from zope.component.interfaces import IAdapterRegistration +from zope.component.interfaces import IComponents +from zope.component.interfaces import IHandlerRegistration +from zope.component.interfaces import IRegistrationEvent +from zope.component.interfaces import ISubscriptionAdapterRegistration +from zope.component.interfaces import IUtilityRegistration +from zope.component.interfaces import Registered +from zope.component.interfaces import Unregistered +from zope.component._api import handle +from zope.component._declaration import adapter +from zope.event import notify + +class Components(object): + + implements(IComponents) + + def __init__(self, name='', bases=()): + assert isinstance(name, basestring) + self.__name__ = name + self._init_registries() + self._init_registrations() + self.__bases__ = tuple(bases) + + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, self.__name__) + + def _init_registries(self): + self.adapters = AdapterRegistry() + self.utilities = AdapterRegistry() + + def _init_registrations(self): + self._utility_registrations = {} + self._adapter_registrations = {} + self._subscription_registrations = [] + self._handler_registrations = [] + + def _getBases(self): + # Subclasses might override + return self.__dict__.get('__bases__', ()) + + def _setBases(self, bases): + # Subclasses might override + self.adapters.__bases__ = tuple([ + base.adapters for base in bases]) + self.utilities.__bases__ = tuple([ + base.utilities for base in bases]) + self.__dict__['__bases__'] = tuple(bases) + + __bases__ = property( + lambda self: self._getBases(), + lambda self, bases: self._setBases(bases), + ) + + def registerUtility(self, component=None, provided=None, name=u'', info=u'', + event=True, factory=None): + if factory: + if component: + raise TypeError("Can't specify factory and component.") + component = factory() + + if provided is None: + provided = _getUtilityProvided(component) + + reg = self._utility_registrations.get((provided, name)) + if reg is not None: + if reg[:2] == (component, info): + # already registered + return + self.unregisterUtility(reg[0], provided, name) + + subscribed = False + for ((p, _), data) in self._utility_registrations.iteritems(): + if p == provided and data[0] == component: + subscribed = True + break + + self._utility_registrations[(provided, name)] = component, info, factory + self.utilities.register((), provided, name, component) + + if not subscribed: + self.utilities.subscribe((), provided, component) + + if event: + notify(Registered( + UtilityRegistration(self, provided, name, component, info, + factory) + )) + + def unregisterUtility(self, component=None, provided=None, name=u'', + factory=None): + if factory: + if component: + raise TypeError("Can't specify factory and component.") + component = factory() + + if provided is None: + if component is None: + raise TypeError("Must specify one of component, factory and " + "provided") + provided = _getUtilityProvided(component) + + old = self._utility_registrations.get((provided, name)) + if (old is None) or ((component is not None) and + (component != old[0])): + return False + + if component is None: + component = old[0] + + # Note that component is now the old thing registered + + del self._utility_registrations[(provided, name)] + self.utilities.unregister((), provided, name) + + subscribed = False + for ((p, _), data) in self._utility_registrations.iteritems(): + if p == provided and data[0] == component: + subscribed = True + break + + if not subscribed: + self.utilities.unsubscribe((), provided, component) + + notify(Unregistered( + UtilityRegistration(self, provided, name, component, *old[1:]) + )) + + return True + + def registeredUtilities(self): + for ((provided, name), data + ) in self._utility_registrations.iteritems(): + yield UtilityRegistration(self, provided, name, *data) + + def queryUtility(self, provided, name=u'', default=None): + return self.utilities.lookup((), provided, name, default) + + def getUtility(self, provided, name=u''): + utility = self.utilities.lookup((), provided, name) + if utility is None: + raise ComponentLookupError(provided, name) + return utility + + def getUtilitiesFor(self, interface): + for name, utility in self.utilities.lookupAll((), interface): + yield name, utility + + def getAllUtilitiesRegisteredFor(self, interface): + return self.utilities.subscriptions((), interface) + + def registerAdapter(self, factory, required=None, provided=None, name=u'', + info=u'', event=True): + if provided is None: + provided = _getAdapterProvided(factory) + required = _getAdapterRequired(factory, required) + self._adapter_registrations[(required, provided, name) + ] = factory, info + self.adapters.register(required, provided, name, factory) + + if event: + notify(Registered( + AdapterRegistration(self, required, provided, name, + factory, info) + )) + + + def unregisterAdapter(self, factory=None, + required=None, provided=None, name=u'', + ): + if provided is None: + if factory is None: + raise TypeError("Must specify one of factory and provided") + provided = _getAdapterProvided(factory) + + if (required is None) and (factory is None): + raise TypeError("Must specify one of factory and required") + + required = _getAdapterRequired(factory, required) + old = self._adapter_registrations.get((required, provided, name)) + if (old is None) or ((factory is not None) and + (factory != old[0])): + return False + + del self._adapter_registrations[(required, provided, name)] + self.adapters.unregister(required, provided, name) + + notify(Unregistered( + AdapterRegistration(self, required, provided, name, + *old) + )) + + return True + + def registeredAdapters(self): + for ((required, provided, name), (component, info) + ) in self._adapter_registrations.iteritems(): + yield AdapterRegistration(self, required, provided, name, + component, info) + + def queryAdapter(self, object, interface, name=u'', default=None): + return self.adapters.queryAdapter(object, interface, name, default) + + def getAdapter(self, object, interface, name=u''): + adapter = self.adapters.queryAdapter(object, interface, name) + if adapter is None: + raise ComponentLookupError(object, interface, name) + return adapter + + def queryMultiAdapter(self, objects, interface, name=u'', default=None): + return self.adapters.queryMultiAdapter( + objects, interface, name, default) + + def getMultiAdapter(self, objects, interface, name=u''): + adapter = self.adapters.queryMultiAdapter(objects, interface, name) + if adapter is None: + raise ComponentLookupError(objects, interface, name) + return adapter + + def getAdapters(self, objects, provided): + for name, factory in self.adapters.lookupAll( + map(providedBy, objects), + provided): + adapter = factory(*objects) + if adapter is not None: + yield name, adapter + + def registerSubscriptionAdapter(self, + factory, required=None, provided=None, + name=u'', info=u'', + event=True): + if name: + raise TypeError("Named subscribers are not yet supported") + if provided is None: + provided = _getAdapterProvided(factory) + required = _getAdapterRequired(factory, required) + self._subscription_registrations.append( + (required, provided, name, factory, info) + ) + self.adapters.subscribe(required, provided, factory) + + if event: + notify(Registered( + SubscriptionRegistration(self, required, provided, name, + factory, info) + )) + + def registeredSubscriptionAdapters(self): + for data in self._subscription_registrations: + yield SubscriptionRegistration(self, *data) + + def unregisterSubscriptionAdapter(self, factory=None, + required=None, provided=None, name=u'', + ): + if name: + raise TypeError("Named subscribers are not yet supported") + if provided is None: + if factory is None: + raise TypeError("Must specify one of factory and provided") + provided = _getAdapterProvided(factory) + + if (required is None) and (factory is None): + raise TypeError("Must specify one of factory and required") + + required = _getAdapterRequired(factory, required) + + if factory is None: + new = [(r, p, n, f, i) + for (r, p, n, f, i) + in self._subscription_registrations + if not (r == required and p == provided) + ] + else: + new = [(r, p, n, f, i) + for (r, p, n, f, i) + in self._subscription_registrations + if not (r == required and p == provided and f == factory) + ] + + if len(new) == len(self._subscription_registrations): + return False + + + self._subscription_registrations[:] = new + self.adapters.unsubscribe(required, provided, factory) + + notify(Unregistered( + SubscriptionRegistration(self, required, provided, name, + factory, '') + )) + + return True + + def subscribers(self, objects, provided): + return self.adapters.subscribers(objects, provided) + + def registerHandler(self, + factory, required=None, + name=u'', info=u'', + event=True): + if name: + raise TypeError("Named handlers are not yet supported") + required = _getAdapterRequired(factory, required) + self._handler_registrations.append( + (required, name, factory, info) + ) + self.adapters.subscribe(required, None, factory) + + if event: + notify(Registered( + HandlerRegistration(self, required, name, factory, info) + )) + + def registeredHandlers(self): + for data in self._handler_registrations: + yield HandlerRegistration(self, *data) + + def unregisterHandler(self, factory=None, required=None, name=u''): + if name: + raise TypeError("Named subscribers are not yet supported") + + if (required is None) and (factory is None): + raise TypeError("Must specify one of factory and required") + + required = _getAdapterRequired(factory, required) + + if factory is None: + new = [(r, n, f, i) + for (r, n, f, i) + in self._handler_registrations + if r != required + ] + else: + new = [(r, n, f, i) + for (r, n, f, i) + in self._handler_registrations + if not (r == required and f == factory) + ] + + if len(new) == len(self._handler_registrations): + return False + + self._handler_registrations[:] = new + self.adapters.unsubscribe(required, None, factory) + + notify(Unregistered( + HandlerRegistration(self, required, name, factory, '') + )) + + return True + + def handle(self, *objects): + self.adapters.subscribers(objects, None) + + +def _getUtilityProvided(component): + provided = list(providedBy(component)) + if len(provided) == 1: + return provided[0] + raise TypeError( + "The utility doesn't provide a single interface " + "and no provided interface was specified.") + +def _getAdapterProvided(factory): + provided = list(implementedBy(factory)) + if len(provided) == 1: + return provided[0] + raise TypeError( + "The adapter factory doesn't implement a single interface " + "and no provided interface was specified.") + + +classTypes = type, types.ClassType +def _getAdapterRequired(factory, required): + if required is None: + try: + required = factory.__component_adapts__ + except AttributeError: + raise TypeError( + "The adapter factory doesn't have a __component_adapts__ " + "attribute and no required specifications were specified" + ) + elif ISpecification.providedBy(required): + raise TypeError("the required argument should be a list of " + "interfaces, not a single interface") + + result = [] + for r in required: + if r is None: + r = Interface + elif not ISpecification.providedBy(r): + if isinstance(r, classTypes): + r = implementedBy(r) + else: + raise TypeError("Required specification must be a " + "specification or class." + ) + result.append(r) + return tuple(result) + + +class UtilityRegistration(object): + + implements(IUtilityRegistration) + + def __init__(self, registry, provided, name, component, doc, factory=None): + (self.registry, self.provided, self.name, self.component, self.info, + self.factory + ) = registry, provided, name, component, doc, factory + + def __repr__(self): + return '%s(%r, %s, %r, %s, %r, %r)' % ( + self.__class__.__name__, + self.registry, + getattr(self.provided, '__name__', None), self.name, + getattr(self.component, '__name__', `self.component`), + self.factory, self.info, + ) + + def __cmp__(self, other): + return cmp(self.__repr__(), other.__repr__()) + +class AdapterRegistration(object): + + implements(IAdapterRegistration) + + def __init__(self, registry, required, provided, name, component, doc): + (self.registry, self.required, self.provided, self.name, + self.factory, self.info + ) = registry, required, provided, name, component, doc + + def __repr__(self): + return '%s(%r, %s, %s, %r, %s, %r)' % ( + self.__class__.__name__, + self.registry, + '[' + ", ".join([r.__name__ for r in self.required]) + ']', + getattr(self.provided, '__name__', None), self.name, + getattr(self.factory, '__name__', `self.factory`), self.info, + ) + + def __cmp__(self, other): + return cmp(self.__repr__(), other.__repr__()) + +class SubscriptionRegistration(AdapterRegistration): + + implementsOnly(ISubscriptionAdapterRegistration) + +class HandlerRegistration(AdapterRegistration): + + implementsOnly(IHandlerRegistration) + + def __init__(self, registry, required, name, handler, doc): + (self.registry, self.required, self.name, self.handler, self.info + ) = registry, required, name, handler, doc + + @property + def factory(self): + return self.handler + + provided = None + + def __repr__(self): + return '%s(%r, %s, %r, %s, %r)' % ( + self.__class__.__name__, + self.registry, + '[' + ", ".join([r.__name__ for r in self.required]) + ']', + self.name, + getattr(self.factory, '__name__', `self.factory`), self.info, + ) + + +@adapter(IUtilityRegistration, IRegistrationEvent) +def dispatchUtilityRegistrationEvent(registration, event): + handle(registration.component, event) + +@adapter(IAdapterRegistration, IRegistrationEvent) +def dispatchAdapterRegistrationEvent(registration, event): + handle(registration.factory, event) + +@adapter(ISubscriptionAdapterRegistration, IRegistrationEvent) +def dispatchSubscriptionAdapterRegistrationEvent(registration, event): + handle(registration.factory, event) + +@adapter(IHandlerRegistration, IRegistrationEvent) +def dispatchHandlerRegistrationEvent(registration, event): + handle(registration.handler, event) diff -Nru zope3-3.4.0/src/zope/component/registry.txt zope3-3.5~bzr18/src/zope/component/registry.txt --- zope3-3.4.0/src/zope/component/registry.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/registry.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1117 @@ +Component-Management objects +============================ + +Component-management objects provide a higher-level +component-management API over the basic adapter-registration API +provided by the zope.interface package. In particular, it provides: + +- utilities + +- support for computing adapters, rather than just looking up adapter + factories. + +- management of registration comments + +The zope.component.registry.Components class provides an +implementation of zope.component.interfaces.IComponents that provides +these features. + + >>> from zope.component import registry + >>> from zope.component import tests + >>> components = registry.Components('comps') + +As components are registered, events are generated. Let's register +an event subscriber, so we can see the events generated: + + >>> import zope.event + >>> def logevent(event): + ... print event + >>> zope.event.subscribers.append(logevent) + +Utilities +--------- + +You can register Utilities using registerUtility: + + >>> components.registerUtility(tests.U1(1)) + Registered event: + UtilityRegistration(, I1, u'', 1, None, u'') + +Here we didn't specify an interface or name. An unnamed utility was +registered for interface I1, since that is only interface implemented +by the U1 class: + + >>> components.getUtility(tests.I1) + U1(1) + +You can also register a utility using a factory instead of a utility instance: + + >>> def factory(): + ... return tests.U1(1) + >>> components.registerUtility(factory=factory) + Unregistered event: + UtilityRegistration(, I1, u'', 1, None, u'') + Registered event: + UtilityRegistration(, I1, u'', 1, >, u'') + + +If a component implements other than one interface or no interface, +then an error will be raised: + + >>> components.registerUtility(tests.U12(2)) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The utility doesn't provide a single interface and + no provided interface was specified. + + >>> components.registerUtility(tests.A) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The utility doesn't provide a single interface and + no provided interface was specified. + + +We can provide an interface if desired: + + >>> components.registerUtility(tests.U12(2), tests.I2) + Registered event: + UtilityRegistration(, I2, u'', 2, None, u'') + +and we can specify a name: + + >>> components.registerUtility(tests.U12(3), tests.I2, u'three') + Registered event: + UtilityRegistration(, I2, u'three', 3, None, u'') + + >>> components.getUtility(tests.I2) + U12(2) + + >>> components.getUtility(tests.I2, 'three') + U12(3) + +If you try to get a utility that doesn't exist, you'll get a component +lookup error: + + >>> components.getUtility(tests.I3) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: + (, u'') + +Unless you use queryUtility: + + >>> components.queryUtility(tests.I3) + >>> components.queryUtility(tests.I3, default=42) + 42 + +You can get information about registered utilities with the +registeredUtilities method: + + >>> for registration in sorted(components.registeredUtilities()): + ... print registration.provided, registration.name + ... print registration.component, registration.info + + U1(1) + + U12(2) + three + U12(3) + +Duplicate registrations replace existing ones: + + >>> components.registerUtility(tests.U1(4), info=u'use 4 now') + Unregistered event: + UtilityRegistration(, I1, u'', 1, >, u'') + Registered event: + UtilityRegistration(, I1, u'', 4, None, u'use 4 now') + >>> components.getUtility(tests.I1) + U1(4) + + >>> for registration in sorted(components.registeredUtilities()): + ... print registration.provided, registration.name + ... print registration.component, registration.info + + U1(4) use 4 now + + U12(2) + three + U12(3) + +As shown in the this example, you can provide an "info" argumemnt when +registering utilities. This provides extra documentation about the +registration itself that is shown when listing registrations. + +You can also unregister utilities: + + >>> components.unregisterUtility(provided=tests.I1) + Unregistered event: + UtilityRegistration(, I1, u'', 4, None, u'use 4 now') + True + +A boolean is returned indicating whether anything changed: + + >>> components.queryUtility(tests.I1) + >>> for registration in sorted(components.registeredUtilities()): + ... print registration.provided, registration.name + ... print registration.component, registration.info + + U12(2) + three + U12(3) + +When you unregister, you can specify a component. If the component +doesn't match the one registered, then nothing happens: + + >>> u5 = tests.U1(5) + >>> components.registerUtility(u5) + Registered event: + UtilityRegistration(, I1, u'', 5, None, u'') + >>> components.unregisterUtility(tests.U1(6)) + False + >>> components.queryUtility(tests.I1) + U1(5) + >>> components.unregisterUtility(u5) + Unregistered event: + UtilityRegistration(, I1, u'', 5, None, u'') + True + >>> components.queryUtility(tests.I1) + +You can get the name and utility for all of the utilities that provide +an interface using getUtilitiesFor: + + >>> sorted(components.getUtilitiesFor(tests.I2)) + [(u'', U12(2)), (u'three', U12(3))] + +getAllUtilitiesRegisteredFor is similar to getUtilitiesFor except that +it includes utilities that are overridden. For example, we'll +register a utility that for an extending interface of I2: + + >>> util = tests.U('ext') + >>> components.registerUtility(util, tests.I2e) + Registered event: + UtilityRegistration(, I2e, u'', ext, None, u'') + +We don't get the new utility for getUtilitiesFor: + + >>> sorted(components.getUtilitiesFor(tests.I2)) + [(u'', U12(2)), (u'three', U12(3))] + +but we do get it from getAllUtilitiesRegisteredFor: + + >>> sorted(map(str, components.getAllUtilitiesRegisteredFor(tests.I2))) + ['U(ext)', 'U12(2)', 'U12(3)'] + +Removing a utility also makes it disappear from getUtilitiesFor: + + >>> components.unregisterUtility(util, tests.I2e) + Unregistered event: + UtilityRegistration(, I2e, u'', ext, None, u'') + True + >>> list(components.getAllUtilitiesRegisteredFor(tests.I2e)) + [] + +Adapters +-------- + +You can register adapters with registerAdapter: + + >>> components.registerAdapter(tests.A12_1) + Registered event: + AdapterRegistration(, [I1, I2], IA1, u'', A12_1, u'') + +Here, we didn't specify required interfaces, a provided interface, or +a name. The required interfaces were determined from the factory +s __component_adapts__ attribute and the provided interface was +determined by introspecting what the factory implements. + + >>> components.getMultiAdapter((tests.U1(6), tests.U12(7)), tests.IA1) + A12_1(U1(6), U12(7)) + +If a factory implements more than one interface, an exception will be +raised: + + >>> components.registerAdapter(tests.A1_12) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single + interface and no provided interface was specified. + +Unless the provided interface is specified: + + >>> components.registerAdapter(tests.A1_12, provided=tests.IA2) + Registered event: + AdapterRegistration(, [I1], IA2, u'', A1_12, u'') + +If a factory doesn't declare an implemented interface, an exception will be +raised: + + >>> components.registerAdapter(tests.A12_) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single + interface and no provided interface was specified. + +Unless the provided interface is specified: + + >>> components.registerAdapter(tests.A12_, provided=tests.IA2) + Registered event: + AdapterRegistration(, [I1, I2], IA2, u'', A12_, u'') + +The required interface needs to be specified in the registration if +the factory doesn't have a __component_adapts__ attribute: + + >>> components.registerAdapter(tests.A_2) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't have a __component_adapts__ + attribute and no required specifications were specified + +Unless the required specifications specified: + + >>> components.registerAdapter(tests.A_2, required=[tests.I3]) + Registered event: + AdapterRegistration(, [I3], IA2, u'', A_2, u'') + +Classes can be specified in place of specifications, in which case the +implementedBy specification for the class is used: + + >>> components.registerAdapter(tests.A_3, required=[tests.U], + ... info="Really class specific") + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + AdapterRegistration(, [zope.component.tests.U], IA3, u'', + A_3, 'Really class specific') + +We can see the adapters that have been registered using the +registeredAdapters method: + + >>> for registration in sorted(components.registeredAdapters()): + ... print registration.required + ... print registration.provided, registration.name + ... print registration.factory, registration.info + ... # doctest: +NORMALIZE_WHITESPACE + (, + ) + + zope.component.tests.A12_1 + (, + ) + + zope.component.tests.A12_ + (,) + + zope.component.tests.A1_12 + (,) + + zope.component.tests.A_2 + (,) + + zope.component.tests.A_3 Really class specific + +As with utilities, we can provide registration information when +registering adapters. + +If you try to fetch an adapter that isn't registered, you'll get a +component-lookup error: + + >>> components.getMultiAdapter((tests.U(8), ), tests.IA1) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: ((U(8),), + , u'') + +unless you use queryAdapter: + + >>> components.queryMultiAdapter((tests.U(8), ), tests.IA1) + >>> components.queryMultiAdapter((tests.U(8), ), tests.IA1, default=42) + 42 + +When looking up an adapter for a single object, you can use the +slightly simpler getAdapter and queryAdapter calls: + + >>> components.getAdapter(tests.U1(9), tests.IA2) + A1_12(U1(9)) + + >>> components.queryAdapter(tests.U1(9), tests.IA2) + A1_12(U1(9)) + + >>> components.getAdapter(tests.U(8), tests.IA1) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: (U(8), + , u'') + + >>> components.queryAdapter(tests.U(8), tests.IA2) + >>> components.queryAdapter(tests.U(8), tests.IA2, default=42) + 42 + +You can unregister an adapter. If a factory is provided and if the +rewuired and provided interfaces, can be infered, then they need not +be provided: + + >>> components.unregisterAdapter(tests.A12_1) + Unregistered event: + AdapterRegistration(, [I1, I2], IA1, u'', A12_1, u'') + True + + >>> for registration in sorted(components.registeredAdapters()): + ... print registration.required + ... print registration.provided, registration.name + ... print registration.factory, registration.info + ... # doctest: +NORMALIZE_WHITESPACE + (, + ) + + zope.component.tests.A12_ + (,) + + zope.component.tests.A1_12 + (,) + + zope.component.tests.A_2 + (,) + + zope.component.tests.A_3 Really class specific + +A boolean is returned indicating whether a change was made. + +If a factory implements more than one interface, an exception will be +raised: + + >>> components.unregisterAdapter(tests.A1_12) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single + interface and no provided interface was specified. + +Unless the provided interface is specified: + + >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2) + Unregistered event: + AdapterRegistration(, [I1], IA2, u'', A1_12, u'') + True + +If a factory doesn't declare an implemented interface, an exception will be +raised: + + >>> components.unregisterAdapter(tests.A12_) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single + interface and no provided interface was specified. + +Unless the provided interface is specified: + + >>> components.unregisterAdapter(tests.A12_, provided=tests.IA2) + Unregistered event: + AdapterRegistration(, [I1, I2], IA2, u'', A12_, u'') + True + +The required interface needs to be specified if the factory doesn't +have a __component_adapts__ attribute: + + >>> components.unregisterAdapter(tests.A_2) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't have a __component_adapts__ + attribute and no required specifications were specified + + >>> components.unregisterAdapter(tests.A_2, required=[tests.I3]) + Unregistered event: + AdapterRegistration(, [I3], IA2, u'', A_2, u'') + True + + >>> for registration in sorted(components.registeredAdapters()): + ... print registration.required + ... print registration.provided, registration.name + ... print registration.factory, registration.info + ... # doctest: +NORMALIZE_WHITESPACE + (,) + + zope.component.tests.A_3 Really class specific + +If a factory is unregistered that is not registered, False is +returned: + + + >>> components.unregisterAdapter(tests.A_2, required=[tests.I3]) + False + >>> components.unregisterAdapter(tests.A12_1, required=[tests.U]) + False + +The factory can be omitted, to unregister *any* factory that matches +specified required and provided interfaces: + + >>> components.unregisterAdapter(required=[tests.U], provided=tests.IA3) + ... # doctest: +NORMALIZE_WHITESPACE + Unregistered event: + AdapterRegistration(, [zope.component.tests.U], + IA3, u'', A_3, 'Really class specific') + True + + >>> for registration in sorted(components.registeredAdapters()): + ... print registration + +Adapters can be named: + + >>> components.registerAdapter(tests.A1_12, provided=tests.IA2, + ... name=u'test') + Registered event: + AdapterRegistration(, [I1], IA2, u'test', A1_12, u'') + + >>> components.queryMultiAdapter((tests.U1(9), ), tests.IA2) + >>> components.queryMultiAdapter((tests.U1(9), ), tests.IA2, name=u'test') + A1_12(U1(9)) + + >>> components.queryAdapter(tests.U1(9), tests.IA2) + >>> components.queryAdapter(tests.U1(9), tests.IA2, name=u'test') + A1_12(U1(9)) + >>> components.getAdapter(tests.U1(9), tests.IA2, name=u'test') + A1_12(U1(9)) + +It is possible to look up all of the adapters that provide an +interface: + + >>> components.registerAdapter(tests.A1_23, provided=tests.IA2, + ... name=u'test 2') + Registered event: + AdapterRegistration(, [I1], IA2, u'test 2', A1_23, u'') + + >>> components.registerAdapter(tests.A1_12, provided=tests.IA2) + Registered event: + AdapterRegistration(, [I1], IA2, u'', A1_12, u'') + + >>> for name, adapter in sorted(components.getAdapters((tests.U1(9), ), + ... tests.IA2)): + ... print name, adapter + A1_12(U1(9)) + test A1_12(U1(9)) + test 2 A1_23(U1(9)) + + +getAdapters is most commonly used as the basis of menu systems. + +If an adapter factory returns None, it is equivalent to there being no +factory: + + >>> components.registerAdapter(tests.noop, + ... required=[tests.IA1], provided=tests.IA2, + ... name=u'test noop') + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + AdapterRegistration(, [IA1], IA2, u'test noop', + noop, u'') + >>> components.queryAdapter(tests.U1(9), tests.IA2, name=u'test noop') + + >>> components.registerAdapter(tests.A1_12, provided=tests.IA2) + Registered event: + AdapterRegistration(, [I1], IA2, u'', A1_12, u'') + + >>> for name, adapter in sorted(components.getAdapters((tests.U1(9), ), + ... tests.IA2)): + ... print name, adapter + A1_12(U1(9)) + test A1_12(U1(9)) + test 2 A1_23(U1(9)) + + + >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2, + ... name=u'test') + Unregistered event: + AdapterRegistration(, [I1], IA2, u'test', A1_12, u'') + True + >>> components.unregisterAdapter(tests.A1_12, provided=tests.IA2) + Unregistered event: + AdapterRegistration(, [I1], IA2, u'', A1_12, u'') + True + >>> for registration in sorted(components.registeredAdapters()): + ... print registration.required + ... print registration.provided, registration.name + ... print registration.factory, registration.info + ... # doctest: +NORMALIZE_WHITESPACE + (,) + test 2 + zope.component.tests.A1_23 + (,) + test noop + + + +Subscribers +----------- + +Subscribers provide a way to get multiple adapters of a given type. +In this regard, subscribers are like named adapters, except that there +isn't any concept of the most specific adapter for a given name. + +Subscribers are registered by calling registerSubscriptionAdapter: + + >>> components.registerSubscriptionAdapter(tests.A1_2) + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + SubscriptionRegistration(, [I1], IA2, u'', A1_2, u'') + + >>> components.registerSubscriptionAdapter( + ... tests.A1_12, provided=tests.IA2) + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + SubscriptionRegistration(, [I1], IA2, u'', A1_12, u'') + + >>> components.registerSubscriptionAdapter( + ... tests.A, [tests.I1], tests.IA2, + ... info='a sample comment') + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + SubscriptionRegistration(, [I1], IA2, u'', + A, 'a sample comment') + +The same rules, with regard to when required and provided interfaces +have to be specified apply as with adapters: + + >>> components.registerSubscriptionAdapter(tests.A1_12) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single + interface and no provided interface was specified. + + >>> components.registerSubscriptionAdapter(tests.A) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single interface and + no provided interface was specified. + + >>> components.registerSubscriptionAdapter(tests.A, required=[tests.IA1]) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single interface + and no provided interface was specified. + +Note that we provided the info argument as a keyword argument above. +That's because there is a name argument that's reserved for future +use. We can give a name, as long as it is an empty string: + + >>> components.registerSubscriptionAdapter( + ... tests.A, [tests.I1], tests.IA2, u'', 'a sample comment') + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + SubscriptionRegistration(, [I1], IA2, u'', + A, 'a sample comment') + + >>> components.registerSubscriptionAdapter( + ... tests.A, [tests.I1], tests.IA2, u'oops', 'a sample comment') + Traceback (most recent call last): + ... + TypeError: Named subscribers are not yet supported + +Subscribers are looked up using the subscribers method: + + >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): + ... print s + A1_2(U1(1)) + A1_12(U1(1)) + A(U1(1),) + A(U1(1),) + +Note that, because we created multiple subscriptions for A, we got multiple +subscriber instances. + +As with normal adapters, if a factory returns None, the result is skipped: + + >>> components.registerSubscriptionAdapter( + ... tests.noop, [tests.I1], tests.IA2) + Registered event: + SubscriptionRegistration(, [I1], IA2, u'', noop, u'') + + >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): + ... print s + A1_2(U1(1)) + A1_12(U1(1)) + A(U1(1),) + A(U1(1),) + +We can get registration information for subscriptions: + + >>> for registration in sorted( + ... components.registeredSubscriptionAdapters()): + ... print registration.required + ... print registration.provided, registration.name + ... print registration.factory, registration.info + (,) + + zope.component.tests.A a sample comment + (,) + + zope.component.tests.A a sample comment + (,) + + zope.component.tests.A1_12 + (,) + + zope.component.tests.A1_2 + (,) + + + +We can also unregister subscriptions in much the same way we can for adapters: + + >>> components.unregisterSubscriptionAdapter(tests.A1_2) + ... # doctest: +NORMALIZE_WHITESPACE + Unregistered event: + SubscriptionRegistration(, [I1], IA2, u'', A1_2, '') + True + + >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): + ... print s + A1_12(U1(1)) + A(U1(1),) + A(U1(1),) + + >>> for registration in sorted( + ... components.registeredSubscriptionAdapters()): + ... print registration.required + ... print registration.provided, registration.name + ... print registration.factory, registration.info + (,) + + zope.component.tests.A a sample comment + (,) + + zope.component.tests.A a sample comment + (,) + + zope.component.tests.A1_12 + (,) + + + + >>> components.unregisterSubscriptionAdapter( + ... tests.A, [tests.I1], tests.IA2) + Unregistered event: + SubscriptionRegistration(, [I1], IA2, u'', A, '') + True + + >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): + ... print s + A1_12(U1(1)) + + >>> for registration in sorted( + ... components.registeredSubscriptionAdapters()): + ... print registration.required + ... print registration.provided, registration.name + ... print registration.factory, registration.info + (,) + + zope.component.tests.A1_12 + (,) + + + +Note here that both registrations for A were removed. + +If we omit the factory, we must specify the required and provided interfaces: + + >>> components.unregisterSubscriptionAdapter(required=[tests.I1]) + Traceback (most recent call last): + ... + TypeError: Must specify one of factory and provided + + >>> components.unregisterSubscriptionAdapter(provided=tests.IA2) + Traceback (most recent call last): + ... + TypeError: Must specify one of factory and required + + >>> components.unregisterSubscriptionAdapter( + ... required=[tests.I1], provided=tests.IA2) + Unregistered event: + SubscriptionRegistration(, [I1], IA2, u'', None, '') + True + + >>> for s in components.subscribers((tests.U1(1), ), tests.IA2): + ... print s + + >>> for registration in sorted( + ... components.registeredSubscriptionAdapters()): + ... print registration.factory + +As when registering, an error is raised if the registration +information can't be determined from the factory and isn't specified: + + >>> components.unregisterSubscriptionAdapter(tests.A1_12) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single + interface and no provided interface was specified. + + >>> components.unregisterSubscriptionAdapter(tests.A) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single interface and + no provided interface was specified. + + >>> components.unregisterSubscriptionAdapter(tests.A, required=[tests.IA1]) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't implement a single interface + and no provided interface was specified. + +If you unregister something that's not registered, nothing will be +changed and False will be returned: + + + >>> components.unregisterSubscriptionAdapter( + ... required=[tests.I1], provided=tests.IA2) + False + +Handlers +-------- + +Handlers are used when you want to perform some function in response +to an event. Handlers aren't expected to return anything when called +and are not registered to provide any interface. + + >>> from zope import component + >>> @component.adapter(tests.I1) + ... def handle1(x): + ... print 'handle1', x + + >>> components.registerHandler(handle1, info="First handler") + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + HandlerRegistration(, [I1], u'', + handle1, 'First handler') + >>> components.handle(tests.U1(1)) + handle1 U1(1) + + >>> @component.adapter(tests.I1, tests.I2) + ... def handle12(x, y): + ... print 'handle12', x, y + + >>> components.registerHandler(handle12) + Registered event: + HandlerRegistration(, [I1, I2], u'', handle12, u'') + >>> components.handle(tests.U1(1), tests.U12(2)) + handle12 U1(1) U12(2) + +If a handler doesn't document interfaces it handles, then +the required interfaces must be specified: + + >>> def handle(*objects): + ... print 'handle', objects + + >>> components.registerHandler(handle) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't have a __component_adapts__ + attribute and no required specifications were specified + + >>> components.registerHandler(handle, required=[tests.I1], + ... info="a comment") + Registered event: + HandlerRegistration(, [I1], u'', handle, 'a comment') + +Handlers can also be registered for classes: + + >>> components.registerHandler(handle, required=[tests.U], + ... info="handle a class") + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + HandlerRegistration(, [zope.component.tests.U], u'', + handle, 'handle a class') + + + >>> components.handle(tests.U1(1)) + handle (U1(1),) + handle1 U1(1) + handle (U1(1),) + +We can list the handler registrations: + + >>> for registration in components.registeredHandlers(): + ... print registration.required + ... print registration.handler, registration.info + ... # doctest: +NORMALIZE_WHITESPACE + (,) + First handler + (, + ) + + (,) + a comment + (,) + handle a class + +and we can unregister handlers: + + >>> components.unregisterHandler(required=[tests.U]) + ... # doctest: +NORMALIZE_WHITESPACE + Unregistered event: + HandlerRegistration(, [zope.component.tests.U], u'', + None, '') + True + + >>> for registration in components.registeredHandlers(): + ... print registration.required + ... print registration.handler, registration.info + ... # doctest: +NORMALIZE_WHITESPACE + (,) + First handler + (, + ) + + (,) + a comment + + >>> components.unregisterHandler(handle12) + Unregistered event: + HandlerRegistration(, [I1, I2], u'', handle12, '') + True + + >>> for registration in components.registeredHandlers(): + ... print registration.required + ... print registration.handler, registration.info + (,) + First handler + (,) + a comment + + >>> components.unregisterHandler(handle12) + False + + >>> components.unregisterHandler() + Traceback (most recent call last): + ... + TypeError: Must specify one of factory and required + + >>> components.registerHandler(handle) + ... # doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: The adapter factory doesn't have a __component_adapts__ + attribute and no required specifications were specified + +Extending +--------- + +Component-management objects can extend other component-management +objects. + + >>> c1 = registry.Components('1') + >>> c1.__bases__ + () + + >>> c2 = registry.Components('2', (c1, )) + >>> c2.__bases__ == (c1, ) + True + + >>> c1.registerUtility(tests.U1(1)) + Registered event: + UtilityRegistration(, I1, u'', 1, None, u'') + + >>> c1.queryUtility(tests.I1) + U1(1) + >>> c2.queryUtility(tests.I1) + U1(1) + >>> c1.registerUtility(tests.U1(2)) + Unregistered event: + UtilityRegistration(, I1, u'', 1, None, u'') + Registered event: + UtilityRegistration(, I1, u'', 2, None, u'') + + >>> c2.queryUtility(tests.I1) + U1(2) + +We can use multiple inheritence: + + >>> c3 = registry.Components('3', (c1, )) + >>> c4 = registry.Components('4', (c2, c3)) + >>> c4.queryUtility(tests.I1) + U1(2) + + >>> c1.registerUtility(tests.U12(1), tests.I2) + Registered event: + UtilityRegistration(, I2, u'', 1, None, u'') + + >>> c4.queryUtility(tests.I2) + U12(1) + + >>> c3.registerUtility(tests.U12(3), tests.I2) + Registered event: + UtilityRegistration(, I2, u'', 3, None, u'') + >>> c4.queryUtility(tests.I2) + U12(3) + + >>> c1.registerHandler(handle1, info="First handler") + Registered event: + HandlerRegistration(, [I1], u'', handle1, 'First handler') + + >>> c2.registerHandler(handle, required=[tests.U]) + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + HandlerRegistration(, [zope.component.tests.U], u'', + handle, u'') + + >>> @component.adapter(tests.I1) + ... def handle3(x): + ... print 'handle3', x + >>> c3.registerHandler(handle3) + Registered event: + HandlerRegistration(, [I1], u'', handle3, u'') + + >>> @component.adapter(tests.I1) + ... def handle4(x): + ... print 'handle4', x + >>> c4.registerHandler(handle4) + Registered event: + HandlerRegistration(, [I1], u'', handle4, u'') + + >>> c4.handle(tests.U1(1)) + handle1 U1(1) + handle3 U1(1) + handle (U1(1),) + handle4 U1(1) + +Redispatch of registration events +--------------------------------- + +Some handlers are available that, if registered, redispatch +registration events to the objects being registered. They depend on +being dispatched to by the object-event dispatcher: + + >>> from zope import component + >>> import zope.component.event + >>> zope.component.getGlobalSiteManager().registerHandler( + ... zope.component.event.objectEventNotify) + ... # doctest: +NORMALIZE_WHITESPACE + Registered event: + HandlerRegistration(, + [IObjectEvent], u'', objectEventNotify, u'') + +To see this, we'll first register a multi-handler to show is when +handlers are called on 2 objects: + + >>> @zope.component.adapter(None, None) + ... def double_handler(o1, o2): + ... print 'Double dispatch:' + ... print ' ', o1 + ... print ' ', o2 + >>> zope.component.getGlobalSiteManager().registerHandler(double_handler) + ... # doctest: +NORMALIZE_WHITESPACE + Double dispatch: + HandlerRegistration(, + [Interface, Interface], u'', double_handler, u'') + Registered event: + HandlerRegistration(, + [Interface, Interface], u'', double_handler, u'') + Registered event: + HandlerRegistration(, + [Interface, Interface], u'', double_handler, u'') + +In the example above, the double_handler reported it's own registration. :) + +Now we'll register our handlers: + + >>> zope.component.getGlobalSiteManager().registerHandler( + ... registry.dispatchUtilityRegistrationEvent) + ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + Double dispatch: + ... + + >>> zope.component.getGlobalSiteManager().registerHandler( + ... registry.dispatchAdapterRegistrationEvent) + ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + Double dispatch: + ... + + >>> zope.component.getGlobalSiteManager().registerHandler( + ... registry.dispatchSubscriptionAdapterRegistrationEvent) + ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + Double dispatch: + ... + + >>> zope.component.getGlobalSiteManager().registerHandler( + ... registry.dispatchHandlerRegistrationEvent) + ... # doctest: +NORMALIZE_WHITESPACE + Double dispatch: + HandlerRegistration(, + [IHandlerRegistration, IRegistrationEvent], u'', + dispatchHandlerRegistrationEvent, u'') + Registered event: + HandlerRegistration(, + [IHandlerRegistration, IRegistrationEvent], u'', + dispatchHandlerRegistrationEvent, u'') + Double dispatch: + + Registered event: + HandlerRegistration(, + [IHandlerRegistration, IRegistrationEvent], u'', + dispatchHandlerRegistrationEvent, u'') + Registered event: + HandlerRegistration(, + [IHandlerRegistration, IRegistrationEvent], u'', + dispatchHandlerRegistrationEvent, u'') + +In the last example above, we can see that the registration of +dispatchHandlerRegistrationEvent was handled by +dispatchHandlerRegistrationEvent and redispatched. This can be seen +in the second double-dispatch output, where the first argument is the +object being registered, which is dispatchHandlerRegistrationEvent. + +If we change some other registrations, we can the double dispatch +taking place: + + >>> components.registerUtility(u5) + ... # doctest: +NORMALIZE_WHITESPACE + Double dispatch: + UtilityRegistration(, I1, u'', 5, None, u'') + Registered event: + UtilityRegistration(, I1, u'', 5, None, u'') + Double dispatch: + U1(5) + Registered event: + UtilityRegistration(, I1, u'', 5, None, u'') + Registered event: + UtilityRegistration(, I1, u'', 5, None, u'') + + >>> components.registerAdapter(tests.A12_1) + ... # doctest: +NORMALIZE_WHITESPACE + Double dispatch: + AdapterRegistration(, [I1, I2], IA1, u'', A12_1, u'') + Registered event: + AdapterRegistration(, [I1, I2], IA1, u'', A12_1, u'') + Double dispatch: + zope.component.tests.A12_1 + Registered event: + AdapterRegistration(, [I1, I2], IA1, u'', A12_1, u'') + Registered event: + AdapterRegistration(, [I1, I2], IA1, u'', A12_1, u'') + + >>> components.registerSubscriptionAdapter(tests.A1_2) + ... # doctest: +NORMALIZE_WHITESPACE + Double dispatch: + SubscriptionRegistration(, [I1], IA2, u'', A1_2, u'') + Registered event: + SubscriptionRegistration(, [I1], IA2, u'', A1_2, u'') + Double dispatch: + zope.component.tests.A1_2 + Registered event: + SubscriptionRegistration(, [I1], IA2, u'', A1_2, u'') + Registered event: + SubscriptionRegistration(, [I1], IA2, u'', A1_2, u'') diff -Nru zope3-3.4.0/src/zope/component/security.py zope3-3.5~bzr18/src/zope/component/security.py --- zope3-3.4.0/src/zope/component/security.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/security.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,175 @@ +############################################################################## +# +# Copyright (c) 2005 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.security support for the configuration handlers + +$Id$ +""" +__docformat__ = "reStructuredText" + +from zope.interface import providedBy +from zope.proxy import ProxyBase, getProxiedObject +from zope.security.adapter import LocatingTrustedAdapterFactory, \ + LocatingUntrustedAdapterFactory, TrustedAdapterFactory +from zope.security.checker import Checker, CheckerPublic, InterfaceChecker +from zope.security.proxy import Proxy + + +PublicPermission = 'zope.Public' + +class PermissionProxy(ProxyBase): + + __slots__ = ('__Security_checker__', ) + + def __providedBy__(self): + return providedBy(getProxiedObject(self)) + __providedBy__ = property(__providedBy__) + +def _checker(_context, permission, allowed_interface, allowed_attributes): + if (not allowed_attributes) and (not allowed_interface): + allowed_attributes = ["__call__"] + + if permission == PublicPermission: + permission = CheckerPublic + + require={} + if allowed_attributes: + for name in allowed_attributes: + require[name] = permission + if allowed_interface: + for i in allowed_interface: + for name in i.names(all=True): + require[name] = permission + + checker = Checker(require) + return checker + +def proxify(ob, checker=None, provides=None, permission=None): + """Try to get the object proxied with the `checker`, but not too soon + + We really don't want to proxy the object unless we need to. + """ + + if checker is None: + if provides is None or permission is None: + raise ValueError, 'Required arguments: checker or both provides and permissions' + if permission == PublicPermission: + permission = CheckerPublic + checker = InterfaceChecker(provides, permission) + ob = PermissionProxy(ob) + ob.__Security_checker__ = checker + return ob + +def protectedFactory(original_factory, provides, permission): + if permission == PublicPermission: + permission = CheckerPublic + checker = InterfaceChecker(provides, permission) + # This has to be named 'factory', aparently, so as not to confuse apidoc :( + def factory(*args): + ob = original_factory(*args) + try: + ob.__Security_checker__ = checker + except AttributeError: + ob = Proxy(ob, checker) + return ob + factory.factory = original_factory + return factory + +def securityAdapterFactory(factory, permission, locate, trusted): + """ + If a permission is provided when wrapping the adapter, it will be + wrapped in a LocatingAdapterFactory. + + >>> class Factory: + ... pass + + If both locate and trusted are False and a non-public + permission is provided, then the factory is wrapped into a + LocatingUntrustedAdapterFactory: + + >>> factory = securityAdapterFactory(Factory, 'zope.AnotherPermission', + ... locate=False, trusted=False) + + >>> isinstance(factory, LocatingUntrustedAdapterFactory) + True + + If a PublicPermission is provided, then the factory is not touched. + + >>> factory = securityAdapterFactory(Factory, PublicPermission, + ... locate=False, trusted=False) + + >>> factory is Factory + True + + Same for CheckerPublic: + + >>> factory = securityAdapterFactory(Factory, CheckerPublic, + ... locate=False, trusted=False) + + >>> factory is Factory + True + + If the permission is None, the factory isn't touched: + + >>> factory = securityAdapterFactory(Factory, None, + ... locate=False, trusted=False) + + >>> factory is Factory + True + + If the factory is trusted and a no permission is provided then the + adapter is wrapped into a TrustedAdapterFactory: + + >>> factory = securityAdapterFactory(Factory, None, + ... locate=False, trusted=True) + + >>> isinstance(factory, TrustedAdapterFactory) + True + + Same for PublicPermission: + + >>> factory = securityAdapterFactory(Factory, PublicPermission, + ... locate=False, trusted=True) + + >>> isinstance(factory, TrustedAdapterFactory) + True + + Same for CheckerPublic: + + >>> factory = securityAdapterFactory(Factory, CheckerPublic, + ... locate=False, trusted=True) + + >>> isinstance(factory, TrustedAdapterFactory) + True + + If the factory is trusted and a locate is true, then the + adapter is wrapped into a LocatingTrustedAdapterFactory: + + >>> factory = securityAdapterFactory(Factory, 'zope.AnotherPermission', + ... locate=True, trusted=True) + + >>> isinstance(factory, LocatingTrustedAdapterFactory) + True + + """ + if permission == PublicPermission: + permission = CheckerPublic + if locate or (permission is not None and permission is not CheckerPublic): + if trusted: + return LocatingTrustedAdapterFactory(factory) + else: + return LocatingUntrustedAdapterFactory(factory) + elif trusted: + return TrustedAdapterFactory(factory) + else: + return factory diff -Nru zope3-3.4.0/src/zope/component/socketexample.txt zope3-3.5~bzr18/src/zope/component/socketexample.txt --- zope3-3.4.0/src/zope/component/socketexample.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/socketexample.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,597 @@ +The Zope 3 Component Architecture (Socket Example) +================================================== + +The component architecture provides an application framework that provides its +functionality through loosely-connected components. A *component* can be any +Python object and has a particular purpose associated with it. Thus, in a +component-based applications you have many small component in contrast to +classical object-oriented development, where you have a few big objects. + +Components communicate via specific APIs, which are formally defined by +interfaces, which are provided by the `zope.interface` package. *Interfaces* +describe the methods and properties that a component is expected to +provide. They are also used as a primary mean to provide developer-level +documentation for the components. For more details about interfaces see +`zope/interface/README.txt`. + +The two main types of components are *adapters* and *utilities*. They will be +discussed in detail later in this document. Both component types are managed +by the *site manager*, with which you can register and access these +components. However, most of the site manager's functionality is hidden behind +the component architecture's public API, which is documented in +`IComponentArchitecture`. + + +Adapters +-------- + +Adapters are a well-established pattern. An *adapter* uses an object providing +one interface to produce an object that provides another interface. Here an +example: Imagine that you purchased an electric shaver in the US, and thus +you require the US socket type. You are now traveling in Germany, where another +socket style is used. You will need a device, an adapter, that converts from +the German to the US socket style. + +The functionality of adapters is actually natively provided by the +`zope.interface` package and is thus well documented there. The `human.txt` +file provides a gentle introduction to adapters, whereby `adapter.txt` is +aimed at providing a comprehensive insight into adapters, but is too abstract +for many as an initial read. Thus, we will only explain adapters in the context +of the component architecture's API. + +So let's say that we have a German socket + + >>> from zope.interface import Interface, implements + + >>> class IGermanSocket(Interface): + ... pass + + >>> class Socket(object): + ... def __repr__(self): + ... return '' %self.__class__.__name__ + + >>> class GermanSocket(Socket): + ... """German wall socket.""" + ... implements(IGermanSocket) + +and we want to convert it to an US socket + + >>> class IUSSocket(Interface): + ... pass + +so that our shaver can be used in Germany. So we go to a German electronics +store to look for an adapter that we can plug in the wall: + + >>> class GermanToUSSocketAdapter(Socket): + ... implements(IUSSocket) + ... __used_for__ = IGermanSocket + ... + ... def __init__(self, socket): + ... self.context = socket + +Note that I could have called the passed in socket any way I like, but +`context` is the standard name accepted. + + +Single Adapters +~~~~~~~~~~~~~~~ + +Before we can use the adapter, we have to buy it and make it part of our +inventory. In the component architecture we do this by registering the adapter +with the framework, more specifically with the global site manager: + + >>> import zope.component + >>> gsm = zope.component.getGlobalSiteManager() + >>> gsm.registerAdapter(GermanToUSSocketAdapter, (IGermanSocket,), IUSSocket) + +`zope.component` is the component architecture API that is being +presented by this file. You registered an adapter from `IGermanSocket` +to `IUSSocket` having no name (thus the empty string). + +Anyways, you finally get back to your hotel room and shave, since you have not +been able to shave in the plane. In the bathroom you discover a socket: + + >>> bathroomDE = GermanSocket() + >>> IGermanSocket.providedBy(bathroomDE) + True + +You now insert the adapter in the German socket + + >>> bathroomUS = zope.component.getAdapter(bathroomDE, IUSSocket, '') + +so that the socket now provides the US version: + + >>> IUSSocket.providedBy(bathroomUS) + True + +Now you can insert your shaver and get on with your day. + +After a week you travel for a couple of days to the Prague and you notice that +the Czech have yet another socket type: + + >>> class ICzechSocket(Interface): + ... pass + + >>> class CzechSocket(Socket): + ... implements(ICzechSocket) + + >>> czech = CzechSocket() + +You try to find an adapter for your shaver in your bag, but you fail, since +you do not have one: + + >>> zope.component.getAdapter(czech, IUSSocket, '') \ + ... #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: (, + , + '') + +or the more graceful way: + + >>> marker = object() + >>> socket = zope.component.queryAdapter(czech, IUSSocket, '', marker) + >>> socket is marker + True + +In the component architecture API any `get*` method will fail with a specific +exception, if a query failed, whereby methods starting with `query*` will +always return a `default` value after a failure. + + +Named Adapters +~~~~~~~~~~~~~~ + +You are finally back in Germany. You also brought your DVD player and a couple +DVDs with you, which you would like to watch. Your shaver was able to convert +automatically from 110 volts to 240 volts, but your DVD player cannot. So you +have to buy another adapter that also handles converting the voltage and the +frequency of the AC current: + + >>> class GermanToUSSocketAdapterAndTransformer(object): + ... implements(IUSSocket) + ... __used_for__ = IGermanSocket + ... + ... def __init__(self, socket): + ... self.context = socket + +Now, we need a way to keep the two adapters apart. Thus we register them with +a name: + + >>> gsm.registerAdapter(GermanToUSSocketAdapter, + ... (IGermanSocket,), IUSSocket, 'shaver',) + >>> gsm.registerAdapter(GermanToUSSocketAdapterAndTransformer, + ... (IGermanSocket,), IUSSocket, 'dvd') + +Now we simply look up the adapters using their labels (called *name*): + + >>> socket = zope.component.getAdapter(bathroomDE, IUSSocket, 'shaver') + >>> socket.__class__ is GermanToUSSocketAdapter + True + + >>> socket = zope.component.getAdapter(bathroomDE, IUSSocket, 'dvd') + >>> socket.__class__ is GermanToUSSocketAdapterAndTransformer + True + +Clearly, we do not have an adapter for the MP3 player + + >>> zope.component.getAdapter(bathroomDE, IUSSocket, 'mp3') \ + ... #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: (, + , + 'mp3') + +but you could use the 'dvd' adapter in this case of course. ;) + +Sometimes you want to know all adapters that are available. Let's say you want +to know about all the adapters that convert a German to a US socket type: + + >>> sockets = list(zope.component.getAdapters((bathroomDE,), IUSSocket)) + >>> len(sockets) + 3 + >>> names = [name for name, socket in sockets] + >>> names.sort() + >>> names + [u'', u'dvd', u'shaver'] + +`zope.component.getAdapters()` returns a list of tuples. The first +entry of the tuple is the name of the adapter and the second is the +adapter itself. + + +Multi-Adapters +~~~~~~~~~~~~~~ + +After watching all the DVDs you brought at least twice, you get tired of them +and you want to listen to some music using your MP3 player. But darn, the MP3 +player plug has a ground pin and all the adapters you have do not support +that: + + >>> class IUSGroundedSocket(IUSSocket): + ... pass + +So you go out another time to buy an adapter. This time, however, you do not +buy yet another adapter, but a piece that provides the grounding plug: + + >>> class IGrounder(Interface): + ... pass + + >>> class Grounder(object): + ... implements(IGrounder) + ... def __repr__(self): + ... return '' + + +Then together they will provided a grounded us socket: + + >>> class GroundedGermanToUSSocketAdapter(object): + ... implements(IUSGroundedSocket) + ... __used_for__ = (IGermanSocket, IGrounder) + ... def __init__(self, socket, grounder): + ... self.socket, self.grounder = socket, grounder + +You now register the combination, so that you know you can create a +`IUSGroundedSocket`: + + >>> gsm.registerAdapter(GroundedGermanToUSSocketAdapter, + ... (IGermanSocket, IGrounder), IUSGroundedSocket, 'mp3') + +Given the grounder + + >>> grounder = Grounder() + +and a German socket + + >>> livingroom = GermanSocket() + +we can now get a grounded US socket: + + >>> socket = zope.component.getMultiAdapter((livingroom, grounder), + ... IUSGroundedSocket, 'mp3') + + >>> socket.__class__ is GroundedGermanToUSSocketAdapter + True + >>> socket.socket is livingroom + True + >>> socket.grounder is grounder + True + +Of course, you do not have a 'dvd' grounded US socket available: + + >>> zope.component.getMultiAdapter((livingroom, grounder), + ... IUSGroundedSocket, 'dvd') \ + ... #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: ((, + ), + , + 'dvd') + + + >>> socket = zope.component.queryMultiAdapter( + ... (livingroom, grounder), IUSGroundedSocket, 'dvd', marker) + >>> socket is marker + True + +Again, you might want to read `adapter.txt` in `zope.interface` for a more +comprehensive coverage of multi-adapters. + +Subscribers +----------- + +While subscribers are directly supported by the adapter registry and are +adapters for all theoretical purposes, practically it might be better to think +of them as separate components. Subscribers are particularly useful for +events. + +Let's say one of our adapters overheated and caused a small fire: + + >>> class IFire(Interface): + ... pass + + >>> class Fire(object): + ... implements(IFire) + + >>> fire = Fire() + +We want to use all available objects to put out the fire: + + >>> class IFireExtinguisher(Interface): + ... def extinguish(): + ... pass + + >>> class FireExtinguisher(object): + ... def __init__(self, fire): + ... pass + ... def extinguish(self): + ... "Place extinguish code here." + ... print 'Used ' + self.__class__.__name__ + '.' + +Here some specific methods to put out the fire: + + >>> class PowderExtinguisher(FireExtinguisher): + ... pass + >>> gsm.registerSubscriptionAdapter(PowderExtinguisher, + ... (IFire,), IFireExtinguisher) + + >>> class Blanket(FireExtinguisher): + ... pass + >>> gsm.registerSubscriptionAdapter(Blanket, (IFire,), IFireExtinguisher) + + >>> class SprinklerSystem(FireExtinguisher): + ... pass + >>> gsm.registerSubscriptionAdapter(SprinklerSystem, + ... (IFire,), IFireExtinguisher) + +Now let use all these things to put out the fire: + + >>> extinguishers = zope.component.subscribers((fire,), IFireExtinguisher) + >>> extinguishers.sort() + >>> for extinguisher in extinguishers: + ... extinguisher.extinguish() + Used Blanket. + Used PowderExtinguisher. + Used SprinklerSystem. + +If no subscribers are found for a particular object, then an empty list is +returned: + + >>> zope.component.subscribers((object(),), IFireExtinguisher) + [] + + +Utilities +--------- + +Utilities are the second type of component, the component architecture +implements. *Utilities* are simply components that provide an interface. When +you register an utility, you always register an instance (in contrast to a +factory for adapters) since the initialization and setup process of a utility +might be complex and is not well defined. In some ways a utility is much more +fundamental than an adapter, because an adapter cannot be used without another +component, but a utility is always self-contained. I like to think of +utilities as the foundation of your application and adapters as components +extending beyond this foundation. + +Back to our story... + +After your vacation is over you fly back home to Tampa, Florida. But it is +August now, the middle of the Hurricane season. And, believe it or not, you are +worried that you will not be able to shave when the power goes out for several +days. (You just hate wet shavers.) + +So you decide to go to your favorite hardware store and by a Diesel-powered +electric generator. The generator provides of course a US-style socket: + + >>> class Generator(object): + ... implements(IUSSocket) + ... def __repr__(self): + ... return '' + + >>> generator = Generator() + +Like for adapters, we now have to add the newly-acquired generator to our +inventory by registering it as a utility: + + >>> gsm.registerUtility(generator, IUSSocket) + +We can now get the utility using + + >>> utility = zope.component.getUtility(IUSSocket) + >>> utility is generator + True + +As you can see, it is very simple to register and retrieve utilities. If a +utility does not exist for a particular interface, such as the German socket, +then the lookup fails + + >>> zope.component.getUtility(IGermanSocket) + Traceback (most recent call last): + ... + ComponentLookupError: (, '') + +or more gracefully when specifying a default value: + + >>> default = object() + >>> utility = zope.component.queryUtility(IGermanSocket, default=default) + >>> utility is default + True + +Note: The only difference between `getUtility()` and `queryUtility()` is the +fact that you can specify a default value for the latter function, so that it +will never cause a `ComponentLookupError`. + + +Named Utilities +~~~~~~~~~~~~~~~ + +It is often desirable to have several utilities providing the same interface +per site. This way you can implement any sort of registry using utilities. For +this reason, utilities -- like adapters -- can be named. + +In the context of our story, we might want to do the following: You really do +not trust gas stations either. What if the roads are blocked after a hurricane +and the gas stations run out of oil. So you look for another renewable power +source. Then you think about solar panels! After a storm there is usually very +nice weather, so why not? Via the Web you order a set of 110V/120W solar +panels that provide a regular US-style socket as output: + + >>> class SolarPanel(object): + ... implements(IUSSocket) + ... def __repr__(self): + ... return '' + + >>> panel = SolarPanel() + +Once it arrives, we add it to our inventory: + + >>> gsm.registerUtility(panel, IUSSocket, 'Solar Panel') + +You can now access the solar panel using + + >>> utility = zope.component.getUtility(IUSSocket, 'Solar Panel') + >>> utility is panel + True + +Of course, if a utility is not available, then the lookup will simply fail + + >>> zope.component.getUtility(IUSSocket, 'Wind Mill') + Traceback (most recent call last): + ... + ComponentLookupError: (, 'Wind Mill') + +or more gracefully when specifying a default value: + + >>> default = object() + >>> utility = zope.component.queryUtility(IUSSocket, 'Wind Mill', + ... default=default) + >>> utility is default + True + +Now you want to look at all the utilities you have for a particular kind. The +following API function will return a list of name/utility pairs: + + >>> utils = list(zope.component.getUtilitiesFor(IUSSocket)) + >>> utils.sort() + >>> utils #doctest: +NORMALIZE_WHITESPACE + [(u'', ), + (u'Solar Panel', )] + +Another method of looking up all utilities is by using +`getAllUtilitiesRegisteredFor(iface)`. This function will return an iterable +of utilities (without names); however, it will also return overridden +utilities. If you are not using multiple site managers, you will not actually +need this method. + + >>> utils = list(zope.component.getAllUtilitiesRegisteredFor(IUSSocket)) + >>> utils.sort() + >>> utils + [, ] + + +Factories +~~~~~~~~~ + +A *factory* is a special kind of utility that exists to create other +components. A factory is always identified by a name. It also provides a title +and description and is able to tell the developer what interfaces the created +object will provide. The advantage of using a factory to create an object +instead of directly instantiating a class or executing any other callable is +that we can refer to the factory by name. As long as the name stays fixed, the +implementation of the callable can be renamed or moved without a breakage in +code. + +Let's say that our solar panel comes in parts and they have to be +assembled. This assembly would be done by a factory, so let's create one for +the solar panel. To do this, we can use a standard implementation of the +`IFactory` interface: + + >>> from zope.component.factory import Factory + >>> factory = Factory(SolarPanel, + ... 'Solar Panel', + ... 'This factory creates a solar panel.') + +Optionally, I could have also specified the interfaces that the created object +will provide, but the factory class is smart enough to determine the +implemented interface from the class. We now register the factory: + + >>> from zope.component.interfaces import IFactory + >>> gsm.registerUtility(factory, IFactory, 'SolarPanel') + +We can now get a list of interfaces the produced object will provide: + + >>> ifaces = zope.component.getFactoryInterfaces('SolarPanel') + >>> IUSSocket in ifaces + True + +By the way, this is equivalent to + + >>> ifaces2 = factory.getInterfaces() + >>> ifaces is ifaces2 + True + +Of course you can also just create an object: + + >>> panel = zope.component.createObject('SolarPanel') + >>> panel.__class__ is SolarPanel + True + +Note: Ignore the first argument (`None`) for now; it is the context of the +utility lookup, which is usually an optional argument, but cannot be in this +case, since all other arguments beside the `name` are passed in as arguments +to the specified callable. + +Once you register several factories + + >>> gsm.registerUtility(Factory(Generator), IFactory, 'Generator') + +you can also determine, which available factories will create objects +providing a certain interface: + + >>> factories = zope.component.getFactoriesFor(IUSSocket) + >>> factories = [(name, factory.__class__) for name, factory in factories] + >>> factories.sort() + >>> factories #doctest: +NORMALIZE_WHITESPACE + [(u'Generator', ), + (u'SolarPanel', )] + + +Site Managers +------------- + +Why do we need site managers? Why is the component architecture API not +sufficient? Some applications, including Zope 3, have a concept of +locations. It is often desirable to have different configurations for these +location; this can be done by overwriting existing or adding new component +registrations. Site managers in locations below the root location, should be +able to delegate requests to their parent locations. The root site manager is +commonly known as *global site manager*, since it is always available. You can +always get the global site manager using the API: + + >>> gsm = zope.component.getGlobalSiteManager() + + >>> from zope.component import globalSiteManager + >>> gsm is globalSiteManager + True + >>> from zope.component.interfaces import IComponentLookup + >>> IComponentLookup.providedBy(gsm) + True + >>> from zope.component.interfaces import IComponents + >>> IComponents.providedBy(gsm) + True + +You can also lookup at site manager in a given context. The only requirement +is that the context can be adapted to a site manager. So let's create a +special site manager: + + >>> from zope.component.globalregistry import BaseGlobalComponents + >>> sm = BaseGlobalComponents() + +Now we create a context that adapts to the site manager via the `__conform__` +method as specified in PEP 246. + + >>> class Context(object): + ... def __init__(self, sm): + ... self.sm = sm + ... def __conform__(self, interface): + ... if interface.isOrExtends(IComponentLookup): + ... return self.sm + +We now instantiate the `Context` with our special site manager: + + >>> context = Context(sm) + >>> context.sm is sm + True + +We can now ask for the site manager of this context: + + >>> lsm = zope.component.getSiteManager(context) + >>> lsm is sm + True + +The site manager instance `lsm` is formally known as a *local site manager* of +`context`. diff -Nru zope3-3.4.0/src/zope/component/standalonetests.py zope3-3.5~bzr18/src/zope/component/standalonetests.py --- zope3-3.4.0/src/zope/component/standalonetests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/standalonetests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,54 @@ +""" +$Id: standalonetests.py 109324 2010-02-22 22:48:31Z sidnei $ +""" +import unittest +import doctest +import sys +import pickle + +if __name__ == "__main__": + sys.path = pickle.loads(sys.stdin.read()) + +from zope import interface +from zope.component.testing import setUp, tearDown + +class I1(interface.Interface): + pass + +class I2(interface.Interface): + pass + +class Ob(object): + interface.implements(I1) + def __repr__(self): + return '' + +ob = Ob() + +class Comp(object): + interface.implements(I2) + def __init__(self, context): + self.context = context + +def providing_adapter_sets_adapter_hook(): + """ + A side effect of importing installs the adapter hook. See + http://www.zope.org/Collectors/Zope3-dev/674. + + >>> import zope.component + >>> zope.component.provideAdapter(Comp, (I1,), I2) + >>> adapter = I2(ob) + >>> adapter.__class__ is Comp + True + >>> adapter.context is ob + True + """ + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite(setUp=setUp, tearDown=tearDown), + )) + +if __name__ == "__main__": + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/component/testfiles/adapter.py zope3-3.5~bzr18/src/zope/component/testfiles/adapter.py --- zope3-3.4.0/src/zope/component/testfiles/adapter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/testfiles/adapter.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,63 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Sample adapter class for testing + +$Id: adapter.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +import zope.interface +import zope.component +import components + +class I1(zope.interface.Interface): + pass + +class I2(zope.interface.Interface): + pass + +class I3(zope.interface.Interface): + def f1(): pass + def f2(): pass + def f3(): pass + +class IS(zope.interface.Interface): + pass + + +class Adapter(object): + def __init__(self, *args): + self.context = args + +class A1(Adapter): + zope.interface.implements(I1) + +class A2(Adapter): + zope.interface.implements(I2) + +class A3(Adapter): + zope.component.adapts(components.IContent, I1, I2) + zope.interface.implements(I3) + +class A4: + pass + +a4 = A4() + +class A5: + zope.interface.implements(I1, I2) + +a5 = A5() + +def Handler(content, *args): + # uninteresting handler + content.args = getattr(content, 'args', ()) + (args, ) diff -Nru zope3-3.4.0/src/zope/component/testfiles/components.py zope3-3.5~bzr18/src/zope/component/testfiles/components.py --- zope3-3.4.0/src/zope/component/testfiles/components.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/testfiles/components.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,60 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Components for testing + +$Id: components.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +from zope.interface import Interface, Attribute, implements +from zope.component import adapts + +class IAppb(Interface): + a = Attribute('test attribute') + def f(): "test func" + +class IApp(IAppb): + pass + +class IApp2(IAppb): + pass + +class IApp3(IAppb): + pass + +class IContent(Interface): pass + +class Content(object): + implements(IContent) + +class Comp(object): + adapts(IContent) + implements(IApp) + + def __init__(self, *args): + # Ignore arguments passed to constructor + pass + + a = 1 + def f(): pass + +class Comp2(object): + def __init__(self, context): + self.context = context + +class Comp3(object): + def __init__(self, context): + self.context = context + +comp = Comp() + +content = Content() diff -Nru zope3-3.4.0/src/zope/component/testfiles/__init__.py zope3-3.5~bzr18/src/zope/component/testfiles/__init__.py --- zope3-3.4.0/src/zope/component/testfiles/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/testfiles/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +#Python package diff -Nru zope3-3.4.0/src/zope/component/testfiles/testlayer.zcml zope3-3.5~bzr18/src/zope/component/testfiles/testlayer.zcml --- zope3-3.4.0/src/zope/component/testfiles/testlayer.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/testfiles/testlayer.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/component/testfiles/views.py zope3-3.5~bzr18/src/zope/component/testfiles/views.py --- zope3-3.4.0/src/zope/component/testfiles/views.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/testfiles/views.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,65 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Views test. + +$Id: views.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +from zope.interface import Interface, implements, directlyProvides + +class Request(object): + + def __init__(self, type): + directlyProvides(self, type) + +class IR(Interface): + pass + +class IV(Interface): + def index(): + pass + +class IC(Interface): pass + +class V1(object): + implements(IV) + + def __init__(self, context, request): + self.context = context + self.request = request + + def index(self): + return 'V1 here' + + def action(self): + return 'done' + +class VZMI(V1): + def index(self): + return 'ZMI here' + +class R1(object): + + def index(self): + return 'R1 here' + + def action(self): + return 'R done' + + def __init__(self, request): + pass + + implements(IV) + +class RZMI(R1): + pass diff -Nru zope3-3.4.0/src/zope/component/testing.py zope3-3.5~bzr18/src/zope/component/testing.py --- zope3-3.4.0/src/zope/component/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Placeless Test Setup + +$Id: testing.py 110537 2010-04-06 03:01:29Z tseaver $ +""" + +# HACK to make sure basicmost event subscriber is installed +import zope.component.event + +# we really don't need special setup now: +from zope.testing.cleanup import CleanUp as PlacelessSetup + +def setUp(test=None): + PlacelessSetup().setUp() + +def tearDown(test=None): + PlacelessSetup().tearDown() diff -Nru zope3-3.4.0/src/zope/component/testlayer.py zope3-3.5~bzr18/src/zope/component/testlayer.py --- zope3-3.4.0/src/zope/component/testlayer.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/testlayer.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,115 @@ +############################################################################## +# +# Copyright (c) 2010 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 os + +from zope.configuration import xmlconfig, config +from zope.testing.cleanup import cleanUp +from zope.component import provideHandler +from zope.component.hooks import setHooks +from zope.component.eventtesting import events, clearEvents + + +class LayerBase(object): + """Sane layer base class. + + zope.testing implements an advanced mechanism so that layer setUp, + tearDown, testSetUp and testTearDown code gets called in the right + order. These methods are supposed to be @classmethods and should + not use super() as the test runner is supposed to take care of that. + + In practice, this mechanism turns out not to be useful and + overcomplicated. It becomes difficult to pass information into + layers (such as a ZCML file to load), because the only way to pass + in information is to subclass, and subclassing these layers leads + to a range of interactions that is hard to reason about. + + We'd rather just use Python and the super mechanism, as we know how + to reason about that. This base class is a hack to make this + possible. + + The hack requires us to set __bases__, __module__ and + __name__. This fools zope.testing into thinking that this layer + instance is a class it can work with. + + It'd be better if zope.testing just called a minimal API and + didn't try to be fancy. Fancy layer inheritance mechanisms can + then be implemented elsewhere if people want to. But unfortunately + it does implement a fancy mechanism and we need to fool it. + """ + + __bases__ = () + + def __init__(self, package, name=None): + if name is None: + name = self.__class__.__name__ + self.__name__ = name + self.__module__ = package.__name__ + self.package = package + + def setUp(self): + pass + + def tearDown(self): + pass + + def testSetUp(self): + pass + + def testTearDown(self): + pass + +class ZCMLLayerBase(LayerBase): + """Base class to load up some ZCML. + """ + def __init__(self, package, name=None, features=None): + super(ZCMLLayerBase, self).__init__(package, name) + self.features = features or [] + + def setUp(self): + setHooks() + context = config.ConfigurationMachine() + xmlconfig.registerCommonDirectives(context) + for feature in self.features: + context.provideFeature(feature) + self.context = self._load_zcml(context) + provideHandler(events.append, (None,)) + + def testTearDown(self): + clearEvents() + + def tearDown(self): + cleanUp() + + def _load_zcml(self, context): + raise NotImplementedError + +class ZCMLFileLayer(ZCMLLayerBase): + """This layer can be used to run tests with a ZCML file loaded. + + The ZCML file is assumed to include sufficient (meta)configuration + so that it can be interpreted itself. I.e. to create a ZCMLLayer + based on another ZCMLLayer's ZCML, just use a ZCML include + statement in your own ZCML to load it. + """ + def __init__(self, package, zcml_file='ftesting.zcml', + name=None, features=None): + super(ZCMLFileLayer, self).__init__(package, name, features) + self.zcml_file = os.path.join(os.path.dirname(package.__file__), + zcml_file) + + def _load_zcml(self, context): + return xmlconfig.file(self.zcml_file, + package=self.package, + context=context, execute=True) diff -Nru zope3-3.4.0/src/zope/component/testlayer.txt zope3-3.5~bzr18/src/zope/component/testlayer.txt --- zope3-3.4.0/src/zope/component/testlayer.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/testlayer.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,100 @@ +Layers +====== + +zope.component.testlayer defines two things: + +* a LayerBase that makes it easier and saner to use zope.testing's + test layers. + +* a ZCMLLayer which lets you implement a layer that loads up some + ZCML. + +LayerBase +--------- + +We check whether our LayerBase can be used to create layers of our +own. We do this simply by subclassing: + + >>> from zope.component.testlayer import LayerBase + >>> class OurLayer(LayerBase): + ... def setUp(self): + ... super(OurLayer, self).setUp() + ... print "setUp called" + ... def tearDown(self): + ... super(OurLayer, self).tearDown() + ... print "tearDown called" + ... def testSetUp(self): + ... super(OurLayer, self).testSetUp() + ... print "testSetUp called" + ... def testTearDown(self): + ... super(OurLayer, self).testTearDown() + ... print "testTearDown called" + +Note that if we wanted to ensure that the methods of the superclass +were called we have to use super(). In this case we actually wouldn't +need to, as these methods do nothing at all, but we just ensure that +they are there in the first place. + +Let's instantiate our layer. We need to supply it with the package the +layer is defined in:: + + >>> import zope.component + >>> layer = OurLayer(zope.component) + +Now we run some tests with this layer: + + >>> import unittest + >>> class TestCase(unittest.TestCase): + ... layer = layer + ... + ... def testFoo(self): + ... print "testFoo" + >>> suite = unittest.TestSuite() + >>> suite.addTest(unittest.makeSuite(TestCase)) + >>> from zope.testing.testrunner.runner import Runner + >>> runner = Runner(args=[], found_suites=[suite]) + >>> succeeded = runner.run() + Running zope.component.OurLayer tests: + Set up zope.component.OurLayer setUp called + in ... seconds. + testSetUp called + testFoo + testTearDown called + Ran 1 tests with 0 failures and 0 errors in ... seconds. + Tearing down left over layers: + Tear down zope.component.OurLayer tearDown called + in ... seconds. + +ZCMLLayer +--------- + +We now want a layer that loads up some ZCML from a file. The default +is ``ftesting.zcml``, but here we'll load a test ``testlayer.zcml``. + + >>> from zope.component.testlayer import ZCMLFileLayer + >>> zcml_file_layer = ZCMLFileLayer( + ... zope.component.testfiles, + ... 'testlayer.zcml') + + >>> class TestCase(unittest.TestCase): + ... layer = zcml_file_layer + ... + ... def testFoo(self): + ... # we should now have the adapter registered + ... from zope import component + ... from zope.component.testfiles import components + ... self.assert_(isinstance( + ... components.IApp2(components.content), components.Comp2)) + +Since the ZCML sets up an adapter, we expect the tests to pass:: + + >>> suite = unittest.TestSuite() + >>> suite.addTest(unittest.makeSuite(TestCase)) + >>> runner = Runner(args=[], found_suites=[suite]) + >>> succeeded = runner.run() + Running zope.component.testfiles.ZCMLFileLayer tests: + Set up zope.component.testfiles.ZCMLFileLayer in ... seconds. + Ran 1 tests with 0 failures and 0 errors in ... seconds. + Tearing down left over layers: + Tear down zope.component.testfiles.ZCMLFileLayer in ... seconds. + diff -Nru zope3-3.4.0/src/zope/component/tests.py zope3-3.5~bzr18/src/zope/component/tests.py --- zope3-3.4.0/src/zope/component/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1745 @@ +############################################################################## +# +# Copyright (c) 2001, 2002, 2009 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. +# +############################################################################## +"""Component Architecture Tests + +$Id: tests.py 111708 2010-04-30 20:33:17Z hannosch $ +""" + +import doctest +import persistent +import re +import sys +import unittest +import transaction +from cStringIO import StringIO + +from zope import interface, component +from zope.interface.verify import verifyObject +from zope.interface.interfaces import IInterface +from zope.testing import renormalizing +from zope.testing.testrunner.layer import UnitTests + +from zope.component.interfaces import ComponentLookupError +from zope.component.interfaces import IComponentArchitecture +from zope.component.interfaces import IComponentLookup +from zope.component.testing import setUp, tearDown, PlacelessSetup +import zope.component.persistentregistry +import zope.component.globalregistry + +from zope.configuration.xmlconfig import XMLConfig, xmlconfig +from zope.configuration.exceptions import ConfigurationError +from zope.security.checker import ProxyFactory + +from zope.component.testfiles.adapter import A1, A2, A3 +from zope.component.testfiles.components import IContent, Content +from zope.component.testfiles.components import IApp +from zope.component.testfiles.views import Request, IC, IV, V1, R1, IR + +# side effect gets component-based event dispatcher installed. +# we should obviously make this more explicit +import zope.component.event + +class I1(interface.Interface): + pass +class I2(interface.Interface): + pass +class I2e(I2): + pass +class I3(interface.Interface): + pass + +class ITestType(IInterface): + pass + +class U: + + def __init__(self, name): + self.__name__ = name + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, self.__name__) + +class U1(U): + interface.implements(I1) + +class U12(U): + interface.implements(I1, I2) + +class IA1(interface.Interface): + pass + +class IA2(interface.Interface): + pass + +class IA3(interface.Interface): + pass + +class A: + + def __init__(self, *context): + self.context = context + + def __repr__(self): + return "%s%r" % (self.__class__.__name__, self.context) + +class A12_1(A): + component.adapts(I1, I2) + interface.implements(IA1) + +class A12_(A): + component.adapts(I1, I2) + +class A_2(A): + interface.implements(IA2) + +class A_3(A): + interface.implements(IA3) + +class A1_12(U): + component.adapts(I1) + interface.implements(IA1, IA2) + +class A1_2(U): + component.adapts(I1) + interface.implements(IA2) + +class A1_23(U): + component.adapts(I1) + interface.implements(IA1, IA3) + +def noop(*args): + pass + +@component.adapter(I1) +def handle1(x): + print 'handle1', x + +def handle(*objects): + print 'handle', objects + +@component.adapter(I1) +def handle3(x): + print 'handle3', x + +@component.adapter(I1) +def handle4(x): + print 'handle4', x + +class Ob(object): + interface.implements(I1) + def __repr__(self): + return '' + + +ob = Ob() + +class Ob2(object): + interface.implements(I2) + def __repr__(self): + return '' + +class Comp(object): + interface.implements(I2) + def __init__(self, context): + self.context = context + +comp = Comp(1) + +class Comp2(object): + interface.implements(I3) + def __init__(self, context): + self.context = context + + +class ConformsToIComponentLookup(object): + """This object allows the sitemanager to conform/adapt to + `IComponentLookup` and thus to itself.""" + + def __init__(self, sitemanager): + self.sitemanager = sitemanager + + def __conform__(self, interface): + """This method is specified by the adapter PEP to do the adaptation.""" + if interface is IComponentLookup: + return self.sitemanager + + +def testInterfaces(): + """Ensure that the component architecture API is provided by + `zope.component`. + + >>> verifyObject(IComponentArchitecture, component) + True + """ + +def test_getGlobalSiteManager(): + """One of the most important functions is to get the global site manager. + + >>> from zope.component.interfaces import IComponentLookup + >>> from zope.component.globalregistry import base + + Get the global site manager via the CA API function: + + >>> gsm = component.getGlobalSiteManager() + + Make sure that the global site manager implements the correct interface + and is the global site manager instance we expect to get. + + >>> IComponentLookup.providedBy(gsm) + True + >>> base is gsm + True + + Finally, ensure that we always get the same global site manager, otherwise + our component registry will always be reset. + + >>> component.getGlobalSiteManager() is gsm + True + """ + +def test_getSiteManager(): + """Make sure that `getSiteManager()` always returns the correct site + manager instance. + + We don't know anything about the default service manager, except that it + is an `IComponentLookup`. + + >>> IComponentLookup.providedBy(component.getSiteManager()) + True + + Calling `getSiteManager()` with no args is equivalent to calling it with a + context of `None`. + + >>> component.getSiteManager() is component.getSiteManager(None) + True + + If the context passed to `getSiteManager()` is not `None`, it is + adapted to `IComponentLookup` and this adapter returned. So, we + create a context that can be adapted to `IComponentLookup` using + the `__conform__` API. + + Let's create the simplest stub-implementation of a site manager possible: + + >>> sitemanager = object() + + Now create a context that knows how to adapt to our newly created site + manager. + + >>> context = ConformsToIComponentLookup(sitemanager) + + Now make sure that the `getSiteManager()` API call returns the correct + site manager. + + >>> component.getSiteManager(context) is sitemanager + True + + Using a context that is not adaptable to `IComponentLookup` should fail. + + >>> component.getSiteManager(ob) #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: ('Could not adapt', , + ) + """ + +def testAdapterInContext(self): + """The `getAdapterInContext()` and `queryAdapterInContext()` API functions + do not only use the site manager to look up the adapter, but first tries + to use the `__conform__()` method of the object to find an adapter as + specified by PEP 246. + + Let's start by creating a component that support's the PEP 246's + `__conform__()` method: + + >>> class Component(object): + ... interface.implements(I1) + ... def __conform__(self, iface, default=None): + ... if iface == I2: + ... return 42 + ... def __repr__(self): + ... return '''''' + + >>> ob = Component() + + We also gave the component a custom representation, so it will be easier + to use in these tests. + + We now have to create a site manager (other than the default global one) + with which we can register adapters for `I1`. + + >>> from zope.component.globalregistry import BaseGlobalComponents + >>> sitemanager = BaseGlobalComponents() + + Now we create a new `context` that knows how to get to our custom site + manager. + + >>> context = ConformsToIComponentLookup(sitemanager) + + We now register an adapter from `I1` to `I3`: + + >>> sitemanager.registerAdapter(lambda x: 43, (I1,), I3, '') + + If an object implements the interface you want to adapt to, + `getAdapterInContext()` should simply return the object. + + >>> component.getAdapterInContext(ob, I1, context) + + >>> component.queryAdapterInContext(ob, I1, context) + + + If an object conforms to the interface you want to adapt to, + `getAdapterInContext()` should simply return the conformed object. + + >>> component.getAdapterInContext(ob, I2, context) + 42 + >>> component.queryAdapterInContext(ob, I2, context) + 42 + + If an adapter isn't registered for the given object and interface, and you + provide no default, raise ComponentLookupError... + + >>> class I4(interface.Interface): + ... pass + + >>> component.getAdapterInContext(ob, I4, context) \\ + ... #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: (, + ) + + ...otherwise, you get the default: + + >>> component.queryAdapterInContext(ob, I4, context, 44) + 44 + + If you ask for an adapter for which something's registered you get the + registered adapter + + >>> component.getAdapterInContext(ob, I3, context) + 43 + >>> component.queryAdapterInContext(ob, I3, context) + 43 + """ + +def testAdapter(): + """The `getAdapter()` and `queryAdapter()` API functions are similar to + `{get|query}AdapterInContext()` functions, except that they do not care + about the `__conform__()` but also handle named adapters. (Actually, the + name is a required argument.) + + If an adapter isn't registered for the given object and interface, and you + provide no default, raise `ComponentLookupError`... + + >>> component.getAdapter(ob, I2, '') #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: (, + , + '') + + ...otherwise, you get the default + + >>> component.queryAdapter(ob, I2, '', '') + '' + + Now get the global site manager and register an adapter from `I1` to `I2` + without a name: + + >>> component.getGlobalSiteManager().registerAdapter( + ... Comp, (I1,), I2, '') + + You should get a sensible error message if you forget that the 'requires' + argument is supposed to be a sequence + + >>> component.getGlobalSiteManager().registerAdapter( + ... Comp, I1, I2, '') + Traceback (most recent call last): + ... + TypeError: the required argument should be a list of interfaces, not a single interface + + You can now simply access the adapter using the `getAdapter()` API + function: + + >>> adapter = component.getAdapter(ob, I2, '') + >>> adapter.__class__ is Comp + True + >>> adapter.context is ob + True + """ + +def testInterfaceCall(): + """Here we test the `adapter_hook()` function that we registered with the + `zope.interface` adapter hook registry, so that we can call interfaces to + do adaptation. + + First, we need to register an adapter: + + >>> component.getGlobalSiteManager().registerAdapter( + ... Comp, [I1], I2, '') + + Then we try to adapt `ob` to provide an `I2` interface by calling the `I2` + interface with the obejct as first argument: + + >>> adapter = I2(ob) + >>> adapter.__class__ is Comp + True + >>> adapter.context is ob + True + + If no adapter is found, a `TypeError is raised... + + >>> I1(Ob2()) #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: ('Could not adapt', , + ) + + ...unless we specify an alternative adapter: + + >>> marker = object() + >>> I2(object(), marker) is marker + True + """ + +def testNamedAdapter(): + """Make sure that adapters with names are correctly selected from the + registry. + + First we register some named adapter: + + >>> component.getGlobalSiteManager().registerAdapter( + ... lambda x: 0, [I1], I2, 'foo') + + If an adapter isn't registered for the given object and interface, + and you provide no default, raise `ComponentLookupError`... + + >>> component.getAdapter(ob, I2, 'bar') \\ + ... #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: + (, , 'bar') + + ...otherwise, you get the default + + >>> component.queryAdapter(ob, I2, 'bar', '') + '' + + But now we register an adapter for the object having the correct name + + >>> component.getGlobalSiteManager().registerAdapter( + ... Comp, [I1], I2, 'bar') + + so that the lookup succeeds: + + >>> adapter = component.getAdapter(ob, I2, 'bar') + >>> adapter.__class__ is Comp + True + >>> adapter.context is ob + True + """ + +def testMultiAdapter(): + """Adapting a combination of 2 objects to an interface + + Multi-adapters adapt one or more objects to another interface. To make + this demonstration non-trivial, we need to create a second object to be + adapted: + + >>> ob2 = Ob2() + + Like for regular adapters, if an adapter isn't registered for the given + objects and interface, and you provide no default, raise + `ComponentLookupError`... + + >>> component.getMultiAdapter((ob, ob2), I3) \\ + ... #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: + ((, ), + , + u'') + + ...otherwise, you get the default + + >>> component.queryMultiAdapter((ob, ob2), I3, default='') + '' + + Note that the name is not a required attribute here. + + To test multi-adapters, we also have to create an adapter class that + handles to context objects: + + >>> class DoubleAdapter(object): + ... interface.implements(I3) + ... def __init__(self, first, second): + ... self.first = first + ... self.second = second + + Now we can register the multi-adapter using + + >>> component.getGlobalSiteManager().registerAdapter( + ... DoubleAdapter, (I1, I2), I3, '') + + Notice how the required interfaces are simply provided by a tuple. Now we + can get the adapter: + + >>> adapter = component.getMultiAdapter((ob, ob2), I3) + >>> adapter.__class__ is DoubleAdapter + True + >>> adapter.first is ob + True + >>> adapter.second is ob2 + True + """ + +def testAdapterForInterfaceNone(): + """Providing an adapter for None says that your adapter can adapt anything + to `I2`. + + >>> component.getGlobalSiteManager().registerAdapter( + ... Comp, (None,), I2, '') + + >>> adapter = I2(ob) + >>> adapter.__class__ is Comp + True + >>> adapter.context is ob + True + + It can really adapt any arbitrary object: + + >>> something = object() + >>> adapter = I2(something) + >>> adapter.__class__ is Comp + True + >>> adapter.context is something + True + """ + +def testGetAdapters(): + """It is sometimes desireable to get a list of all adapters that are + registered for a particular output interface, given a set of + objects. + + Let's register some adapters first: + + >>> component.getGlobalSiteManager().registerAdapter( + ... Comp, [I1], I2, '') + >>> component.getGlobalSiteManager().registerAdapter( + ... Comp, [None], I2, 'foo') + + Now we get all the adapters that are registered for `ob` that provide + `I2`: + + >>> adapters = sorted(component.getAdapters((ob,), I2)) + >>> [(name, adapter.__class__.__name__) for name, adapter in adapters] + [(u'', 'Comp'), (u'foo', 'Comp')] + + Note that the output doesn't include None values. If an adapter + factory returns None, it is as if it wasn't present. + + >>> component.getGlobalSiteManager().registerAdapter( + ... lambda context: None, [I1], I2, 'nah') + >>> adapters = sorted(component.getAdapters((ob,), I2)) + >>> [(name, adapter.__class__.__name__) for name, adapter in adapters] + [(u'', 'Comp'), (u'foo', 'Comp')] + + """ + +def testUtility(): + """Utilities are components that simply provide an interface. They are + instantiated at the time or before they are registered. Here we test the + simple query interface. + + Before we register any utility, there is no utility available, of + course. The pure instatiation of an object does not make it a utility. If + you do not specify a default, you get a `ComponentLookupError`... + + >>> component.getUtility(I1) #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: \ + (, '') + + ...otherwise, you get the default + + >>> component.queryUtility(I1, default='') + '' + >>> component.queryUtility(I2, default='') + '' + + Now we declare `ob` to be the utility providing `I1` + + >>> component.getGlobalSiteManager().registerUtility(ob, I1) + + so that the component is now available: + + >>> component.getUtility(I1) is ob + True + """ + +def testNamedUtility(): + """Like adapters, utilities can be named. + + Just because you register an utility having no name + + >>> component.getGlobalSiteManager().registerUtility(ob, I1) + + does not mean that they are available when you specify a name: + + >>> component.getUtility(I1, name='foo') \\ + ... #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: + (, 'foo') + + + ...otherwise, you get the default + + >>> component.queryUtility(I1, name='foo', default='') + '' + + Registering the utility under the correct name + + >>> component.getGlobalSiteManager().registerUtility( + ... ob, I1, name='foo') + + really helps: + + >>> component.getUtility(I1, 'foo') is ob + True + """ + +def test_getAllUtilitiesRegisteredFor(): + """Again, like for adapters, it is often useful to get a list of all + utilities that have been registered for a particular interface. Utilities + providing a derived interface are also listed. + + Thus, let's create a derivative interface of `I1`: + + >>> class I11(I1): + ... pass + + >>> class Ob11(Ob): + ... interface.implements(I11) + + >>> ob11 = Ob11() + >>> ob_bob = Ob() + + Now we register the new utilities: + + >>> gsm = component.getGlobalSiteManager() + >>> gsm.registerUtility(ob, I1) + >>> gsm.registerUtility(ob11, I11) + >>> gsm.registerUtility(ob_bob, I1, name='bob') + >>> gsm.registerUtility(Comp(2), I2) + + We can now get all the utilities that provide interface `I1`: + + >>> uts = list(component.getAllUtilitiesRegisteredFor(I1)) + >>> uts = sorted([util.__class__.__name__ for util in uts]) + >>> uts + ['Ob', 'Ob', 'Ob11'] + + Note that `getAllUtilitiesRegisteredFor()` does not return the names of + the utilities. + """ + +def testNotBrokenWhenNoSiteManager(): + """Make sure that the adapter lookup is not broken, when no site manager + is available. + + Both of those things emit `DeprecationWarnings`. + + >>> I2(ob) #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + TypeError: ('Could not adapt', + , + ) + + + >>> I2(ob, 42) + 42 + """ + + +def testNo__component_adapts__leakage(): + """ + We want to make sure that an `adapts()` call in a class definition + doesn't affect instances. + + >>> class C: + ... component.adapts() + + >>> C.__component_adapts__ + () + >>> C().__component_adapts__ + Traceback (most recent call last): + ... + AttributeError: __component_adapts__ + """ + +def test_ability_to_pickle_globalsitemanager(): + """ + We need to make sure that it is possible to pickle the global site manager + and its two global adapter registries. + + >>> from zope.component import globalSiteManager + >>> import cPickle + >>> pickle = cPickle.dumps(globalSiteManager) + >>> sm = cPickle.loads(pickle) + >>> sm is globalSiteManager + True + + Now let's ensure that the registries themselves can be pickled as well: + + >>> pickle = cPickle.dumps(globalSiteManager.adapters) + >>> adapters = cPickle.loads(pickle) + >>> adapters is globalSiteManager.adapters + True + """ + +def test_persistent_component_managers(): + """ +Here, we'll demonstrate that changes work even when data are stored in +a database and when accessed from multiple connections. + +Start by setting up a database and creating two transaction +managers and database connections to work with. + + >>> import ZODB.tests.util + >>> db = ZODB.tests.util.DB() + >>> import transaction + >>> t1 = transaction.TransactionManager() + >>> c1 = db.open(transaction_manager=t1) + >>> r1 = c1.root() + >>> t2 = transaction.TransactionManager() + >>> c2 = db.open(transaction_manager=t2) + >>> r2 = c2.root() + +Create a set of components registries in the database, alternating +connections. + + >>> from zope.component.persistentregistry import PersistentComponents + + >>> _ = t1.begin() + >>> r1[1] = PersistentComponents('1') + >>> t1.commit() + + >>> _ = t2.begin() + >>> r2[2] = PersistentComponents('2', (r2[1], )) + >>> t2.commit() + + >>> _ = t1.begin() + >>> r1[3] = PersistentComponents('3', (r1[1], )) + >>> t1.commit() + + >>> _ = t2.begin() + >>> r2[4] = PersistentComponents('4', (r2[2], r2[3])) + >>> t2.commit() + + >>> _ = t1.begin() + >>> r1[1].__bases__ + () + >>> r1[2].__bases__ == (r1[1], ) + True + + >>> r1[1].registerUtility(U1(1)) + >>> r1[1].queryUtility(I1) + U1(1) + >>> r1[2].queryUtility(I1) + U1(1) + >>> t1.commit() + + >>> _ = t2.begin() + >>> r2[1].registerUtility(U1(2)) + >>> r2[2].queryUtility(I1) + U1(2) + + >>> r2[4].queryUtility(I1) + U1(2) + >>> t2.commit() + + + >>> _ = t1.begin() + >>> r1[1].registerUtility(U12(1), I2) + >>> r1[4].queryUtility(I2) + U12(1) + >>> t1.commit() + + + >>> _ = t2.begin() + >>> r2[3].registerUtility(U12(3), I2) + >>> r2[4].queryUtility(I2) + U12(3) + >>> t2.commit() + + >>> _ = t1.begin() + + >>> r1[1].registerHandler(handle1, info="First handler") + >>> r1[2].registerHandler(handle, required=[U]) + + >>> r1[3].registerHandler(handle3) + + >>> r1[4].registerHandler(handle4) + + >>> r1[4].handle(U1(1)) + handle1 U1(1) + handle3 U1(1) + handle (U1(1),) + handle4 U1(1) + + >>> t1.commit() + + >>> _ = t2.begin() + >>> r2[4].handle(U1(1)) + handle1 U1(1) + handle3 U1(1) + handle (U1(1),) + handle4 U1(1) + >>> t2.abort() + + >>> db.close() + """ + +def persistent_registry_doesnt_scew_up_subsribers(): + """ + >>> import ZODB.tests.util + >>> db = ZODB.tests.util.DB() + >>> import transaction + >>> t1 = transaction.TransactionManager() + >>> c1 = db.open(transaction_manager=t1) + >>> r1 = c1.root() + >>> t2 = transaction.TransactionManager() + >>> c2 = db.open(transaction_manager=t2) + >>> r2 = c2.root() + + >>> from zope.component.persistentregistry import PersistentComponents + + >>> _ = t1.begin() + >>> r1[1] = PersistentComponents('1') + >>> r1[1].registerHandler(handle1) + >>> r1[1].registerSubscriptionAdapter(handle1, provided=I2) + >>> _ = r1[1].unregisterHandler(handle1) + >>> _ = r1[1].unregisterSubscriptionAdapter(handle1, provided=I2) + >>> t1.commit() + >>> _ = t1.begin() + >>> r1[1].registerHandler(handle1) + >>> r1[1].registerSubscriptionAdapter(handle1, provided=I2) + >>> t1.commit() + + >>> _ = t2.begin() + >>> len(list(r2[1].registeredHandlers())) + 1 + >>> len(list(r2[1].registeredSubscriptionAdapters())) + 1 + >>> t2.abort() + + """ + + + +class GlobalRegistry: + pass + +base = zope.component.globalregistry.GlobalAdapterRegistry( + GlobalRegistry, 'adapters') +GlobalRegistry.adapters = base +def clear_base(): + base.__init__(GlobalRegistry, 'adapters') + +class IFoo(interface.Interface): + pass +class Foo(persistent.Persistent): + interface.implements(IFoo) + name = '' + def __init__(self, name=''): + self.name = name + + def __repr__(self): + return 'Foo(%r)' % self.name + +def test_deghostification_of_persistent_adapter_registries(): + """ + +We want to make sure that we see updates corrextly. + + >>> len(base._v_subregistries) + 0 + + >>> import ZODB.tests.util + >>> db = ZODB.tests.util.DB() + >>> tm1 = transaction.TransactionManager() + >>> c1 = db.open(transaction_manager=tm1) + >>> r1 = zope.component.persistentregistry.PersistentAdapterRegistry( + ... (base,)) + >>> r2 = zope.component.persistentregistry.PersistentAdapterRegistry((r1,)) + >>> c1.root()[1] = r1 + >>> c1.root()[2] = r2 + >>> tm1.commit() + >>> r1._p_deactivate() + + >>> len(base._v_subregistries) + 0 + + >>> tm2 = transaction.TransactionManager() + >>> c2 = db.open(transaction_manager=tm2) + >>> r1 = c2.root()[1] + >>> r2 = c2.root()[2] + + >>> r1.lookup((), IFoo, '') + + >>> base.register((), IFoo, '', Foo('')) + >>> r1.lookup((), IFoo, '') + Foo('') + + >>> r2.lookup((), IFoo, '1') + + >>> r1.register((), IFoo, '1', Foo('1')) + + >>> r2.lookup((), IFoo, '1') + Foo('1') + + >>> r1.lookup((), IFoo, '2') + >>> r2.lookup((), IFoo, '2') + + >>> base.register((), IFoo, '2', Foo('2')) + + >>> r1.lookup((), IFoo, '2') + Foo('2') + + >>> r2.lookup((), IFoo, '2') + Foo('2') + +Cleanup: + + >>> db.close() + >>> clear_base() + + """ + + +def test_multi_handler_unregistration(): + """ + There was a bug where multiple handlers for the same required + specification would all be removed when one of them was + unregistered: + + >>> class I(zope.interface.Interface): + ... pass + >>> def factory1(event): + ... print "| Factory 1 is here" + >>> def factory2(event): + ... print "| Factory 2 is here" + >>> class Event(object): + ... zope.interface.implements(I) + >>> from zope.component.registry import Components + >>> registry = Components() + >>> registry.registerHandler(factory1, [I,]) + >>> registry.registerHandler(factory2, [I,]) + >>> registry.handle(Event()) + | Factory 1 is here + | Factory 2 is here + >>> registry.unregisterHandler(factory1, [I,]) + True + >>> registry.handle(Event()) + | Factory 2 is here + """ + +def test_next_utilities(): + """ + It is common for a utility to delegate its answer to a utility + providing the same interface in one of the component registry's + bases. Let's first create a global utility:: + + >>> import zope.interface + >>> class IMyUtility(zope.interface.Interface): + ... pass + + >>> class MyUtility(ConformsToIComponentLookup): + ... zope.interface.implements(IMyUtility) + ... def __init__(self, id, sm): + ... self.id = id + ... self.sitemanager = sm + ... def __repr__(self): + ... return "%s('%s')" % (self.__class__.__name__, self.id) + + >>> from zope.component import getGlobalSiteManager + >>> gsm = getGlobalSiteManager() + + >>> gutil = MyUtility('global', gsm) + >>> gsm.registerUtility(gutil, IMyUtility, 'myutil') + + Now, let's create two registries and set up the bases hierarchy:: + + >>> from zope.component.registry import Components + >>> sm1 = Components('sm1', bases=(gsm, )) + >>> sm1_1 = Components('sm1_1', bases=(sm1, )) + + Now we create two utilities and insert them in our folder hierarchy: + + >>> util1 = MyUtility('one', sm1) + >>> sm1.registerUtility(util1, IMyUtility, 'myutil') + >>> IComponentLookup(util1) is sm1 + True + + >>> util1_1 = MyUtility('one-one', sm1_1) + >>> sm1_1.registerUtility(util1_1, IMyUtility, 'myutil') + >>> IComponentLookup(util1_1) is sm1_1 + True + + Now, if we ask `util1_1` for its next available utility we get the + ``one`` utility:: + + >>> from zope.component import getNextUtility + >>> getNextUtility(util1_1, IMyUtility, 'myutil') + MyUtility('one') + + Next we ask `util1` for its next utility and we should get the global version: + + >>> getNextUtility(util1, IMyUtility, 'myutil') + MyUtility('global') + + However, if we ask the global utility for the next one, an error is raised + + >>> getNextUtility(gutil, IMyUtility, + ... 'myutil') #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ComponentLookupError: + No more utilities for , + 'myutil' have been found. + + You can also use `queryNextUtility` and specify a default: + + >>> from zope.component import queryNextUtility + >>> queryNextUtility(gutil, IMyUtility, 'myutil', 'default') + 'default' + + Let's now ensure that the function also works with multiple registries. First + we create another base registry: + + >>> myregistry = Components() + + We now set up another utility into that registry: + + >>> custom_util = MyUtility('my_custom_util', myregistry) + >>> myregistry.registerUtility(custom_util, IMyUtility, 'my_custom_util') + + We add it as a base to the local site manager: + + >>> sm1.__bases__ = (myregistry,) + sm1.__bases__ + + Both the ``myregistry`` and global utilities should be available: + + >>> queryNextUtility(sm1, IMyUtility, 'my_custom_util') + MyUtility('my_custom_util') + >>> queryNextUtility(sm1, IMyUtility, 'myutil') + MyUtility('global') + + Note, if the context cannot be converted to a site manager, the default is + retruned: + + >>> queryNextUtility(object(), IMyUtility, 'myutil', 'default') + 'default' + """ + +def dont_leak_utility_registrations_in__subscribers(): + """ + + We've observed utilities getting left in _subscribers when they + get unregistered. + + >>> import zope.component.registry + >>> reg = zope.component.registry.Components() + >>> class C: + ... def __init__(self, name): + ... self.name = name + ... def __repr__(self): + ... return "C(%s)" % self.name + + >>> c1 = C(1) + >>> reg.registerUtility(c1, I1) + >>> reg.registerUtility(c1, I1) + >>> list(reg.getAllUtilitiesRegisteredFor(I1)) + [C(1)] + + >>> reg.unregisterUtility(provided=I1) + True + >>> list(reg.getAllUtilitiesRegisteredFor(I1)) + [] + + >>> reg.registerUtility(c1, I1) + >>> reg.registerUtility(C(2), I1) + + >>> list(reg.getAllUtilitiesRegisteredFor(I1)) + [C(2)] + + """ + +def test_zcml_handler_site_manager(): + """ + The ZCML directives provided by zope.component use the ``getSiteManager`` + method to get the registry where to register the components. This makes + possible to hook ``getSiteManager`` before loading a ZCML file: + + >>> from zope.component.registry import Components + >>> registry = Components() + >>> def dummy(context=None): + ... return registry + >>> from zope.component import getSiteManager + >>> ignore = getSiteManager.sethook(dummy) + + >>> from zope.component.testfiles.components import comp, IApp + >>> from zope.component.zcml import handler + >>> handler('registerUtility', comp, IApp, u'') + >>> registry.getUtility(IApp) is comp + True + >>> ignore = getSiteManager.reset() + + """ + +class StandaloneTests(unittest.TestCase): + def testStandalone(self): + import subprocess + import sys + import os + import tempfile + import pickle + + executable = os.path.abspath(sys.executable) + program = os.path.join(os.path.dirname(__file__), 'standalonetests.py') + process = subprocess.Popen([executable, program], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE) + pickle.dump(sys.path, process.stdin) + process.stdin.close() + + try: + process.wait() + except OSError, e: + if e.errno != 4: # MacIntel raises apparently unimportant EINTR? + raise # TODO verify sanity of a pass on EINTR :-/ + lines = process.stdout.readlines() + process.stdout.close() + success = True + # Interpret the result: We scan the output from the end backwards + # until we find either a line that says 'OK' (which means the tests + # ran successfully) or a line that starts with quite a few dashes + # (which means we didn't find a line that says 'OK' within the summary + # of the test runner and the tests did not run successfully.) + for l in reversed(lines): + l = l.strip() + if not l: + continue + if l.startswith('-----'): + break + if l.endswith('OK'): + sucess = True + if not success: + self.fail(''.join(lines)) + +class HookableTests(unittest.TestCase): + + def test_ctor_no_func(self): + from zope.component.hookable import hookable + self.assertRaises(TypeError, hookable) + + def test_ctor_simple(self): + from zope.component.hookable import hookable + def foo(): + pass + hooked = hookable(foo) + self.failUnless(hooked.original is foo) + self.failUnless(hooked.implementation is foo) + + def test_ctor_extra_arg(self): + from zope.component.hookable import hookable + def foo(): + pass + self.assertRaises(TypeError, hookable, foo, foo) + + def test_ctor_extra_arg(self): + from zope.component.hookable import hookable + def foo(): + pass + self.assertRaises(TypeError, hookable, foo, nonesuch=foo) + + def test_sethook(self): + from zope.component.hookable import hookable + def foo(): + pass + def bar(): + pass + hooked = hookable(foo) + hooked.sethook(bar) + self.failUnless(hooked.original is foo) + self.failUnless(hooked.implementation is bar) + + def test_reset(self): + from zope.component.hookable import hookable + def foo(): + pass + def bar(): + pass + hooked = hookable(foo) + hooked.sethook(bar) + hooked.reset() + self.failUnless(hooked.original is foo) + self.failUnless(hooked.implementation is foo) + + def test_cant_assign_original(self): + from zope.component.hookable import hookable + def foo(): + pass + def bar(): + pass + hooked = hookable(foo) + try: + hooked.original = bar + except TypeError: + pass + except AttributeError: + pass + else: + self.fail('Assigned original') + + def test_cant_delete_original(self): + from zope.component.hookable import hookable + def foo(): + pass + hooked = hookable(foo) + try: + del hooked.original + except TypeError: + pass + except AttributeError: + pass + else: + self.fail('Deleted original') + + def test_cant_assign_original(self): + from zope.component.hookable import hookable + def foo(): + pass + def bar(): + pass + hooked = hookable(foo) + try: + hooked.implementation = bar + except TypeError: + pass + except AttributeError: + pass + else: + self.fail('Assigned implementation') + + def test_readonly_original(self): + from zope.component.hookable import hookable + def foo(): + pass + hooked = hookable(foo) + try: + del hooked.implementation + except TypeError: + pass + except AttributeError: + pass + else: + self.fail('Deleted implementation') + +class Ob3(object): + interface.implements(IC) + +template = """ + %s + """ + + +class ResourceViewTests(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(ResourceViewTests, self).setUp() + XMLConfig('meta.zcml', zope.component)() + XMLConfig('meta.zcml', zope.security)() + + def testView(self): + ob = Ob3() + request = Request(IV) + self.assertEqual( + zope.component.queryMultiAdapter((ob, request), name=u'test'), None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + self.assertEqual( + zope.component.queryMultiAdapter((ob, request), + name=u'test').__class__, + V1) + + + def testMultiView(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + + ob = Ob3() + a1 = A1() + a2 = A2() + request = Request(IV) + view = zope.component.queryMultiAdapter((ob, a1, a2, request), + name=u'test') + self.assertEqual(view.__class__, A3) + self.assertEqual(view.context, (ob, a1, a2, request)) + + + def testMultiView_fails_w_multiple_factories(self): + self.assertRaises( + ConfigurationError, + xmlconfig, + StringIO(template % + ''' + + ''' + ) + ) + + def testView_w_multiple_factories(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + ob = Ob3() + + # The view should be a V1 around an A3, around an A2, around + # an A1, anround ob: + view = zope.component.queryMultiAdapter((ob, Request(IV)), name=u'test') + self.assertEqual(view.__class__, V1) + a3 = view.context + self.assertEqual(a3.__class__, A3) + a2 = a3.context[0] + self.assertEqual(a2.__class__, A2) + a1 = a2.context[0] + self.assertEqual(a1.__class__, A1) + self.assertEqual(a1.context[0], ob) + + def testView_fails_w_no_factories(self): + self.assertRaises(ConfigurationError, + xmlconfig, + StringIO(template % + ''' + + ''' + ), + ) + + + def testViewThatProvidesAnInterface(self): + ob = Ob3() + self.assertEqual( + zope.component.queryMultiAdapter((ob, Request(IR)), IV, u'test'), + None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + self.assertEqual( + zope.component.queryMultiAdapter((ob, Request(IR)), IV, u'test'), + None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.queryMultiAdapter((ob, Request(IR)), IV, u'test') + self.assertEqual(v.__class__, V1) + + + def testUnnamedViewThatProvidesAnInterface(self): + ob = Ob3() + self.assertEqual( + zope.component.queryMultiAdapter((ob, Request(IR)), IV), None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.queryMultiAdapter((ob, Request(IR)), IV) + self.assertEqual(v, None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.queryMultiAdapter((ob, Request(IR)), IV) + self.assertEqual(v.__class__, V1) + + def testViewHavingARequiredClass(self): + xmlconfig(StringIO(template % ( + ''' + + ''' + ))) + + content = Content() + a1 = zope.component.getMultiAdapter((content, Request(IR))) + self.assert_(isinstance(a1, A1)) + + class MyContent: + interface.implements(IContent) + + self.assertRaises(ComponentLookupError, zope.component.getMultiAdapter, + (MyContent(), Request(IR))) + + def testInterfaceProtectedView(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = ProxyFactory(zope.component.getMultiAdapter((Ob3(), Request(IV)), + name='test')) + self.assertEqual(v.index(), 'V1 here') + self.assertRaises(Exception, getattr, v, 'action') + + def testAttributeProtectedView(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = ProxyFactory(zope.component.getMultiAdapter((Ob3(), Request(IV)), + name='test')) + self.assertEqual(v.action(), 'done') + self.assertRaises(Exception, getattr, v, 'index') + + def testInterfaceAndAttributeProtectedView(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.getMultiAdapter((Ob3(), Request(IV)), name='test') + self.assertEqual(v.index(), 'V1 here') + self.assertEqual(v.action(), 'done') + + def testDuplicatedInterfaceAndAttributeProtectedView(self): + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.getMultiAdapter((Ob3(), Request(IV)), name='test') + self.assertEqual(v.index(), 'V1 here') + self.assertEqual(v.action(), 'done') + + def testIncompleteProtectedViewNoPermission(self): + self.assertRaises( + ConfigurationError, + xmlconfig, + StringIO(template % + ''' + + ''' + )) + + def testViewUndefinedPermission(self): + config = StringIO(template % ( + ''' + + ''' + )) + self.assertRaises(ValueError, xmlconfig, config, testing=1) + + def testResource(self): + ob = Ob3() + self.assertEqual( + zope.component.queryAdapter(Request(IV), name=u'test'), None) + xmlconfig(StringIO(template % ( + ''' + + ''' + ))) + + self.assertEqual( + zope.component.queryAdapter(Request(IV), name=u'test').__class__, + R1) + + def testResourceThatProvidesAnInterface(self): + ob = Ob3() + self.assertEqual(zope.component.queryAdapter(Request(IR), IV, u'test'), + None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.queryAdapter(Request(IR), IV, name=u'test') + self.assertEqual(v, None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.queryAdapter(Request(IR), IV, name=u'test') + self.assertEqual(v.__class__, R1) + + def testUnnamedResourceThatProvidesAnInterface(self): + ob = Ob3() + self.assertEqual(zope.component.queryAdapter(Request(IR), IV), None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.queryAdapter(Request(IR), IV) + self.assertEqual(v, None) + + xmlconfig(StringIO(template % + ''' + + ''' + )) + + v = zope.component.queryAdapter(Request(IR), IV) + self.assertEqual(v.__class__, R1) + + def testResourceUndefinedPermission(self): + + config = StringIO(template % ( + ''' + + ''' + )) + self.assertRaises(ValueError, xmlconfig, config, testing=1) + + +class ConditionalSecurityLayer(UnitTests): + + __name__ = 'ConditionalSecurity' + __bases__ = () + + def setUp(self): + setUp() + self.modules = {} + for m in ('zope.security', 'zope.proxy'): + self.modules[m] = sys.modules[m] + sys.modules[m] = None + import zope.component.zcml + reload(zope.component.zcml) + + def tearDown(self): + tearDown() + for m in ('zope.security', 'zope.proxy'): + sys.modules[m] = self.modules[m] + import zope.component.zcml + reload(zope.component.zcml) + + +def setUpRegistryTests(tests): + setUp() + +def tearDownRegistryTests(tests): + tearDown() + import zope.event + zope.event.subscribers.pop() + +def clearZCML(test=None): + tearDown() + setUp() + XMLConfig('meta.zcml', component)() + +def test_suite(): + checker = renormalizing.RENormalizing([ + (re.compile('at 0x[0-9a-fA-F]+'), 'at '), + (re.compile(r":"), + r'exceptions.\1Error:'), + ]) + + zcml_conditional = doctest.DocFileSuite('zcml_conditional.txt', checker=checker) + zcml_conditional.layer = ConditionalSecurityLayer() + + hooks_conditional = doctest.DocFileSuite('hooks.txt', checker=checker) + hooks_conditional.layer = ConditionalSecurityLayer() + + return unittest.TestSuite(( + doctest.DocTestSuite(setUp=setUp, tearDown=tearDown), + unittest.makeSuite(HookableTests), + doctest.DocTestSuite('zope.component.interface', + setUp=setUp, tearDown=tearDown), + doctest.DocTestSuite('zope.component.nexttesting'), + doctest.DocFileSuite('README.txt', + setUp=setUp, tearDown=tearDown), + doctest.DocFileSuite('socketexample.txt', + setUp=setUp, tearDown=tearDown), + doctest.DocFileSuite('factory.txt', + setUp=setUp, tearDown=tearDown), + doctest.DocFileSuite('registry.txt', checker=checker, + setUp=setUpRegistryTests, + tearDown=tearDownRegistryTests), + doctest.DocFileSuite('hooks.txt',checker=checker, + setUp=setUp, tearDown=tearDown), + doctest.DocFileSuite('event.txt', + setUp=setUp, tearDown=tearDown), + doctest.DocTestSuite('zope.component.security'), + doctest.DocFileSuite('zcml.txt', checker=checker, + setUp=setUp, tearDown=tearDown), + doctest.DocFileSuite('testlayer.txt', + optionflags=(doctest.ELLIPSIS + + doctest.NORMALIZE_WHITESPACE + + doctest.REPORT_NDIFF)), + zcml_conditional, + hooks_conditional, + unittest.makeSuite(StandaloneTests), + unittest.makeSuite(ResourceViewTests), + )) + +if __name__ == "__main__": + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/component/zcml_conditional.txt zope3-3.5~bzr18/src/zope/component/zcml_conditional.txt --- zope3-3.4.0/src/zope/component/zcml_conditional.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/zcml_conditional.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,612 @@ +ZCML directives without zope.security support +============================================= + +This tests run without zope.security available: + + >>> from zope.component.zcml import check_security_support + >>> check_security_support() + Traceback (most recent call last): + ... + ConfigurationError: security proxied components are not supported because zope.security is not available + +Components may be registered using the registration API exposed by +``zope.component`` (provideAdapter, provideUtility, etc.). They may +also be registered using configuration files. The common way to do +that is by using ZCML (Zope Configuration Markup Language), an XML +spelling of component registration. + +In ZCML, each XML element is a *directive*. There are different +top-level directives that let us register components. We will +introduce them one by one here. + +This helper will let us easily execute ZCML snippets: + + >>> from cStringIO import StringIO + >>> from zope.configuration.xmlconfig import xmlconfig + >>> def runSnippet(snippet): + ... template = """\ + ... + ... %s + ... """ + ... xmlconfig(StringIO(template % snippet)) + +adapter +------- + +Adapters play a key role in the Component Architecture. In ZCML, they +are registered with the directive. + + >>> from zope.component.testfiles.adapter import A1, A2, A3, Handler + >>> from zope.component.testfiles.adapter import I1, I2, I3, IS + >>> from zope.component.testfiles.components import IContent, Content, Comp, comp + +Before we register the first test adapter, we can verify that adapter +lookup doesn't work yet: + + >>> from zope.component.tests import clearZCML + >>> clearZCML() + >>> from zope.component.testfiles.components import IApp + >>> IApp(Content(), None) is None + True + +Then we register the adapter and see that the lookup works: + + >>> runSnippet(''' + ... ''') + + >>> IApp(Content()).__class__ + + +It is also possible to give adapters names. Then the combination of +required interface, provided interface and name makes the adapter +lookup unique. The name is supplied using the ``name`` argument to +the directive: + + >>> from zope.component.tests import clearZCML + >>> clearZCML() + >>> import zope.component + >>> zope.component.queryAdapter(Content(), IApp, 'test') is None + True + + >>> runSnippet(''' + ... ''') + + >>> zope.component.getAdapter(Content(), IApp, 'test').__class__ + + +Adapter factories +~~~~~~~~~~~~~~~~~ + +It is possible to supply more than one adapter factory. In this case, +during adapter lookup each factory will be called and the return value +will be given to the next factory. The return value of the last +factory is returned as the result of the adapter lookup. For examle: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + +The resulting adapter is an A3, around an A2, around an A1, around the +adapted object: + + >>> content = Content() + >>> a3 = IApp(content) + >>> a3.__class__ is A3 + True + + >>> a2 = a3.context[0] + >>> a2.__class__ is A2 + True + + >>> a1 = a2.context[0] + >>> a1.__class__ is A1 + True + + >>> a1.context[0] is content + True + +Of course, if no factory is provided at all, we will get an error: + + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-8.8 + ValueError: No factory specified + +Declaring ``for`` and ``provides`` in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The directive can figure out from the in-line Python +declaration (using ``zope.component.adapts()`` or +``zope.component.adapter()`` as well as ``zope.interface.implements``) +what the adapter should be registered for and what it provides:: + + >>> clearZCML() + >>> IApp(Content(), None) is None + True + + >>> runSnippet(''' + ... ''') + + >>> IApp(Content()).__class__ + + +Of course, if the adapter has no ``implements()`` declaration, ZCML +can't figure out what it provides: + + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-7.8 + TypeError: Missing 'provides' attribute + +On the other hand, if the factory implements more than one interface, +ZCML can't figure out what it should provide either: + + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-7.8 + TypeError: Missing 'provides' attribute + +A not so common edge case is registering adapters directly for +classes, not for interfaces. For example: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a1 = zope.component.getAdapter(content, I1, '') + >>> isinstance(a1, A1) + True + +This time, any object providing ``IContent`` won't work if it's not an +instance of the ``Content`` class: + + >>> import zope.interface + >>> class MyContent: + ... zope.interface.implements(IContent) + >>> zope.component.getAdapter(MyContent(), I1, '') # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ComponentLookupError: ... + +Multi-adapters +~~~~~~~~~~~~~~ + +Conventional adapters adapt one object to provide another interface. +Multi-adapters adapt several objects at once: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a1 = A1() + >>> a2 = A2() + >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3) + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1, a2) + True + +You can even adapt an empty list of objects (we call this a +null-adapter): + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> a3 = zope.component.queryMultiAdapter((), I3) + >>> a3.__class__ is A3 + True + >>> a3.context == () + True + +Even with multi-adapters, ZCML can figure out the ``for`` and +``provides`` parameters from the Python declarations: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3) + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1, a2) + True + +Chained factories are not supported for multi-adapters, though: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-11.8 + ValueError: Can't use multiple factories and multiple for + +And neither for null-adapters: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-9.8 + ValueError: Can't use multiple factories and multiple for + +subscriber +---------- + +With the directive you can register subscription +adapters or event subscribers with the adapter registry. Consider +this very typical example of a directive: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a1 = A1() + + >>> subscribers = zope.component.subscribers((content, a1), IS) + >>> a3 = subscribers[0] + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1) + True + +Note how ZCML provides some additional information when registering +components, such as the ZCML filename and line numbers: + + >>> sm = zope.component.getSiteManager() + >>> doc = [reg.info for reg in sm.registeredSubscriptionAdapters() + ... if reg.provided is IS][0] + >>> print doc + File "", line 4.2-9.8 + Could not read source. + +The "fun" behind subscription adapters/subscribers is that when +several ones are declared for the same for/provides, they are all +found. With regular adapters, the most specific one (and in doubt the +one registered last) wins. Consider these two subscribers: + + >>> clearZCML() + >>> runSnippet(''' + ... + ... ''') + + >>> subscribers = zope.component.subscribers((content, a1), IS) + >>> len(subscribers) + 2 + >>> sorted([a.__class__.__name__ for a in subscribers]) + ['A2', 'A3'] + +Declaring ``for`` and ``provides`` in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Like the directive, the directive can +figure out from the in-line Python declaration (using +``zope.component.adapts()`` or ``zope.component.adapter()``) what the +subscriber should be registered for: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a2 = A2() + >>> subscribers = zope.component.subscribers((content, a1, a2), IS) + + >>> a3 = subscribers[0] + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1, a2) + True + +In the same way the directive can figure out what a subscriber +provides: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> sm = zope.component.getSiteManager() + >>> a3 = sm.adapters.subscriptions((IContent, I1, I2), None)[0] + >>> a3 is A3 + True + +A not so common edge case is declaring subscribers directly for +classes, not for interfaces. For example: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> subs = list(zope.component.subscribers((Content(),), I1)) + >>> isinstance(subs[0], A1) + True + +This time, any object providing ``IContent`` won't work if it's not an +instance of the ``Content`` class: + + >>> list(zope.component.subscribers((MyContent(),), I1)) + [] + +Event subscriber (handlers) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes, subscribers don't need to be adapters that actually provide +anything. It's enough that a callable is called for a certain event. + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + +In this case, simply getting the subscribers is enough to invoke them: + + >>> list(zope.component.subscribers((content, a1), None)) + [] + >>> content.args == ((a1,),) + True + + +utility +------- + +Apart from adapters (and subscription adapters), the Component +Architecture knows a second kind of component: utilities. They are +registered using the directive. + +Before we register the first test utility, we can verify that utility +lookup doesn't work yet: + + >>> clearZCML() + >>> zope.component.queryUtility(IApp) is None + True + +Then we register the utility: + + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp) is comp + True + +Like adapters, utilities can also have names. There can be more than +one utility registered for a certain interface, as long as they each +have a different name. + +First, we make sure that there's no utility yet: + + >>> clearZCML() + >>> zope.component.queryUtility(IApp, 'test') is None + True + +Then we register it: + + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp, 'test') is comp + True + +Utilities can also be registered from a factory. In this case, the +ZCML handler calls the factory (without any arguments) and registers +the returned value as a utility. Typically, you'd pass a class for +the factory: + + >>> clearZCML() + >>> zope.component.queryUtility(IApp) is None + True + + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp).__class__ is Comp + True + +Declaring ``provides`` in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Like other directives, can also figure out which interface +a utility provides from the Python declaration: + + >>> clearZCML() + >>> zope.component.queryUtility(IApp) is None + True + + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp) is comp + True + +It won't work if the component that is to be registered doesn't +provide anything: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-4.61 + TypeError: Missing 'provides' attribute + +Or if more than one interface is provided (then the ZCML directive +handler doesn't know under which the utility should be registered): + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-4.61 + TypeError: Missing 'provides' attribute + +We can repeat the same drill for utility factories: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp).__class__ is Comp + True + + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-4.59 + TypeError: Missing 'provides' attribute + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-4.59 + TypeError: Missing 'provides' attribute + +interface +--------- + +The directive lets us register an interface. Interfaces +are registered as named utilities. We therefore needn't go though all +the lookup details again, it is sufficient to see whether the +directive handler emits the right actions. + +First we provide a stub configuration context: + + >>> import re, pprint + >>> atre = re.compile(' at [0-9a-fA-Fx]+') + >>> class Context(object): + ... actions = () + ... def action(self, discriminator, callable, args): + ... self.actions += ((discriminator, callable, args), ) + ... def __repr__(self): + ... stream = StringIO() + ... pprinter = pprint.PrettyPrinter(stream=stream, width=60) + ... pprinter.pprint(self.actions) + ... r = stream.getvalue() + ... return (''.join(atre.split(r))).strip() + >>> context = Context() + +Then we provide a test interface that we'd like to register: + + >>> from zope.interface import Interface + >>> class I(Interface): + ... pass + +It doesn't yet provide ``ITestType``: + + >>> from zope.component.tests import ITestType + >>> ITestType.providedBy(I) + False + +However, after calling the directive handler... + + >>> from zope.component.zcml import interface + >>> interface(context, I, ITestType) + >>> context + ((None, + , + ('', + , + )),) + +...it does provide ``ITestType``: + + >>> from zope.interface.interfaces import IInterface + >>> ITestType.extends(IInterface) + True + >>> IInterface.providedBy(I) + True diff -Nru zope3-3.4.0/src/zope/component/zcml.py zope3-3.5~bzr18/src/zope/component/zcml.py --- zope3-3.4.0/src/zope/component/zcml.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/zcml.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,680 @@ +############################################################################## +# +# Copyright (c) 2005 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. +# +############################################################################## +"""Component Architecture configuration handlers + +$Id: zcml.py 110537 2010-04-06 03:01:29Z tseaver $ +""" +__docformat__ = "reStructuredText" + +import warnings +import zope.component +import zope.configuration.fields +import zope.interface +import zope.schema + +from zope.component.interface import provideInterface +from zope.configuration.exceptions import ConfigurationError +from zope.i18nmessageid import MessageFactory + +try: + from zope.component.security import _checker, proxify, protectedFactory, \ + securityAdapterFactory + from zope.security.zcml import Permission +except ImportError: + SECURITY_SUPPORT = False + from zope.schema import TextLine as Permission +else: + SECURITY_SUPPORT = True + +_ = MessageFactory('zope') + +def check_security_support(): + if not SECURITY_SUPPORT: + raise ConfigurationError("security proxied components are not " + "supported because zope.security is not available") + +def handler(methodName, *args, **kwargs): + method = getattr(zope.component.getSiteManager(), methodName) + method(*args, **kwargs) + +class IBasicComponentInformation(zope.interface.Interface): + + component = zope.configuration.fields.GlobalObject( + title=_("Component to use"), + description=_("Python name of the implementation object. This" + " must identify an object in a module using the" + " full dotted name. If specified, the" + " ``factory`` field must be left blank."), + required=False, + ) + + permission = Permission( + title=_("Permission"), + description=_("Permission required to use this component."), + required=False, + ) + + factory = zope.configuration.fields.GlobalObject( + title=_("Factory"), + description=_("Python name of a factory which can create the" + " implementation object. This must identify an" + " object in a module using the full dotted name." + " If specified, the ``component`` field must" + " be left blank."), + required=False, + ) + +class IAdapterDirective(zope.interface.Interface): + """ + Register an adapter + """ + + factory = zope.configuration.fields.Tokens( + title=_("Adapter factory/factories"), + description=_("A list of factories (usually just one) that create" + " the adapter instance."), + required=True, + value_type=zope.configuration.fields.GlobalObject() + ) + + provides = zope.configuration.fields.GlobalInterface( + title=_("Interface the component provides"), + description=_("This attribute specifies the interface the adapter" + " instance must provide."), + required=False, + ) + + for_ = zope.configuration.fields.Tokens( + title=_("Specifications to be adapted"), + description=_("This should be a list of interfaces or classes"), + required=False, + value_type=zope.configuration.fields.GlobalObject( + missing_value=object(), + ), + ) + + permission = Permission( + title=_("Permission"), + description=_("This adapter is only available, if the principal" + " has this permission."), + required=False, + ) + + name = zope.schema.TextLine( + title=_("Name"), + description=_("Adapters can have names.\n\n" + "This attribute allows you to specify the name for" + " this adapter."), + required=False, + ) + + trusted = zope.configuration.fields.Bool( + title=_("Trusted"), + description=_("""Make the adapter a trusted adapter + + Trusted adapters have unfettered access to the objects they + adapt. If asked to adapt security-proxied objects, then, + rather than getting an unproxied adapter of security-proxied + objects, you get a security-proxied adapter of unproxied + objects. + """), + required=False, + default=False, + ) + + locate = zope.configuration.fields.Bool( + title=_("Locate"), + description=_("""Make the adapter a locatable adapter + + Located adapter should be used if a non-public permission + is used. + """), + required=False, + default=False, + ) + +def _rolledUpFactory(factories): + # This has to be named 'factory', aparently, so as not to confuse + # apidoc :( + def factory(ob): + for f in factories: + ob = f(ob) + return ob + # Store the original factory for documentation + factory.factory = factories[0] + return factory + +def adapter(_context, factory, provides=None, for_=None, permission=None, + name='', trusted=False, locate=False): + + if for_ is None: + if len(factory) == 1: + for_ = zope.component.adaptedBy(factory[0]) + + if for_ is None: + raise TypeError("No for attribute was provided and can't " + "determine what the factory adapts.") + + for_ = tuple(for_) + + if provides is None: + if len(factory) == 1: + p = list(zope.interface.implementedBy(factory[0])) + if len(p) == 1: + provides = p[0] + + if provides is None: + raise TypeError("Missing 'provides' attribute") + + # Generate a single factory from multiple factories: + factories = factory + if len(factories) == 1: + factory = factories[0] + elif len(factories) < 1: + raise ValueError("No factory specified") + elif len(factories) > 1 and len(for_) != 1: + raise ValueError("Can't use multiple factories and multiple for") + else: + factory = _rolledUpFactory(factories) + + if permission is not None: + check_security_support() + factory = protectedFactory(factory, provides, permission) + + # invoke custom adapter factories + if locate or permission is not None or trusted: + check_security_support() + factory = securityAdapterFactory(factory, permission, locate, trusted) + + _context.action( + discriminator = ('adapter', for_, provides, name), + callable = handler, + args = ('registerAdapter', + factory, for_, provides, name, _context.info), + ) + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', provides) + ) + if for_: + for iface in for_: + if iface is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', iface) + ) + +class ISubscriberDirective(zope.interface.Interface): + """ + Register a subscriber + """ + + factory = zope.configuration.fields.GlobalObject( + title=_("Subscriber factory"), + description=_("A factory used to create the subscriber instance."), + required=False, + ) + + handler = zope.configuration.fields.GlobalObject( + title=_("Handler"), + description=_("A callable object that handles events."), + required=False, + ) + + provides = zope.configuration.fields.GlobalInterface( + title=_("Interface the component provides"), + description=_("This attribute specifies the interface the adapter" + " instance must provide."), + required=False, + ) + + for_ = zope.configuration.fields.Tokens( + title=_("Interfaces or classes that this subscriber depends on"), + description=_("This should be a list of interfaces or classes"), + required=False, + value_type=zope.configuration.fields.GlobalObject( + missing_value = object(), + ), + ) + + permission = Permission( + title=_("Permission"), + description=_("This subscriber is only available, if the" + " principal has this permission."), + required=False, + ) + + trusted = zope.configuration.fields.Bool( + title=_("Trusted"), + description=_("""Make the subscriber a trusted subscriber + + Trusted subscribers have unfettered access to the objects they + adapt. If asked to adapt security-proxied objects, then, + rather than getting an unproxied subscriber of security-proxied + objects, you get a security-proxied subscriber of unproxied + objects. + """), + required=False, + default=False, + ) + + locate = zope.configuration.fields.Bool( + title=_("Locate"), + description=_("""Make the subscriber a locatable subscriber + + Located subscribers should be used if a non-public permission + is used. + """), + required=False, + default=False, + ) + +_handler = handler +def subscriber(_context, for_=None, factory=None, handler=None, provides=None, + permission=None, trusted=False, locate=False): + if factory is None: + if handler is None: + raise TypeError("No factory or handler provided") + if provides is not None: + raise TypeError("Cannot use handler with provides") + factory = handler + else: + if handler is not None: + raise TypeError("Cannot use handler with factory") + if provides is None: + raise TypeError( + "You must specify a provided interface when registering " + "a factory") + + if for_ is None: + for_ = zope.component.adaptedBy(factory) + if for_ is None: + raise TypeError("No for attribute was provided and can't " + "determine what the factory (or handler) adapts.") + + if permission is not None: + check_security_support() + factory = protectedFactory(factory, provides, permission) + + for_ = tuple(for_) + + # invoke custom adapter factories + if locate or permission is not None or trusted: + check_security_support() + factory = securityAdapterFactory(factory, permission, locate, trusted) + + if handler is not None: + _context.action( + discriminator = None, + callable = _handler, + args = ('registerHandler', + handler, for_, u'', _context.info), + ) + else: + _context.action( + discriminator = None, + callable = _handler, + args = ('registerSubscriptionAdapter', + factory, for_, provides, u'', _context.info), + ) + + if provides is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', provides) + ) + + # For each interface, state that the adapter provides that interface. + for iface in for_: + if iface is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', iface) + ) + +class IUtilityDirective(IBasicComponentInformation): + """Register a utility.""" + + provides = zope.configuration.fields.GlobalInterface( + title=_("Provided interface"), + description=_("Interface provided by the utility."), + required=False, + ) + + name = zope.schema.TextLine( + title=_("Name"), + description=_("Name of the registration. This is used by" + " application code when locating a utility."), + required=False, + ) + +def utility(_context, provides=None, component=None, factory=None, + permission=None, name=''): + if factory and component: + raise TypeError("Can't specify factory and component.") + + if provides is None: + if factory: + provides = list(zope.interface.implementedBy(factory)) + else: + provides = list(zope.interface.providedBy(component)) + if len(provides) == 1: + provides = provides[0] + else: + raise TypeError("Missing 'provides' attribute") + + if permission is not None: + check_security_support() + component = proxify(component, provides=provides, permission=permission) + + _context.action( + discriminator = ('utility', provides, name), + callable = handler, + args = ('registerUtility', component, provides, name), + kw = dict(factory=factory), + ) + _context.action( + discriminator = None, + callable = provideInterface, + args = (provides.__module__ + '.' + provides.getName(), provides) + ) + +class IInterfaceDirective(zope.interface.Interface): + """ + Define an interface + """ + + interface = zope.configuration.fields.GlobalInterface( + title=_("Interface"), + required=True, + ) + + type = zope.configuration.fields.GlobalInterface( + title=_("Interface type"), + required=False, + ) + + name = zope.schema.TextLine( + title=_("Name"), + required=False, + ) + +def interface(_context, interface, type=None, name=''): + _context.action( + discriminator = None, + callable = provideInterface, + args = (name, interface, type) + ) + +class IBasicViewInformation(zope.interface.Interface): + """This is the basic information for all views.""" + + for_ = zope.configuration.fields.Tokens( + title=_("Specifications of the objects to be viewed"), + description=_("""This should be a list of interfaces or classes + """), + required=True, + value_type=zope.configuration.fields.GlobalObject( + missing_value=object(), + ), + ) + + permission = Permission( + title=_("Permission"), + description=_("The permission needed to use the view."), + required=False, + ) + + class_ = zope.configuration.fields.GlobalObject( + title=_("Class"), + description=_("A class that provides attributes used by the view."), + required=False, + ) + + layer = zope.configuration.fields.GlobalInterface( + title=_("The layer the view is in."), + description=_(""" + A skin is composed of layers. It is common to put skin + specific views in a layer named after the skin. If the 'layer' + attribute is not supplied, it defaults to 'default'."""), + required=False, + ) + + allowed_interface = zope.configuration.fields.Tokens( + title=_("Interface that is also allowed if user has permission."), + description=_(""" + By default, 'permission' only applies to viewing the view and + any possible sub views. By specifying this attribute, you can + make the permission also apply to everything described in the + supplied interface. + + Multiple interfaces can be provided, separated by + whitespace."""), + required=False, + value_type=zope.configuration.fields.GlobalInterface(), + ) + + allowed_attributes = zope.configuration.fields.Tokens( + title=_("View attributes that are also allowed if the user" + " has permission."), + description=_(""" + By default, 'permission' only applies to viewing the view and + any possible sub views. By specifying 'allowed_attributes', + you can make the permission also apply to the extra attributes + on the view object."""), + required=False, + value_type=zope.configuration.fields.PythonIdentifier(), + ) + +class IBasicResourceInformation(zope.interface.Interface): + """ + Basic information for resources + """ + + name = zope.schema.TextLine( + title=_("The name of the resource."), + description=_("The name shows up in URLs/paths. For example 'foo'."), + required=True, + default=u'', + ) + + provides = zope.configuration.fields.GlobalInterface( + title=_("The interface this component provides."), + description=_(""" + A view can provide an interface. This would be used for + views that support other views."""), + required=False, + default=zope.interface.Interface, + ) + + type = zope.configuration.fields.GlobalInterface( + title=_("Request type"), + required=True + ) + + +class IViewDirective(IBasicViewInformation, IBasicResourceInformation): + """Register a view for a component""" + + factory = zope.configuration.fields.Tokens( + title=_("Factory"), + required=False, + value_type=zope.configuration.fields.GlobalObject(), + ) + +def view(_context, factory, type, name, for_, layer=None, + permission=None, allowed_interface=None, allowed_attributes=None, + provides=zope.interface.Interface): + + if ((allowed_attributes or allowed_interface) + and (not permission)): + raise ConfigurationError( + "Must use name attribute with allowed_interface or " + "allowed_attributes" + ) + + if not factory: + raise ConfigurationError("No view factory specified.") + + if permission is not None: + check_security_support() + + checker = _checker(_context, permission, + allowed_interface, allowed_attributes) + + class ProxyView(object): + """Class to create simple proxy views.""" + + def __init__(self, factory, checker): + self.factory = factory + self.checker = checker + + def __call__(self, *objects): + return proxify(self.factory(*objects), self.checker) + + factory[-1] = ProxyView(factory[-1], checker) + + + if not for_: + raise ValueError("No for interfaces specified"); + for_ = tuple(for_) + + # Generate a single factory from multiple factories: + factories = factory + if len(factories) == 1: + factory = factories[0] + elif len(factories) < 1: + raise ValueError("No factory specified") + elif len(factories) > 1 and len(for_) > 1: + raise ValueError("Can't use multiple factories and multiple for") + else: + def factory(ob, request): + for f in factories[:-1]: + ob = f(ob) + return factories[-1](ob, request) + + # BBB 2006/02/18, to be removed after 12 months + if layer is not None: + for_ = for_ + (layer,) + warnings.warn_explicit( + "The 'layer' argument of the 'view' directive has been " + "deprecated. Use the 'type' argument instead. If you have " + "an existing 'type' argument IBrowserRequest, replace it with the " + "'layer' argument (the layer subclasses IBrowserRequest). " + "which subclasses BrowserRequest.", + DeprecationWarning, _context.info.file, _context.info.line) + else: + for_ = for_ + (type,) + + _context.action( + discriminator = ('view', for_, name, provides), + callable = handler, + args = ('registerAdapter', + factory, for_, provides, name, _context.info), + ) + if type is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', type) + ) + + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', provides) + ) + + if for_ is not None: + for iface in for_: + if iface is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', iface) + ) + + +class IResourceDirective(IBasicComponentInformation, + IBasicResourceInformation): + """Register a resource""" + + layer = zope.configuration.fields.GlobalInterface( + title=_("The layer the resource is in."), + required=False, + ) + + allowed_interface = zope.configuration.fields.Tokens( + title=_("Interface that is also allowed if user has permission."), + required=False, + value_type=zope.configuration.fields.GlobalInterface(), + ) + + allowed_attributes = zope.configuration.fields.Tokens( + title=_("View attributes that are also allowed if user" + " has permission."), + required=False, + value_type=zope.configuration.fields.PythonIdentifier(), + ) + +def resource(_context, factory, type, name, layer=None, + permission=None, + allowed_interface=None, allowed_attributes=None, + provides=zope.interface.Interface): + + if ((allowed_attributes or allowed_interface) + and (not permission)): + raise ConfigurationError( + "Must use name attribute with allowed_interface or " + "allowed_attributes" + ) + + if permission is not None: + check_security_support() + + checker = _checker(_context, permission, + allowed_interface, allowed_attributes) + + def proxyResource(request, factory=factory, checker=checker): + return proxify(factory(request), checker) + + factory = proxyResource + + if layer is not None: + warnings.warn_explicit( + "The 'layer' argument of the 'resource' directive has been " + "deprecated. Use the 'type' argument instead.", + DeprecationWarning, _context.info.file, _context.info.line) + type = layer + + _context.action( + discriminator = ('resource', name, type, provides), + callable = handler, + args = ('registerAdapter', + factory, (type,), provides, name, _context.info), + ) + _context.action( + discriminator = None, + callable = provideInterface, + args = (type.__module__ + '.' + type.__name__, type) + ) + _context.action( + discriminator = None, + callable = provideInterface, + args = (provides.__module__ + '.' + provides.__name__, type) + ) diff -Nru zope3-3.4.0/src/zope/component/zcml.txt zope3-3.5~bzr18/src/zope/component/zcml.txt --- zope3-3.4.0/src/zope/component/zcml.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/component/zcml.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1021 @@ +ZCML directives +=============== + +Components may be registered using the registration API exposed by +``zope.component`` (provideAdapter, provideUtility, etc.). They may +also be registered using configuration files. The common way to do +that is by using ZCML (Zope Configuration Markup Language), an XML +spelling of component registration. + +In ZCML, each XML element is a *directive*. There are different +top-level directives that let us register components. We will +introduce them one by one here. + +This helper will let us easily execute ZCML snippets: + + >>> from cStringIO import StringIO + >>> from zope.configuration.xmlconfig import xmlconfig + >>> def runSnippet(snippet): + ... template = """\ + ... + ... %s + ... """ + ... xmlconfig(StringIO(template % snippet)) + +adapter +------- + +Adapters play a key role in the Component Architecture. In ZCML, they +are registered with the directive. + + >>> from zope.component.testfiles.adapter import A1, A2, A3, Handler + >>> from zope.component.testfiles.adapter import I1, I2, I3, IS + >>> from zope.component.testfiles.components import IContent, Content, Comp, comp + +Before we register the first test adapter, we can verify that adapter +lookup doesn't work yet: + + >>> from zope.component.tests import clearZCML + >>> clearZCML() + >>> from zope.component.testfiles.components import IApp + >>> IApp(Content(), None) is None + True + +Then we register the adapter and see that the lookup works: + + >>> runSnippet(''' + ... ''') + + >>> IApp(Content()).__class__ + + +It is also possible to give adapters names. Then the combination of +required interface, provided interface and name makes the adapter +lookup unique. The name is supplied using the ``name`` argument to +the directive: + + >>> from zope.component.tests import clearZCML + >>> clearZCML() + >>> import zope.component + >>> zope.component.queryAdapter(Content(), IApp, 'test') is None + True + + >>> runSnippet(''' + ... ''') + + >>> zope.component.getAdapter(Content(), IApp, 'test').__class__ + + +Adapter factories +~~~~~~~~~~~~~~~~~ + +It is possible to supply more than one adapter factory. In this case, +during adapter lookup each factory will be called and the return value +will be given to the next factory. The return value of the last +factory is returned as the result of the adapter lookup. For examle: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + +The resulting adapter is an A3, around an A2, around an A1, around the +adapted object: + + >>> content = Content() + >>> a3 = IApp(content) + >>> a3.__class__ is A3 + True + + >>> a2 = a3.context[0] + >>> a2.__class__ is A2 + True + + >>> a1 = a2.context[0] + >>> a1.__class__ is A1 + True + + >>> a1.context[0] is content + True + +Of course, if no factory is provided at all, we will get an error: + + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-8.8 + ValueError: No factory specified + + +Declaring ``for`` and ``provides`` in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The directive can figure out from the in-line Python +declaration (using ``zope.component.adapts()`` or +``zope.component.adapter()`` as well as ``zope.interface.implements``) +what the adapter should be registered for and what it provides:: + + >>> clearZCML() + >>> IApp(Content(), None) is None + True + + >>> runSnippet(''' + ... ''') + + >>> IApp(Content()).__class__ + + +Of course, if the adapter has no ``implements()`` declaration, ZCML +can't figure out what it provides: + + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-7.8 + TypeError: Missing 'provides' attribute + +On the other hand, if the factory implements more than one interface, +ZCML can't figure out what it should provide either: + + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-7.8 + TypeError: Missing 'provides' attribute + +A not so common edge case is registering adapters directly for +classes, not for interfaces. For example: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a1 = zope.component.getAdapter(content, I1, '') + >>> isinstance(a1, A1) + True + +This time, any object providing ``IContent`` won't work if it's not an +instance of the ``Content`` class: + + >>> import zope.interface + >>> class MyContent: + ... zope.interface.implements(IContent) + >>> zope.component.getAdapter(MyContent(), I1, '') # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ComponentLookupError: ... + +Multi-adapters +~~~~~~~~~~~~~~ + +Conventional adapters adapt one object to provide another interface. +Multi-adapters adapt several objects at once: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a1 = A1() + >>> a2 = A2() + >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3) + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1, a2) + True + +You can even adapt an empty list of objects (we call this a +null-adapter): + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> a3 = zope.component.queryMultiAdapter((), I3) + >>> a3.__class__ is A3 + True + >>> a3.context == () + True + +Even with multi-adapters, ZCML can figure out the ``for`` and +``provides`` parameters from the Python declarations: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> a3 = zope.component.queryMultiAdapter((content, a1, a2), I3) + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1, a2) + True + +Chained factories are not supported for multi-adapters, though: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-11.8 + ValueError: Can't use multiple factories and multiple for + +And neither for null-adapters: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-9.8 + ValueError: Can't use multiple factories and multiple for + +Protected adapters +~~~~~~~~~~~~~~~~~~ + +Adapters can be protected with a permission. First we have to define +a permission for which we'll have to register the +directive: + + >>> clearZCML() + >>> IApp(Content(), None) is None + True + + >>> import zope.security + >>> from zope.configuration.xmlconfig import XMLConfig + >>> XMLConfig('meta.zcml', zope.security)() + >>> runSnippet(''' + ... + ... ''') + +We see that the adapter is a location proxy now so that the +appropriate permissions can be found from the context: + + >>> IApp(Content()).__class__ + + >>> type(IApp(Content())) + + +We can also go about it a different way. Let's make a public adapter +and wrap the adapter in a security proxy. That often happens when +an adapter is turned over to untrusted code: + + >>> clearZCML() + >>> IApp(Content(), None) is None + True + + >>> runSnippet(''' + ... ''') + + >>> from zope.security.checker import ProxyFactory + >>> adapter = ProxyFactory(IApp(Content())) + >>> from zope.security.proxy import getTestProxyItems + >>> items = [item[0] for item in getTestProxyItems(adapter)] + >>> items + ['a', 'f'] + + >>> from zope.security.proxy import removeSecurityProxy + >>> removeSecurityProxy(adapter).__class__ is Comp + True + +Of course, this still works when we let the ZCML directive handler +figure out ``for`` and ``provides`` from the Python declarations: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> adapter = ProxyFactory(IApp(Content())) + >>> [item[0] for item in getTestProxyItems(adapter)] + ['a', 'f'] + >>> removeSecurityProxy(adapter).__class__ is Comp + True + +It also works with multi adapters: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a1 = A1() + >>> a2 = A2() + >>> a3 = ProxyFactory(zope.component.queryMultiAdapter((content, a1, a2), I3)) + >>> a3.__class__ == A3 + True + >>> [item[0] for item in getTestProxyItems(a3)] + ['f1', 'f2', 'f3'] + +It's probably not worth mentioning, but when we try to protect an +adapter with a permission that doesn't exist, we'll obviously get an +error: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ConfigurationExecutionError: exceptions.ValueError: ('Undefined permission id', 'zope.UndefinedPermission') + in: + File "", line 4.2-9.8 + Could not read source. + +Trusted adapters +~~~~~~~~~~~~~~~~ + +Trusted adapters are adapters that are trusted to do anything with the +objects they are given so that these objects are not security-proxied. +They are registered using the ``trusted`` argument to the +directive: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + +With an unproxied object, it's business as usual: + + >>> ob = Content() + >>> type(I1(ob)) is A1 + True + +With a security-proxied object, however, we get a security-proxied +adapter: + + >>> p = ProxyFactory(ob) + >>> a = I1(p) + >>> type(a) + + +While the adapter is security-proxied, the object it adapts is now +proxy-free. The adapter has umlimited access to it: + + >>> a = removeSecurityProxy(a) + >>> type(a) is A1 + True + >>> a.context[0] is ob + True + +We can also protect the trusted adapter with a permission: + + >>> clearZCML() + >>> XMLConfig('meta.zcml', zope.security)() + >>> runSnippet(''' + ... + ... ''') + +Again, with an unproxied object, it's business as usual: + + >>> ob = Content() + >>> type(I1(ob)) is A1 + True + +With a security-proxied object, we again get a security-proxied +adapter: + + >>> p = ProxyFactory(ob) + >>> a = I1(p) + >>> type(a) + + +Since we protected the adapter with a permission, we now encounter a +location proxy behind the security proxy: + + >>> a = removeSecurityProxy(a) + >>> type(a) + + >>> a.context[0] is ob + True + +There's one exception to all of this: When you use the public +permission (``zope.Public``), there will be no location proxy: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> ob = Content() + >>> p = ProxyFactory(ob) + >>> a = I1(p) + >>> type(a) + + + >>> a = removeSecurityProxy(a) + >>> type(a) is A1 + True + +We can also explicitply pass the ``locate`` argument to make sure we +get location proxies: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> ob = Content() + >>> p = ProxyFactory(ob) + >>> a = I1(p) + >>> type(a) + + + >>> a = removeSecurityProxy(a) + >>> type(a) + + + +subscriber +---------- + +With the directive you can register subscription +adapters or event subscribers with the adapter registry. Consider +this very typical example of a directive: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a1 = A1() + + >>> subscribers = zope.component.subscribers((content, a1), IS) + >>> a3 = subscribers[0] + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1) + True + +Note how ZCML provides some additional information when registering +components, such as the ZCML filename and line numbers: + + >>> sm = zope.component.getSiteManager() + >>> doc = [reg.info for reg in sm.registeredSubscriptionAdapters() + ... if reg.provided is IS][0] + >>> print doc + File "", line 4.2-9.8 + Could not read source. + +The "fun" behind subscription adapters/subscribers is that when +several ones are declared for the same for/provides, they are all +found. With regular adapters, the most specific one (and in doubt the +one registered last) wins. Consider these two subscribers: + + >>> clearZCML() + >>> runSnippet(''' + ... + ... ''') + + >>> subscribers = zope.component.subscribers((content, a1), IS) + >>> len(subscribers) + 2 + >>> sorted([a.__class__.__name__ for a in subscribers]) + ['A2', 'A3'] + +Declaring ``for`` and ``provides`` in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Like the directive, the directive can +figure out from the in-line Python declaration (using +``zope.component.adapts()`` or ``zope.component.adapter()``) what the +subscriber should be registered for: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> content = Content() + >>> a2 = A2() + >>> subscribers = zope.component.subscribers((content, a1, a2), IS) + + >>> a3 = subscribers[0] + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1, a2) + True + +In the same way the directive can figure out what a subscriber +provides: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> sm = zope.component.getSiteManager() + >>> a3 = sm.adapters.subscriptions((IContent, I1, I2), None)[0] + >>> a3 is A3 + True + +A not so common edge case is declaring subscribers directly for +classes, not for interfaces. For example: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + + >>> subs = list(zope.component.subscribers((Content(),), I1)) + >>> isinstance(subs[0], A1) + True + +This time, any object providing ``IContent`` won't work if it's not an +instance of the ``Content`` class: + + >>> list(zope.component.subscribers((MyContent(),), I1)) + [] + +Protected subscribers +~~~~~~~~~~~~~~~~~~~~~ + +Subscribers can also be protected with a permission. First we have to +define a permission for which we'll have to register the +directive: + + >>> clearZCML() + >>> XMLConfig('meta.zcml', zope.security)() + >>> runSnippet(''' + ... + ... ''') + + >>> subscribers = zope.component.subscribers((content, a1), IS) + >>> a3 = subscribers[0] + >>> a3.__class__ is A3 + True + >>> type(a3) + + >>> a3.context == (content, a1) + True + +Trusted subscribers +~~~~~~~~~~~~~~~~~~~ + +Like trusted adapters, trusted subscribers are subscribers that are +trusted to do anything with the objects they are given so that these +objects are not security-proxied. In analogy to the +directive, they are registered using the ``trusted`` argument to the + directive: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + +With an unproxied object, it's business as usual: + + >>> subscribers = zope.component.subscribers((content, a1), IS) + >>> a3 = subscribers[0] + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1) + True + >>> type(a3) is A3 + True + +Now with a proxied object. We will see that the subscriber has +unproxied access to it, but the subscriber itself is proxied: + + >>> p = ProxyFactory(content) + >>> a3 = zope.component.subscribers((p, a1), IS)[0] + >>> type(a3) + + +There's no location proxy behind the security proxy: + + >>> removeSecurityProxy(a3).context[0] is content + True + >>> type(removeSecurityProxy(a3)) is A3 + True + +If you want the trusted subscriber to be located, you'll also have to +use the ``locate`` argument: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + +Again, it's business as usual with an unproxied object: + + >>> subscribers = zope.component.subscribers((content, a1), IS) + >>> a3 = subscribers[0] + >>> a3.__class__ is A3 + True + >>> a3.context == (content, a1) + True + >>> type(a3) is A3 + True + +With a proxied object, we again get a security-proxied subscriber: + + >>> p = ProxyFactory(content) + >>> a3 = zope.component.subscribers((p, a1), IS)[0] + + >>> type(a3) + + + >>> removeSecurityProxy(a3).context[0] is content + True + +However, thanks to the ``locate`` argument, we now have a location +proxy behind the security proxy: + + >>> type(removeSecurityProxy(a3)) + + +Event subscriber (handlers) +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes, subscribers don't need to be adapters that actually provide +anything. It's enough that a callable is called for a certain event. + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + +In this case, simply getting the subscribers is enough to invoke them: + + >>> list(zope.component.subscribers((content, a1), None)) + [] + >>> content.args == ((a1,),) + True + + +utility +------- + +Apart from adapters (and subscription adapters), the Component +Architecture knows a second kind of component: utilities. They are +registered using the directive. + +Before we register the first test utility, we can verify that utility +lookup doesn't work yet: + + >>> clearZCML() + >>> zope.component.queryUtility(IApp) is None + True + +Then we register the utility: + + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp) is comp + True + +Like adapters, utilities can also have names. There can be more than +one utility registered for a certain interface, as long as they each +have a different name. + +First, we make sure that there's no utility yet: + + >>> clearZCML() + >>> zope.component.queryUtility(IApp, 'test') is None + True + +Then we register it: + + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp, 'test') is comp + True + +Utilities can also be registered from a factory. In this case, the +ZCML handler calls the factory (without any arguments) and registers +the returned value as a utility. Typically, you'd pass a class for +the factory: + + >>> clearZCML() + >>> zope.component.queryUtility(IApp) is None + True + + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp).__class__ is Comp + True + +Declaring ``provides`` in Python +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Like other directives, can also figure out which interface +a utility provides from the Python declaration: + + >>> clearZCML() + >>> zope.component.queryUtility(IApp) is None + True + + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp) is comp + True + +It won't work if the component that is to be registered doesn't +provide anything: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-4.61 + TypeError: Missing 'provides' attribute + +Or if more than one interface is provided (then the ZCML directive +handler doesn't know under which the utility should be registered): + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-4.61 + TypeError: Missing 'provides' attribute + +We can repeat the same drill for utility factories: + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + >>> zope.component.getUtility(IApp).__class__ is Comp + True + + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-4.59 + TypeError: Missing 'provides' attribute + + >>> clearZCML() + >>> runSnippet(''' + ... ''') + Traceback (most recent call last): + ... + ZopeXMLConfigurationError: File "", line 4.2-4.59 + TypeError: Missing 'provides' attribute + +Protected utilities +~~~~~~~~~~~~~~~~~~~ + +TODO:: + + def testProtectedUtility(self): + """Test that we can protect a utility. + + Also: + Check that multiple configurations for the same utility and + don't interfere. + """ + self.assertEqual(zope.component.queryUtility(IV), None) + xmlconfig(StringIO(template % ( + ''' + + + + + ''' + ))) + + utility = ProxyFactory(zope.component.getUtility(IApp)) + items = getTestProxyItems(utility) + self.assertEqual(items, [('a', 'tell.everyone'), + ('f', 'tell.everyone') + ]) + self.assertEqual(removeSecurityProxy(utility), comp) + + def testUtilityUndefinedPermission(self): + config = StringIO(template % ( + ''' + + ''' + )) + self.assertRaises(ValueError, xmlconfig, config, + testing=1) + +interface +--------- + +The directive lets us register an interface. Interfaces +are registered as named utilities. We therefore needn't go though all +the lookup details again, it is sufficient to see whether the +directive handler emits the right actions. + +First we provide a stub configuration context: + + >>> import re, pprint + >>> atre = re.compile(' at [0-9a-fA-Fx]+') + >>> class Context(object): + ... actions = () + ... def action(self, discriminator, callable, args): + ... self.actions += ((discriminator, callable, args), ) + ... def __repr__(self): + ... stream = StringIO() + ... pprinter = pprint.PrettyPrinter(stream=stream, width=60) + ... pprinter.pprint(self.actions) + ... r = stream.getvalue() + ... return (''.join(atre.split(r))).strip() + >>> context = Context() + +Then we provide a test interface that we'd like to register: + + >>> from zope.interface import Interface + >>> class I(Interface): + ... pass + +It doesn't yet provide ``ITestType``: + + >>> from zope.component.tests import ITestType + >>> ITestType.providedBy(I) + False + +However, after calling the directive handler... + + >>> from zope.component.zcml import interface + >>> interface(context, I, ITestType) + >>> context + ((None, + , + ('', + , + )),) + +...it does provide ``ITestType``: + + >>> from zope.interface.interfaces import IInterface + >>> ITestType.extends(IInterface) + True + >>> IInterface.providedBy(I) + True diff -Nru zope3-3.4.0/src/zope/componentvocabulary/configure.zcml zope3-3.5~bzr18/src/zope/componentvocabulary/configure.zcml --- zope3-3.4.0/src/zope/componentvocabulary/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/componentvocabulary/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/componentvocabulary/i18n.py zope3-3.5~bzr18/src/zope/componentvocabulary/i18n.py --- zope3-3.4.0/src/zope/componentvocabulary/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/componentvocabulary/i18n.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Customization of zope.i18n for the Zope application server + +$Id: i18n.py 73604 2007-03-25 12:17:44Z dobe $ +""" +__docformat__ = 'restructuredtext' + +# import this as _ to create i18n messages in the zope domain +from zope.i18nmessageid import MessageFactory +ZopeMessageFactory = MessageFactory('zope') diff -Nru zope3-3.4.0/src/zope/componentvocabulary/__init__.py zope3-3.5~bzr18/src/zope/componentvocabulary/__init__.py --- zope3-3.4.0/src/zope/componentvocabulary/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/componentvocabulary/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,15 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Component vocabularies. +""" diff -Nru zope3-3.4.0/src/zope/componentvocabulary/tests/__init__.py zope3-3.5~bzr18/src/zope/componentvocabulary/tests/__init__.py --- zope3-3.4.0/src/zope/componentvocabulary/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/componentvocabulary/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/componentvocabulary/tests/test_vocabulary.py zope3-3.5~bzr18/src/zope/componentvocabulary/tests/test_vocabulary.py --- zope3-3.4.0/src/zope/componentvocabulary/tests/test_vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/componentvocabulary/tests/test_vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation 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. +# +############################################################################## +"""Vocabulary tests + +$Id$ +""" +__docformat__ = "reStructuredText" +import doctest + + +def test_suite(): + return doctest.DocTestSuite('zope.componentvocabulary.vocabulary') diff -Nru zope3-3.4.0/src/zope/componentvocabulary/vocabulary.py zope3-3.5~bzr18/src/zope/componentvocabulary/vocabulary.py --- zope3-3.4.0/src/zope/componentvocabulary/vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/componentvocabulary/vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,427 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Utility Vocabulary. + +This vocabulary provides terms for all utilities providing a given interface. + +$Id: vocabulary.py 100139 2009-05-19 20:15:33Z faassen $ +""" +__docformat__ = "reStructuredText" + +import zope.component +from zope.component.interface import interfaceToName +from zope.component.interfaces import IUtilityRegistration +from zope.interface import implements, classProvides, Interface, providedBy +from zope.interface.interfaces import IInterface +from zope.security.proxy import removeSecurityProxy +from zope.schema.interfaces import IVocabularyTokenized +from zope.schema.interfaces import ITokenizedTerm, ITitledTokenizedTerm +from zope.schema.interfaces import IVocabularyFactory +from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm + +from zope.componentvocabulary.i18n import ZopeMessageFactory as _ + +class UtilityTerm(object): + """A term representing a utility. + + The token of the term is the name of the utility. Here is a brief example + on how the IVocabulary interface is handled in this term as a + utility: + + >>> from zope.interface.verify import verifyObject + >>> from zope.schema.interfaces import IVocabulary + >>> term = UtilityTerm(IVocabulary, 'zope.schema.interfaces.IVocabulary') + >>> verifyObject(ITokenizedTerm, term) + True + + >>> term.value + + >>> term.token + 'zope.schema.interfaces.IVocabulary' + + >>> term + + """ + implements(ITokenizedTerm) + + def __init__(self, value, token): + """Create a term for value and token.""" + self.value = value + self.token = token + + def __repr__(self): + return '' %( + self.token, self.value.__class__.__name__) + + +class UtilityVocabulary(object): + """Vocabulary that provides utilities of a specified interface. + + Here is a short example of how the vocabulary should work. + + First we need to create a utility interface and some utilities: + + >>> class IObject(Interface): + ... 'Simple interface to mark object utilities.' + >>> + >>> class Object(object): + ... implements(IObject) + ... def __init__(self, name): + ... self.name = name + ... def __repr__(self): + ... return '' %self.name + + Now we register some utilities for IObject + + >>> from zope import component + >>> object1 = Object('object1') + >>> component.provideUtility(object1, IObject, 'object1') + >>> object2 = Object('object2') + >>> component.provideUtility(object2, IObject, 'object2') + >>> object3 = Object('object3') + >>> component.provideUtility(object3, IObject, 'object3') + >>> object4 = Object('object4') + + We are now ready to create a vocabulary that we can use; in our case + everything is global, so the context is None. + + >>> vocab = UtilityVocabulary(None, interface=IObject) + >>> import pprint + >>> pprint.pprint(vocab._terms.items()) + [(u'object1', ), + (u'object2', ), + (u'object3', )] + + Now let's see how the other methods behave in this context. First we can + just use the 'in' opreator to test whether a value is available. + + >>> object1 in vocab + True + >>> object4 in vocab + False + + We can also create a lazy iterator. Note that the utility terms might + appear in a different order than the utilities were registered. + + >>> iterator = iter(vocab) + >>> terms = list(iterator) + >>> names = [term.token for term in terms] + >>> names.sort() + >>> names + [u'object1', u'object2', u'object3'] + + Determining the amount of utilities available via the vocabulary is also + possible. + + >>> len(vocab) + 3 + + Next we are looking at some of the more vocabulary-characteristic API + methods. + + One can get a term for a given value using ``getTerm()``: + + >>> vocab.getTerm(object1) + + >>> vocab.getTerm(object4) + Traceback (most recent call last): + ... + LookupError: + + On the other hand, if you want to get a term by the token, then you do + that with: + + >>> vocab.getTermByToken('object1') + + >>> vocab.getTermByToken('object4') + Traceback (most recent call last): + ... + LookupError: object4 + + That's it. It is all pretty straight forward, but it allows us to easily + create a vocabulary for any utility. In fact, to make it easy to register + such a vocabulary via ZCML, the `interface` argument to the constructor + can be a string that is resolved via the utility registry. The ZCML looks + like this: + + + + >>> component.provideUtility(IObject, IInterface, + ... 'zope.app.utility.vocabulary.IObject') + >>> vocab = UtilityVocabulary( + ... None, interface='zope.app.utility.vocabulary.IObject') + >>> pprint.pprint(vocab._terms.items()) + [(u'object1', ), + (u'object2', ), + (u'object3', )] + + Sometimes it is desirable to only select the name of a utility. For + this purpose a `nameOnly` argument was added to the constructor, in which + case the UtilityTerm's value is not the utility itself but the name of the + utility. + + >>> vocab = UtilityVocabulary(None, interface=IObject, nameOnly=True) + >>> pprint.pprint([term.value for term in vocab]) + [u'object1', u'object2', u'object3'] + """ + implements(IVocabularyTokenized) + classProvides(IVocabularyFactory) + + # override these in subclasses + interface = Interface + nameOnly = False + + def __init__(self, context, **kw): + if kw: + # BBB 2006/02/24, to be removed after 12 months + # the 'interface' and 'nameOnly' parameters are supposed to be + # set as class-level attributes in custom subclasses now. + self.nameOnly = bool(kw.get('nameOnly', False)) + interface = kw.get('interface', Interface) + if isinstance(interface, (str, unicode)): + interface = zope.component.getUtility(IInterface, interface) + self.interface = interface + + utils = zope.component.getUtilitiesFor(self.interface, context) + self._terms = dict( + (name, UtilityTerm(self.nameOnly and name or util, name)) + for name, util in utils) + + def __contains__(self, value): + """See zope.schema.interfaces.IBaseVocabulary""" + return value in (term.value for term in self._terms.values()) + + def getTerm(self, value): + """See zope.schema.interfaces.IBaseVocabulary""" + try: + return [term for name, term in self._terms.items() + if term.value == value][0] + except IndexError: + raise LookupError(value) + + def getTermByToken(self, token): + """See zope.schema.interfaces.IVocabularyTokenized""" + try: + return self._terms[token] + except KeyError: + raise LookupError(token) + + def __iter__(self): + """See zope.schema.interfaces.IIterableVocabulary""" + # Sort the terms by the token (utility name) + values = self._terms.values() + values.sort(lambda x, y: cmp(x.token, y.token)) + return iter(values) + + def __len__(self): + """See zope.schema.interfaces.IIterableVocabulary""" + return len(self._terms) + +class InterfacesVocabulary(UtilityVocabulary): + classProvides(IVocabularyFactory) + interface = IInterface + +class ObjectInterfacesVocabulary(SimpleVocabulary): + """A vocabulary that provides a list of all interfaces that its context + provides. + + Here a quick demonstration: + + >>> from zope.interface import Interface, implements + >>> class I1(Interface): + ... pass + >>> class I2(Interface): + ... pass + >>> class I3(I2): + ... pass + + >>> class Object(object): + ... implements(I3, I1) + + >>> vocab = ObjectInterfacesVocabulary(Object()) + >>> import pprint + >>> names = [term.token for term in vocab] + >>> names.sort() + >>> pprint.pprint(names) + ['zope.componentvocabulary.vocabulary.I1', + 'zope.componentvocabulary.vocabulary.I2', + 'zope.componentvocabulary.vocabulary.I3', + 'zope.interface.Interface'] + """ + classProvides(IVocabularyFactory) + + def __init__(self, context): + # Remove the security proxy so the values from the vocabulary + # are the actual interfaces and not proxies. + component = removeSecurityProxy(context) + interfaces = providedBy(component).flattened() + terms = [SimpleTerm(interface, interfaceToName(context, interface)) + for interface in interfaces] + super(ObjectInterfacesVocabulary, self).__init__(terms) + +class UtilityComponentInterfacesVocabulary(ObjectInterfacesVocabulary): + classProvides(IVocabularyFactory) + + def __init__(self, context): + if IUtilityRegistration.providedBy(context): + context = context.component + super(UtilityComponentInterfacesVocabulary, self).__init__( + context) + + +class UtilityNameTerm: + r"""Simple term that provides a utility name as a value. + + >>> t1 = UtilityNameTerm('abc') + >>> t2 = UtilityNameTerm(u'\xC0\xDF\xC7') + >>> t1.value + u'abc' + >>> t2.value + u'\xc0\xdf\xc7' + >>> t1.title + u'abc' + >>> repr(t2.title) + "u'\\xc0\\xdf\\xc7'" + >>> ITitledTokenizedTerm.providedBy(t1) + True + + The tokens used for form values are Base-64 encodings of the + names, with the letter 't' prepended to ensure the unnamed utility + is supported: + + >>> t1.token + 'tYWJj' + >>> t2.token + 'tw4DDn8OH' + + The unnamed utility is given an artificial title for use in user + interfaces: + + >>> t3 = UtilityNameTerm(u'') + >>> t3.title + u'(unnamed utility)' + + """ + + implements(ITitledTokenizedTerm) + + def __init__(self, value): + self.value = unicode(value) + + @property + def token(self): + # Return our value as a token. This is required to be 7-bit + # printable ascii. We'll use base64 generated from the UTF-8 + # representation. (The default encoding rules should not be + # allowed to apply.) + return "t" + self.value.encode('utf-8').encode('base64')[:-1] + + @property + def title(self): + return self.value or _("(unnamed utility)") + + +class UtilityNames: + """Vocabulary with utility names for a single interface as values. + + >>> class IMyUtility(Interface): + ... pass + + >>> class MyUtility(object): + ... implements(IMyUtility) + + >>> vocab = UtilityNames(IMyUtility) + + >>> from zope.schema.interfaces import IVocabulary + >>> IVocabulary.providedBy(vocab) + True + >>> IVocabularyTokenized.providedBy(vocab) + True + + >>> from zope.component.testing import PlacelessSetup + >>> from zope import component + >>> ps = PlacelessSetup() + >>> ps.setUp() + + >>> component.provideUtility(MyUtility(), IMyUtility, 'one') + >>> component.provideUtility(MyUtility(), IMyUtility, 'two') + + >>> unames = UtilityNames(IMyUtility) + >>> len(list(unames)) + 2 + >>> L = [t.value for t in unames] + >>> L.sort() + >>> L + [u'one', u'two'] + + >>> u'one' in vocab + True + >>> u'three' in vocab + False + >>> component.provideUtility(MyUtility(), IMyUtility, 'three') + >>> u'three' in vocab + True + + >>> component.provideUtility(MyUtility(), IMyUtility) + >>> u'' in vocab + True + >>> term1 = vocab.getTerm(u'') + >>> term2 = vocab.getTermByToken(term1.token) + >>> term2.value + u'' + >>> term3 = vocab.getTerm(u'one') + >>> term3.token + 'tb25l' + >>> term3a = vocab.getTermByToken('tb25l') + >>> term3.value + u'one' + + >>> ps.tearDown() + """ + + implements(IVocabularyTokenized) + + def __init__(self, interface): + self.interface = interface + + def __contains__(self, value): + return zope.component.queryUtility(self.interface, value) is not None + + def getTerm(self, value): + if value in self: + return UtilityNameTerm(value) + raise ValueError(value) + + def getTermByToken(self, token): + for name, ut in zope.component.getUtilitiesFor(self.interface): + name = unicode(name) + if token == "t": + if not name: + break + elif UtilityNameTerm(name).token == token: + break + else: + raise LookupError("no matching token: %r" % token) + return self.getTerm(name) + + def __iter__(self): + for name, ut in zope.component.getUtilitiesFor(self.interface): + yield UtilityNameTerm(name) + + def __len__(self): + """Return the number of valid terms, or sys.maxint.""" + return len(list(zope.component.getUtilitiesFor(self.interface))) diff -Nru zope3-3.4.0/src/zope/configuration/config.py zope3-3.5~bzr18/src/zope/configuration/config.py --- zope3-3.4.0/src/zope/configuration/config.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/config.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1635 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Configuration processor + +See README.txt. + +$Id: config.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +__docformat__ = 'restructuredtext' +import __builtin__ +import os.path +import sys + +import zope.schema + +from keyword import iskeyword +from zope.configuration.exceptions import ConfigurationError +from zope.configuration.interfaces import IConfigurationContext +from zope.configuration.interfaces import IGroupingContext +from zope.interface.adapter import AdapterRegistry +from zope.interface import Interface, implements, providedBy +from zope.configuration import fields + + +zopens = 'http://namespaces.zope.org/zope' +metans = 'http://namespaces.zope.org/meta' +testns = 'http://namespaces.zope.org/test' + +_import_chickens = {}, {}, ("*",) # dead chickens needed by __import__ + +class ConfigurationContext(object): + """Mix-in that implements IConfigurationContext + + Subclasses provide a ``package`` attribute and a ``basepath`` + attribute. If the base path is not None, relative paths are + converted to absolute paths using the the base path. If the + package is not none, relative imports are performed relative to + the package. + + In general, the basepath and package attributes should be + consistent. When a package is provided, the base path should be + set to the path of the package directory. + + Subclasses also provide an ``actions`` attribute, which is a list + of actions, an ``includepath`` attribute, and an ``info`` + attribute. + + The include path is appended to each action and is used when + resolving conflicts among actions. Normally, only the a + ConfigurationMachine provides the actions attribute. Decorators + simply use the actions of the context they decorate. The + ``includepath`` attribute is a tuple of names. Each name is + typically the name of an included configuration file. + + The ``info`` attribute contains descriptive information helpful + when reporting errors. If not set, it defaults to an empty string. + + The actions attribute is a sequence of tuples with items: + + - discriminator, a value that identifies the action. Two actions + that have the same (non None) discriminator conflict. + + - an object that is called to execute the action, + + - positional arguments for the action + + - keyword arguments for the action + + - a tuple of include file names (defaults to ()) + + - an object that has descriptive information about + the action (defaults to '') + + For brevity, trailing items after the callable in the tuples are + ommitted if they are empty. + + """ + + def __init__(self): + super(ConfigurationContext, self).__init__() + self._seen_files = set() + self._features = set() + + def resolve(self, dottedname): + """Resolve a dotted name to an object + + Examples: + + + >>> c = ConfigurationContext() + >>> import zope, zope.interface + >>> c.resolve('zope') is zope + 1 + >>> c.resolve('zope.interface') is zope.interface + 1 + + >>> c.resolve('zope.configuration.eek') #doctest: +NORMALIZE_WHITESPACE + Traceback (most recent call last): + ... + ConfigurationError: + ImportError: Module zope.configuration has no global eek + + >>> c.resolve('.config.ConfigurationContext') + Traceback (most recent call last): + ... + AttributeError: 'ConfigurationContext' object has no attribute """ \ + """'package' + >>> import zope.configuration + >>> c.package = zope.configuration + >>> c.resolve('.') is zope.configuration + 1 + >>> c.resolve('.config.ConfigurationContext') is ConfigurationContext + 1 + >>> c.resolve('..interface') is zope.interface + 1 + >>> c.resolve('unicode') + + """ + + name = dottedname.strip() + if not name: + raise ValueError("The given name is blank") + + if name == '.': + return self.package + + names = name.split('.') + if not names[-1]: + raise ValueError( + "Trailing dots are no longer supported in dotted names") + + if len(names) == 1: + # Check for built-in objects + marker = object() + obj = getattr(__builtin__, names[0], marker) + if obj is not marker: + return obj + + if not names[0]: + # Got a relative name. Convert it to abs using package info + if self.package is None: + raise ConfigurationError( + "Can't use leading dots in dotted names, " + "no package has been set.") + pnames = self.package.__name__.split(".") + pnames.append('') + while names and not names[0]: + try: + names.pop(0) + except IndexError: + raise ConfigurationError("Invalid global name", name) + try: + pnames.pop() + except IndexError: + raise ConfigurationError("Invalid global name", name) + names[0:0] = pnames + + # Now we should have an absolute dotted name + + # Split off object name: + oname, mname = names[-1], '.'.join(names[:-1]) + + # Import the module + if not mname: + # Just got a single name. Must me a module + mname = oname + oname = '' + + try: + mod = __import__(mname, *_import_chickens) + except ImportError, v: + if sys.exc_info()[2].tb_next is not None: + # ImportError was caused deeper + raise + raise ConfigurationError( + "ImportError: Couldn't import %s, %s" % (mname, v)) + + if not oname: + # see not mname case above + return mod + + + try: + obj = getattr(mod, oname) + return obj + except AttributeError: + # No such name, maybe it's a module that we still need to import + try: + return __import__(mname+'.'+oname, *_import_chickens) + except ImportError: + if sys.exc_info()[2].tb_next is not None: + # ImportError was caused deeper + raise + raise ConfigurationError( + "ImportError: Module %s has no global %s" % (mname, oname)) + + def path(self, filename): + """ + Examples: + + >>> c = ConfigurationContext() + >>> c.path("/x/y/z") == os.path.normpath("/x/y/z") + 1 + >>> c.path("y/z") + Traceback (most recent call last): + ... + AttributeError: 'ConfigurationContext' object has no attribute """ \ + """'package' + >>> import zope.configuration + >>> c.package = zope.configuration + >>> import os + >>> d = os.path.dirname(zope.configuration.__file__) + >>> c.path("y/z") == d + os.path.normpath("/y/z") + 1 + >>> c.path("y/./z") == d + os.path.normpath("/y/z") + 1 + >>> c.path("y/../z") == d + os.path.normpath("/z") + 1 + """ + + filename = os.path.normpath(filename) + if os.path.isabs(filename): + return filename + + # Got a relative path, combine with base path. + # If we have no basepath, compute the base path from the package + # path. + + basepath = getattr(self, 'basepath', '') + + if not basepath: + if self.package is None: + basepath = os.getcwd() + else: + basepath = os.path.dirname(self.package.__file__) + basepath = os.path.abspath(basepath) + self.basepath = basepath + + return os.path.join(basepath, filename) + + def checkDuplicate(self, filename): + """Check for duplicate imports of the same file. + + Raises an exception if this file had been processed before. This + is better than an unlimited number of conflict errors. + + >>> c = ConfigurationContext() + >>> c.checkDuplicate('/foo.zcml') + >>> try: + ... c.checkDuplicate('/foo.zcml') + ... except ConfigurationError, e: + ... # On Linux the exact msg has /foo, on Windows \foo. + ... str(e).endswith("foo.zcml' included more than once") + True + + You may use different ways to refer to the same file: + + >>> import zope.configuration + >>> c.package = zope.configuration + >>> import os + >>> d = os.path.dirname(zope.configuration.__file__) + >>> c.checkDuplicate('bar.zcml') + >>> try: + ... c.checkDuplicate(d + os.path.normpath('/bar.zcml')) + ... except ConfigurationError, e: + ... str(e).endswith("bar.zcml' included more than once") + ... + True + + """ #' <-- bow to font-lock + path = self.path(filename) + if path in self._seen_files: + raise ConfigurationError('%r included more than once' % path) + self._seen_files.add(path) + + def processFile(self, filename): + """Check whether a file needs to be processed + + Return True if processing is needed and False otherwise. If + the file needs to be processed, it will be marked as + processed, assuming that the caller will procces the file if + it needs to be procssed. + + >>> c = ConfigurationContext() + >>> c.processFile('/foo.zcml') + True + >>> c.processFile('/foo.zcml') + False + + You may use different ways to refer to the same file: + + >>> import zope.configuration + >>> c.package = zope.configuration + >>> import os + >>> d = os.path.dirname(zope.configuration.__file__) + >>> c.processFile('bar.zcml') + True + >>> c.processFile('bar.zcml') + False + + """ #' <-- bow to font-lock + path = self.path(filename) + if path in self._seen_files: + return False + self._seen_files.add(path) + return True + + def action(self, discriminator, callable=None, args=(), kw={}, order=0): + """Add an action with the given discriminator, callable and arguments + + For testing purposes, the callable and arguments may be omitted. + In that case, a default noop callable is used. + + The discriminator must be given, but it can be None, to indicate that + the action never conflicts. + + Let's look at some examples: + + >>> c = ConfigurationContext() + + Normally, the context gets actions from subclasses. We'll provide + an actions attribute ourselves: + + >>> c.actions = [] + + We'll use a test callable that has a convenient string representation + + >>> from zope.configuration.tests.directives import f + + >>> c.action(1, f, (1, ), {'x': 1}) + >>> c.actions + [(1, f, (1,), {'x': 1})] + + >>> c.action(None) + >>> c.actions + [(1, f, (1,), {'x': 1}), (None, None)] + + Now set the include path and info: + + >>> c.includepath = ('foo.zcml',) + >>> c.info = "?" + >>> c.action(None) + >>> c.actions[-1] + (None, None, (), {}, ('foo.zcml',), '?') + + Finally, we can add an order argument to crudely control the order + of execution: + + >>> c.action(None, order=99999) + >>> c.actions[-1] + (None, None, (), {}, ('foo.zcml',), '?', 99999) + + """ + action = (discriminator, callable, args, kw, + getattr(self, 'includepath', ()), + getattr(self, 'info', ''), + order, + ) + + # remove trailing false items + while (len(action) > 2) and not action[-1]: + action = action[:-1] + + self.actions.append(action) + + def hasFeature(self, feature): + """Check whether a named feature has been provided. + + Initially no features are provided + + >>> c = ConfigurationContext() + >>> c.hasFeature('onlinehelp') + False + + You can declare that a feature is provided + + >>> c.provideFeature('onlinehelp') + + and it becomes available + + >>> c.hasFeature('onlinehelp') + True + + """ + return feature in self._features + + def provideFeature(self, feature): + """Declare thata named feature has been provided. + + See `hasFeature` for examples. + """ + self._features.add(feature) + + +class ConfigurationAdapterRegistry(object): + """Simple adapter registry that manages directives as adapters + + >>> r = ConfigurationAdapterRegistry() + >>> c = ConfigurationMachine() + >>> r.factory(c, ('http://www.zope.com','xxx')) + Traceback (most recent call last): + ... + ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'xxx') + >>> from zope.configuration.interfaces import IConfigurationContext + >>> def f(): + ... pass + + >>> r.register(IConfigurationContext, ('http://www.zope.com', 'xxx'), f) + >>> r.factory(c, ('http://www.zope.com','xxx')) is f + 1 + >>> r.factory(c, ('http://www.zope.com','yyy')) is f + Traceback (most recent call last): + ... + ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'yyy') + >>> r.register(IConfigurationContext, 'yyy', f) + >>> r.factory(c, ('http://www.zope.com','yyy')) is f + 1 + + Test the documentation feature: + + >>> r._docRegistry + [] + >>> r.document(('ns', 'dir'), IFullInfo, IConfigurationContext, None, + ... 'inf', None) + >>> r._docRegistry[0][0] == ('ns', 'dir') + 1 + >>> r._docRegistry[0][1] is IFullInfo + 1 + >>> r._docRegistry[0][2] is IConfigurationContext + 1 + >>> r._docRegistry[0][3] is None + 1 + >>> r._docRegistry[0][4] == 'inf' + 1 + >>> r._docRegistry[0][5] is None + 1 + >>> r.document('all-dir', None, None, None, None) + >>> r._docRegistry[1][0] + ('', 'all-dir') + """ + + + def __init__(self): + super(ConfigurationAdapterRegistry, self).__init__() + self._registry = {} + # Stores tuples of form: + # (namespace, name), schema, usedIn, info, parent + self._docRegistry = [] + + def register(self, interface, name, factory): + r = self._registry.get(name) + if r is None: + r = AdapterRegistry() + self._registry[name] = r + + r.register([interface], Interface, '', factory) + + def document(self, name, schema, usedIn, handler, info, parent=None): + if isinstance(name, (str, unicode)): + name = ('', name) + self._docRegistry.append((name, schema, usedIn, handler, info, parent)) + + def factory(self, context, name): + r = self._registry.get(name) + if r is None: + # Try namespace-independent name + ns, n = name + r = self._registry.get(n) + if r is None: + raise ConfigurationError("Unknown directive", ns, n) + + f = r.lookup1(providedBy(context), Interface) + if f is None: + raise ConfigurationError( + "The directive %s cannot be used in this context" % (name, )) + return f + +class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext): + """Configuration machine + + Example: + + >>> machine = ConfigurationMachine() + >>> ns = "http://www.zope.org/testing" + + Register a directive: + + >>> machine((metans, "directive"), + ... namespace=ns, name="simple", + ... schema="zope.configuration.tests.directives.ISimple", + ... handler="zope.configuration.tests.directives.simple") + + and try it out: + + >>> machine((ns, "simple"), a=u"aa", c=u"cc") + + >>> machine.actions + [(('simple', u'aa', u'xxx', 'cc'), f, (u'aa', u'xxx', 'cc'))] + + A more extensive example can be found in the unit tests. + """ + + implements(IConfigurationContext) + + package = None + basepath = None + includepath = () + info = '' + + def __init__(self): + super(ConfigurationMachine, self).__init__() + self.actions = [] + self.stack = [RootStackItem(self)] + self.i18n_strings = {} + _bootstrap(self) + + def begin(self, __name, __data=None, __info=None, **kw): + if __data: + if kw: + raise TypeError("Can't provide a mapping object and keyword " + "arguments") + else: + __data = kw + self.stack.append(self.stack[-1].contained(__name, __data, __info)) + + def end(self): + self.stack.pop().finish() + + def __call__(self, __name, __info=None, **__kw): + self.begin(__name, __kw, __info) + self.end() + + def getInfo(self): + return self.stack[-1].context.info + + def setInfo(self, info): + self.stack[-1].context.info = info + + def execute_actions(self, clear=True, testing=False): + """Execute the configuration actions + + This calls the action callables after resolving conflicts + + For example: + + >>> output = [] + >>> def f(*a, **k): + ... output.append(('f', a, k)) + >>> context = ConfigurationMachine() + >>> context.actions = [ + ... (1, f, (1,)), + ... (1, f, (11,), {}, ('x', )), + ... (2, f, (2,)), + ... ] + >>> context.execute_actions() + >>> output + [('f', (1,), {}), ('f', (2,), {})] + + If the action raises an error, we convert it to a + ConfigurationExecutionError. + + >>> output = [] + >>> def bad(): + ... bad.xxx + >>> context.actions = [ + ... (1, f, (1,)), + ... (1, f, (11,), {}, ('x', )), + ... (2, f, (2,)), + ... (3, bad, (), {}, (), 'oops') + ... ] + >>> try: + ... v = context.execute_actions() + ... except ConfigurationExecutionError, v: + ... pass + >>> print v + exceptions.AttributeError: 'function' object has no attribute 'xxx' + in: + oops + + + Note that actions executed before the error still have an effect: + + >>> output + [('f', (1,), {}), ('f', (2,), {})] + + + """ + try: + for action in resolveConflicts(self.actions): + (discriminator, callable, args, kw, includepath, info, order + ) = expand_action(*action) + if callable is None: + continue + try: + callable(*args, **kw) + except (KeyboardInterrupt, SystemExit): + raise + except: + if testing: + raise + t, v, tb = sys.exc_info() + raise ConfigurationExecutionError(t, v, info), None, tb + finally: + if clear: + del self.actions[:] + + +class ConfigurationExecutionError(ConfigurationError): + """An error occurred during execution of a configuration action + """ + + def __init__(self, etype, evalue, info): + self.etype, self.evalue, self.info = etype, evalue, info + + def __str__(self): + return "%s: %s\n in:\n %s" % (self.etype, self.evalue, self.info) + +############################################################################## +# Stack items + +class IStackItem(Interface): + """Configuration machine stack items + + Stack items are created when a directive is being processed. + + A stack item is created for each directive use. + """ + + def contained(name, data, info): + """Begin processing a contained directive + + The data are a dictionary of attribute names mapped to unicode + strings. + + The info argument is an object that can be converted to a + string and that contains information about the directive. + + The begin method returns the next item to be placed on the stack. + """ + + def finish(): + """Finish processing a directive + """ + +class SimpleStackItem(object): + """Simple stack item + + A simple stack item can't have anything added after it. It can + only be removed. It is used for simple directives and + subdirectives, which can't contain other directives. + + It also defers any computation until the end of the directive + has been reached. + """ + + implements(IStackItem) + + def __init__(self, context, handler, info, *argdata): + newcontext = GroupingContextDecorator(context) + newcontext.info = info + self.context = newcontext + self.handler = handler + self.argdata = argdata + + def contained(self, name, data, info): + raise ConfigurationError("Invalid directive %s" % str(name)) + + def finish(self): + # We're going to use the context that was passed to us, which wasn't + # created for the directive. We want to set it's info to the one + # passed to us while we make the call, so we'll save the old one + # and restore it. + context = self.context + args = toargs(context, *self.argdata) + actions = self.handler(context, **args) + if actions: + # we allow the handler to return nothing + for action in actions: + context.action(*action) + +class RootStackItem(object): + + def __init__(self, context): + self.context = context + + def contained(self, name, data, info): + """Handle a contained directive + + We have to compute a new stack item by getting a named adapter + for the current context object. + + """ + factory = self.context.factory(self.context, name) + if factory is None: + raise ConfigurationError("Invalid directive", name) + adapter = factory(self.context, data, info) + return adapter + + def finish(self): + pass + +class GroupingStackItem(RootStackItem): + """Stack item for a grouping directive + + A grouping stack item is in the stack when a grouping directive is + being processed. Grouping directives group other directives. + Often, they just manage common data, but they may also take + actions, either before or after contained directives are executed. + + A grouping stack item is created with a grouping directive + definition, a configuration context, and directive data. + + To see how this works, let's look at an example: + + We need a context. We'll just use a configuration machine + + >>> context = ConfigurationMachine() + + We need a callable to use in configuration actions. We'll use a + convenient one from the tests: + + >>> from zope.configuration.tests.directives import f + + We need a handler for the grouping directive. This is a class + that implements a context decorator. The decorator must also + provide ``before`` and ``after`` methods that are called before + and after any contained directives are processed. We'll typically + subclass ``GroupingContextDecorator``, which provides context + decoration, and default ``before`` and ``after`` methods. + + + >>> class SampleGrouping(GroupingContextDecorator): + ... def before(self): + ... self.action(('before', self.x, self.y), f) + ... def after(self): + ... self.action(('after'), f) + + We'll use our decorator to decorate our initial context, providing + keyword arguments x and y: + + >>> dec = SampleGrouping(context, x=1, y=2) + + Note that the keyword arguments are made attributes of the + decorator. + + Now we'll create the stack item. + + >>> item = GroupingStackItem(dec) + + We still haven't called the before action yet, which we can verify + by looking at the context actions: + + >>> context.actions + [] + + Subdirectives will get looked up as adapters of the context. + + We'll create a simple handler: + + >>> def simple(context, data, info): + ... context.action(("simple", context.x, context.y, data), f) + ... return info + + and register it with the context: + + >>> context.register(IConfigurationContext, (testns, 'simple'), simple) + + This handler isn't really a propert handler, because it doesn't + return a new context. It will do for this example. + + Now we'll call the contained method on the stack item: + + >>> item.contained((testns, 'simple'), {'z': 'zope'}, "someinfo") + 'someinfo' + + We can verify thet the simple method was called by looking at the + context actions. Note that the before method was called before + handling the contained directive. + + >>> from pprint import PrettyPrinter + >>> pprint=PrettyPrinter(width=60).pprint + + >>> pprint(context.actions) + [(('before', 1, 2), f), + (('simple', 1, 2, {'z': 'zope'}), f)] + + Finally, we call finish, which calls the decorator after method: + + >>> item.finish() + + >>> pprint(context.actions) + [(('before', 1, 2), f), + (('simple', 1, 2, {'z': 'zope'}), f), + ('after', f)] + + + If there were no nested directives: + + >>> context = ConfigurationMachine() + >>> dec = SampleGrouping(context, x=1, y=2) + >>> item = GroupingStackItem(dec) + >>> item.finish() + + Then before will be when we call finish: + + >>> pprint(context.actions) + [(('before', 1, 2), f), ('after', f)] + + """ + + implements(IStackItem) + + def __init__(self, context): + super(GroupingStackItem, self).__init__(context) + + def __callBefore(self): + actions = self.context.before() + if actions: + for action in actions: + self.context.action(*action) + self.__callBefore = noop + + def contained(self, name, data, info): + self.__callBefore() + return RootStackItem.contained(self, name, data, info) + + def finish(self): + self.__callBefore() + actions = self.context.after() + if actions: + for action in actions: + self.context.action(*action) + +def noop(): + pass + +class ComplexStackItem(object): + """Complex stack item + + A complex stack item is in the stack when a complex directive is + being processed. It only allows subdirectives to be used. + + A complex stack item is created with a complex directive + definition (IComplexDirectiveContext), a configuration context, + and directive data. + + To see how this works, let's look at an example: + + We need a context. We'll just use a configuration machine + + >>> context = ConfigurationMachine() + + We need a callable to use in configuration actions. We'll use a + convenient one from the tests: + + >>> from zope.configuration.tests.directives import f + + We need a handler for the complex directive. This is a class + with a method for each subdirective: + + >>> class Handler(object): + ... def __init__(self, context, x, y): + ... self.context, self.x, self.y = context, x, y + ... context.action('init', f) + ... def sub(self, context, a, b): + ... context.action(('sub', a, b), f) + ... def __call__(self): + ... self.context.action(('call', self.x, self.y), f) + + We need a complex directive definition: + + >>> class Ixy(Interface): + ... x = zope.schema.TextLine() + ... y = zope.schema.TextLine() + >>> definition = ComplexDirectiveDefinition( + ... context, name="test", schema=Ixy, + ... handler=Handler) + >>> class Iab(Interface): + ... a = zope.schema.TextLine() + ... b = zope.schema.TextLine() + >>> definition['sub'] = Iab, '' + + OK, now that we have the context, handler and definition, we're + ready to use a stack item. + + >>> item = ComplexStackItem(definition, context, {'x': u'xv', 'y': u'yv'}, + ... 'foo') + + When we created the definition, the handler (factory) was called. + + >>> context.actions + [('init', f, (), {}, (), 'foo')] + + If a subdirective is provided, the ``contained`` method of the stack item + is called. It will lookup the subdirective schema and call the + corresponding method on the handler instance: + + >>> simple = item.contained(('somenamespace', 'sub'), + ... {'a': u'av', 'b': u'bv'}, 'baz') + >>> simple.finish() + + Note that the name passed to ``contained`` is a 2-part name, consisting of + a namespace and a name within the namespace. + + >>> from pprint import PrettyPrinter + >>> pprint=PrettyPrinter(width=60).pprint + + >>> pprint(context.actions) + [('init', f, (), {}, (), 'foo'), + (('sub', u'av', u'bv'), f, (), {}, (), 'baz')] + + The new stack item returned by contained is one that doesn't allow + any more subdirectives, + + When all of the subdirectives have been provided, the ``finish`` + method is called: + + >>> item.finish() + + The stack item will call the handler if it is callable. + + >>> pprint(context.actions) + [('init', f, (), {}, (), 'foo'), + (('sub', u'av', u'bv'), f, (), {}, (), 'baz'), + (('call', u'xv', u'yv'), f, (), {}, (), 'foo')] + + + """ + + implements(IStackItem) + + def __init__(self, meta, context, data, info): + newcontext = GroupingContextDecorator(context) + newcontext.info = info + self.context = newcontext + self.meta = meta + + # Call the handler contructor + args = toargs(newcontext, meta.schema, data) + self.handler = self.meta.handler(newcontext, **args) + + def contained(self, name, data, info): + """Handle a subdirective + """ + + # Look up the subdirective meta data on our meta object + ns, name = name + schema = self.meta.get(name) + if schema is None: + raise ConfigurationError("Invalid directive", name) + schema = schema[0] # strip off info + handler = getattr(self.handler, name) + return SimpleStackItem(self.context, handler, info, schema, data) + + def finish(self): + + # when we're done, we call the handler, which might return more actions + + # Need to save and restore old info + + try: + actions = self.handler() + except AttributeError, v: + if v[0] == '__call__': + return # noncallable + raise + except TypeError: + return # non callable + + if actions: + # we allow the handler to return nothing + for action in actions: + self.context.action(*action) + +############################################################################## +# Helper classes + +class GroupingContextDecorator(ConfigurationContext): + """Helper mix-in class for building grouping directives + + See the discussion (and test) in GroupingStackItem. + """ + + implements(IConfigurationContext, IGroupingContext) + + def __init__(self, context, **kw): + self.context = context + for name, v in kw.items(): + setattr(self, name, v) + + def __getattr__(self, name, + getattr=getattr, setattr=setattr): + v = getattr(self.context, name) + # cache result in self + setattr(self, name, v) + return v + + def before(self): + pass + + def after(self): + pass + +############################################################################## +# Directive-definition + +class DirectiveSchema(fields.GlobalInterface): + """A field that contains a global variable value that must be a schema + """ + +class IDirectivesInfo(Interface): + """Schema for the ``directives`` directive + """ + + namespace = zope.schema.URI( + title=u"Namespace", + description=u"The namespace in which directives' names will be defined", + ) + +class IDirectivesContext(IDirectivesInfo, IConfigurationContext): + pass + +class DirectivesHandler(GroupingContextDecorator): + """Handler for the directives directive + + This is just a grouping directive that adds a namespace attribute + to the normal directive context. + + """ + implements(IDirectivesContext) + + +class IDirectiveInfo(Interface): + """Information common to all directive definitions have + """ + + name = zope.schema.TextLine( + title = u"Directive name", + description = u"The name of the directive being defined", + ) + + schema = DirectiveSchema( + title = u"Directive handler", + description = u"The dotted name of the directive handler", + ) + +class IFullInfo(IDirectiveInfo): + """Information that all top-level directives (not subdirectives) have + """ + + handler = fields.GlobalObject( + title = u"Directive handler", + description = u"The dotted name of the directive handler", + ) + + usedIn = fields.GlobalInterface( + title = u"The directive types the directive can be used in", + description = (u"The interface of the directives that can contain " + u"the directive" + ), + default = IConfigurationContext, + ) + +class IStandaloneDirectiveInfo(IDirectivesInfo, IFullInfo): + """Info for full directives defined outside a directives directives + """ + +def defineSimpleDirective(context, name, schema, handler, + namespace='', usedIn=IConfigurationContext): + """Define a simple directive + + Define and register a factory that invokes the simple directive + and returns a new stack item, which is always the same simple stack item. + + If the namespace is '*', the directive is registered for all namespaces. + + for example: + + >>> context = ConfigurationMachine() + >>> from zope.configuration.tests.directives import f + >>> class Ixy(Interface): + ... x = zope.schema.TextLine() + ... y = zope.schema.TextLine() + >>> def s(context, x, y): + ... context.action(('s', x, y), f) + + >>> defineSimpleDirective(context, 's', Ixy, s, testns) + + >>> context((testns, "s"), x=u"vx", y=u"vy") + >>> context.actions + [(('s', u'vx', u'vy'), f)] + + >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy") + Traceback (most recent call last): + ... + ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 's') + + >>> context = ConfigurationMachine() + >>> defineSimpleDirective(context, 's', Ixy, s, "*") + + >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy") + >>> context.actions + [(('s', u'vx', u'vy'), f)] + + """ + + namespace = namespace or context.namespace + if namespace != '*': + name = namespace, name + + def factory(context, data, info): + return SimpleStackItem(context, handler, info, schema, data) + factory.schema = schema + + context.register(usedIn, name, factory) + context.document(name, schema, usedIn, handler, context.info) + +def defineGroupingDirective(context, name, schema, handler, + namespace='', usedIn=IConfigurationContext): + """Define a grouping directive + + Define and register a factory that sets up a grouping directive. + + If the namespace is '*', the directive is registered for all namespaces. + + for example: + + >>> context = ConfigurationMachine() + >>> from zope.configuration.tests.directives import f + >>> class Ixy(Interface): + ... x = zope.schema.TextLine() + ... y = zope.schema.TextLine() + + We won't bother creating a special grouping directive class. We'll + just use GroupingContextDecorator, which simply sets up a grouping + context that has extra attributes defined by a schema: + + >>> defineGroupingDirective(context, 'g', Ixy, + ... GroupingContextDecorator, testns) + + >>> context.begin((testns, "g"), x=u"vx", y=u"vy") + >>> context.stack[-1].context.x + u'vx' + >>> context.stack[-1].context.y + u'vy' + + >>> context(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy") + Traceback (most recent call last): + ... + ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 'g') + + >>> context = ConfigurationMachine() + >>> defineGroupingDirective(context, 'g', Ixy, + ... GroupingContextDecorator, "*") + + >>> context.begin(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy") + >>> context.stack[-1].context.x + u'vx' + >>> context.stack[-1].context.y + u'vy' + + """ + + namespace = namespace or context.namespace + if namespace != '*': + name = namespace, name + + def factory(context, data, info): + args = toargs(context, schema, data) + newcontext = handler(context, **args) + newcontext.info = info + return GroupingStackItem(newcontext) + factory.schema = schema + + context.register(usedIn, name, factory) + context.document(name, schema, usedIn, handler, context.info) + + +class IComplexDirectiveContext(IFullInfo, IConfigurationContext): + pass + +class ComplexDirectiveDefinition(GroupingContextDecorator, dict): + """Handler for defining complex directives + + See the description and tests for ComplexStackItem. + """ + + implements(IComplexDirectiveContext) + + def before(self): + + def factory(context, data, info): + return ComplexStackItem(self, context, data, info) + factory.schema = self.schema + + self.register(self.usedIn, (self.namespace, self.name), factory) + self.document((self.namespace, self.name), self.schema, self.usedIn, + self.handler, self.info) + +def subdirective(context, name, schema): + context.document((context.namespace, name), schema, context.usedIn, + getattr(context.handler, name, context.handler), + context.info, context.context) + context.context[name] = schema, context.info + +############################################################################## +# Features + +class IProvidesDirectiveInfo(Interface): + """Information for a directive""" + + feature = zope.schema.TextLine( + title = u"Feature name", + description = u"""The name of the feature being provided + + You can test available features with zcml:condition="have featurename". + """, + ) + +def provides(context, feature): + """Declare that a feature is provided in context. + + >>> c = ConfigurationContext() + >>> provides(c, 'apidoc') + >>> c.hasFeature('apidoc') + True + + Spaces are not allowed in feature names (this is reserved for providing + many features with a single directive in the futute). + + >>> provides(c, 'apidoc onlinehelp') + Traceback (most recent call last): + ... + ValueError: Only one feature name allowed + + >>> c.hasFeature('apidoc onlinehelp') + False + + """ + if len(feature.split()) > 1: + raise ValueError("Only one feature name allowed") + context.provideFeature(feature) + + +############################################################################## +# Argument conversion + +def toargs(context, schema, data): + """Marshal data to an argument dictionary using a schema + + Names that are python keywords have an underscore added as a + suffix in the schema and in the argument list, but are used + without the underscore in the data. + + The fields in the schema must all implement IFromUnicode. + + All of the items in the data must have corresponding fields in the + schema unless the schema has a true tagged value named + 'keyword_arguments'. + + Here's an example: + + >>> from zope import schema + + >>> class schema(Interface): + ... in_ = zope.schema.Int(constraint=lambda v: v > 0) + ... f = zope.schema.Float() + ... n = zope.schema.TextLine(min_length=1, default=u"rob") + ... x = zope.schema.BytesLine(required=False) + ... u = zope.schema.URI() + + >>> context = ConfigurationMachine() + >>> from pprint import PrettyPrinter + >>> pprint=PrettyPrinter(width=50).pprint + + >>> pprint(toargs(context, schema, + ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z', + ... 'u': u'http://www.zope.org' })) + {'f': 1.2, + 'in_': 1, + 'n': u'bob', + 'u': 'http://www.zope.org', + 'x': 'x.y.z'} + + If we have extra data, we'll get an error: + + >>> toargs(context, schema, + ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z', + ... 'u': u'http://www.zope.org', 'a': u'1'}) + Traceback (most recent call last): + ... + ConfigurationError: ('Unrecognized parameters:', 'a') + + Unless we set a tagged value to say that extra arguments are ok: + + >>> schema.setTaggedValue('keyword_arguments', True) + + >>> pprint(toargs(context, schema, + ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z', + ... 'u': u'http://www.zope.org', 'a': u'1'})) + {'a': u'1', + 'f': 1.2, + 'in_': 1, + 'n': u'bob', + 'u': 'http://www.zope.org', + 'x': 'x.y.z'} + + + If we ommit required data we get an error telling us what was omitted: + + >>> pprint(toargs(context, schema, + ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z'})) + Traceback (most recent call last): + ... + ConfigurationError: ('Missing parameter:', 'u') + + Although we can omit not-required data: + + >>> pprint(toargs(context, schema, + ... {'in': u'1', 'f': u'1.2', 'n': u'bob', + ... 'u': u'http://www.zope.org', 'a': u'1'})) + {'a': u'1', + 'f': 1.2, + 'in_': 1, + 'n': u'bob', + 'u': 'http://www.zope.org'} + + And we can ommit required fields if they have valid defaults + (defaults that are valid values): + + + >>> pprint(toargs(context, schema, + ... {'in': u'1', 'f': u'1.2', + ... 'u': u'http://www.zope.org', 'a': u'1'})) + {'a': u'1', + 'f': 1.2, + 'in_': 1, + 'n': u'rob', + 'u': 'http://www.zope.org'} + + We also get an error if any data was invalid: + + >>> pprint(toargs(context, schema, + ... {'in': u'0', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z', + ... 'u': u'http://www.zope.org', 'a': u'1'})) + Traceback (most recent call last): + ... + ConfigurationError: ('Invalid value for', 'in', '0') + + """ + + data = dict(data) + args = {} + for name, field in schema.namesAndDescriptions(True): + field = field.bind(context) + n = name + if n.endswith('_') and iskeyword(n[:-1]): + n = n[:-1] + + s = data.get(n, data) + if s is not data: + s = unicode(s) + del data[n] + + try: + args[str(name)] = field.fromUnicode(s) + except zope.schema.ValidationError, v: + raise ConfigurationError( + "Invalid value for", n, str(v)), None, sys.exc_info()[2] + elif field.required: + # if the default is valid, we can use that: + default = field.default + try: + field.validate(default) + except zope.schema.ValidationError: + raise ConfigurationError("Missing parameter:", n) + args[str(name)] = default + + if data: + # we had data left over + try: + keyword_arguments = schema.getTaggedValue('keyword_arguments') + except KeyError: + keyword_arguments = False + if not keyword_arguments: + raise ConfigurationError("Unrecognized parameters:", *data) + + for name in data: + args[str(name)] = data[name] + + return args + +############################################################################## +# Conflict resolution + +def expand_action(discriminator, callable=None, args=(), kw={}, + includepath=(), info='', order=0): + return (discriminator, callable, args, kw, + includepath, info, order) + +def resolveConflicts(actions): + """Resolve conflicting actions + + Given an actions list, identify and try to resolve conflicting actions. + Actions conflict if they have the same non-null discriminator. + Conflicting actions can be resolved if the include path of one of + the actions is a prefix of the includepaths of the other + conflicting actions and is unequal to the include paths in the + other conflicting actions. + + Here are some examples to illustrate how this works: + + >>> from zope.configuration.tests.directives import f + >>> from pprint import PrettyPrinter + >>> pprint=PrettyPrinter(width=60).pprint + >>> pprint(resolveConflicts([ + ... (None, f), + ... (1, f, (1,), {}, (), 'first'), + ... (1, f, (2,), {}, ('x',), 'second'), + ... (1, f, (3,), {}, ('y',), 'third'), + ... (4, f, (4,), {}, ('y',), 'should be last', 99999), + ... (3, f, (3,), {}, ('y',)), + ... (None, f, (5,), {}, ('y',)), + ... ])) + [(None, f), + (1, f, (1,), {}, (), 'first'), + (3, f, (3,), {}, ('y',)), + (None, f, (5,), {}, ('y',)), + (4, f, (4,), {}, ('y',), 'should be last')] + + >>> try: + ... v = resolveConflicts([ + ... (None, f), + ... (1, f, (2,), {}, ('x',), 'eek'), + ... (1, f, (3,), {}, ('y',), 'ack'), + ... (4, f, (4,), {}, ('y',)), + ... (3, f, (3,), {}, ('y',)), + ... (None, f, (5,), {}, ('y',)), + ... ]) + ... except ConfigurationConflictError, v: + ... pass + >>> print v + Conflicting configuration actions + For: 1 + eek + ack + + """ + + # organize actions by discriminators + unique = {} + output = [] + for i in range(len(actions)): + (discriminator, callable, args, kw, includepath, info, order + ) = expand_action(*(actions[i])) + + order = order or i + if discriminator is None: + # The discriminator is None, so this directive can + # never conflict. We can add it directly to the + # configuration actions. + output.append( + (order, discriminator, callable, args, kw, includepath, info) + ) + continue + + + a = unique.setdefault(discriminator, []) + a.append( + (includepath, order, callable, args, kw, info) + ) + + # Check for conflicts + conflicts = {} + for discriminator, dups in unique.items(): + + # We need to sort the actions by the paths so that the shortest + # path with a given prefix comes first: + dups.sort() + (basepath, i, callable, args, kw, baseinfo) = dups[0] + output.append( + (i, discriminator, callable, args, kw, basepath, baseinfo) + ) + for includepath, i, callable, args, kw, info in dups[1:]: + # Test whether path is a prefix of opath + if (includepath[:len(basepath)] != basepath # not a prefix + or + (includepath == basepath) + ): + if discriminator not in conflicts: + conflicts[discriminator] = [baseinfo] + conflicts[discriminator].append(info) + + + if conflicts: + raise ConfigurationConflictError(conflicts) + + # Now put the output back in the original order, and return it: + output.sort() + r = [] + for o in output: + action = o[1:] + while len(action) > 2 and not action[-1]: + action = action[:-1] + r.append(action) + + return r + +class ConfigurationConflictError(ConfigurationError): + + def __init__(self, conflicts): + self._conflicts = conflicts + + def __str__(self): + r = ["Conflicting configuration actions"] + items = self._conflicts.items() + items.sort() + for discriminator, infos in items: + r.append(" For: %s" % (discriminator, )) + for info in infos: + for line in unicode(info).rstrip().split(u'\n'): + r.append(u" "+line) + + return "\n".join(r) + + +############################################################################## +# Bootstap code + + +def _bootstrap(context): + + # Set enough machinery to register other directives + + # Define the directive (simple directive) directive by calling it's + # handler directly + + info = 'Manually registered in zope/configuration/config.py' + + context.info = info + defineSimpleDirective( + context, + namespace=metans, name='directive', + schema=IStandaloneDirectiveInfo, + handler=defineSimpleDirective) + context.info = '' + + # OK, now that we have that, we can use the machine to define the + # other directives. This isn't the easiest way to proceed, but it lets + # us eat our own dogfood. :) + + # Standalone groupingDirective + context((metans, 'directive'), + info, + name='groupingDirective', + namespace=metans, + handler="zope.configuration.config.defineGroupingDirective", + schema="zope.configuration.config.IStandaloneDirectiveInfo" + ) + + # Now we can use the grouping directive to define the directives directive + context((metans, 'groupingDirective'), + info, + name='directives', + namespace=metans, + handler="zope.configuration.config.DirectivesHandler", + schema="zope.configuration.config.IDirectivesInfo" + ) + + # directive and groupingDirective inside directives + context((metans, 'directive'), + info, + name='directive', + namespace=metans, + usedIn="zope.configuration.config.IDirectivesContext", + handler="zope.configuration.config.defineSimpleDirective", + schema="zope.configuration.config.IFullInfo" + ) + context((metans, 'directive'), + info, + name='groupingDirective', + namespace=metans, + usedIn="zope.configuration.config.IDirectivesContext", + handler="zope.configuration.config.defineGroupingDirective", + schema="zope.configuration.config.IFullInfo" + ) + + # Setup complex directive directive, both standalone, and in + # directives directive + context((metans, 'groupingDirective'), + info, + name='complexDirective', + namespace=metans, + handler="zope.configuration.config.ComplexDirectiveDefinition", + schema="zope.configuration.config.IStandaloneDirectiveInfo" + ) + context((metans, 'groupingDirective'), + info, + name='complexDirective', + namespace=metans, + usedIn="zope.configuration.config.IDirectivesContext", + handler="zope.configuration.config.ComplexDirectiveDefinition", + schema="zope.configuration.config.IFullInfo" + ) + + # Finally, setup subdirective directive + context((metans, 'directive'), + info, + name='subdirective', + namespace=metans, + usedIn="zope.configuration.config.IComplexDirectiveContext", + handler="zope.configuration.config.subdirective", + schema="zope.configuration.config.IDirectiveInfo" + ) + + # meta:provides + context((metans, 'directive'), + info, + name='provides', + namespace=metans, + handler="zope.configuration.config.provides", + schema="zope.configuration.config.IProvidesDirectiveInfo" + ) + diff -Nru zope3-3.4.0/src/zope/configuration/docutils.py zope3-3.5~bzr18/src/zope/configuration/docutils.py --- zope3-3.4.0/src/zope/configuration/docutils.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/docutils.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,89 @@ +############################################################################## +# +# 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 + +$Id: docutils.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import re + +para_sep = re.compile('\n{2,}') +whitespace=re.compile('[ \t\n\r]+') + +def wrap(text, width=78, indent=0): + """Makes sure that we keep a line length of a certain width. + + Examples: + + >>> print wrap('foo bar')[:-2] + foo bar + >>> print wrap('foo bar', indent=2)[:-2] + foo bar + >>> print wrap('foo bar, more foo bar', 10)[:-2] + foo bar, + more foo + bar + >>> print wrap('foo bar, more foo bar', 10, 2)[:-2] + foo bar, + more foo + bar + """ + paras = para_sep.split(text.strip()) + + new_paras = [] + for par in paras: + words= filter(None, whitespace.split(par)) + + lines = [] + line = [] + length = indent + for word in words: + if length + len(word) <= width: + line.append(word) + length += len(word) + 1 + else: + lines.append(' '*indent + ' '.join(line)) + line = [word] + length = len(word) + 1 + indent + + lines.append(' '*indent + ' '.join(line)) + + new_paras.append('\n'.join(lines)) + + return '\n\n'.join(new_paras) + '\n\n' + + +def makeDocStructures(context): + """Creates two structures that provide a friendly format for + documentation. + + 'namespaces' is a dictionary that maps namespaces to a directives + dictionary with the key being the name of the directive and the value is a + tuple: (schema, handler, info). + + 'subdirs' maps a (namespace, name) pair to a list of subdirectives that + have the form (namespace, name, schema, info). + """ + namespaces = {} + subdirs = {} + registry = context._docRegistry + for (namespace, name), schema, usedIn, handler, info, parent in registry: + if not parent: + ns_entry = namespaces.setdefault(namespace, {}) + ns_entry[name] = (schema, handler, info) + else: + sd_entry = subdirs.setdefault((parent.namespace, parent.name), []) + sd_entry.append((namespace, name, schema, handler, info)) + return namespaces, subdirs diff -Nru zope3-3.4.0/src/zope/configuration/exceptions.py zope3-3.5~bzr18/src/zope/configuration/exceptions.py --- zope3-3.4.0/src/zope/configuration/exceptions.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/exceptions.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# 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 + +$Id: exceptions.py 110550 2010-04-06 06:50:36Z tseaver $ +""" + +class ConfigurationError(Exception): + """There was an error in a configuration + """ diff -Nru zope3-3.4.0/src/zope/configuration/exclude.txt zope3-3.5~bzr18/src/zope/configuration/exclude.txt --- zope3-3.4.0/src/zope/configuration/exclude.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/exclude.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,56 @@ +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: + + >>> import logging, sys + >>> logger = logging.getLogger('config') + >>> oldlevel = logger.level + >>> logger.setLevel(logging.DEBUG) + >>> handler = logging.StreamHandler(sys.stdout) + >>> logger.addHandler(handler) + +Now, we'll include the zope.configuration.tests.excludedemo config: + + >>> from zope.configuration import xmlconfig + >>> _ = xmlconfig.string('') + include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml + include /zope.configuration/src/zope/configuration/tests/excludedemo/sub/configure.zcml + include /zope.configuration/src/zope/configuration/tests/excludedemo/spam.zcml + +Each run of the configuration machinery runs with fresh state, so +rerunning gives the same thing: + + >>> _ = xmlconfig.string('') + include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml + include /zope.configuration/src/zope/configuration/tests/excludedemo/sub/configure.zcml + include /zope.configuration/src/zope/configuration/tests/excludedemo/spam.zcml + +Now, we'll use the exclude directive to exclude the two files included +by the configuration file in zope.configuration.tests.excludedemo: + + >>> _ = xmlconfig.string( + ... ''' + ... + ... + ... + ... + ... + ... ''') + include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml + + +.. cleanup + + >>> logger.setLevel(oldlevel) + >>> logger.removeHandler(handler) diff -Nru zope3-3.4.0/src/zope/configuration/fields.py zope3-3.5~bzr18/src/zope/configuration/fields.py --- zope3-3.4.0/src/zope/configuration/fields.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/fields.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,449 @@ +############################################################################## +# +# 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 + +$Id: fields.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +__docformat__ = 'restructuredtext' +import os, re, warnings +from zope import schema +from zope.schema.interfaces import IFromUnicode +from zope.schema.interfaces import ConstraintNotSatisfied +from zope.configuration.exceptions import ConfigurationError +from zope.interface import implements +from zope.configuration.interfaces import InvalidToken + +PYIDENTIFIER_REGEX = u'\\A[a-zA-Z_]+[a-zA-Z0-9_]*\\Z' +pyidentifierPattern = re.compile(PYIDENTIFIER_REGEX) + +class PythonIdentifier(schema.TextLine): + r"""This field describes a python identifier, i.e. a variable name. + + Let's look at an example: + + >>> class FauxContext(object): + ... pass + >>> context = FauxContext() + >>> field = PythonIdentifier().bind(context) + + Let's test the fromUnicode method: + + >>> field.fromUnicode(u'foo') + u'foo' + >>> field.fromUnicode(u'foo3') + u'foo3' + >>> field.fromUnicode(u'_foo3') + u'_foo3' + + Now let's see whether validation works alright + + >>> for value in (u'foo', u'foo3', u'foo_', u'_foo3', u'foo_3', u'foo3_'): + ... field._validate(value) + >>> + >>> from zope import schema + >>> + >>> for value in (u'3foo', u'foo:', u'\\', u''): + ... try: + ... field._validate(value) + ... except schema.ValidationError: + ... print 'Validation Error' + Validation Error + Validation Error + Validation Error + Validation Error + + """ + implements(IFromUnicode) + + def fromUnicode(self, u): + return u.strip() + + def _validate(self, value): + super(PythonIdentifier, self)._validate(value) + if pyidentifierPattern.match(value) is None: + raise schema.ValidationError(value) + +class GlobalObject(schema.Field): + """An object that can be accessed as a module global. + + Examples: + + First, we need to set up a stub name resolver: + + >>> d = {'x': 1, 'y': 42, 'z': 'zope'} + >>> class fakeresolver(dict): + ... def resolve(self, n): + ... return self[n] + + >>> fake = fakeresolver(d) + + + >>> g = GlobalObject(value_type=schema.Int()) + >>> gg = g.bind(fake) + >>> gg.fromUnicode("x") + 1 + >>> gg.fromUnicode(" x \\n ") + 1 + >>> gg.fromUnicode("y") + 42 + >>> gg.fromUnicode("z") + Traceback (most recent call last): + ... + WrongType: ('zope', (, ), '') + + >>> g = GlobalObject(constraint=lambda x: x%2 == 0) + >>> gg = g.bind(fake) + >>> gg.fromUnicode("x") + Traceback (most recent call last): + ... + ConstraintNotSatisfied: 1 + >>> gg.fromUnicode("y") + 42 + >>> g = GlobalObject() + >>> gg = g.bind(fake) + >>> gg.fromUnicode('*') + >>> + + """ + + implements(IFromUnicode) + + def __init__(self, value_type=None, **kw): + self.value_type = value_type + super(GlobalObject, self).__init__(**kw) + + def _validate(self, value): + super(GlobalObject, self)._validate(value) + if self.value_type is not None: + self.value_type.validate(value) + + def fromUnicode(self, u): + name = str(u.strip()) + + # special case, mostly for interfaces + if name == '*': + return None + + try: + value = self.context.resolve(name) + except ConfigurationError, v: + raise schema.ValidationError(v) + + self.validate(value) + return value + +class GlobalInterface(GlobalObject): + """An interface that can be accessed from a module. + + First, we need to set up a stub name resolver: + + >>> class Foo(object): pass + + >>> from zope.interface import Interface + >>> class IFoo(Interface): pass + + >>> d = {'Foo': Foo, 'IFoo': IFoo} + >>> class fakeresolver(dict): + ... def resolve(self, n): + ... return self[n] + + >>> fake = fakeresolver(d) + + Now verify constraints are checked correctly. + + >>> g = GlobalInterface() + >>> gg = g.bind(fake) + >>> gg.fromUnicode('IFoo') + + >>> gg.fromUnicode(' IFoo ') + + >>> gg.fromUnicode('Foo') + Traceback (most recent call last): + ... + WrongType: ('An interface is required', , '') + """ + + def __init__(self, **kw): + super(GlobalInterface, self).__init__(schema.InterfaceField(), **kw) + +class Tokens(schema.List): + """A list that can be read from a space-separated string + + Consider GlobalObject tokens: + + Examples: + + First, we need to set up a stub name resolver: + + >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'} + >>> class fakeresolver(dict): + ... def resolve(self, n): + ... return self[n] + + >>> fake = fakeresolver(d) + + + >>> g = Tokens(value_type=GlobalObject()) + >>> gg = g.bind(fake) + >>> gg.fromUnicode(" \\n x y z \\n") + [1, 42, 'zope'] + + >>> g = Tokens(value_type= + ... GlobalObject(value_type= + ... schema.Int(constraint=lambda x: x%2 == 0))) + >>> gg = g.bind(fake) + >>> gg.fromUnicode("x y") + Traceback (most recent call last): + ... + InvalidToken: 1 in x y + + >>> gg.fromUnicode("z y") + Traceback (most recent call last): + ... + InvalidToken: ('zope', (, ), '') in z y + >>> gg.fromUnicode("y y") + [42, 42] + >>> + + """ + implements(IFromUnicode) + + def fromUnicode(self, u): + u = u.strip() + if u: + vt = self.value_type.bind(self.context) + values = [] + for s in u.split(): + try: + v = vt.fromUnicode(s) + except schema.ValidationError, v: + raise InvalidToken("%s in %s" % (v, u)) + else: + values.append(v) + else: + values = [] + + self.validate(values) + + return values + +class Path(schema.Text): + r"""A file path name, which may be input as a relative path + + Input paths are converted to absolute paths and normalized. + + Let's look at an example: + + First, we need a "context" for the field that has a path + function for converting relative path to an absolute path. + + We'll be careful to do this in an os-independent fashion. + + >>> class FauxContext(object): + ... def path(self, p): + ... return os.path.join(os.sep, 'faux', 'context', p) + + >>> context = FauxContext() + >>> field = Path().bind(context) + + Lets try an absolute path first: + + >>> p = unicode(os.path.join(os.sep, 'a', 'b')) + >>> n = field.fromUnicode(p) + >>> n.split(os.sep) + [u'', u'a', u'b'] + + This should also work with extra spaces around the path: + + >>> p = " \n %s \n\n " % p + >>> n = field.fromUnicode(p) + >>> n.split(os.sep) + [u'', u'a', u'b'] + + Now try a relative path: + + >>> p = unicode(os.path.join('a', 'b')) + >>> n = field.fromUnicode(p) + >>> n.split(os.sep) + [u'', u'faux', u'context', u'a', u'b'] + + + """ + + implements(IFromUnicode) + + def fromUnicode(self, u): + u = u.strip() + if os.path.isabs(u): + return os.path.normpath(u) + + return self.context.path(u) + + +class Bool(schema.Bool): + """A boolean value + + Values may be input (in upper or lower case) as any of: + yes, no, y, n, true, false, t, or f. + + >>> Bool().fromUnicode(u"yes") + 1 + >>> Bool().fromUnicode(u"y") + 1 + >>> Bool().fromUnicode(u"true") + 1 + >>> Bool().fromUnicode(u"no") + 0 + """ + + implements(IFromUnicode) + + def fromUnicode(self, u): + u = u.lower() + if u in ('1', 'true', 'yes', 't', 'y'): + return True + if u in ('0', 'false', 'no', 'f', 'n'): + return False + raise schema.ValidationError + +class MessageID(schema.Text): + """Text string that should be translated. + + When a string is converted to a message ID, it is also + recorded in the context. + + >>> class Info(object): + ... file = 'file location' + ... line = 8 + + >>> class FauxContext(object): + ... i18n_strings = {} + ... info = Info() + + >>> context = FauxContext() + >>> field = MessageID().bind(context) + + There is a fallback domain when no domain has been specified. + + Exchange the warn function so we can make test whether the warning + has been issued + + >>> warned = None + >>> def fakewarn(*args, **kw): + ... global warned + ... warned = args + + >>> import warnings + >>> realwarn = warnings.warn + >>> warnings.warn = fakewarn + + >>> i = field.fromUnicode(u"Hello world!") + >>> i + u'Hello world!' + >>> i.domain + 'untranslated' + >>> warned + ("You did not specify an i18n translation domain for the '' """ \ + """field in file location",) + + >>> warnings.warn = realwarn + + With the domain specified: + + >>> context.i18n_strings = {} + >>> context.i18n_domain = 'testing' + + We can get a message id: + + >>> i = field.fromUnicode(u"Hello world!") + >>> i + u'Hello world!' + >>> i.domain + 'testing' + + In addition, the string has been registered with the context: + + >>> context.i18n_strings + {'testing': {u'Hello world!': [('file location', 8)]}} + + >>> i = field.fromUnicode(u"Foo Bar") + >>> i = field.fromUnicode(u"Hello world!") + >>> from pprint import PrettyPrinter + >>> pprint=PrettyPrinter(width=70).pprint + >>> pprint(context.i18n_strings) + {'testing': {u'Foo Bar': [('file location', 8)], + u'Hello world!': [('file location', 8), + ('file location', 8)]}} + + >>> from zope.i18nmessageid import Message + >>> isinstance(context.i18n_strings['testing'].keys()[0], Message) + 1 + + Explicit Message IDs + + >>> i = field.fromUnicode(u'[View-Permission] View') + >>> i + u'View-Permission' + >>> i.default + u'View' + + >>> i = field.fromUnicode(u'[] [Some] text') + >>> i + u'[Some] text' + >>> i.default is None + True + """ + + implements(IFromUnicode) + + __factories = {} + + def fromUnicode(self, u): + context = self.context + domain = getattr(context, 'i18n_domain', '') + if not domain: + domain = 'untranslated' + warnings.warn( + "You did not specify an i18n translation domain for the "\ + "'%s' field in %s" % (self.getName(), context.info.file ) + ) + v = super(MessageID, self).fromUnicode(u) + + # Check whether there is an explicit message is specified + default = None + if v.startswith('[]'): + v = v[2:].lstrip() + elif v.startswith('['): + end = v.find(']') + default = v[end+2:] + v = v[1:end] + + # Convert to a message id, importing the factory, if necessary + factory = self.__factories.get(domain) + if factory is None: + import zope.i18nmessageid + factory = zope.i18nmessageid.MessageFactory(domain) + self.__factories[domain] = factory + + msgid = factory(v, default) + + # Record the string we got for the domain + i18n_strings = context.i18n_strings + strings = i18n_strings.get(domain) + if strings is None: + strings = i18n_strings[domain] = {} + locations = strings.setdefault(msgid, []) + locations.append((context.info.file, context.info.line)) + + return msgid diff -Nru zope3-3.4.0/src/zope/configuration/__init__.py zope3-3.5~bzr18/src/zope/configuration/__init__.py --- zope3-3.4.0/src/zope/configuration/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# 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 + diff -Nru zope3-3.4.0/src/zope/configuration/interfaces.py zope3-3.5~bzr18/src/zope/configuration/interfaces.py --- zope3-3.4.0/src/zope/configuration/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,115 @@ +############################################################################## +# +# 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 + +$Id: interfaces.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +from zope.interface import Interface +from zope.schema import BytesLine +from zope.schema.interfaces import ValidationError + +class InvalidToken(ValidationError): + """Invaid token in list.""" + +class IConfigurationContext(Interface): + """Configuration Context + + The configuration context manages information about the state of + the configuration system, such as the package containing the + configuration file. More importantly, it provides methods for + importing objects and opening files relative to the package. + """ + + package = BytesLine( + title=u"The current package name", + description=u"""\ + This is the name of the package containing the configuration + file being executed. If the configuration file was not + included by package, then this is None. + """, + required=False, + ) + + def resolve(dottedname): + """Resolve a dotted name to an object + + A dotted name is constructed by concatenating a dotted module + name with a global name within the module using a dot. For + example, the object named "spam" in the foo.bar module has a + dotted name of foo.bar.spam. If the current package is a + prefix of a dotted name, then the package name can be relaced + with a leading dot, So, for example, if the configuration file + is in the foo package, then the dotted name foo.bar.spam can + be shortened to .bar.spam. + + If the current package is multiple levels deep, multiple + leading dots can be used to refer to higher-level modules. + For example, if the current package is x.y.z, the dotted + object name ..foo refers to x.y.foo. + """ + + def path(filename): + """Compute a full file name for the given file + + If the filename is relative to the package, then the returned + name will include the package path, otherwise, the original + file name is returned. + """ + + def checkDuplicate(filename): + """Check for duplicate imports of the same file. + + Raises an exception if this file had been processed before. This + is better than an unlimited number of conflict errors. + """ + + def processFile(filename): + """Check whether a file needs to be processed. + + Return True if processing is needed and False otherwise. If + the file needs to be processed, it will be marked as + processed, assuming that the caller will procces the file if + it needs to be procssed. + """ + + def action(self, discriminator, callable, args=(), kw={}, order=0): + """Record a configuration action + + The job of most directives is to compute actions for later + processing. The action method is used to record those + actions. The discriminator is used to to find actions that + conflict. Actions conflict if they have the same + discriminator. The exception to this is the special case of + the discriminator with the value None. An actions with a + discriminator of None never conflicts with other actions. This + is possible to add an order argument to crudely control the + order of execution + """ + + 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 + """ diff -Nru zope3-3.4.0/src/zope/configuration/name.py zope3-3.5~bzr18/src/zope/configuration/name.py --- zope3-3.4.0/src/zope/configuration/name.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/name.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,85 @@ +############################################################################## +# +# 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 + +$Id: name.py 110550 2010-04-06 06:50:36Z tseaver $ +""" + +import os +from types import ModuleType + +def resolve(name, package='zopeproducts', _silly=('__doc__',), _globals={}): + name = name.strip() + + if name.startswith('.'): + name=package+name + + if name.endswith('.') or name.endswith('+'): + name = name[:-1] + repeat = 1 + else: + repeat = 0 + + names=name.split('.') + last=names[-1] + mod='.'.join(names[:-1]) + + if not mod: + return __import__(name, _globals, _globals, _silly) + + while 1: + m=__import__(mod, _globals, _globals, _silly) + try: + a=getattr(m, last) + except AttributeError: + if not repeat: + return __import__(name, _globals, _globals, _silly) + + else: + if not repeat or (not isinstance(a, ModuleType)): + return a + mod += '.' + last + + +def getNormalizedName(name, package): + name=name.strip() + if name.startswith('.'): + name=package+name + + if name.endswith('.') or name.endswith('+'): + name = name[:-1] + repeat = 1 + else: + repeat = 0 + name=name.split(".") + while len(name)>1 and name[-1]==name[-2]: + name.pop() + repeat=1 + name=".".join(name) + if repeat: + name+="+" + return name + +def path(file='', package = 'zopeproducts', _silly=('__doc__',), _globals={}): + try: package = __import__(package, _globals, _globals, _silly) + except ImportError: + if file and os.path.abspath(file) == file: + # The package didn't matter + return file + raise + + path = os.path.dirname(package.__file__) + if file: + path = os.path.join(path, file) + return path diff -Nru zope3-3.4.0/src/zope/configuration/README.txt zope3-3.5~bzr18/src/zope/configuration/README.txt --- zope3-3.4.0/src/zope/configuration/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,75 @@ +========================== +Zope configuration system +========================== + +The zope configuration system provides an extensible system for +supporting variouse kinds of configurations. + +It is based on the idea of configuration directives. Users of the +configuration system provide configuration directives in some +language that express configuration choices. The intent is that the +language be pluggable. An XML language is provided by default. + +Configuration is performed in three stages. In the first stage, +directives are processed to compute configuration actions. +Configuration actions consist of: + +- A discriminator + +- A callable + +- Positional arguments + +- Keyword arguments + +The actions are essentially delayed function calls. Two or more +actions conflict if they have the same discriminator. The +configuration system has rules for resolving conflicts. If conflicts +cannot be resolved, an error will result. Conflict resolution +typically discards all but one of the conflicting actions, so that +the remaining action of the originally-conflicting actions no longer +conflicts. Non-conflicting actions are executed in the order that +they were created by passing the positional and non-positional +arguments to the action callable. + +The system is extensible. There is a meta-configuration language for +defining configuration directives. A directive is defined by +providing meta data about the directive and handler code to process +the directive. There are four kinds of directives: + +- Simple directives compute configuration actions. Their handlers + are typically functions that take a context and zero or more + keyword arguments and return a sequence of configuration actions. + + To learn how to create simple directives, see `tests/test_simple.py`. + + +- Grouping directives collect information to be used by nested + directives. They are called with a context object which they adapt + to some interface that extends IConfigurationContext. + + To learn how to create grouping directives, look at the + documentation in zopeconfigure.py, which provides the implementation + of the zope `configure` directive. + + Other directives can be nested in grouping directives. + + To learn how to implement nested directives, look at the + documentation in `tests/test_nested.py`. + +- Complex directives are directives that have subdirectives. + Subdirectives have handlers that are simply methods of complex + directives. Complex diretives are handled by factories, typically + classes, that create objects that have methods for handling + subdirectives. These objects also have __call__ methods that are + called when processing of subdirectives is finished. + + Complex directives only exist to support old directive + handlers. They will probably be deprecated in the future. + +- Subdirectives are nested in complex directives. They are like + simple directives except that they hane handlers that are complex + directive methods. + + Subdirectives, like complex directives only exist to support old + directive handlers. They will probably be deprecated in the future. diff -Nru zope3-3.4.0/src/zope/configuration/stxdocs.py zope3-3.5~bzr18/src/zope/configuration/stxdocs.py --- zope3-3.4.0/src/zope/configuration/stxdocs.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/stxdocs.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,165 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""STX Configuration Documentation Renderer + +Usage: stxdocs.py [options] +Options: + -h / --help + Print this message and exit. + + -f + Specifies the root ZCML meta directives file, relative to the current + location. All included files will be considered as well + + -o + Specifies a directory, relative to the current location in which the + documentation is stored. Note that this tool will create + sub-directories with files in them. + +$Id: stxdocs.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +import sys, os, getopt +import zope.configuration +from zope.schema import getFieldsInOrder +from zope.configuration import config, xmlconfig +from zope.configuration.docutils import wrap, makeDocStructures + +def usage(code, msg=''): + # Python 2.1 required + print >> sys.stderr, __doc__ + if msg: + print >> sys.stderr, msg + sys.exit(code) + +def _directiveDocs(name, schema, handler, info, indent_offset=0): + """Generate the documentation for one directive.""" + + # Write out the name of the directive + text = ' '*indent_offset + text += '%s\n\n' %name + + # Specify the file and location it has been declared + if isinstance(info, xmlconfig.ParserInfo): + # We do not want to specify the whole path; starting at the 'zope' + # package is enough. + base_dir = os.path.dirname(os.path.dirname( + zope.configuration.__file__))[:-4] + file = info.file.replace(base_dir, '') + + info_text = 'File %s, lines %i - %i.' %(file, info.line, info.eline) + text += wrap(info_text, 78, indent_offset+2) + + elif isinstance(info, (str, unicode)) and info: + text += wrap(info, 78, indent_offset+2) + + # Insert Handler information + if handler is not None: + handler_path = handler.__module__ + '.' + handler.__name__ + text += wrap('Handler: %s' %handler_path, 78, indent_offset+2) + + # Use the schema documentation string as main documentation text for the + # directive. + text += wrap(schema.getDoc(), 78, indent_offset+2) + text += ' '*indent_offset + ' Attributes\n\n' + + # Create directive attribute documentation + for name, field in getFieldsInOrder(schema): + name = name.strip('_') + if field.required: + opt = 'required' + else: + opt = 'optional, default=%s' %repr(field.default) + text += ' '*indent_offset + text += ' %s -- %s (%s)\n\n' %(name, field.__class__.__name__, opt) + + text += wrap(field.title, 78, indent_offset+6) + text += wrap(field.description, 78, indent_offset+6) + + return text + +def _subDirectiveDocs(subdirs, namespace, name): + """Appends a list of sub-directives and their full specification.""" + if subdirs.has_key((namespace, name)): + text = '\n Subdirectives\n\n' + sub_dirs = [] + # Simply walk through all sub-directives here. + subs = subdirs[(namespace, name)] + for sd_ns, sd_name, sd_schema, sd_handler, sd_info in subs: + sub_dirs.append(_directiveDocs( + sd_name, sd_schema, sd_handler, sd_info, 4)) + + return text + '\n\n'.join(sub_dirs) + return '' + +def makedocs(target_dir, zcml_file): + """Generate the documentation tree. + + All we need for this is a starting ZCML file and a directory in which to + put the documentation. + """ + context = xmlconfig.file(zcml_file, execute=False) + namespaces, subdirs = makeDocStructures(context) + + for namespace, directives in namespaces.items(): + ns_dir = os.path.join(target_dir, namespace.split('/')[-1]) + # Create a directory for the namespace, if necessary + if not os.path.exists(ns_dir): + os.mkdir(ns_dir) + + # Create a file for each directive + for name, (schema, handler, info) in directives.items(): + dir_file = os.path.join(ns_dir, name+'.stx') + text = _directiveDocs(name, schema, handler, info) + text += _subDirectiveDocs(subdirs, namespace, name) + open(dir_file, 'w').write(text) + +def _makeabs(path): + """Make an absolute path from the possibly relative path.""" + if not path == os.path.abspath(path): + cwd = os.getcwd() + # This is for symlinks. + if os.environ.has_key('PWD'): + cwd = os.environ['PWD'] + path = os.path.normpath(os.path.join(cwd, path)) + return path + +def main(argv=sys.argv): + try: + opts, args = getopt.getopt( + sys.argv[1:], + 'h:f:o:', + ['help']) + except getopt.error, msg: + usage(1, msg) + + zcml_file = None + output_dir = None + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-o', ): + output_dir = arg + elif opt in ('-f', ): + zcml_file = _makeabs(arg) + if not os.path.exists(zcml_file): + usage(1, 'The specified zcml file does not exist.') + + if zcml_file is None or output_dir is None: + usage(0, "Both, the '-f' and '-o' option are required") + + # Generate the docs + makedocs(output_dir, zcml_file) + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/zope/configuration/tests/bad.py zope3-3.5~bzr18/src/zope/configuration/tests/bad.py --- zope3-3.4.0/src/zope/configuration/tests/bad.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/bad.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,3 @@ +# I'm bad. I want to be bad. Don't try to change me. + +import bad_to_the_bone diff -Nru zope3-3.4.0/src/zope/configuration/tests/conditions.zcml zope3-3.5~bzr18/src/zope/configuration/tests/conditions.zcml --- zope3-3.4.0/src/zope/configuration/tests/conditions.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/conditions.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,78 @@ + + + + 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. + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/directives.py zope3-3.5~bzr18/src/zope/configuration/tests/directives.py --- zope3-3.4.0/src/zope/configuration/tests/directives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/directives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,90 @@ +############################################################################## +# +# 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 + +$Id: directives.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +from zope.interface import Interface, implements +from zope.schema import Text, BytesLine +from zope.configuration.config import GroupingContextDecorator +from zope.configuration.interfaces import IConfigurationContext +from zope.configuration.fields import GlobalObject + + +class F(object): + def __repr__(self): + return 'f' + def __call__(self, *a, **k): + pass + +f = F() + +class ISimple(Interface): + + a = Text() + b = Text(required=False) + c = BytesLine() + +def simple(context, a=None, c=None, b=u"xxx"): + return [(('simple', a, b, c), f, (a, b, c))] + +def newsimple(context, a, c, b=u"xxx"): + context.action(('newsimple', a, b, c), f, (a, b, c)) + + +class IPackaged(Interface): + + package = GlobalObject() + +class IPackagedContext(IPackaged, IConfigurationContext): + pass + +class Packaged(GroupingContextDecorator): + + implements(IPackagedContext) + + +class IFactory(Interface): + + factory = GlobalObject() + +def factory(context, factory): + context.action(('factory', 1,2), factory) + +class Complex(object): + + def __init__(self, context, a, c, b=u"xxx"): + self.a, self.b, self.c = a, b, c + context.action("Complex.__init__") + + def factory(self, context, factory): + return [(('Complex.factory', 1,2), factory, (self.a, ))] + + def factory2(self, context, factory): + return [(('Complex.factory', 1,2), factory, (self.a, ))] + + def __call__(self): + return [(('Complex', 1,2), f, (self.b, self.c))] + + +class Ik(Interface): + for_ = BytesLine() + class_ = BytesLine() + x = BytesLine() + +def k(context, for_, class_, x): + context.action(('k', for_), f, (for_, class_, x)) + +def kkw(context, for_, class_, x, **kw): + context.action(('k', for_), f, (for_, class_, x, kw)) diff -Nru zope3-3.4.0/src/zope/configuration/tests/excludedemo/configure.zcml zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/configure.zcml --- zope3-3.4.0/src/zope/configuration/tests/excludedemo/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/excludedemo/__init__.py zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/__init__.py --- zope3-3.4.0/src/zope/configuration/tests/excludedemo/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# diff -Nru zope3-3.4.0/src/zope/configuration/tests/excludedemo/spam.zcml zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/spam.zcml --- zope3-3.4.0/src/zope/configuration/tests/excludedemo/spam.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/spam.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/configuration/tests/excludedemo/sub/configure.zcml zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/sub/configure.zcml --- zope3-3.4.0/src/zope/configuration/tests/excludedemo/sub/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/sub/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/configuration/tests/excludedemo/sub/__init__.py zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/sub/__init__.py --- zope3-3.4.0/src/zope/configuration/tests/excludedemo/sub/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/excludedemo/sub/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# diff -Nru zope3-3.4.0/src/zope/configuration/tests/__init__.py zope3-3.5~bzr18/src/zope/configuration/tests/__init__.py --- zope3-3.4.0/src/zope/configuration/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/bar1.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/bar1.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/bar1.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/bar1.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/bar21.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/bar21.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/bar21.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/bar21.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/bar2.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/bar2.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/bar2.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/bar2.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/baro2.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baro2.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/baro2.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baro2.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/baro.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baro.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/baro.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baro.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/bar.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/bar.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/bar.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/bar.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/baz1.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baz1.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/baz1.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baz1.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,12 @@ + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/baz2.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baz2.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/baz2.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baz2.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/baz3.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baz3.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/baz3.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/baz3.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/configure.zcml zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/configure.zcml --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/configure.zcml.in zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/configure.zcml.in --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/configure.zcml.in 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/configure.zcml.in 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/foo.py zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/foo.py --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/foo.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/foo.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,39 @@ +############################################################################## +# +# 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 + +$Id: foo.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +from zope.interface import Interface +from zope import schema + +data = [] + +class S1(Interface): + x = schema.BytesLine() + y = schema.Int() + +class stuff(object): + def __init__(self, args, info, basepath, package, includepath): + (self.args, self.info, self.basepath, self.package, self.includepath + ) = args, info, basepath, package, includepath + +def handler(_context, **kw): + args = kw.items() + args.sort() + args = tuple(args) + discriminator = args + args = (stuff(args, _context.info, _context.basepath, _context.package, + _context.includepath), ) + _context.action(discriminator, data.append, args) diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/foo.zcml.in zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/foo.zcml.in --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/foo.zcml.in 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/foo.zcml.in 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/samplepackage/__init__.py zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/__init__.py --- zope3-3.4.0/src/zope/configuration/tests/samplepackage/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/samplepackage/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/configuration/tests/sample.zcml zope3-3.5~bzr18/src/zope/configuration/tests/sample.zcml --- zope3-3.4.0/src/zope/configuration/tests/sample.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/sample.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/schema.zcml zope3-3.5~bzr18/src/zope/configuration/tests/schema.zcml --- zope3-3.4.0/src/zope/configuration/tests/schema.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/schema.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,66 @@ + + + + + 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 + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/simple.zcml zope3-3.5~bzr18/src/zope/configuration/tests/simple.zcml --- zope3-3.4.0/src/zope/configuration/tests/simple.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/simple.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,37 @@ + + + + + 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, + + + + + + + + diff -Nru zope3-3.4.0/src/zope/configuration/tests/test_conditions.py zope3-3.5~bzr18/src/zope/configuration/tests/test_conditions.py --- zope3-3.4.0/src/zope/configuration/tests/test_conditions.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/test_conditions.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,109 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +r'''How to conditionalize specific directives + +There is a "condition" attribute in the +"http://namespaces.zope.org/zcml" namespace which is honored on all +elements in ZCML. The value of the attribute is an expression +which is used to determine if that element and its descendents are +used. If the condition is true, processing continues normally, +otherwise that element and its descendents are ignored. + +Currently the expression is always of the form "have featurename", and it +checks for the presence of a . + +Our demonstration uses a trivial registry; each registration consists +of a simple id inserted in the global `registry` in this module. We +can checked that a registration was made by checking whether the id is +present in `registry`. + +We start by loading the example ZCML file, *conditions.zcml*:: + + >>> import zope.configuration.tests + >>> import zope.configuration.xmlconfig + + >>> context = zope.configuration.xmlconfig.file("conditions.zcml", + ... zope.configuration.tests) + +To show that our sample directive works, we see that the unqualified +registration was successful:: + + >>> "unqualified.registration" in registry + True + +When the expression specified with ``zcml:condition`` evaluates to +true, the element it is attached to and all contained elements (not +otherwise conditioned) should be processed normally:: + + >>> "direct.true.condition" in registry + True + >>> "nested.true.condition" in registry + True + +However, when the expression evaluates to false, the conditioned +element and all contained elements should be ignored:: + + >>> "direct.false.condition" in registry + False + >>> "nested.false.condition" in registry + False + +Conditions on container elements affect the conditions in nested +elements in a reasonable way. If an "outer" condition is true, nested +conditions are processed normally:: + + >>> "true.condition.nested.in.true" in registry + True + >>> "false.condition.nested.in.true" in registry + False + +If the outer condition is false, inner conditions are not even +evaluated, and the nested elements are ignored:: + + >>> "true.condition.nested.in.false" in registry + False + >>> "false.condition.nested.in.false" in registry + False + +Now we need to clean up after ourselves:: + + >>> del registry[:] + +''' +__docformat__ = "reStructuredText" + +import doctest +import zope.interface +import zope.schema + + +class IRegister(zope.interface.Interface): + """Trivial sample registry.""" + + id = zope.schema.Id( + title=u"Identifier", + description=u"Some identifier that can be checked.", + required=True, + ) + +registry = [] + +def register(context, id): + context.action(discriminator=('Register', id), + callable=registry.append, + args=(id,) + ) + +def test_suite(): + return doctest.DocTestSuite() diff -Nru zope3-3.4.0/src/zope/configuration/tests/test_config.py zope3-3.5~bzr18/src/zope/configuration/tests/test_config.py --- zope3-3.4.0/src/zope/configuration/tests/test_config.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/test_config.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,347 @@ +############################################################################## +# +# 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. + +$Id: test_config.py 111665 2010-04-30 17:36:06Z hannosch $ +""" + +import sys +import unittest +import re +from doctest import DocTestSuite +from zope.testing import renormalizing +from zope.configuration.config import metans, ConfigurationMachine +from zope.configuration import config + +def test_config_extended_example(): + """Configuration machine + + Examples: + + >>> machine = ConfigurationMachine() + >>> ns = "http://www.zope.org/testing" + + Register some test directives: + + Start with a grouping directive that sets a package: + + >>> machine((metans, "groupingDirective"), + ... name="package", namespace=ns, + ... schema="zope.configuration.tests.directives.IPackaged", + ... handler="zope.configuration.tests.directives.Packaged", + ... ) + + Now we can set the package: + + >>> machine.begin((ns, "package"), + ... package="zope.configuration.tests.directives", + ... ) + + Which makes it easier to define the other directives: + + First, define some simple directives: + + >>> machine((metans, "directive"), + ... namespace=ns, name="simple", + ... schema=".ISimple", handler=".simple") + + >>> machine((metans, "directive"), + ... namespace=ns, name="newsimple", + ... schema=".ISimple", handler=".newsimple") + + + and try them out: + + >>> machine((ns, "simple"), "first", a=u"aa", c=u"cc") + >>> machine((ns, "newsimple"), "second", a=u"naa", c=u"ncc", b=u"nbb") + + >>> from pprint import PrettyPrinter + >>> pprint=PrettyPrinter(width=50).pprint + + >>> pprint(machine.actions) + [(('simple', u'aa', u'xxx', 'cc'), + f, + (u'aa', u'xxx', 'cc'), + {}, + (), + 'first'), + (('newsimple', u'naa', u'nbb', 'ncc'), + f, + (u'naa', u'nbb', 'ncc'), + {}, + (), + 'second')] + + + Define and try a simple directive that uses a component: + + >>> machine((metans, "directive"), + ... namespace=ns, name="factory", + ... schema=".IFactory", handler=".factory") + + + >>> machine((ns, "factory"), factory=u".f") + >>> pprint(machine.actions[-1:]) + [(('factory', 1, 2), f)] + + Define and try a complex directive: + + >>> machine.begin((metans, "complexDirective"), + ... namespace=ns, name="testc", + ... schema=".ISimple", handler=".Complex") + + >>> machine((metans, "subdirective"), + ... name="factory", schema=".IFactory") + + >>> machine.end() + + >>> machine.begin((ns, "testc"), None, "third", a=u'ca', c='cc') + >>> machine((ns, "factory"), "fourth", factory=".f") + + Note that we can't call a complex method unless there is a directive for + it: + + >>> machine((ns, "factory2"), factory=".f") + Traceback (most recent call last): + ... + ConfigurationError: ('Invalid directive', 'factory2') + + + >>> machine.end() + >>> pprint(machine.actions) + [(('simple', u'aa', u'xxx', 'cc'), + f, + (u'aa', u'xxx', 'cc'), + {}, + (), + 'first'), + (('newsimple', u'naa', u'nbb', 'ncc'), + f, + (u'naa', u'nbb', 'ncc'), + {}, + (), + 'second'), + (('factory', 1, 2), f), + ('Complex.__init__', None, (), {}, (), 'third'), + (('Complex.factory', 1, 2), + f, + (u'ca',), + {}, + (), + 'fourth'), + (('Complex', 1, 2), + f, + (u'xxx', 'cc'), + {}, + (), + 'third')] + + Done with the package + + >>> machine.end() + + + Verify that we can use a simple directive outside of the package: + + >>> machine((ns, "simple"), a=u"oaa", c=u"occ", b=u"obb") + + But we can't use the factory directive, because it's only valid + inside a package directive: + + >>> machine((ns, "factory"), factory=u".F") + Traceback (most recent call last): + ... + ConfigurationError: ('Invalid value for', 'factory',""" \ + """ "Can't use leading dots in dotted names, no package has been set.") + + >>> pprint(machine.actions) + [(('simple', u'aa', u'xxx', 'cc'), + f, + (u'aa', u'xxx', 'cc'), + {}, + (), + 'first'), + (('newsimple', u'naa', u'nbb', 'ncc'), + f, + (u'naa', u'nbb', 'ncc'), + {}, + (), + 'second'), + (('factory', 1, 2), f), + ('Complex.__init__', None, (), {}, (), 'third'), + (('Complex.factory', 1, 2), + f, + (u'ca',), + {}, + (), + 'fourth'), + (('Complex', 1, 2), + f, + (u'xxx', 'cc'), + {}, + (), + 'third'), + (('simple', u'oaa', u'obb', 'occ'), + f, + (u'oaa', u'obb', 'occ'))] + + """ + #' + +def test_keyword_handling(): + """ + >>> machine = ConfigurationMachine() + >>> ns = "http://www.zope.org/testing" + + Register some test directives: + + Start with a grouping directive that sets a package: + + >>> machine((metans, "groupingDirective"), + ... name="package", namespace=ns, + ... schema="zope.configuration.tests.directives.IPackaged", + ... handler="zope.configuration.tests.directives.Packaged", + ... ) + + Now we can set the package: + + >>> machine.begin((ns, "package"), + ... package="zope.configuration.tests.directives", + ... ) + + Which makes it easier to define the other directives: + + >>> machine((metans, "directive"), + ... namespace=ns, name="k", + ... schema=".Ik", handler=".k") + + + >>> machine((ns, "k"), "yee ha", **{"for": u"f", "class": u"c", "x": u"x"}) + + >>> machine.actions + [(('k', 'f'), f, ('f', 'c', 'x'), {}, (), 'yee ha')] + """ + +def test_basepath_absolute(): + """Path must always return an absolute path. + + >>> import os + >>> class stub: + ... __file__ = os.path.join('relative', 'path') + >>> c = config.ConfigurationContext() + >>> c.package = stub() + + >>> os.path.isabs(c.path('y/z')) + True + """ + + +def test_trailing_dot_in_resolve(): + """Dotted names are no longer allowed to end in dots + + >>> c = config.ConfigurationContext() + + >>> c.resolve('zope.') + Traceback (most recent call last): + ... + ValueError: Trailing dots are no longer supported in dotted names + + >>> c.resolve(' ') + Traceback (most recent call last): + ... + ValueError: The given name is blank + """ + +def test_bad_dotted_last_import(): + """ + >>> c = config.ConfigurationContext() + + Import error caused by a bad last component in the dotted name. + + >>> c.resolve('zope.configuration.tests.nosuch') + Traceback (most recent call last): + ... + ConfigurationError: ImportError: Module zope.configuration.tests""" \ + """ has no global nosuch + """ + +def test_bad_dotted_import(): + """ + >>> c = config.ConfigurationContext() + + Import error caused by a totally wrong dotted name. + + >>> c.resolve('zope.configuration.nosuch.noreally') + Traceback (most recent call last): + ... + ConfigurationError: ImportError: Couldn't import""" \ + """ zope.configuration.nosuch, No module named nosuch + """ + +def test_bad_sub_last_import(): + """ + >>> c = config.ConfigurationContext() + + Import error caused by a bad sub import inside the referenced + dotted name. Here we keep the standard traceback. + + >>> c.resolve('zope.configuration.tests.victim') + Traceback (most recent call last): + ... + File "...bad.py", line 3 in ? + import bad_to_the_bone + ImportError: No module named bad_to_the_bone + + Cleanup: + + >>> for name in ('zope.configuration.tests.victim', + ... 'zope.configuration.tests.bad'): + ... if name in sys.modules: + ... del sys.modules[name] + """ + +def test_bad_sub_import(): + """ + >>> c = config.ConfigurationContext() + + Import error caused by a bad sub import inside part of the referenced + dotted name. Here we keep the standard traceback. + + >>> c.resolve('zope.configuration.tests.victim.nosuch') + Traceback (most recent call last): + ... + File "...bad.py", line 3 in ? + import bad_to_the_bone + ImportError: No module named bad_to_the_bone + + Cleanup: + + >>> for name in ('zope.configuration.tests.victim', + ... 'zope.configuration.tests.bad'): + ... if name in sys.modules: + ... del sys.modules[name] + """ + +def test_suite(): + checker = renormalizing.RENormalizing([ + (re.compile(r":"), + r'exceptions.\1Error:'), + ]) + return unittest.TestSuite(( + DocTestSuite('zope.configuration.fields'), + DocTestSuite('zope.configuration.config',checker=checker), + DocTestSuite(), + )) + +if __name__ == '__main__': unittest.main() diff -Nru zope3-3.4.0/src/zope/configuration/tests/test_docutils.py zope3-3.5~bzr18/src/zope/configuration/tests/test_docutils.py --- zope3-3.4.0/src/zope/configuration/tests/test_docutils.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/test_docutils.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,26 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Doc Tests for for zope.configuration.docutils + +$Id: test_docutils.py 111665 2010-04-30 17:36:06Z hannosch $ +""" +import unittest +from doctest import DocTestSuite + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite('zope.configuration.docutils'), + )) + +if __name__ == '__main__': unittest.main() diff -Nru zope3-3.4.0/src/zope/configuration/tests/test_nested.py zope3-3.5~bzr18/src/zope/configuration/tests/test_nested.py --- zope3-3.4.0/src/zope/configuration/tests/test_nested.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/test_nested.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,321 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +r"""Creating nested directives + +When using ZCML, you sometimes nest ZCML directives. This is typically +done either to: + +- Avoid repetative input. Information shared among multiple + directives is provided in a surrounding directive. + +- Put together information that is too complex or structured to express + with a single set of directive parameters. + +Grouping directives are used to handle both of these cases. See the +documentation in ``../zopeconfigure.py``. This file describes the +implementation of the zope ``configure`` directive, which groups +directives that use a common package or internationalization domain. +The documentation in ``../zopeconfigure.py`` provides background for +the documentation here. You should also have read the documentation +in ``test_simple.py``, which documents how to create simple +directives. + +This file shows you how to handle the second case above. In this case, +we have grouping directives that are meant to collaborate with +specific contained directives. To do this, you have the grouping +directives declare a more specific (or alternate) interface to +``IConfigurationContext``. Directives designed to work with those +grouping directives are registered for the new interface. + +Let's look at example. Suppose we wanted to be able to define schema +using ZCML. We'd use a grouping directive to specify schemas and +contained directives to specify fields within the schema. We'll use a +schema registry to hold the defined schemas. + +A schema has a name, an id, some documentation, and some fields. +We'll provide the name and the id as parameters. We'll define fields +as subdirectives and documentation as text contained in the schema +directive. The schema directive uses the schema, ``ISchemaInfo`` for +it's parameters. + +We also define the schema, ISchema, that specifies an attribute that +nested field directives will use to store the fields they define. + +The class, ``Schema``, provides the handler for the schema directive. (If +you haven't read the documentation in ``zopeconfigure.py``, you need +to do so now.) The constructor saves its arguments as attributes +and initializes its ``fields`` attribute. + +The ``after`` method of the ``Schema`` class creates a schema and +computes an action to register the schema in the schema registry. The +discriminator prevents two schema directives from registering the same +schema. + +It's important to note that when we call the ``action`` method on +``self``, rather than on ``self.context``. This is because, in a +grouping directive handler, the handler instance is itself a context. +When we call the ``action`` method, the method stores additional meta +data associated with the context it was called on. This meta data +includes an include path, used when resolving conflicting actions, +and an object that contains information about the XML source used +to invole the directive. If we called the action method on +``self.context``, the wrong meta data would be associated with the +configuration action. + +The file ``schema.zcml`` contains the meta-configuration directive +that defines the schema directive. + +To define fields, we'll create directives to define the fields. +Let's start with a ``text`` field. ``ITextField`` defines the schema for +text field parameters. It extends ``IFieldInfo``, which defines data +common to all fields. We also define a simple handler method, +textField, that takes a context and keyword arguments. (For +information on writing simple directives, see ``test_simple.py``.) +We've abstracted most of the logic into the function ``field``. + +The ``field`` function computes a field instance using the +constructor, and the keyword arguments passed to it. It also uses the +context information object to get the text content of the directive, +which it uses for the field description. + +After computing the field instance, it gets the ``Schema`` instance, +which is the context of the context passed to the function. The +function checks to see if there is already a field with that name. If +there is, it raises an error. Otherwise, it saves the field. + +We also define an ``IIntInfo`` schema and ``intField`` handler +function to support defining integer fields. + +We register the ``text`` and ``int`` directives in ``schema.zcml``. +These are like the simple directive definition we saw in +``test_simple.py`` with an important exception. We provide a +``usedIn`` parameter to say that these directives can *only* ne used +in a ``ISchema`` context. In other words, these can only be used +inside of ``schema`` directives. + +The ``schema.zcml`` file also contains some sample ``schema`` +directives. We can execute the file: + +>>> from zope.configuration import tests +>>> context = xmlconfig.file("schema.zcml", tests) + +And verify that the schema registery has the schemas we expect: + +>>> from pprint import PrettyPrinter +>>> pprint=PrettyPrinter(width=70).pprint +>>> pprint(list(schema_registry)) +['zope.configuration.tests.test_nested.I1', + 'zope.configuration.tests.test_nested.I2'] + +>>> def sorted(x): +... r = list(x) +... r.sort() +... return r + +>>> i1 = schema_registry['zope.configuration.tests.test_nested.I1'] +>>> sorted(i1) +['a', 'b'] +>>> i1['a'].__class__.__name__ +'Text' +>>> i1['a'].description.strip() +u'A\n\n Blah blah' +>>> i1['a'].min_length +1 +>>> i1['b'].__class__.__name__ +'Int' +>>> i1['b'].description.strip() +u'B\n\n Not feeling very creative' +>>> i1['b'].min +1 +>>> i1['b'].max +10 + +>>> i2 = schema_registry['zope.configuration.tests.test_nested.I2'] +>>> sorted(i2) +['x', 'y'] + + +Now let's look at some error situations. For example, let's see what +happens if we use a field directive outside of a schema dorective. +(Note that we used the context we created above, so we don't have to +redefine our directives: + +>>> try: +... v = xmlconfig.string( +... '', +... context) +... except xmlconfig.ZopeXMLConfigurationError, v: +... pass +>>> print v +File "", line 1.0 + ConfigurationError: The directive """ \ + """(u'http://sample.namespaces.zope.org/schema', u'text') """ \ + """cannot be used in this context + +Let's see what happens if we declare duplicate fields: + +>>> try: +... v = xmlconfig.string( +... ''' +... +... +... +... +... ''', +... context) +... except xmlconfig.ZopeXMLConfigurationError, v: +... pass +>>> print v +File "", line 5.7-5.24 + ValueError: ('Duplicate field', 'x') + +$Id: test_nested.py 111665 2010-04-30 17:36:06Z hannosch $ +""" + +import unittest +from doctest import DocTestSuite +from zope import interface, schema +from zope.configuration import config, xmlconfig, fields + + +schema_registry = {} + +class ISchemaInfo(interface.Interface): + """Parameter schema for the schema directive + """ + + name = schema.TextLine( + title=u"The schema name", + description=u"This is a descriptive name for the schema." + ) + + id = schema.Id( + title=u"The unique id for the schema" + ) + +class ISchema(interface.Interface): + """Interface that distinguishes the schema directive + """ + + fields = interface.Attribute("Dictionary of field definitions" + ) + +class Schema(config.GroupingContextDecorator): + """Handle schema directives + """ + + interface.implements(config.IConfigurationContext, ISchema) + + def __init__(self, context, name, id): + self.context, self.name, self.id = context, name, id + self.fields = {} + + def after(self): + schema = interface.Interface.__class__( + self.name, + (interface.Interface, ), + self.fields + ) + schema.__doc__ = self.info.text.strip() + self.action( + discriminator=('schema', self.id), + callable=schema_registry.__setitem__, + args=(self.id, schema), + ) + + +class IFieldInfo(interface.Interface): + + name = schema.BytesLine( + title=u"The field name" + ) + + title = schema.TextLine( + title=u"Title", + description=u"A short summary or label", + default=u"", + required=False, + ) + + required = fields.Bool( + title=u"Required", + description=u"Determines whether a value is required.", + default=True) + + readonly = fields.Bool( + title=u"Read Only", + description=u"Can the value be modified?", + required=False, + default=False) + +class ITextInfo(IFieldInfo): + + min_length = schema.Int( + title=u"Minimum length", + description=u"Value after whitespace processing cannot have less than " + u"min_length characters. If min_length is None, there is " + u"no minimum.", + required=False, + min=0, # needs to be a positive number + default=0) + + max_length = schema.Int( + title=u"Maximum length", + description=u"Value after whitespace processing cannot have greater " + u"or equal than max_length characters. If max_length is " + u"None, there is no maximum.", + required=False, + min=0, # needs to be a positive number + default=None) + +def field(context, constructor, name, **kw): + + # Compute the field + field = constructor(description=context.info.text.strip(), **kw) + + # Save it in the schema's field dictionary + schema = context.context + if name in schema.fields: + raise ValueError("Duplicate field", name) + schema.fields[name] = field + + +def textField(context, **kw): + field(context, schema.Text, **kw) + +class IIntInfo(IFieldInfo): + + min = schema.Int( + title=u"Start of the range", + required=False, + default=None + ) + + max = schema.Int( + title=u"End of the range (excluding the value itself)", + required=False, + default=None + ) + +def intField(context, **kw): + field(context, schema.Int, **kw) + + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite(), + )) + +if __name__ == '__main__': unittest.main() diff -Nru zope3-3.4.0/src/zope/configuration/tests/test_simple.py zope3-3.5~bzr18/src/zope/configuration/tests/test_simple.py --- zope3-3.4.0/src/zope/configuration/tests/test_simple.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/test_simple.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,210 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +r"""How to write a simple directive + +This module documents how to write a simple directive. + +A simple directive is a directive that doesn't contain other +directives. It can be implemented via a fairly simple function. +To implement a simple directive, you need to do 3 things: + +- You need to create a schema to describe the directive parameters, + +- You need to write a directive handler, and + +- You need to register the directive. + +In this module, we'll implement a contrived example that records +information about files in a file registry. The file registry is just +the list, ``file_registry``. Our registry will contain tuples +with: + + - file path + + - file title + + - description + + - Information about where the file was defined + +Our schema is defined in IRegisterFile (see below). Our schema lists +the path and title. We'll get the description and other information +for free, as we'll see later. The title is not required, and may be +ommmitted. + +The job of a configuration handler is to compute one or more +configuration actions. Configuration actions are defered function +calls. The handler doesn't perform the actions. It just computes +actions, which may be performed later if they are not overridden by +other directives. + +Out handler is given in the function, ``registerFile``. It takes a context, +a path and a title. All directive handlers take the directive context +as the first argument. A directive context, at a minimim, implements, +``zope.configuration.IConfigurationContext``. (Specialized contexts +can implement more specific interfaces. We'll say more about that when +we talk about grouping directives.) The title argument +must have a default value, because we indicated that the title was not +required in the schema. (Alternatively, we could have made the title +required, but provided a default value in the schema. + +In the first line of function `registerFile`, we get the context information +object. This object contains information about the configuration +directive, such as the file and location within the file of the +directive. + +The context information object also has a text attribute that contains +the textual data contained by the configuration directive. (This is +the concatenation of all of the xml text nodes directly contained by +the directive.) We use this for our description in the second line +of the handler. + +The last thing the handler does is to compute an action by calling the +action method of the context. It passes the action method 3 keyword +arguments: + +- discriminator + + The discriminator is used to identify the action to be performed so + that duplicate actions can be detected. Two actions are duplicated, + and this conflict, if they have the same discriminator values and + the values are not ``None``. Conflicting actions can be resolved if + one of the conflicting actions is from a configuration file that + directly or indirectly includes the files containing the other + conflicting actions. + + In function ``registerFile``, we a tuple with the string + ``'RegisterFile'`` and the path to be registered. + +- callable + + The callable is the object to be called to perform the action. + +- args + + The args argument contains positinal arguments to be passed to the + callable. In function ``registerFile``, we pass a tuple containing a + ``FileInfo`` object. + + (Note that there's nothing special about the FileInfo class. It has + nothing to do with creating simple directives. It's just used in + this example to organize the application data.) + + +The final step in implementing the simple directive is to register +it. We do that with the zcml ``meta:directive`` directive. This is +given in the file simple.zcml. Here we specify the name, namespace, +schema, and handler for the directive. We also provide a +documentation for the directive as text between the start and end +tags. + +The file simple.zcml also includes some directives that use the new +directive to register some files. + +Now let's try it all out: + +>>> from zope.configuration import tests +>>> context = xmlconfig.file("simple.zcml", tests) + +Now we should see some file information in the registry: + +>>> from zope.configuration.tests.test_xmlconfig import clean_text_w_paths +>>> from zope.configuration.tests.test_xmlconfig import clean_path +>>> for i in file_registry: +... print "path:", clean_path(i.path) +... print "title:", i.title +... print "description:", '\n'.join( +... [l.rstrip() +... for l in i.description.strip().split('\n') +... if l.rstrip()]) +... print "info:" +... print clean_text_w_paths(i.info) +path: tests/test_simple.py +title: How to create a simple directive +description: Describes how to implement a simple directive +info: +File "tests/simple.zcml", line 19.2-24.2 + + Describes how to implement a simple directive + +path: tests/simple.zcml +title: +description: Shows the ZCML directives needed to register a simple directive. + Also show some usage examples, +info: +File "tests/simple.zcml", line 26.2-30.2 + + Shows the ZCML directives needed to register a simple directive. + Also show some usage examples, + +path: tests/__init__.py +title: Make this a package +description: +info: +File "tests/simple.zcml", line 32.2-32.67 + + + +We'll clean up after ourselves: + +>>> del file_registry[:] + +$Id: test_simple.py 111665 2010-04-30 17:36:06Z hannosch $ +""" + +file_registry = [] + + +import unittest +from doctest import DocTestSuite +from zope import interface +from zope import schema +from zope.configuration import fields, xmlconfig + +class IRegisterFile(interface.Interface): + + path = fields.Path( + title=u"File path", + description=u"This is the path name of the file to be registered." + ) + + title = schema.Text( + title=u"Short summary of the file", + description=u"This will be used in file listings", + required = False + ) + +class FileInfo(object): + + def __init__(self, path, title, description, info): + (self.path, self.title, self.description, self.info + ) = path, title, description, info + +def registerFile(context, path, title=u""): + info = context.info + description = info.text.strip() + context.action(discriminator=('RegisterFile', path), + callable=file_registry.append, + args=(FileInfo(path, title, description, info),) + ) + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite(), + )) + +if __name__ == '__main__': unittest.main() diff -Nru zope3-3.4.0/src/zope/configuration/tests/test_xmlconfig.py zope3-3.5~bzr18/src/zope/configuration/tests/test_xmlconfig.py --- zope3-3.4.0/src/zope/configuration/tests/test_xmlconfig.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/test_xmlconfig.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,639 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Test XML configuration (ZCML) machinery. + +$Id: test_xmlconfig.py 111665 2010-04-30 17:36:06Z hannosch $ +""" +import unittest +import os +import re +from doctest import DocTestSuite, DocFileSuite +from zope.testing import renormalizing +from zope.configuration import xmlconfig, config +from zope.configuration.tests.samplepackage import foo +from pprint import PrettyPrinter, pprint + + +class FauxLocator(object): + def __init__(self, file, line, column): + self.file, self.line, self.column = file, line, column + def getSystemId(self): + return self.file + def getLineNumber(self): + return self.line + def getColumnNumber(self): + return self.column + +class FauxContext(object): + + def setInfo(self, info): + self.info = info + def getInfo(self): + return self.info + def begin(self, name, data, info): + self.begin_args = name, data + self.info = info + def end(self): + self.end_called = 1 + +def path(*p): + return os.path.join(os.path.dirname(__file__), *p) + +def test_ConfigurationHandler_normal(): + """ + >>> context = FauxContext() + >>> locator = FauxLocator('tests//sample.zcml', 1, 1) + >>> handler = xmlconfig.ConfigurationHandler(context) + >>> handler.setDocumentLocator(locator) + + >>> handler.startElementNS((u"ns", u"foo"), u"foo", + ... {(u"xxx", u"splat"): u"splatv", + ... (None, u"a"): u"avalue", + ... (None, u"b"): u"bvalue", + ... }) + >>> context.info + File "tests//sample.zcml", line 1.1 + >>> from pprint import PrettyPrinter + >>> pprint=PrettyPrinter(width=50).pprint + >>> pprint(context.begin_args) + ((u'ns', u'foo'), + {'a': u'avalue', 'b': u'bvalue'}) + >>> getattr(context, "end_called", 0) + 0 + + >>> locator.line, locator.column = 7, 16 + >>> handler.endElementNS((u"ns", u"foo"), u"foo") + >>> context.info + File "tests//sample.zcml", line 1.1-7.16 + >>> context.end_called + 1 + + """ + +def test_ConfigurationHandler_err_start(): + """ + + >>> class FauxContext(FauxContext): + ... def begin(self, *args): + ... raise AttributeError("xxx") + + >>> context = FauxContext() + >>> locator = FauxLocator('tests//sample.zcml', 1, 1) + >>> handler = xmlconfig.ConfigurationHandler(context) + >>> handler.setDocumentLocator(locator) + + >>> try: + ... v = handler.startElementNS((u"ns", u"foo"), u"foo", + ... {(u"xxx", u"splat"): u"splatv", + ... (None, u"a"): u"avalue", + ... (None, u"b"): u"bvalue", + ... }) + ... except xmlconfig.ZopeXMLConfigurationError, v: + ... pass + >>> print v + File "tests//sample.zcml", line 1.1 + AttributeError: xxx + + """ + +def test_ConfigurationHandler_err_end(): + """ + + >>> class FauxContext(FauxContext): + ... def end(self): + ... raise AttributeError("xxx") + + >>> context = FauxContext() + >>> locator = FauxLocator('tests//sample.zcml', 1, 1) + >>> handler = xmlconfig.ConfigurationHandler(context) + >>> handler.setDocumentLocator(locator) + + >>> handler.startElementNS((u"ns", u"foo"), u"foo", + ... {(u"xxx", u"splat"): u"splatv", + ... (None, u"a"): u"avalue", + ... (None, u"b"): u"bvalue", + ... }) + + >>> locator.line, locator.column = 7, 16 + >>> try: + ... v = handler.endElementNS((u"ns", u"foo"), u"foo") + ... except xmlconfig.ZopeXMLConfigurationError, v: + ... pass + >>> print v + File "tests//sample.zcml", line 1.1-7.16 + AttributeError: xxx + + """ + +def clean_info_path(s): + part1 = s[:6] + part2 = s[6:s.find('"', 6)] + part2 = part2[part2.rfind("tests"):] + part2 = part2.replace(os.sep, '/') + part3 = s[s.find('"', 6):].rstrip() + return part1+part2+part3 + +def clean_path(s): + s = s[s.rfind("tests"):] + s = s.replace(os.sep, '/') + return s + +def test_processxmlfile(): + """ + + >>> file = open(path("samplepackage", "configure.zcml")) + >>> context = config.ConfigurationMachine() + >>> xmlconfig.registerCommonDirectives(context) + >>> xmlconfig.processxmlfile(file, context) + + >>> foo.data + [] + + >>> context.execute_actions() + + >>> data = foo.data.pop() + + >>> data.args + (('x', 'blah'), ('y', 0)) + + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/configure.zcml", line 12.2-12.29 + + >>> print clean_info_path(str(data.info)) + File "tests/samplepackage/configure.zcml", line 12.2-12.29 + + + >>> data.package + >>> data.basepath + """ + +def test_file(): + """ + + >>> file_name = path("samplepackage", "configure.zcml") + >>> context = xmlconfig.file(file_name) + + >>> data = foo.data.pop() + + >>> data.args + (('x', 'blah'), ('y', 0)) + + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/configure.zcml", line 12.2-12.29 + + >>> print clean_info_path(str(data.info)) + File "tests/samplepackage/configure.zcml", line 12.2-12.29 + + + >>> data.package + >>> print clean_path(data.basepath) + tests/samplepackage + """ + +def test_include_by_package(): + """ + >>> context = config.ConfigurationMachine() + >>> xmlconfig.registerCommonDirectives(context) + >>> import zope.configuration.tests.samplepackage as package + >>> xmlconfig.include(context, 'configure.zcml', package) + >>> context.execute_actions() + + >>> data = foo.data.pop() + + >>> data.args + (('x', 'blah'), ('y', 0)) + + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/configure.zcml", line 12.2-12.29 + + >>> print clean_info_path(str(data.info)) + File "tests/samplepackage/configure.zcml", line 12.2-12.29 + + + >>> data.package is package + 1 + + >>> data.basepath[-13:] + 'samplepackage' + + >>> [clean_path(p) for p in data.includepath] + ['tests/samplepackage/configure.zcml'] + + + """ + +# Not any more +## Including the same file more than once produces an error: + +## >>> try: +## ... xmlconfig.include(context, 'configure.zcml', package) +## ... except xmlconfig.ConfigurationError, e: +## ... 'OK' +## ... +## 'OK' + +def test_include_by_file(): + """ + >>> context = config.ConfigurationMachine() + >>> xmlconfig.registerCommonDirectives(context) + >>> here = os.path.dirname(__file__) + >>> path = os.path.join(here, "samplepackage", "foo.zcml") + >>> xmlconfig.include(context, path) + >>> context.execute_actions() + + >>> data = foo.data.pop() + + >>> data.args + (('x', 'foo'), ('y', 2)) + + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/foo.zcml.in", line 12.2-12.28 + + >>> print clean_info_path(str(data.info)) + File "tests/samplepackage/foo.zcml.in", line 12.2-12.28 + + + >>> data.package + + >>> data.basepath[-13:] + 'samplepackage' + + >>> [clean_path(p) for p in data.includepath] + ['tests/samplepackage/foo.zcml.in'] + """ + +def test_include_by_file_glob(): + """ + >>> context = config.ConfigurationMachine() + >>> xmlconfig.registerCommonDirectives(context) + >>> here = os.path.dirname(__file__) + >>> path = os.path.join(here, "samplepackage/baz*.zcml") + >>> xmlconfig.include(context, files=path) + >>> context.execute_actions() + + >>> data = foo.data.pop() + >>> data.args + (('x', 'foo'), ('y', 3)) + + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/baz3.zcml", line 5.2-5.28 + + >>> print clean_info_path(str(data.info)) + File "tests/samplepackage/baz3.zcml", line 5.2-5.28 + + + >>> data.package + + >>> data.basepath[-13:] + 'samplepackage' + + >>> [clean_path(p) for p in data.includepath] + ['tests/samplepackage/baz3.zcml'] + + >>> data = foo.data.pop() + >>> data.args + (('x', 'foo'), ('y', 2)) + + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/baz2.zcml", line 5.2-5.28 + + >>> print clean_info_path(str(data.info)) + File "tests/samplepackage/baz2.zcml", line 5.2-5.28 + + + >>> data.package + + >>> data.basepath[-13:] + 'samplepackage' + + >>> [clean_path(p) for p in data.includepath] + ['tests/samplepackage/baz2.zcml'] + """ + +def clean_actions(actions): + return [ + {'discriminator': discriminator, + 'info': clean_info_path(`info`), + 'includepath': [clean_path(p) for p in includepath], + } + for (discriminator, callable, args, kw, includepath, info, order) + in [config.expand_action(*action) for action in actions] + ] + +def clean_text_w_paths(error): + r = [] + for line in unicode(error).split("\n"): + line = line.rstrip() + if not line: + continue + l = line.find('File "') + if l >= 0: + line = line[:l] + clean_info_path(line[l:]) + r.append(line) + return '\n'.join(r) + +def test_includeOverrides(): + """ + When we have conflicting directives, we can resolve them if one of + the conflicting directives was from a file that included all of + the others. The problem with this is that this requires that all + of the overriding directives be in one file, typically the + top-most including file. This isn't very convenient. Fortunately, + we can overcome this with the includeOverrides directive. Let's + look at an example to see how this works. + + Look at the file bar.zcml. It includes bar1.zcml and bar2.zcml. + bar2.zcml includes configure.zcml and has a foo + directive. bar2.zcml includes bar21.zcml. bar2.zcml has a foo + directive that conflicts with one in bar1.zcml. bar2.zcml also + overrides a foo directive in bar21.zcml. bar21.zcml has a foo + directive that conflicts with one in in configure.zcml. Whew! + + Let's see what happens when we try to process bar.zcml. + + >>> context = config.ConfigurationMachine() + >>> xmlconfig.registerCommonDirectives(context) + + >>> here = os.path.dirname(__file__) + >>> path = os.path.join(here, "samplepackage", "bar.zcml") + >>> xmlconfig.include(context, path) + + So far so good, let's look at the configuration actions: + + >>> pprint=PrettyPrinter(width=70).pprint + >>> pprint(clean_actions(context.actions)) + [{'discriminator': (('x', 'blah'), ('y', 0)), + 'includepath': ['tests/samplepackage/bar.zcml', + 'tests/samplepackage/bar1.zcml', + 'tests/samplepackage/configure.zcml'], + 'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'}, + {'discriminator': (('x', 'blah'), ('y', 1)), + 'includepath': ['tests/samplepackage/bar.zcml', + 'tests/samplepackage/bar1.zcml'], + 'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'}, + {'discriminator': (('x', 'blah'), ('y', 0)), + 'includepath': ['tests/samplepackage/bar.zcml', + 'tests/samplepackage/bar2.zcml', + 'tests/samplepackage/bar21.zcml'], + 'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'}, + {'discriminator': (('x', 'blah'), ('y', 2)), + 'includepath': ['tests/samplepackage/bar.zcml', + 'tests/samplepackage/bar2.zcml', + 'tests/samplepackage/bar21.zcml'], + 'info': 'File "tests/samplepackage/bar21.zcml", line 4.2-4.24'}, + {'discriminator': (('x', 'blah'), ('y', 2)), + 'includepath': ['tests/samplepackage/bar.zcml', + 'tests/samplepackage/bar2.zcml'], + 'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'}, + {'discriminator': (('x', 'blah'), ('y', 1)), + 'includepath': ['tests/samplepackage/bar.zcml', + 'tests/samplepackage/bar2.zcml'], + 'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}] + + As you can see, there are a number of conflicts (actions with the same + discriminator). Some of these can be resolved, but many can't, as + we'll find if we try to execuse the actions: + + >>> try: + ... v = context.execute_actions() + ... except config.ConfigurationConflictError, v: + ... pass + >>> print clean_text_w_paths(str(v)) + Conflicting configuration actions + For: (('x', 'blah'), ('y', 0)) + File "tests/samplepackage/configure.zcml", line 12.2-12.29 + + File "tests/samplepackage/bar21.zcml", line 3.2-3.24 + + For: (('x', 'blah'), ('y', 1)) + File "tests/samplepackage/bar1.zcml", line 5.2-5.24 + + File "tests/samplepackage/bar2.zcml", line 6.2-6.24 + + + Note that the conflicts for (('x', 'blah'), ('y', 2)) aren't + included in the error because they could be resolved. + + Let's try this again using includeOverrides. We'll include + baro.zcml which includes bar2.zcml as overrides. + + >>> context = config.ConfigurationMachine() + >>> xmlconfig.registerCommonDirectives(context) + >>> path = os.path.join(here, "samplepackage", "baro.zcml") + >>> xmlconfig.include(context, path) + + Now, if we look at the actions: + + >>> pprint(clean_actions(context.actions)) + [{'discriminator': (('x', 'blah'), ('y', 0)), + 'includepath': ['tests/samplepackage/baro.zcml', + 'tests/samplepackage/bar1.zcml', + 'tests/samplepackage/configure.zcml'], + 'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'}, + {'discriminator': (('x', 'blah'), ('y', 1)), + 'includepath': ['tests/samplepackage/baro.zcml', + 'tests/samplepackage/bar1.zcml'], + 'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'}, + {'discriminator': (('x', 'blah'), ('y', 0)), + 'includepath': ['tests/samplepackage/baro.zcml'], + 'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'}, + {'discriminator': (('x', 'blah'), ('y', 2)), + 'includepath': ['tests/samplepackage/baro.zcml'], + 'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'}, + {'discriminator': (('x', 'blah'), ('y', 1)), + 'includepath': ['tests/samplepackage/baro.zcml'], + 'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}] + + We see that: + + - The conflicting actions between bar2.zcml and bar21.zcml have + been resolved, and + + - The remaining (after conflict resolution) actions from bar2.zcml + and bar21.zcml have the includepath that they would have if they + were defined in baro.zcml and this override the actions from + bar1.zcml and configure.zcml. + + We can now execute the actions without problem, since the + remaining conflicts are resolvable: + + >>> context.execute_actions() + + We should now have three entries in foo.data: + + >>> len(foo.data) + 3 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 0)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar21.zcml", line 3.2-3.24 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 2)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar2.zcml", line 5.2-5.24 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 1)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar2.zcml", line 6.2-6.24 + + + We expect the exact same results when using includeOverrides with + the ``files`` argument instead of the ``file`` argument. The + baro2.zcml file uses the former: + + >>> context = config.ConfigurationMachine() + >>> xmlconfig.registerCommonDirectives(context) + >>> path = os.path.join(here, "samplepackage", "baro2.zcml") + >>> xmlconfig.include(context, path) + + Actions look like above: + + >>> pprint(clean_actions(context.actions)) + [{'discriminator': (('x', 'blah'), ('y', 0)), + 'includepath': ['tests/samplepackage/baro2.zcml', + 'tests/samplepackage/bar1.zcml', + 'tests/samplepackage/configure.zcml'], + 'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'}, + {'discriminator': (('x', 'blah'), ('y', 1)), + 'includepath': ['tests/samplepackage/baro2.zcml', + 'tests/samplepackage/bar1.zcml'], + 'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'}, + {'discriminator': (('x', 'blah'), ('y', 0)), + 'includepath': ['tests/samplepackage/baro2.zcml'], + 'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'}, + {'discriminator': (('x', 'blah'), ('y', 2)), + 'includepath': ['tests/samplepackage/baro2.zcml'], + 'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'}, + {'discriminator': (('x', 'blah'), ('y', 1)), + 'includepath': ['tests/samplepackage/baro2.zcml'], + 'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}] + + >>> context.execute_actions() + >>> len(foo.data) + 3 + >>> del foo.data[:] + + """ + +def test_XMLConfig(): + """Test processing a configuration file. + + We'll use the same example from test_includeOverrides: + + >>> here = os.path.dirname(__file__) + >>> path = os.path.join(here, "samplepackage", "baro.zcml") + + First, process the configuration file: + + >>> x = xmlconfig.XMLConfig(path) + + Second, call the resulting object to process the actions: + + >>> x() + + And verify the data as above: + + >>> len(foo.data) + 3 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 0)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar21.zcml", line 3.2-3.24 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 2)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar2.zcml", line 5.2-5.24 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 1)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar2.zcml", line 6.2-6.24 + + Finally, clean up. + + >>> from zope.testing.cleanup import CleanUp + >>> CleanUp().cleanUp() + """ + +def test_XMLConfig_w_module(): + """Test processing a configuration file for a module. + + We'll use the same example from test_includeOverrides: + + >>> import zope.configuration.tests.samplepackage as module + + First, process the configuration file: + + >>> x = xmlconfig.XMLConfig("baro.zcml", module) + + Second, call the resulting object to process the actions: + + >>> x() + + And verify the data as above: + + >>> len(foo.data) + 3 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 0)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar21.zcml", line 3.2-3.24 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 2)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar2.zcml", line 5.2-5.24 + + >>> data = foo.data.pop(0) + >>> data.args + (('x', 'blah'), ('y', 1)) + >>> print clean_info_path(`data.info`) + File "tests/samplepackage/bar2.zcml", line 6.2-6.24 + + Finally, clean up. + + >>> from zope.testing.cleanup import CleanUp + >>> CleanUp().cleanUp() + """ + + + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite('zope.configuration.xmlconfig'), + DocTestSuite(), + DocFileSuite('../exclude.txt', + checker=renormalizing.RENormalizing([ + (re.compile('include [^\n]+zope.configuration[\S+]'), + 'include /zope.configuration\2'), + (re.compile(r'\\'), '/'), + ])) + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/configuration/tests/victim.py zope3-3.5~bzr18/src/zope/configuration/tests/victim.py --- zope3-3.4.0/src/zope/configuration/tests/victim.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/tests/victim.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +import bad diff -Nru zope3-3.4.0/src/zope/configuration/xmlconfig.py zope3-3.5~bzr18/src/zope/configuration/xmlconfig.py --- zope3-3.4.0/src/zope/configuration/xmlconfig.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/xmlconfig.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,727 @@ +############################################################################## +# +# 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 + +$Id: xmlconfig.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import errno +import os +import sys +import logging +import zope.configuration.config as config + +from glob import glob +from xml.sax import make_parser +from xml.sax.xmlreader import InputSource +from xml.sax.handler import ContentHandler, feature_namespaces +from xml.sax import SAXParseException +from zope import schema +from zope.configuration.exceptions import ConfigurationError +from zope.configuration.zopeconfigure import IZopeConfigure, ZopeConfigure +from zope.interface import Interface + +logger = logging.getLogger("config") + +ZCML_NAMESPACE = "http://namespaces.zope.org/zcml" +ZCML_CONDITION = (ZCML_NAMESPACE, u"condition") + + +class ZopeXMLConfigurationError(ConfigurationError): + """Zope XML Configuration error + + These errors are wrappers for other errors. The include configuration + info and the wrapped error type and value: + + >>> v = ZopeXMLConfigurationError("blah", AttributeError, "xxx") + >>> print v + 'blah' + AttributeError: xxx + + """ + + def __init__(self, info, etype, evalue): + self.info, self.etype, self.evalue = info, etype, evalue + + def __str__(self): + # Only use the repr of the info. This is because we expect to + # get a parse info and we only want the location information. + return "%s\n %s: %s" % ( + `self.info`, self.etype.__name__, self.evalue) + +class ZopeSAXParseException(ConfigurationError): + """Sax Parser errors, reformatted in an emacs friendly way + + >>> v = ZopeSAXParseException("foo.xml:12:3:Not well formed") + >>> print v + File "foo.xml", line 12.3, Not well formed + + """ + + def __init__(self, v): + self._v = v + + def __str__(self): + v = self._v + s = tuple(str(v).split(':')) + if len(s) == 4: + return 'File "%s", line %s.%s, %s' % s + else: + return str(v) + +class ParserInfo(object): + """Information about a directive based on parser data + + This includes the directive location, as well as text data + contained in the directive. + + >>> info = ParserInfo('tests//sample.zcml', 1, 0) + >>> info + File "tests//sample.zcml", line 1.0 + + >>> print info + File "tests//sample.zcml", line 1.0 + + >>> info.characters("blah\\n") + >>> info.characters("blah") + >>> info.text + u'blah\\nblah' + + >>> info.end(7, 0) + >>> info + File "tests//sample.zcml", line 1.0-7.0 + + >>> print info + File "tests//sample.zcml", line 1.0-7.0 + + + + + + + + + """ + + text = u'' + + def __init__(self, file, line, column): + self.file, self.line, self.column = file, line, column + self.eline, self.ecolumn = line, column + + def end(self, line, column): + self.eline, self.ecolumn = line, column + + def __repr__(self): + if (self.line, self.column) == (self.eline, self.ecolumn): + return 'File "%s", line %s.%s' % ( + self.file, self.line, self.column) + + return 'File "%s", line %s.%s-%s.%s' % ( + self.file, self.line, self.column, self.eline, self.ecolumn) + + def __str__(self): + if (self.line, self.column) == (self.eline, self.ecolumn): + return 'File "%s", line %s.%s' % ( + self.file, self.line, self.column) + + file = self.file + if file == 'tests//sample.zcml': + # special case for testing + file = os.path.join(os.path.dirname(__file__), + 'tests', 'sample.zcml') + + try: + f = open(file) + except IOError: + src = " Could not read source." + else: + lines = f.readlines()[self.line-1:self.eline] + ecolumn = self.ecolumn + if lines[-1][ecolumn:ecolumn+2] == '', ecolumn) + if l >= 0: + lines[-1] = lines[-1][:l+1] + else: + lines[-1] = lines[-1][:ecolumn+1] + + column = self.column + if lines[0][:column].strip(): + # Remove text before start if it's noy whitespace + lines[0] = lines[0][self.column:] + + try: + src = u''.join([u" "+l for l in lines]) + except UnicodeDecodeError: + # XXX: + # I hope so most internation zcml will use UTF-8 as encoding + # otherwise this code must be made more clever + src = u''.join([u" "+l.decode('utf-8') for l in lines]) + # unicode won't be printable, at least on my console + src = src.encode('ascii','replace') + + return "%s\n%s" % (`self`, src) + + def characters(self, characters): + self.text += characters + + +class ConfigurationHandler(ContentHandler): + """Interface to the xml parser + + Translate parser events into calls into the configuration system. + """ + + def __init__(self, context, testing=0): + self.context = context + self.testing = testing + self.ignore_depth = 0 + + def setDocumentLocator(self, locator): + self.locator = locator + + def characters(self, text): + self.context.getInfo().characters(text) + + def startElementNS(self, name, qname, attrs): + if self.ignore_depth: + self.ignore_depth += 1 + return + + data = {} + for (ns, aname), value in attrs.items(): + # NB: even though on CPython, 'ns' will be ``None`` always, + # do not change the below to "if ns is None" because Jython's + # sax parser generates attrs that have empty strings for + # the namepace instead of ``None``. + if not ns: + aname = str(aname) + data[aname] = value + if (ns, aname) == ZCML_CONDITION: + # need to process the expression to determine if we + # use this element and it's descendents + use = self.evaluateCondition(value) + if not use: + self.ignore_depth = 1 + return + + info = ParserInfo( + self.locator.getSystemId(), + self.locator.getLineNumber(), + self.locator.getColumnNumber(), + ) + + try: + self.context.begin(name, data, info) + except (KeyboardInterrupt, SystemExit): + raise + except: + if self.testing: + raise + raise ZopeXMLConfigurationError(info, sys.exc_info()[0], + sys.exc_info()[1]), None, sys.exc_info()[2] + + self.context.setInfo(info) + + def evaluateCondition(self, expression): + """Evaluate a ZCML condition. + + `expression` is a string of the form "verb arguments". + + Currently the supported verbs are 'have', 'not-have', + 'installed' and 'not-installed'. + + The 'have' verb takes one argument: the name of a feature. + + >>> from zope.configuration.config import ConfigurationContext + >>> context = ConfigurationContext() + >>> context.provideFeature('apidoc') + >>> c = ConfigurationHandler(context, testing=True) + >>> c.evaluateCondition("have apidoc") + True + >>> c.evaluateCondition("not-have apidoc") + False + >>> c.evaluateCondition("have onlinehelp") + False + >>> c.evaluateCondition("not-have onlinehelp") + True + + Ill-formed expressions raise an error + + >>> c.evaluateCondition("want apidoc") + Traceback (most recent call last): + ... + ValueError: Invalid ZCML condition: 'want apidoc' + + >>> c.evaluateCondition("have x y") + Traceback (most recent call last): + ... + ValueError: Only one feature allowed: 'have x y' + + >>> c.evaluateCondition("have") + Traceback (most recent call last): + ... + ValueError: Feature name missing: 'have' + + + The 'installed' verb takes one argument: the dotted name of a + pacakge. If the pacakge is found, in other words, can be imported, + then the condition will return true. + + >>> from zope.configuration.config import ConfigurationContext + >>> context = ConfigurationContext() + >>> c = ConfigurationHandler(context, testing=True) + >>> c.evaluateCondition('installed zope.interface') + True + >>> c.evaluateCondition('not-installed zope.interface') + False + >>> c.evaluateCondition('installed zope.foo') + False + >>> c.evaluateCondition('not-installed zope.foo') + True + + Ill-formed expressions raise an error + + >>> c.evaluateCondition("installed foo bar") + Traceback (most recent call last): + ... + ValueError: Only one package allowed: 'installed foo bar' + + >>> c.evaluateCondition("installed") + Traceback (most recent call last): + ... + ValueError: Package name missing: 'installed' + """ + arguments = expression.split(None) + verb = arguments.pop(0) + + if verb in ('have', 'not-have'): + if not arguments: + raise ValueError("Feature name missing: %r" % expression) + if len(arguments) > 1: + raise ValueError("Only one feature allowed: %r" % expression) + + if verb == 'have': + return self.context.hasFeature(arguments[0]) + elif verb == 'not-have': + return not self.context.hasFeature(arguments[0]) + + elif verb in ('installed', 'not-installed'): + if not arguments: + raise ValueError("Package name missing: %r" % expression) + if len(arguments) > 1: + raise ValueError("Only one package allowed: %r" % expression) + + try: + __import__(arguments[0]) + installed = True + except ImportError: + installed = False + + if verb == 'installed': + return installed + elif verb == 'not-installed': + return not installed + else: + raise ValueError("Invalid ZCML condition: %r" % expression) + + def endElementNS(self, name, qname): + # If ignore_depth is set, this element will be ignored, even + # if this this decrements ignore_depth to 0. + if self.ignore_depth: + self.ignore_depth -= 1 + return + + info = self.context.getInfo() + info.end( + self.locator.getLineNumber(), + self.locator.getColumnNumber(), + ) + + try: + self.context.end() + except (KeyboardInterrupt, SystemExit): + raise + except: + if self.testing: + raise + raise ZopeXMLConfigurationError(info, sys.exc_info()[0], + sys.exc_info()[1]), None, sys.exc_info()[2] + + +def processxmlfile(file, context, testing=False): + """Process a configuration file + + See examples in tests/text_xmlconfig.py + """ + src = InputSource(getattr(file, 'name', '')) + src.setByteStream(file) + parser = make_parser() + parser.setContentHandler(ConfigurationHandler(context, testing=testing)) + parser.setFeature(feature_namespaces, True) + try: + parser.parse(src) + except SAXParseException: + raise ZopeSAXParseException(sys.exc_info()[1]), None, sys.exc_info()[2] + + +def openInOrPlain(filename): + """Open a file, falling back to filename.in. + + If the requested file does not exist and filename.in does, fall + back to filename.in. If opening the original filename fails for + any other reason, allow the failure to propogate. + + For example, the tests/samplepackage dirextory has files: + + configure.zcml + configure.zcml.in + foo.zcml.in + + If we open configure.zcml, we'll get that file: + + >>> here = os.path.dirname(__file__) + >>> path = os.path.join(here, 'tests', 'samplepackage', 'configure.zcml') + >>> f = openInOrPlain(path) + >>> f.name[-14:] + 'configure.zcml' + + But if we open foo.zcml, we'll get foo.zcml.in, since there isn't a + foo.zcml: + + >>> path = os.path.join(here, 'tests', 'samplepackage', 'foo.zcml') + >>> f = openInOrPlain(path) + >>> f.name[-11:] + 'foo.zcml.in' + + Make sure other IOErrors are re-raised. We need to do this in a + try-except block because different errors are raised on Windows and + on Linux. + + >>> try: + ... f = openInOrPlain('.') + ... except IOError: + ... print "passed" + ... else: + ... print "failed" + ... + passed + + """ + try: + fp = open(filename) + except IOError, (code, msg): + if code == errno.ENOENT: + fn = filename + ".in" + if os.path.exists(fn): + fp = open(fn) + else: + raise + else: + raise + return fp + +class IInclude(Interface): + """The ``include``, ``includeOverrides`` and ``exclude`` directives + + These directives allows you to include or preserve including of another + ZCML file in the configuration. This enables you to write configuration + files in each package and then link them together. + """ + + file = schema.BytesLine( + title=u"Configuration file name", + description=u"The name of a configuration file to be included/excluded, " + u"relative to the directive containing the " + u"including configuration file.", + required=False, + ) + + files = schema.BytesLine( + title=u"Configuration file name pattern", + description=u""" + The names of multiple configuration files to be included/excluded, + expressed as a file-name pattern, relative to the directive + containing the including or excluding configuration file. The pattern + can include: + + - ``*`` matches 0 or more characters + + - ``?`` matches a single character + + - ``[]`` matches any character in seq + + - ``[!]`` matches any character not in seq + + The file names are included in sorted order, where sorting is + without regard to case. + """, + required=False, + ) + + package = config.fields.GlobalObject( + title=u"Include or exclude package", + description=u""" + Include or exclude the named file (or configure.zcml) from the directory + of this package. + """, + required=False, + ) + + +def include(_context, file=None, package=None, files=None): + """Include a zcml file + + See examples in tests/text_xmlconfig.py + """ + + if files: + if file: + raise ValueError("Must specify only one of file or files") + elif not file: + file = 'configure.zcml' + + # BBB 2006/12/19 -- to be removed after 12 months + # This is a backward-compatibility support for old site.conf + + if package and (package.__name__ == 'zope.app'): + try: + import zope.app.zcmlfiles + except ImportError: + pass # maybe this is an old zope without zope.app.zcmlfiles + else: + dirpath, filename = os.path.split(file) + # be careful, because zope.app is a namespace package + # we can't assume that zcmlfiles is a subdirectory of the + # zope.app package + dirpath = os.path.dirname(zope.app.zcmlfiles.__file__) + file = os.path.join(dirpath, filename) + import warnings + warnings.warn('In configuration file: %s ' + 'replace: ' + 'with: ' + 'This will go away in Zope 3.6.' % os.path.abspath(file), + DeprecationWarning, + 2) + + # This is a tad tricky. We want to behave as a grouping directive. + + context = config.GroupingContextDecorator(_context) + if package is not None: + context.package = package + context.basepath = None + + if files: + paths = glob(context.path(files)) + paths = zip([path.lower() for path in paths], paths) + paths.sort() + paths = [path for (l, path) in paths] + else: + paths = [context.path(file)] + + for path in paths: + if context.processFile(path): + f = openInOrPlain(path) + logger.debug("include %s" % f.name) + + context.basepath = os.path.dirname(path) + context.includepath = _context.includepath + (f.name, ) + _context.stack.append(config.GroupingStackItem(context)) + + processxmlfile(f, context) + f.close() + assert _context.stack[-1].context is context + _context.stack.pop() + +def exclude(_context, file=None, package=None, files=None): + """Exclude a zcml file + + This directive should be used before any ZML that includes + configuration you want to exclude. + """ + + if files: + if file: + raise ValueError("Must specify only one of file or files") + elif not file: + file = 'configure.zcml' + + + context = config.GroupingContextDecorator(_context) + if package is not None: + context.package = package + context.basepath = None + + if files: + paths = glob(context.path(files)) + paths = zip([path.lower() for path in paths], paths) + paths.sort() + paths = [path for (l, path) in paths] + else: + paths = [context.path(file)] + + for path in paths: + # processFile returns a boolean indicating if the file has been + # processed or not, it *also* marks the file as having been processed, + # here the side effect is used to keep the given file from being + # processed in the future + context.processFile(path) + +def includeOverrides(_context, file=None, package=None, files=None): + """Include zcml file containing overrides + + The actions in the included file are added to the context as if they + were in the including file directly. + + See the detailed example in test_includeOverrides in + tests/text_xmlconfig.py + """ + + # We need to remember how many actions we had before + nactions = len(_context.actions) + + # We'll give the new actions this include path + includepath = _context.includepath + + # Now we'll include the file. We'll munge the actions after + include(_context, file, package, files) + + # Now we'll grab the new actions, resolve conflicts, + # and munge the includepath: + newactions = [] + for action in config.resolveConflicts(_context.actions[nactions:]): + (discriminator, callable, args, kw, oldincludepath, info, order + ) = config.expand_action(*action) + newactions.append( + (discriminator, callable, args, kw, includepath, info, order) + ) + + # and replace the new actions with the munched new actions: + _context.actions[nactions:] = newactions + +def registerCommonDirectives(context): + # We have to use the direct definition functions to define + # a directive for all namespaces. + + config.defineSimpleDirective( + context, "include", IInclude, include, namespace="*") + + config.defineSimpleDirective( + context, "exclude", IInclude, exclude, namespace="*") + + config.defineSimpleDirective( + context, "includeOverrides", IInclude, includeOverrides, namespace="*") + + config.defineGroupingDirective( + context, + name="configure", + namespace="*", + schema=IZopeConfigure, + handler=ZopeConfigure, + ) + +def file(name, package=None, context=None, execute=True): + """Execute a zcml file + """ + + if context is None: + context = config.ConfigurationMachine() + registerCommonDirectives(context) + context.package = package + + include(context, name, package) + if execute: + context.execute_actions() + + return context + +def string(s, context=None, name="", execute=True): + """Execute a zcml string + """ + from StringIO import StringIO + + if context is None: + context = config.ConfigurationMachine() + registerCommonDirectives(context) + + f = StringIO(s) + f.name = name + processxmlfile(f, context) + + if execute: + context.execute_actions() + + return context + + +############################################################################## +# Backward compatability, mainly for tests + + +_context = None +def _clearContext(): + global _context + _context = config.ConfigurationMachine() + registerCommonDirectives(_context) + +def _getContext(): + global _context + if _context is None: + _clearContext() + try: + from zope.testing.cleanup import addCleanUp + except ImportError: + pass + else: + addCleanUp(_clearContext) + del addCleanUp + return _context + +class XMLConfig(object): + """Provide high-level handling of configuration files. + + See examples in tests/text_xmlconfig.py + """ + + def __init__(self, file_name, module=None): + context = _getContext() + include(context, file_name, module) + self.context = context + + def __call__(self): + self.context.execute_actions() + +def xmlconfig(file, testing=False): + context = _getContext() + processxmlfile(file, context, testing=testing) + context.execute_actions(testing=testing) + + +def testxmlconfig(file, context=None): + """xmlconfig that doesn't raise configuration errors + + This is useful for testing, as it doesn't mask exception types. + """ + context = _getContext() + processxmlfile(file, context, testing=True) + context.execute_actions(testing=True) + diff -Nru zope3-3.4.0/src/zope/configuration/zopeconfigure.py zope3-3.5~bzr18/src/zope/configuration/zopeconfigure.py --- zope3-3.4.0/src/zope/configuration/zopeconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/configuration/zopeconfigure.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,147 @@ +############################################################################## +# +# 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 paramter. The package parameter +is specified as a ``GlobalObject``. This means it must be given as a +dotted name that can be resolved through import. The i18n domain is +just a plain (not unicode) string. + +The handler class has a constructor that takes a context to be adapted +and zero or more arguments (depending on the paramter schema). The +handler class must implement +``zope.configuration.interfaces.IGroupingContext``, which defines +hooks ``before`` and ``after``, that are called with no arguments +before and after nested directives are processed. If a grouping +directive handler creates any actions, or does any computation, this +is normally done in either the ``before`` or ``after`` hooks. +Grouping handlers are normally decorators. + +The base class, +``zope.configuration.config.GroupingContextDecorator``, is normally +used to define grouping directive handlers. It provides: + +- An implementation of IConfigurationContext, which grouping directive + handlers should normally implement, + +- A default implementation of ``IGroupingContext`` that provides empty + hooks. + +- Decorator support that uses a ``__getattr__`` method to delegate + attribute accesses to adapted contexts, and + +- A constructor that sets the ``context`` attribute to the adapted + context and assigns keyword arguments to attributes. + +The ``ZopeConfigure`` provides handling for the ``configure`` +directive. It subclasses GroupingContextDecorator, and overrides the +constructor to set the ``basepath`` attribute if a ``package`` argument +is provided. Note that it delegates the job of assigning paramters to +attribute to the ``GroupingContextDecorator`` constructor. + +The last step is to register the directive using the meta +configuration directive. If we wanted to register the Zope +``configure`` directive for the ``zope`` namespace, we'd use a +meta-configuration directive like:: + + + Zope configure + + The ``configure`` node is normally used as the root node for a + configuration file. It can also be used to specify a package or + internationalization domain for a group of directives within a + file by grouping those directives. + + +We use the groupingDirective meta-directive to register a grouping +directive. The parameters are self explanatory. The textual contents +of the directive provide documentation text, excluding parameter +documentation, which is provided by the schema. + +(The Zope ``configuration`` directive is actually registered using a +lower-level Python API because it is registered for all namespaces, +which isn't supported using the meta-configuration directives.) + +$Id: zopeconfigure.py 110550 2010-04-06 06:50:36Z tseaver $ +""" +__docformat__ = 'restructuredtext' +import os +import zope.configuration.config as config +from zope import schema +from zope.interface import Interface + +class IZopeConfigure(Interface): + """The ``zope:configure`` Directive + + The zope configuration directive is a pure grouping directive. It + doesn't compute any actions on it's own. Instead, it allows a package to + be specified, affecting the interpretation of relative dotted names and + file paths. It also allows an i18n domain to be specified. The + information collected is used by subdirectives. + + It may seem that this directive can only be used once per file, but it can + be applied whereever it is convenient. + """ + + package = config.fields.GlobalObject( + title=u"Package", + description=u"The package to be used for evaluating relative imports " + u"and file names.", + required=False) + + i18n_domain = schema.BytesLine( + title=u"Internationalization domain", + description=u"This is a name for the software project. It must be a " + u"legal file-system name as it will be used to contruct " + u"names for directories containing translation data. " + u"\n" + u"The domain defines a namespace for the message ids " + u"used by a project.", + required=False) + + +class ZopeConfigure(config.GroupingContextDecorator): + __doc__ = __doc__ + + def __init__(self, context, **kw): + super(ZopeConfigure, self).__init__(context, **kw) + if 'package' in kw: + # if we have a package, we want to also define basepath + # so we don't acquire one + self.basepath = os.path.dirname(self.package.__file__) + diff -Nru zope3-3.4.0/src/zope/container/btree.py zope3-3.5~bzr18/src/zope/container/btree.py --- zope3-3.4.0/src/zope/container/btree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/btree.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,135 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 module provides a sample btree container implementation. + +$Id: btree.py 107468 2009-12-31 20:12:53Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +from persistent import Persistent +from BTrees.OOBTree import OOBTree +from BTrees.Length import Length + +from zope.container.interfaces import IBTreeContainer +from zope.container.contained import Contained, setitem, uncontained +from zope.interface import implements + + +class Lazy(object): + """Lazy Attributes. + """ + + def __init__(self, func, name=None): + if name is None: + name = func.__name__ + self.data = (func, name) + + def __get__(self, inst, class_): + if inst is None: + return self + + func, name = self.data + value = func(inst) + inst.__dict__[name] = value + + return value + + +class BTreeContainer(Contained, Persistent): + + implements(IBTreeContainer) + + def __init__(self): + # We keep the previous attribute to store the data + # for backward compatibility + self._SampleContainer__data = self._newContainerData() + self.__len = Length() + + def _newContainerData(self): + """Construct an item-data container + + Subclasses should override this if they want different data. + + The value returned is a mapping object that also has get, + has_key, keys, items, and values methods. + The default implementation uses an OOBTree. + """ + return OOBTree() + + def __contains__(self, key): + '''See interface IReadContainer + + >>> c = BTreeContainer() + >>> "a" in c + False + >>> c["a"] = 1 + >>> "a" in c + True + >>> "A" in c + False + ''' + return key in self._SampleContainer__data + + @Lazy + def _BTreeContainer__len(self): + l = Length() + ol = len(self._SampleContainer__data) + if ol > 0: + l.change(ol) + self._p_changed = True + return l + + def __len__(self): + return self.__len() + + def _setitemf(self, key, value): + # make sure our lazy property gets set + l = self.__len + self._SampleContainer__data[key] = value + l.change(1) + + def __iter__(self): + return iter(self._SampleContainer__data) + + def __getitem__(self, key): + '''See interface `IReadContainer`''' + return self._SampleContainer__data[key] + + def get(self, key, default=None): + '''See interface `IReadContainer`''' + return self._SampleContainer__data.get(key, default) + + def __setitem__(self, key, value): + setitem(self, self._setitemf, key, value) + + def __delitem__(self, key): + # make sure our lazy property gets set + l = self.__len + uncontained(self._SampleContainer__data[key], self, key) + del self._SampleContainer__data[key] + l.change(-1) + + has_key = __contains__ + + def items(self, key=None): + return self._SampleContainer__data.items(key) + + def keys(self, key=None): + return self._SampleContainer__data.keys(key) + + def values(self, key=None): + return self._SampleContainer__data.values(key) + + + diff -Nru zope3-3.4.0/src/zope/container/configure.zcml zope3-3.5~bzr18/src/zope/container/configure.zcml --- zope3-3.4.0/src/zope/container/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + Handler dispatches moved events to sublocations of the original object. + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/container/constraints.py zope3-3.5~bzr18/src/zope/container/constraints.py --- zope3-3.4.0/src/zope/container/constraints.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/constraints.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,474 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 containment constraints + + Either a container or an object can provide constraints on the + containment relationship. + + A container expresses constraints through a precondition on it's + `__setitem__` method in it's interface. + + Preconditions can be simple callable objects, like functions. They + should raise a ``zope.interface.Invalid`` exception to indicate that a + constraint isn't satisfied: + + >>> def preNoZ(container, name, ob): + ... "Silly precondition example" + ... if name.startswith("Z"): + ... raise zope.interface.Invalid("Names can not start with Z") + + >>> class I1(zope.interface.Interface): + ... def __setitem__(name, on): + ... "Add an item" + ... __setitem__.precondition = preNoZ + + >>> from zope.container.interfaces import IContainer + >>> class C1(object): + ... zope.interface.implements(I1, IContainer) + ... def __repr__(self): + ... return 'C1' + + Given such a precondition, we can then check whether an object can be + added: + + >>> c1 = C1() + >>> checkObject(c1, "bob", None) + >>> checkObject(c1, "Zbob", None) + Traceback (most recent call last): + ... + Invalid: Names can not start with Z + + We can also express constaints on the containers an object can be + added to. We do this by setting a field constraint on an object's + `__parent__` attribute: + + >>> import zope.schema + + A field constraint is a callable object that returns a boolean value: + + >>> def con1(container): + ... "silly container constraint" + ... if not hasattr(container, 'x'): + ... return False + ... return True + + >>> class I2(zope.interface.Interface): + ... __parent__ = zope.schema.Field(constraint = con1) + + >>> class O(object): + ... zope.interface.implements(I2) + + If the constraint isn't satisfied, we'll get a validation error when we + check whether the object can be added: + + >>> checkObject(c1, "bob", O()) + Traceback (most recent call last): + ... + ConstraintNotSatisfied: C1 + + Note that the validation error isn't very informative. For that + reason, it's better for constraints to raise Invalid errors when they + aren't satisfied: + + >>> def con1(container): + ... "silly container constraint" + ... if not hasattr(container, 'x'): + ... raise zope.interface.Invalid("What, no x?") + ... return True + + >>> class I2(zope.interface.Interface): + ... __parent__ = zope.schema.Field(constraint = con1) + + >>> class O(object): + ... zope.interface.implements(I2) + + >>> checkObject(c1, "bob", O()) + Traceback (most recent call last): + ... + Invalid: What, no x? + + >>> c1.x = 1 + >>> checkObject(c1, "bob", O()) + + The `checkObject` function is handy when checking whether we can add an + existing object to a container, but, sometimes, we want to check + whether an object produced by a factory can be added. To do this, we + use `checkFactory`: + + >>> class Factory(object): + ... def __call__(self): + ... return O() + ... def getInterfaces(self): + ... return zope.interface.implementedBy(O) + + >>> factory = Factory() + + >>> checkFactory(c1, "bob", factory) + True + + >>> del c1.x + >>> checkFactory(c1, "bob", factory) + False + + Unlike `checkObject`, `checkFactory`: + + - Returns a boolean value + + - Takes a factory (e.g. a class) rather than an argument. + + The container constraint we defined for C1 isn't actually used to + check the factory: + + >>> c1.x = 1 + >>> checkFactory(c1, "Zbob", factory) + True + + To work with `checkFactory`, a container precondition has to + implement a factory method. This is because a factory, rather than + an object is passed. To illustrate this, we'll make preNoZ its own + factory method: + + >>> preNoZ.factory = preNoZ + + We can do this (silly thing) because preNoZ doesn't use the object + argument. + + >>> checkFactory(c1, "Zbob", factory) + False + + $Id: constraints.py 107468 2009-12-31 20:12:53Z hannosch $ + """ +__docformat__ = 'restructuredtext' + +import sys + +from zope.dottedname.resolve import resolve +import zope.schema +from zope.interface import providedBy +from zope.container.interfaces import InvalidItemType, InvalidContainerType +from zope.container.i18n import ZopeMessageFactory as _ +from zope.container.interfaces import IContainer + +def checkObject(container, name, object): + """Check containement constraints for an object and container + """ + + # check __setitem__ precondition + containerProvided = providedBy(container) + __setitem__ = containerProvided.get('__setitem__') + if __setitem__ is not None: + precondition = __setitem__.queryTaggedValue('precondition') + if precondition is not None: + precondition(container, name, object) + + # check the constraint on __parent__ + __parent__ = providedBy(object).get('__parent__') + if __parent__ is not None: + try: + validate = __parent__.validate + except AttributeError: + pass + else: + validate(container) + + + if not containerProvided.extends(IContainer): + # If it doesn't implement IContainer, it can't contain stuff. + raise TypeError( + _('Container is not a valid Zope container.') + ) + +def checkFactory(container, name, factory): + __setitem__ = providedBy(container).get('__setitem__') + if __setitem__ is not None: + precondition = __setitem__.queryTaggedValue('precondition') + if precondition is not None: + try: + precondition = precondition.factory + except AttributeError: + pass + else: + try: + precondition(container, name, factory) + except zope.interface.Invalid: + return False + + # check the constraint on __parent__ + __parent__ = factory.getInterfaces().get('__parent__') + if __parent__ is not None: + try: + validate = __parent__.validate + except AttributeError: + pass + else: + try: + validate(container) + except zope.interface.Invalid: + return False + + return True + + +class readproperty(object): + + def __init__(self, func): + self.func = func + + def __get__(self, inst, class_): + if inst is None: + return self + + func = self.func + return func(inst) + + +class IItemTypePrecondition(zope.interface.Interface): + + def __call__(container, name, object): + """Test whether container setitem arguments are valid. + + Raise zope.interface.Invalid if the object is invalid. + """ + + def factory(container, name, factory): + """Test whether objects provided by the factory are acceptable + + Return a boolean value. + """ + + +class _TypesBased(object): + + @readproperty + def types(self): + raw_types, module = self.raw_types + types = [] + for t in raw_types: + if isinstance(t, str): + t = resolve(t, module) + types.append(t) + + self.types = types + return types + + def __init__(self, *types, **kw): + if [t for t in types if isinstance(t, str)]: + # have dotted names + module = kw.get('module', sys._getframe(1).f_globals['__name__']) + self.raw_types = types, module + else: + self.types = types + + +class ItemTypePrecondition(_TypesBased): + """Specify a `__setitem__` precondition that restricts item types + + Items must be one of the given types. + + >>> class I1(zope.interface.Interface): + ... pass + >>> class I2(zope.interface.Interface): + ... pass + + + >>> precondition = ItemTypePrecondition(I1, I2) + + >>> class Ob(object): + ... pass + >>> ob = Ob() + + >>> class Factory(object): + ... def __call__(self): + ... return Ob() + ... def getInterfaces(self): + ... return zope.interface.implementedBy(Ob) + + >>> factory = Factory() + + >>> try: + ... precondition(None, 'foo', ob) + ... except InvalidItemType, v: + ... print v[0], (v[1] is ob), (v[2] == (I1, I2)) + ... else: + ... print 'Should have failed' + None True True + + >>> try: + ... precondition.factory(None, 'foo', factory) + ... except InvalidItemType, v: + ... print v[0], (v[1] is factory), (v[2] == (I1, I2)) + ... else: + ... print 'Should have failed' + None True True + + >>> zope.interface.classImplements(Ob, I2) + >>> precondition(None, 'foo', ob) + >>> precondition.factory(None, 'foo', factory) + + """ + + zope.interface.implements(IItemTypePrecondition) + + def __call__(self, container, name, object): + for iface in self.types: + if iface.providedBy(object): + return + raise InvalidItemType(container, object, self.types) + + def factory(self, container, name, factory): + implemented = factory.getInterfaces() + + for iface in self.types: + if implemented.isOrExtends(iface): + return + raise InvalidItemType(container, factory, self.types) + + +def contains(*types): + """Declare that a container type contains only the given types + + This is used within a class suite defining an interface to create + a __setitem__ specification with a precondition allowing only the + given types: + + >>> class IFoo(zope.interface.Interface): + ... pass + >>> class IBar(zope.interface.Interface): + ... pass + >>> class IFooBarContainer(IContainer): + ... contains(IFoo, IBar) + + >>> __setitem__ = IFooBarContainer['__setitem__'] + >>> __setitem__.getTaggedValue('precondition').types == (IFoo, IBar) + True + + It is invalid to call contains outside a class suite: + + >>> contains(IFoo, IBar) + Traceback (most recent call last): + ... + TypeError: contains not called from suite + """ + + frame = sys._getframe(1) + f_locals = frame.f_locals + f_globals = frame.f_globals + + if not (f_locals is not f_globals + and f_locals.get('__module__') + and f_locals.get('__module__') == f_globals.get('__name__') + ): + raise TypeError("contains not called from suite") + + def __setitem__(key, value): + pass + __setitem__.__doc__ = IContainer['__setitem__'].__doc__ + __setitem__.precondition = ItemTypePrecondition( + *types, + **dict(module=f_globals['__name__']) + ) + f_locals['__setitem__'] = __setitem__ + + +class IContainerTypesConstraint(zope.interface.Interface): + + def __call__(object): + """Test whether object is valid. + + Return True if valid. + Raise zope.interface.Invalid if the objet is invalid. + """ + + +class ContainerTypesConstraint(_TypesBased): + """Constrain a container to be one of a number of types + + >>> class I1(zope.interface.Interface): + ... pass + >>> class I2(zope.interface.Interface): + ... pass + >>> class Ob(object): + ... pass + >>> ob = Ob() + >>> constraint = ContainerTypesConstraint(I1, I2) + >>> try: + ... constraint(ob) + ... except InvalidContainerType, v: + ... print (v[0] is ob), (v[1] == (I1, I2)) + ... else: + ... print 'Should have failed' + True True + + >>> zope.interface.classImplements(Ob, I2) + >>> constraint(Ob()) + True + + """ + + zope.interface.implements(IContainerTypesConstraint) + + def __call__(self, object): + for iface in self.types: + if iface.providedBy(object): + return True + else: + raise InvalidContainerType(object, self.types) + + +def containers(*types): + """Declare the container types a type can be contained in + + This is used within a class suite defining an interface to create + a __parent__ specification with a constraint allowing only the + given types: + + >>> class IFoo(IContainer): + ... pass + >>> class IBar(IContainer): + ... pass + + >>> from zope.location.interfaces import IContained + >>> class IFooBarContained(IContained): + ... containers(IFoo, IBar) + + >>> __parent__ = IFooBarContained['__parent__'] + >>> __parent__.constraint.types == (IFoo, IBar) + True + + It is invalid to call containers outside a class suite: + + >>> containers(IFoo, IBar) + Traceback (most recent call last): + ... + TypeError: containers not called from suite + """ + + frame = sys._getframe(1) + f_locals = frame.f_locals + f_globals = frame.f_globals + + if not (f_locals is not f_globals + and f_locals.get('__module__') + and f_locals.get('__module__') == f_globals.get('__name__') + ): + raise TypeError("containers not called from suite") + + __parent__ = zope.schema.Field( + constraint = ContainerTypesConstraint( + *types, + **dict(module=f_globals['__name__']) + ) + ) + f_locals['__parent__'] = __parent__ + diff -Nru zope3-3.4.0/src/zope/container/constraints.txt zope3-3.5~bzr18/src/zope/container/constraints.txt --- zope3-3.4.0/src/zope/container/constraints.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/constraints.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ +========================= + Containment constraints +========================= + +Containment constraints allow us to express restrictions on the types +of items that can be placed in containers or on the types of +containers an item can be placed in. We express these constraints in +interfaces. Let's define some container and item interfaces: + + >>> from zope.container.interfaces import IContainer + >>> from zope.location.interfaces import IContained + >>> from zope.container.constraints import containers, contains + + >>> class IBuddyFolder(IContainer): + ... contains('.IBuddy') + + +In this example, we used the contains function to declare that objects +that provide IBuddyFolder can only contain items that provide IBuddy. +Note that we used a string containing a dotted name for the IBuddy +interface. This is because IBuddy hasn't been defined yet. When we +define IBuddy, we can use IBuddyFolder directly: + + >>> class IBuddy(IContained): + ... containers(IBuddyFolder) + + +Now, with these interfaces in place, we can define Buddy and +BuddyFolder classes and verify that we can put buddies in buddy +folders: + + >>> from zope import interface + + >>> class Buddy: + ... interface.implements(IBuddy) + + >>> class BuddyFolder: + ... interface.implements(IBuddyFolder) + + >>> from zope.container.constraints import checkObject, checkFactory + >>> from zope.component.factory import Factory + + >>> checkObject(BuddyFolder(), 'x', Buddy()) + >>> checkFactory(BuddyFolder(), 'x', Factory(Buddy)) + True + +If we try to use other containers or folders, we'll get errors: + + >>> class Container: + ... interface.implements(IContainer) + + >>> class Contained: + ... interface.implements(IContained) + + >>> checkObject(Container(), 'x', Buddy()) + ... # doctest: +ELLIPSIS + Traceback (most recent call last): + InvalidContainerType: ... + + >>> checkFactory(Container(), 'x', Factory(Buddy)) + False + + >>> checkObject(BuddyFolder(), 'x', Contained()) + ... # doctest: +ELLIPSIS + Traceback (most recent call last): + InvalidItemType: ... + + >>> checkFactory(BuddyFolder(), 'x', Factory(Contained)) + False + +In the example, we defined the container first and then the items. We +could have defined these in the opposite order: + + >>> class IContact(IContained): + ... containers('.IContacts') + + >>> class IContacts(IContainer): + ... contains(IContact) + + >>> class Contact: + ... interface.implements(IContact) + + >>> class Contacts: + ... interface.implements(IContacts) + + >>> checkObject(Contacts(), 'x', Contact()) + + >>> checkFactory(Contacts(), 'x', Factory(Contact)) + True + + >>> checkObject(Contacts(), 'x', Buddy()) + ... # doctest: +ELLIPSIS + Traceback (most recent call last): + InvalidItemType: ... + + >>> checkFactory(Contacts(), 'x', Factory(Buddy)) + False + + diff -Nru zope3-3.4.0/src/zope/container/contained.py zope3-3.5~bzr18/src/zope/container/contained.py --- zope3-3.4.0/src/zope/container/contained.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/contained.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,924 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Classes to support implementing `IContained` + +$Id: contained.py 111341 2010-04-24 12:01:17Z ccomb $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +import zope.interface.declarations +from zope.interface import providedBy +from zope.interface.declarations import getObjectSpecification +from zope.interface.declarations import ObjectSpecification +from zope.event import notify +from zope.location.interfaces import ILocation, ISublocations +from zope.security.checker import selectChecker, CombinedChecker +from zope.lifecycleevent import ObjectModifiedEvent + +from zope.container.i18n import ZopeMessageFactory as _ +from zope.location.interfaces import IContained +from zope.container.interfaces import INameChooser +from zope.container.interfaces import IReservedNames, NameReserved +from zope.container.interfaces import IContainerModifiedEvent +from zope.container._zope_container_contained import ContainedProxyBase +from zope.container._zope_container_contained import getProxiedObject + +from zope.lifecycleevent import ObjectMovedEvent +from zope.lifecycleevent import ObjectAddedEvent +from zope.lifecycleevent import ObjectRemovedEvent + +# BBB ZODB3 < 3.10 +try: + from ZODB.interfaces import IBroken +except ImportError: + from zope.broken.interfaces import IBroken + + +class Contained(object): + """Stupid mix-in that defines `__parent__` and `__name__` attributes""" + + zope.interface.implements(IContained) + + __parent__ = __name__ = None + +class ContainerModifiedEvent(ObjectModifiedEvent): + """The container has been modified.""" + + zope.interface.implements(IContainerModifiedEvent) + + +def dispatchToSublocations(object, event): + """Dispatch an event to sublocations of a given object + + When a move event happens for an object, it's important to notify + subobjects as well. + + We do this based on locations. + + Suppose, for example, that we define some location objects. + + >>> class L(object): + ... zope.interface.implements(ILocation) + ... def __init__(self, name): + ... self.__name__ = name + ... self.__parent__ = None + ... def __repr__(self): + ... return '%s(%s)' % ( + ... self.__class__.__name__, str(self.__name__)) + + >>> class C(L): + ... zope.interface.implements(ISublocations) + ... def __init__(self, name, *subs): + ... L.__init__(self, name) + ... self.subs = subs + ... for sub in subs: + ... sub.__parent__ = self + ... def sublocations(self): + ... return self.subs + + >>> c = C(1, + ... C(11, + ... L(111), + ... L(112), + ... ), + ... C(12, + ... L(121), + ... L(122), + ... L(123), + ... L(124), + ... ), + ... L(13), + ... ) + + Now, if we call the dispatcher, it should call event handlers + for all of the objects. + + Lets create an event handler that records the objects it sees: + + >>> seen = [] + >>> def handler(ob, event): + ... seen.append((ob, event.object)) + + Note that we record the the object the handler is called on as + well as the event object: + + Now we'll register it: + + >>> from zope import component + >>> from zope.lifecycleevent.interfaces import IObjectMovedEvent + >>> component.provideHandler(handler, [None, IObjectMovedEvent]) + + We also register our dispatcher: + + >>> component.provideHandler(dispatchToSublocations, + ... [None, IObjectMovedEvent]) + + We can then call the dispatcher for the root object: + + >>> event = ObjectRemovedEvent(c) + >>> dispatchToSublocations(c, event) + + Now, we should have seen all of the subobjects: + + >>> seenreprs = map(repr, seen) + >>> seenreprs.sort() + >>> seenreprs + ['(C(11), C(1))', '(C(12), C(1))', '(L(111), C(1))',""" \ + """ '(L(112), C(1))', '(L(121), C(1))', '(L(122), C(1))',""" \ + """ '(L(123), C(1))', '(L(124), C(1))', '(L(13), C(1))'] + + We see that we get entries for each of the subobjects and + that,for each entry, the event object is top object. + + This suggests that location event handlers need to be aware that + the objects they are called on and the event objects could be + different. + + """ + subs = ISublocations(object, None) + if subs is not None: + for sub in subs.sublocations(): + for ignored in zope.component.subscribers((sub, event), None): + pass # They do work in the adapter fetch + +class ContainerSublocations(object): + """Get the sublocations for a container + + Obviously, this is the container values: + + >>> class MyContainer(object): + ... def __init__(self, **data): + ... self.data = data + ... def __iter__(self): + ... return iter(self.data) + ... def __getitem__(self, key): + ... return self.data[key] + + >>> container = MyContainer(x=1, y=2, z=42) + >>> adapter = ContainerSublocations(container) + >>> sublocations = list(adapter.sublocations()) + >>> sublocations.sort() + >>> sublocations + [1, 2, 42] + + """ + + def __init__(self, container): + self.container = container + + def sublocations(self): + container = self.container + for key in container: + yield container[key] + + +def containedEvent(object, container, name=None): + """Establish the containment of the object in the container + + The object and necessary event are returned. The object may be a + `ContainedProxy` around the original object. The event is an added + event, a moved event, or None. + + If the object implements `IContained`, simply set its `__parent__` + and `__name__` attributes: + + >>> container = {} + >>> item = Contained() + >>> x, event = containedEvent(item, container, u'foo') + >>> x is item + True + >>> item.__parent__ is container + True + >>> item.__name__ + u'foo' + + We have an added event: + + >>> event.__class__.__name__ + 'ObjectAddedEvent' + >>> event.object is item + True + >>> event.newParent is container + True + >>> event.newName + u'foo' + >>> event.oldParent + >>> event.oldName + + Now if we call contained again: + + >>> x2, event = containedEvent(item, container, u'foo') + >>> x2 is item + True + >>> item.__parent__ is container + True + >>> item.__name__ + u'foo' + + We don't get a new added event: + + >>> event + + If the object already had a parent but the parent or name was + different, we get a moved event: + + >>> x, event = containedEvent(item, container, u'foo2') + >>> event.__class__.__name__ + 'ObjectMovedEvent' + >>> event.object is item + True + >>> event.newParent is container + True + >>> event.newName + u'foo2' + >>> event.oldParent is container + True + >>> event.oldName + u'foo' + + If the `object` implements `ILocation`, but not `IContained`, set its + `__parent__` and `__name__` attributes *and* declare that it + implements `IContained`: + + >>> from zope.location import Location + >>> item = Location() + >>> IContained.providedBy(item) + False + >>> x, event = containedEvent(item, container, 'foo') + >>> x is item + True + >>> item.__parent__ is container + True + >>> item.__name__ + 'foo' + >>> IContained.providedBy(item) + True + + + If the `object` doesn't even implement `ILocation`, put a + `ContainedProxy` around it: + + >>> item = [] + >>> x, event = containedEvent(item, container, 'foo') + >>> x is item + False + >>> x.__parent__ is container + True + >>> x.__name__ + 'foo' + + Make sure we don't lose existing directly provided interfaces. + + >>> from zope.interface import Interface, directlyProvides + >>> class IOther(Interface): + ... pass + >>> from zope.location import Location + >>> item = Location() + >>> directlyProvides(item, IOther) + >>> IOther.providedBy(item) + True + >>> x, event = containedEvent(item, container, 'foo') + >>> IOther.providedBy(item) + True + """ + + if not IContained.providedBy(object): + if ILocation.providedBy(object): + zope.interface.alsoProvides(object, IContained) + else: + object = ContainedProxy(object) + + oldparent = object.__parent__ + oldname = object.__name__ + + if oldparent is container and oldname == name: + # No events + return object, None + + object.__parent__ = container + object.__name__ = name + + if oldparent is None or oldname is None: + event = ObjectAddedEvent(object, container, name) + else: + event = ObjectMovedEvent(object, oldparent, oldname, container, name) + + return object, event + +def contained(object, container, name=None): + """Establish the containment of the object in the container + + Just return the contained object without an event. This is a convenience + "macro" for: + + ``containedEvent(object, container, name)[0]`` + + This function is only used for tests. + """ + return containedEvent(object, container, name)[0] + +def notifyContainerModified(object, *descriptions): + """Notify that the container was modified.""" + notify(ContainerModifiedEvent(object, *descriptions)) + +def setitem(container, setitemf, name, object): + """Helper function to set an item and generate needed events + + This helper is needed, in part, because the events need to get + published after the `object` has been added to the `container`. + + If the item implements `IContained`, simply set its `__parent__` + and `__name__` attributes: + + >>> class IItem(zope.interface.Interface): + ... pass + >>> class Item(Contained): + ... zope.interface.implements(IItem) + ... def setAdded(self, event): + ... self.added = event + ... def setMoved(self, event): + ... self.moved = event + + >>> from zope.lifecycleevent.interfaces import IObjectAddedEvent + >>> from zope.lifecycleevent.interfaces import IObjectMovedEvent + + >>> from zope import component + >>> component.provideHandler(lambda obj, event: obj.setAdded(event), + ... [IItem, IObjectAddedEvent]) + >>> component.provideHandler(lambda obj, event: obj.setMoved(event), + ... [IItem, IObjectMovedEvent]) + + >>> item = Item() + + >>> container = {} + >>> setitem(container, container.__setitem__, u'c', item) + >>> container[u'c'] is item + 1 + >>> item.__parent__ is container + 1 + >>> item.__name__ + u'c' + + If we run this using the testing framework, we'll use `getEvents` to + track the events generated: + + >>> from zope.component.eventtesting import getEvents + >>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent + + We have an added event: + + >>> len(getEvents(IObjectAddedEvent)) + 1 + >>> event = getEvents(IObjectAddedEvent)[-1] + >>> event.object is item + 1 + >>> event.newParent is container + 1 + >>> event.newName + u'c' + >>> event.oldParent + >>> event.oldName + + As well as a modification event for the container: + + >>> len(getEvents(IObjectModifiedEvent)) + 1 + >>> getEvents(IObjectModifiedEvent)[-1].object is container + 1 + + The item's hooks have been called: + + >>> item.added is event + 1 + >>> item.moved is event + 1 + + We can suppress events and hooks by setting the `__parent__` and + `__name__` first: + + >>> item = Item() + >>> item.__parent__, item.__name__ = container, 'c2' + >>> setitem(container, container.__setitem__, u'c2', item) + >>> len(container) + 2 + >>> len(getEvents(IObjectAddedEvent)) + 1 + >>> len(getEvents(IObjectModifiedEvent)) + 1 + + >>> getattr(item, 'added', None) + >>> getattr(item, 'moved', None) + + If the item had a parent or name (as in a move or rename), + we generate a move event, rather than an add event: + + >>> setitem(container, container.__setitem__, u'c3', item) + >>> len(container) + 3 + >>> len(getEvents(IObjectAddedEvent)) + 1 + >>> len(getEvents(IObjectModifiedEvent)) + 2 + >>> len(getEvents(IObjectMovedEvent)) + 2 + + (Note that we have 2 move events because add are move events.) + + We also get the move hook called, but not the add hook: + + >>> event = getEvents(IObjectMovedEvent)[-1] + >>> getattr(item, 'added', None) + >>> item.moved is event + 1 + + If we try to replace an item without deleting it first, we'll get + an error: + + >>> setitem(container, container.__setitem__, u'c', []) + Traceback (most recent call last): + ... + KeyError: u'c' + + + >>> del container[u'c'] + >>> setitem(container, container.__setitem__, u'c', []) + >>> len(getEvents(IObjectAddedEvent)) + 2 + >>> len(getEvents(IObjectModifiedEvent)) + 3 + + + If the object implements `ILocation`, but not `IContained`, set it's + `__parent__` and `__name__` attributes *and* declare that it + implements `IContained`: + + >>> from zope.location import Location + >>> item = Location() + >>> IContained.providedBy(item) + 0 + >>> setitem(container, container.__setitem__, u'l', item) + >>> container[u'l'] is item + 1 + >>> item.__parent__ is container + 1 + >>> item.__name__ + u'l' + >>> IContained.providedBy(item) + 1 + + We get new added and modification events: + + >>> len(getEvents(IObjectAddedEvent)) + 3 + >>> len(getEvents(IObjectModifiedEvent)) + 4 + + If the object doesn't even implement `ILocation`, put a + `ContainedProxy` around it: + + >>> item = [] + >>> setitem(container, container.__setitem__, u'i', item) + >>> container[u'i'] + [] + >>> container[u'i'] is item + 0 + >>> item = container[u'i'] + >>> item.__parent__ is container + 1 + >>> item.__name__ + u'i' + >>> IContained.providedBy(item) + 1 + + >>> len(getEvents(IObjectAddedEvent)) + 4 + >>> len(getEvents(IObjectModifiedEvent)) + 5 + + We'll get type errors if we give keys that aren't unicode or ascii keys: + + >>> setitem(container, container.__setitem__, 42, item) + Traceback (most recent call last): + ... + TypeError: name not unicode or ascii string + + >>> setitem(container, container.__setitem__, None, item) + Traceback (most recent call last): + ... + TypeError: name not unicode or ascii string + + >>> setitem(container, container.__setitem__, 'hello ' + chr(200), item) + Traceback (most recent call last): + ... + TypeError: name not unicode or ascii string + + and we'll get a value error of we give an empty string or unicode: + + >>> setitem(container, container.__setitem__, '', item) + Traceback (most recent call last): + ... + ValueError: empty names are not allowed + + >>> setitem(container, container.__setitem__, u'', item) + Traceback (most recent call last): + ... + ValueError: empty names are not allowed + + """ + # Do basic name check: + if isinstance(name, str): + try: + name = unicode(name) + except UnicodeError: + raise TypeError("name not unicode or ascii string") + elif not isinstance(name, unicode): + raise TypeError("name not unicode or ascii string") + + if not name: + raise ValueError("empty names are not allowed") + + old = container.get(name) + if old is object: + return + if old is not None: + raise KeyError(name) + + object, event = containedEvent(object, container, name) + setitemf(name, object) + if event: + notify(event) + notifyContainerModified(container) + +fixing_up = False +def uncontained(object, container, name=None): + """Clear the containment relationship between the `object` and + the `container`. + + If we run this using the testing framework, we'll use `getEvents` to + track the events generated: + + >>> from zope.component.eventtesting import getEvents + >>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent + >>> from zope.lifecycleevent.interfaces import IObjectRemovedEvent + + We'll start by creating a container with an item: + + >>> class Item(Contained): + ... pass + + >>> item = Item() + >>> container = {u'foo': item} + >>> x, event = containedEvent(item, container, u'foo') + >>> item.__parent__ is container + 1 + >>> item.__name__ + u'foo' + + Now we'll remove the item. It's parent and name are cleared: + + >>> uncontained(item, container, u'foo') + >>> item.__parent__ + >>> item.__name__ + + We now have a new removed event: + + >>> len(getEvents(IObjectRemovedEvent)) + 1 + >>> event = getEvents(IObjectRemovedEvent)[-1] + >>> event.object is item + 1 + >>> event.oldParent is container + 1 + >>> event.oldName + u'foo' + >>> event.newParent + >>> event.newName + + As well as a modification event for the container: + + >>> len(getEvents(IObjectModifiedEvent)) + 1 + >>> getEvents(IObjectModifiedEvent)[-1].object is container + 1 + + Now if we call uncontained again: + + >>> uncontained(item, container, u'foo') + + We won't get any new events, because __parent__ and __name__ are None: + + >>> len(getEvents(IObjectRemovedEvent)) + 1 + >>> len(getEvents(IObjectModifiedEvent)) + 1 + + But, if either the name or parent are not ``None`` and they are not the + container and the old name, we'll get a modified event but not a removed + event. + + >>> item.__parent__, item.__name__ = container, None + >>> uncontained(item, container, u'foo') + >>> len(getEvents(IObjectRemovedEvent)) + 1 + >>> len(getEvents(IObjectModifiedEvent)) + 2 + + >>> item.__parent__, item.__name__ = None, u'bar' + >>> uncontained(item, container, u'foo') + >>> len(getEvents(IObjectRemovedEvent)) + 1 + >>> len(getEvents(IObjectModifiedEvent)) + 3 + + """ + try: + oldparent = object.__parent__ + oldname = object.__name__ + except AttributeError: + # The old object doesn't implements IContained + # Maybe we're converting old data: + if not fixing_up: + raise + oldparent = None + oldname = None + + if oldparent is not container or oldname != name: + if oldparent is not None or oldname is not None: + notifyContainerModified(container) + return + + event = ObjectRemovedEvent(object, oldparent, oldname) + notify(event) + + if not IBroken.providedBy(object): + object.__parent__ = None + object.__name__ = None + notifyContainerModified(container) + +class NameChooser(object): + + zope.interface.implements(INameChooser) + + def __init__(self, context): + self.context = context + + def checkName(self, name, object): + """See zope.container.interfaces.INameChooser + + We create and populate a dummy container + + >>> from zope.container.sample import SampleContainer + >>> container = SampleContainer() + >>> container['foo'] = 'bar' + >>> from zope.container.contained import NameChooser + + An invalid name raises a ValueError: + + >>> NameChooser(container).checkName('+foo', object()) + Traceback (most recent call last): + ... + ValueError: Names cannot begin with '+' or '@' or contain '/' + + A name that already exists raises a KeyError: + + >>> NameChooser(container).checkName('foo', object()) + Traceback (most recent call last): + ... + KeyError: u'The given name is already being used' + + A name must be a string or unicode string: + + >>> NameChooser(container).checkName(2, object()) + Traceback (most recent call last): + ... + TypeError: ('Invalid name type', ) + + A correct name returns True: + + >>> NameChooser(container).checkName('2', object()) + True + + We can reserve some names by providing a IReservedNames adapter + to a container: + + >>> from zope.container.interfaces import IContainer + >>> class ReservedNames(object): + ... zope.component.adapts(IContainer) + ... zope.interface.implements(IReservedNames) + ... + ... def __init__(self, context): + ... self.reservedNames = set(('reserved', 'other')) + + >>> zope.component.getSiteManager().registerAdapter(ReservedNames) + + >>> NameChooser(container).checkName('reserved', None) + Traceback (most recent call last): + ... + NameReserved: reserved + """ + + if isinstance(name, str): + name = unicode(name) + elif not isinstance(name, unicode): + raise TypeError("Invalid name type", type(name)) + + if not name: + raise ValueError( + _("An empty name was provided. Names cannot be empty.") + ) + + if name[:1] in '+@' or '/' in name: + raise ValueError( + _("Names cannot begin with '+' or '@' or contain '/'") + ) + + reserved = IReservedNames(self.context, None) + if reserved is not None: + if name in reserved.reservedNames: + raise NameReserved(name) + + if name in self.context: + raise KeyError( + _("The given name is already being used") + ) + + return True + + + def chooseName(self, name, object): + """See zope.container.interfaces.INameChooser + + The name chooser is expected to choose a name without error + + We create and populate a dummy container + + >>> from zope.container.sample import SampleContainer + >>> container = SampleContainer() + >>> container['foobar.old'] = 'rst doc' + + >>> from zope.container.contained import NameChooser + + the suggested name is converted to unicode: + + >>> NameChooser(container).chooseName('foobar', object()) + u'foobar' + + If it already exists, a number is appended but keeps the same extension: + + >>> NameChooser(container).chooseName('foobar.old', object()) + u'foobar-2.old' + + Bad characters are turned into dashes: + + >>> NameChooser(container).chooseName('foo/foo', object()) + u'foo-foo' + + If no name is suggested, it is based on the object type: + + >>> NameChooser(container).chooseName('', []) + u'list' + + """ + + container = self.context + + # convert to unicode and remove characters that checkName does not allow + try: + name = unicode(name) + except: + name = u'' + name = name.replace('/', '-').lstrip('+@') + + if not name: + name = unicode(object.__class__.__name__) + + # for an existing name, append a number. + # We should keep client's os.path.extsep (not ours), we assume it's '.' + dot = name.rfind('.') + if dot >= 0: + suffix = name[dot:] + name = name[:dot] + else: + suffix = '' + + n = name + suffix + i = 1 + while n in container: + i += 1 + n = name + u'-' + unicode(i) + suffix + + # Make sure the name is valid. We may have started with something bad. + self.checkName(n, object) + + return n + + +class DecoratorSpecificationDescriptor( + zope.interface.declarations.ObjectSpecificationDescriptor): + """Support for interface declarations on decorators + + >>> from zope.interface import * + >>> class I1(Interface): + ... pass + >>> class I2(Interface): + ... pass + >>> class I3(Interface): + ... pass + >>> class I4(Interface): + ... pass + + >>> class D1(ContainedProxy): + ... implements(I1) + + + >>> class D2(ContainedProxy): + ... implements(I2) + + >>> class X: + ... implements(I3) + + >>> x = X() + >>> directlyProvides(x, I4) + + Interfaces of X are ordered with the directly-provided interfaces first + + >>> [interface.getName() for interface in list(providedBy(x))] + ['I4', 'I3'] + + When we decorate objects, what order should the interfaces come in? One + could argue that decorators are less specific, so they should come last. + + >>> [interface.getName() for interface in list(providedBy(D1(x)))] + ['I4', 'I3', 'I1', 'IContained', 'IPersistent'] + + >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))] + ['I4', 'I3', 'I1', 'IContained', 'IPersistent', 'I2'] + """ + def __get__(self, inst, cls=None): + if inst is None: + return getObjectSpecification(cls) + else: + provided = providedBy(getProxiedObject(inst)) + + # Use type rather than __class__ because inst is a proxy and + # will return the proxied object's class. + cls = type(inst) + return ObjectSpecification(provided, cls) + + +class DecoratedSecurityCheckerDescriptor(object): + """Descriptor for a Decorator that provides a decorated security checker. + """ + def __get__(self, inst, cls=None): + if inst is None: + return self + else: + proxied_object = getProxiedObject(inst) + checker = getattr(proxied_object, '__Security_checker__', None) + if checker is None: + checker = selectChecker(proxied_object) + wrapper_checker = selectChecker(inst) + if wrapper_checker is None: + return checker + elif checker is None: + return wrapper_checker + else: + return CombinedChecker(wrapper_checker, checker) + +class ContainedProxyClassProvides(zope.interface.declarations.ClassProvides): + + def __set__(self, inst, value): + inst = getProxiedObject(inst) + inst.__provides__ = value + + def __delete__(self, inst): + inst = getProxiedObject(inst) + del inst.__provides__ + +class ContainedProxy(ContainedProxyBase): + + # Prevent proxies from having their own instance dictionaries: + __slots__ = () + + __safe_for_unpickling__ = True + + zope.interface.implements(IContained) + + __providedBy__ = DecoratorSpecificationDescriptor() + + __Security_checker__ = DecoratedSecurityCheckerDescriptor() + +ContainedProxy.__provides__ = ContainedProxyClassProvides(ContainedProxy, type) + diff -Nru zope3-3.4.0/src/zope/container/dependency.py zope3-3.5~bzr18/src/zope/container/dependency.py --- zope3-3.4.0/src/zope/container/dependency.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/dependency.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## + +# BBB imports +from zope.app.dependable.dependency import exception_msg +from zope.app.dependable.dependency import CheckDependency diff -Nru zope3-3.4.0/src/zope/container/directory.py zope3-3.5~bzr18/src/zope/container/directory.py --- zope3-3.4.0/src/zope/container/directory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/directory.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,118 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""File-system representation adapters for containers + +This module includes two adapters (adapter factories, really) for +providing a file-system representation for containers: + +`noop` + Factory that "adapts" `IContainer` to `IWriteDirectory`. + This is a lie, since it just returns the original object. + +`Cloner` + An `IDirectoryFactory` adapter that just clones the original object. + +$Id: directory.py 105849 2009-11-19 07:04:24Z tlotze $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import implements + +from zope.component.interfaces import ISite +from zope.security.proxy import removeSecurityProxy +import zope.filerepresentation.interfaces + +MARKER = object() + +def noop(container): + """Adapt an `IContainer` to an `IWriteDirectory` by just returning it + + This "works" because `IContainer` and `IWriteDirectory` have the same + methods, however, the output doesn't actually implement `IWriteDirectory`. + """ + return container + + +class Cloner(object): + """`IContainer` to `IDirectoryFactory` adapter that clones + + This adapter provides a factory that creates a new empty container + of the same class as it's context. + """ + + implements(zope.filerepresentation.interfaces.IDirectoryFactory) + + def __init__(self, context): + self.context = context + + def __call__(self, name): + + # We remove the security proxy so we can actually call the + # class and return an unproxied new object. (We can't use a + # trusted adapter, because the result must be unproxied.) By + # registering this adapter, one effectively gives permission + # to clone the class. Don't use this for classes that have + # exciting side effects as a result of instantiation. :) + + return removeSecurityProxy(self.context).__class__() + + +class RootDirectoryFactory(object): + + def __init__(self, context): + pass + + def __call__(self, name): + return Folder() + + +class ReadDirectory(object): + """Adapter to provide a file-system rendition of folders.""" + + def __init__(self, context): + self.context = context + + def keys(self): + keys = self.context.keys() + if ISite.providedBy(self.context): + return list(keys) + ['++etc++site'] + return keys + + def get(self, key, default=None): + if key == '++etc++site' and ISite.providedBy(self.context): + return self.context.getSiteManager() + return self.context.get(key, default) + + def __iter__(self): + return iter(self.keys()) + + def __getitem__(self, key): + v = self.get(key, MARKER) + if v is MARKER: + raise KeyError(key) + return v + + def values(self): + return map(self.get, self.keys()) + + def __len__(self): + l = len(self.context) + if ISite.providedBy(self.context): + l += 1 + return l + + def items(self): + get = self.get + return [(key, get(key)) for key in self.keys()] + + def __contains__(self, key): + return self.get(key) is not None diff -Nru zope3-3.4.0/src/zope/container/find.py zope3-3.5~bzr18/src/zope/container/find.py --- zope3-3.4.0/src/zope/container/find.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/find.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,89 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Find Support + +$Id: find.py 68442 2006-06-01 12:54:41Z mj $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import implements +from interfaces import IFind, IIdFindFilter, IObjectFindFilter +from interfaces import IReadContainer + +class FindAdapter(object): + + implements(IFind) + + __used_for__ = IReadContainer + + def __init__(self, context): + self._context = context + + def find(self, id_filters=None, object_filters=None): + 'See IFind' + id_filters = id_filters or [] + object_filters = object_filters or [] + result = [] + container = self._context + for id, object in container.items(): + _find_helper(id, object, container, + id_filters, object_filters, + result) + return result + + +def _find_helper(id, object, container, id_filters, object_filters, result): + for id_filter in id_filters: + if not id_filter.matches(id): + break + else: + # if we didn't break out of the loop, all name filters matched + # now check all object filters + for object_filter in object_filters: + if not object_filter.matches(object): + break + else: + # if we didn't break out of the loop, all filters matched + result.append(object) + + if not IReadContainer.providedBy(object): + return + + container = object + for id, object in container.items(): + _find_helper(id, object, container, id_filters, object_filters, result) + +class SimpleIdFindFilter(object): + + implements(IIdFindFilter) + + def __init__(self, ids): + self._ids = ids + + def matches(self, id): + 'See INameFindFilter' + return id in self._ids + +class SimpleInterfacesFindFilter(object): + """Filter objects on the provided interfaces""" + implements(IObjectFindFilter) + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def matches(self, object): + for iface in self.interfaces: + if iface.providedBy(object): + return True + return False diff -Nru zope3-3.4.0/src/zope/container/folder.py zope3-3.5~bzr18/src/zope/container/folder.py --- zope3-3.4.0/src/zope/container/folder.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/folder.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,104 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""The standard Zope Folder. + +$Id: folder.py 96116 2009-02-05 00:47:40Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +from BTrees.OOBTree import OOBTree +from persistent import Persistent +from zope.container.interfaces import IContainer, IContentContainer +from zope.container.contained import Contained, setitem, uncontained +from zope.interface import implements, directlyProvides + +# XXX This container implementation is really only used by +# zope.site.folder.Folder. Please do not use it. + +# XXX Check whether this IContainer implementation cannot really +# be replaced by the BTreeContainer. + +class Folder(Persistent, Contained): + """The standard Zope Folder implementation.""" + + implements(IContentContainer) + + def __init__(self): + self.data = OOBTree() + + def keys(self): + """Return a sequence-like object containing the names + associated with the objects that appear in the folder + """ + return self.data.keys() + + def __iter__(self): + return iter(self.data.keys()) + + def values(self): + """Return a sequence-like object containing the objects that + appear in the folder. + """ + return self.data.values() + + def items(self): + """Return a sequence-like object containing tuples of the form + (name, object) for the objects that appear in the folder. + """ + return self.data.items() + + def __getitem__(self, name): + """Return the named object, or raise ``KeyError`` if the object + is not found. + """ + return self.data[name] + + def get(self, name, default=None): + """Return the named object, or the value of the `default` + argument if the object is not found. + """ + return self.data.get(name, default) + + def __contains__(self, name): + """Return true if the named object appears in the folder.""" + return self.data.has_key(name) + + def __len__(self): + """Return the number of objects in the folder.""" + return len(self.data) + + def __setitem__(self, name, object): + """Add the given object to the folder under the given name.""" + + if not (isinstance(name, str) or isinstance(name, unicode)): + raise TypeError("Name must be a string rather than a %s" % + name.__class__.__name__) + try: + unicode(name) + except UnicodeError: + raise TypeError("Non-unicode names must be 7-bit-ascii only") + if not name: + raise TypeError("Name must not be empty") + + if name in self.data: + raise KeyError("name, %s, is already in use" % name) + + setitem(self, self.data.__setitem__, name, object) + + def __delitem__(self, name): + """Delete the named object from the folder. Raises a KeyError + if the object is not found.""" + uncontained(self.data[name], self, name) + del self.data[name] + diff -Nru zope3-3.4.0/src/zope/container/i18n.py zope3-3.5~bzr18/src/zope/container/i18n.py --- zope3-3.4.0/src/zope/container/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/i18n.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Customization of zope.i18n for the Zope application server + +$Id: i18n.py 73547 2007-03-25 09:03:04Z dobe $ +""" +__docformat__ = 'restructuredtext' + +# import this as _ to create i18n messages in the zope domain +from zope.i18nmessageid import MessageFactory +ZopeMessageFactory = MessageFactory('zope') diff -Nru zope3-3.4.0/src/zope/container/__init__.py zope3-3.5~bzr18/src/zope/container/__init__.py --- zope3-3.4.0/src/zope/container/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/container/interfaces.py zope3-3.5~bzr18/src/zope/container/interfaces.py --- zope3-3.4.0/src/zope/container/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,293 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Container-related interfaces + +$Id: interfaces.py 100045 2009-05-17 17:41:52Z chrism $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface, Invalid +from zope.interface.common.mapping import IItemMapping +from zope.interface.common.mapping import IReadMapping, IEnumerableMapping +from zope.location.interfaces import ILocation +from zope.schema import Set + +from zope.lifecycleevent.interfaces import IObjectModifiedEvent + +# the following imports provide backwards compatibility for consumers; +# do not remove them +from zope.lifecycleevent.interfaces import IObjectMovedEvent +from zope.lifecycleevent.interfaces import IObjectAddedEvent +from zope.lifecycleevent.interfaces import IObjectRemovedEvent +from zope.location.interfaces import IContained +# /end backwards compatibility imports + +from zope.container.i18n import ZopeMessageFactory as _ + +class DuplicateIDError(KeyError): + pass + +class ContainerError(Exception): + """An error of a container with one of its components.""" + +class InvalidContainerType(Invalid, TypeError): + """The type of a container is not valid.""" + +class InvalidItemType(Invalid, TypeError): + """The type of an item is not valid.""" + +class InvalidType(Invalid, TypeError): + """The type of an object is not valid.""" + + + +class IItemContainer(IItemMapping): + """Minimal readable container.""" + + +class ISimpleReadContainer(IItemContainer, IReadMapping): + """Readable content containers.""" + + +class IReadContainer(ISimpleReadContainer, IEnumerableMapping): + """Readable containers that can be enumerated.""" + + +class IWriteContainer(Interface): + """An interface for the write aspects of a container.""" + + def __setitem__(name, object): + """Add the given `object` to the container under the given name. + + Raises a ``TypeError`` if the key is not a unicode or ascii string. + + Raises a ``ValueError`` if the key is empty, or if the key contains + a character which is not allowed in an object name. + + Raises a ``KeyError`` if the key violates a uniqueness constraint. + + The container might choose to add a different object than the + one passed to this method. + + If the object doesn't implement `IContained`, then one of two + things must be done: + + 1. If the object implements `ILocation`, then the `IContained` + interface must be declared for the object. + + 2. Otherwise, a `ContainedProxy` is created for the object and + stored. + + The object's `__parent__` and `__name__` attributes are set to the + container and the given name. + + If the old parent was ``None``, then an `IObjectAddedEvent` is + generated, otherwise, an `IObjectMovedEvent` is generated. An + `IContainerModifiedEvent` is generated for the container. + + If the object replaces another object, then the old object is + deleted before the new object is added, unless the container + vetos the replacement by raising an exception. + + If the object's `__parent__` and `__name__` were already set to + the container and the name, then no events are generated and + no hooks. This allows advanced clients to take over event + generation. + + """ + + def __delitem__(name): + """Delete the named object from the container. + + Raises a ``KeyError`` if the object is not found. + + If the deleted object's `__parent__` and `__name__` match the + container and given name, then an `IObjectRemovedEvent` is + generated and the attributes are set to ``None``. If the object + can be adapted to `IObjectMovedEvent`, then the adapter's + `moveNotify` method is called with the event. + + Unless the object's `__parent__` and `__name__` attributes were + initially ``None``, generate an `IContainerModifiedEvent` for the + container. + + If the object's `__parent__` and `__name__` were already set to + ``None``, then no events are generated. This allows advanced + clients to take over event generation. + + """ + + +class IItemWriteContainer(IWriteContainer, IItemContainer): + """A write container that also supports minimal reads.""" + + +class IContainer(IReadContainer, IWriteContainer): + """Readable and writable content container.""" + + +class IContentContainer(IContainer): + """A container that is to be used as a content type.""" + + +class IBTreeContainer(IContainer): + """Container that supports BTree semantics for some methods.""" + + def items(key=None): + """Return an iterator over the key-value pairs in the container. + + If ``None`` is passed as `key`, this method behaves as if no argument + were passed; exactly as required for ``IContainer.items()``. + + If `key` is in the container, the first item provided by the iterator + will correspond to that key. Otherwise, the first item will be for + the key that would come next if `key` were in the container. + + """ + + def keys(key=None): + """Return an iterator over the keys in the container. + + If ``None`` is passed as `key`, this method behaves as if no argument + were passed; exactly as required for ``IContainer.keys()``. + + If `key` is in the container, the first key provided by the iterator + will be that key. Otherwise, the first key will be the one that would + come next if `key` were in the container. + + """ + + def values(key=None): + """Return an iterator over the values in the container. + + If ``None`` is passed as `key`, this method behaves as if no argument + were passed; exactly as required for ``IContainer.values()``. + + If `key` is in the container, the first value provided by the iterator + will correspond to that key. Otherwise, the first value will be for + the key that would come next if `key` were in the container. + + """ + + +class IOrdered(Interface): + """Objects whose contents are maintained in order.""" + + + def updateOrder(order): + """Revise the order of keys, replacing the current ordering. + + order is a list or a tuple containing the set of existing keys in + the new order. `order` must contain ``len(keys())`` items and cannot + contain duplicate keys. + + Raises ``TypeError`` if order is not a tuple or a list. + + Raises ``ValueError`` if order contains an invalid set of keys. + """ + + +class IOrderedContainer(IOrdered, IContainer): + """Containers whose contents are maintained in order.""" + + + +class IContainerNamesContainer(IContainer): + """Containers that always choose names for their items.""" + +class IReservedNames(Interface): + """A sequence of names that are reserved for that container""" + + reservedNames = Set( + title=_(u'Reserved Names'), + description=_(u'Names that are not allowed for addable content'), + required=True, + ) + +class NameReserved(ValueError): + __doc__ = _("""The name is reserved for this container""") + +############################################################################## +# Adding objects + +class UnaddableError(ContainerError): + """An object cannot be added to a container.""" + + def __init__(self, container, obj, message=""): + self.container = container + self.obj = obj + self.message = message and ": %s" % message + + def __str__(self): + return ("%(obj)s cannot be added " + "to %(container)s%(message)s" % self.__dict__) + + +class INameChooser(Interface): + + def checkName(name, object): + """Check whether an object name is valid. + + Raises a user error if the name is not valid. + """ + + def chooseName(name, object): + """Choose a unique valid name for the object. + + The given name and object may be taken into account when + choosing the name. + + chooseName is expected to always choose a valid name (that would pass + the checkName test) and never raise an error. + + """ + + +############################################################################## +# Modifying containers + + +class IContainerModifiedEvent(IObjectModifiedEvent): + """The container has been modified. + + This event is specific to "containerness" modifications, which means + addition, removal or reordering of sub-objects. + """ + + +############################################################################## +# Finding objects + +class IFind(Interface): + """ + Find support for containers. + """ + + def find(id_filters=None, object_filters=None): + """Find object that matches all filters in all sub-objects. + + This container itself is not included. + """ + + +class IObjectFindFilter(Interface): + + def matches(object): + """Return True if the object matches the filter criteria.""" + + +class IIdFindFilter(Interface): + + def matches(id): + """Return True if the id matches the filter criteria.""" diff -Nru zope3-3.4.0/src/zope/container/ordered.py zope3-3.5~bzr18/src/zope/container/ordered.py --- zope3-3.4.0/src/zope/container/ordered.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/ordered.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,315 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Ordered container implementation. + +$Id: ordered.py 99988 2009-05-15 22:10:08Z pcardune $ +""" +__docformat__ = 'restructuredtext' + +from zope.container.interfaces import IOrderedContainer +from zope.interface import implements +from persistent import Persistent +from persistent.dict import PersistentDict +from persistent.list import PersistentList +from types import StringTypes, TupleType, ListType +from zope.container.contained import Contained, setitem, uncontained +from zope.container.contained import notifyContainerModified + +class OrderedContainer(Persistent, Contained): + """ `OrderedContainer` maintains entries' order as added and moved. + + >>> oc = OrderedContainer() + >>> int(IOrderedContainer.providedBy(oc)) + 1 + >>> len(oc) + 0 + """ + + implements(IOrderedContainer) + + def __init__(self): + + self._data = PersistentDict() + self._order = PersistentList() + + def keys(self): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc.keys() + [] + >>> oc['foo'] = 'bar' + >>> oc.keys() + ['foo'] + >>> oc['baz'] = 'quux' + >>> oc.keys() + ['foo', 'baz'] + >>> int(len(oc._order) == len(oc._data)) + 1 + """ + + return self._order[:] + + def __iter__(self): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc.keys() + [] + >>> oc['foo'] = 'bar' + >>> oc['baz'] = 'quux' + >>> [i for i in oc] + ['foo', 'baz'] + >>> int(len(oc._order) == len(oc._data)) + 1 + """ + + return iter(self.keys()) + + def __getitem__(self, key): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc['foo'] = 'bar' + >>> oc['foo'] + 'bar' + """ + + return self._data[key] + + def get(self, key, default=None): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc['foo'] = 'bar' + >>> oc.get('foo') + 'bar' + >>> oc.get('funky', 'No chance, dude.') + 'No chance, dude.' + """ + + return self._data.get(key, default) + + def values(self): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc.keys() + [] + >>> oc['foo'] = 'bar' + >>> oc.values() + ['bar'] + >>> oc['baz'] = 'quux' + >>> oc.values() + ['bar', 'quux'] + >>> int(len(oc._order) == len(oc._data)) + 1 + """ + + return [self._data[i] for i in self._order] + + def __len__(self): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> int(len(oc) == 0) + 1 + >>> oc['foo'] = 'bar' + >>> int(len(oc) == 1) + 1 + """ + + return len(self._data) + + def items(self): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc.keys() + [] + >>> oc['foo'] = 'bar' + >>> oc.items() + [('foo', 'bar')] + >>> oc['baz'] = 'quux' + >>> oc.items() + [('foo', 'bar'), ('baz', 'quux')] + >>> int(len(oc._order) == len(oc._data)) + 1 + """ + + return [(i, self._data[i]) for i in self._order] + + def __contains__(self, key): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc['foo'] = 'bar' + >>> int('foo' in oc) + 1 + >>> int('quux' in oc) + 0 + """ + + return self._data.has_key(key) + + has_key = __contains__ + + def __setitem__(self, key, object): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc.keys() + [] + >>> oc['foo'] = 'bar' + >>> oc._order + ['foo'] + >>> oc['baz'] = 'quux' + >>> oc._order + ['foo', 'baz'] + >>> int(len(oc._order) == len(oc._data)) + 1 + + >>> oc['foo'] = 'baz' + Traceback (most recent call last): + ... + KeyError: u'foo' + >>> oc._order + ['foo', 'baz'] + """ + + existed = self._data.has_key(key) + + bad = False + if isinstance(key, StringTypes): + try: + unicode(key) + except UnicodeError: + bad = True + else: + bad = True + if bad: + raise TypeError("'%s' is invalid, the key must be an " + "ascii or unicode string" % key) + if len(key) == 0: + raise ValueError("The key cannot be an empty string") + + # We have to first update the order, so that the item is available, + # otherwise most API functions will lie about their available values + # when an event subscriber tries to do something with the container. + if not existed: + self._order.append(key) + + # This function creates a lot of events that other code listens to. + try: + setitem(self, self._data.__setitem__, key, object) + except Exception, e: + if not existed: + self._order.remove(key) + raise e + + return key + + def __delitem__(self, key): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc.keys() + [] + >>> oc['foo'] = 'bar' + >>> oc['baz'] = 'quux' + >>> oc['zork'] = 'grue' + >>> oc.items() + [('foo', 'bar'), ('baz', 'quux'), ('zork', 'grue')] + >>> int(len(oc._order) == len(oc._data)) + 1 + >>> del oc['baz'] + >>> oc.items() + [('foo', 'bar'), ('zork', 'grue')] + >>> int(len(oc._order) == len(oc._data)) + 1 + """ + + uncontained(self._data[key], self, key) + del self._data[key] + self._order.remove(key) + + def updateOrder(self, order): + """ See `IOrderedContainer`. + + >>> oc = OrderedContainer() + >>> oc['foo'] = 'bar' + >>> oc['baz'] = 'quux' + >>> oc['zork'] = 'grue' + >>> oc.keys() + ['foo', 'baz', 'zork'] + >>> oc.updateOrder(['baz', 'foo', 'zork']) + >>> oc.keys() + ['baz', 'foo', 'zork'] + >>> oc.updateOrder(['baz', 'zork', 'foo']) + >>> oc.keys() + ['baz', 'zork', 'foo'] + >>> oc.updateOrder(['baz', 'zork', 'foo']) + >>> oc.keys() + ['baz', 'zork', 'foo'] + >>> oc.updateOrder(('zork', 'foo', 'baz')) + >>> oc.keys() + ['zork', 'foo', 'baz'] + >>> oc.updateOrder(['baz', 'zork']) + Traceback (most recent call last): + ... + ValueError: Incompatible key set. + >>> oc.updateOrder(['foo', 'bar', 'baz', 'quux']) + Traceback (most recent call last): + ... + ValueError: Incompatible key set. + >>> oc.updateOrder(1) + Traceback (most recent call last): + ... + TypeError: order must be a tuple or a list. + >>> oc.updateOrder('bar') + Traceback (most recent call last): + ... + TypeError: order must be a tuple or a list. + >>> oc.updateOrder(['baz', 'zork', 'quux']) + Traceback (most recent call last): + ... + ValueError: Incompatible key set. + >>> del oc['baz'] + >>> del oc['zork'] + >>> del oc['foo'] + >>> len(oc) + 0 + """ + + if not isinstance(order, ListType) and \ + not isinstance(order, TupleType): + raise TypeError('order must be a tuple or a list.') + + if len(order) != len(self._order): + raise ValueError("Incompatible key set.") + + was_dict = {} + will_be_dict = {} + new_order = PersistentList() + + for i in range(len(order)): + was_dict[self._order[i]] = 1 + will_be_dict[order[i]] = 1 + new_order.append(order[i]) + + if will_be_dict != was_dict: + raise ValueError("Incompatible key set.") + + self._order = new_order + notifyContainerModified(self) diff -Nru zope3-3.4.0/src/zope/container/sample.py zope3-3.5~bzr18/src/zope/container/sample.py --- zope3-3.4.0/src/zope/container/sample.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/sample.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,91 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 container implementation. + +This is primarily for testing purposes. + +It might be useful as a mix-in for some classes, but many classes will +need a very different implementation. + +$Id: sample.py 95341 2009-01-28 15:59:18Z wosc $ +""" +__docformat__ = 'restructuredtext' + +from zope.container.interfaces import IContainer +from zope.interface import implements +from zope.container.contained import Contained, setitem, uncontained + + +class SampleContainer(Contained): + """Sample container implementation suitable for testing. + + It is not suitable, directly as a base class unless the subclass + overrides `_newContainerData` to return a persistent mapping object. + """ + implements(IContainer) + + def __init__(self): + self.__data = self._newContainerData() + + def _newContainerData(self): + """Construct an item-data container + + Subclasses should override this if they want different data. + + The value returned is a mapping object that also has `get`, + `has_key`, `keys`, `items`, and `values` methods. + """ + return {} + + def keys(self): + '''See interface `IReadContainer`''' + return self.__data.keys() + + def __iter__(self): + return iter(self.__data) + + def __getitem__(self, key): + '''See interface `IReadContainer`''' + return self.__data[key] + + def get(self, key, default=None): + '''See interface `IReadContainer`''' + return self.__data.get(key, default) + + def values(self): + '''See interface `IReadContainer`''' + return self.__data.values() + + def __len__(self): + '''See interface `IReadContainer`''' + return len(self.__data) + + def items(self): + '''See interface `IReadContainer`''' + return self.__data.items() + + def __contains__(self, key): + '''See interface `IReadContainer`''' + return self.__data.has_key(key) + + has_key = __contains__ + + def __setitem__(self, key, object): + '''See interface `IWriteContainer`''' + setitem(self, self.__data.__setitem__, key, object) + + def __delitem__(self, key): + '''See interface `IWriteContainer`''' + uncontained(self.__data[key], self, key) + del self.__data[key] diff -Nru zope3-3.4.0/src/zope/container/size.py zope3-3.5~bzr18/src/zope/container/size.py --- zope3-3.4.0/src/zope/container/size.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/size.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,41 @@ + +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Adapters that give the size of an object. + +$Id: size.py 95341 2009-01-28 15:59:18Z wosc $ +""" +__docformat__ = 'restructuredtext' + +from zope.container.i18n import ZopeMessageFactory as _ +from zope.size.interfaces import ISized +from zope.interface import implements + +class ContainerSized(object): + + implements(ISized) + + def __init__(self, container): + self._container = container + + def sizeForSorting(self): + """See `ISized`""" + return ('item', len(self._container)) + + def sizeForDisplay(self): + """See `ISized`""" + num_items = len(self._container) + if num_items == 1: + return _('1 item') + return _('${items} items', mapping={'items': str(num_items)}) diff -Nru zope3-3.4.0/src/zope/container/testing.py zope3-3.5~bzr18/src/zope/container/testing.py --- zope3-3.4.0/src/zope/container/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,83 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Unit test logic for setting up and tearing down basic infrastructure + +$Id: placelesssetup.py 95341 2009-01-28 15:59:18Z wosc $ +""" +from zope import component +from zope.component.testing import PlacelessSetup as CAPlacelessSetup +from zope.component.eventtesting import PlacelessSetup as EventPlacelessSetup + +from zope.traversing.interfaces import ITraversable, IContainmentRoot +import zope.traversing.testing +import zope.interface + +from zope.container.interfaces import IWriteContainer, INameChooser +from zope.container.contained import NameChooser +from zope.container.interfaces import ISimpleReadContainer +from zope.container.traversal import ContainerTraversable +from zope.container.sample import SampleContainer + +# XXX we would like to swap the names of the *PlacelessSetup classes +# in here as that would seem to follow the convention better, but +# unfortunately that would break compatibility with zope.app.testing +# (which expects this PlacelessSetup) so it will have to wait. + +class PlacelessSetup(object): + + def setUp(self): + component.provideAdapter(NameChooser, (IWriteContainer,), INameChooser) + +class ContainerPlacelessSetup(CAPlacelessSetup, + EventPlacelessSetup, + PlacelessSetup): + + def setUp(self, doctesttest=None): + CAPlacelessSetup.setUp(self) + EventPlacelessSetup.setUp(self) + PlacelessSetup.setUp(self) + +ps = ContainerPlacelessSetup() +setUp = ps.setUp + +def tearDown(): + tearDown_ = ps.tearDown + def tearDown(doctesttest=None): + tearDown_() + return tearDown + +tearDown = tearDown() + +del ps + +class ContainerPlacefulSetup(ContainerPlacelessSetup): + def setUp(self, doctesttest=None): + ContainerPlacelessSetup.setUp(self, doctesttest) + zope.traversing.testing.setUp() + component.provideAdapter(ContainerTraversable, + (ISimpleReadContainer,), ITraversable) + + def tearDown(self, docttesttest=None): + ContainerPlacelessSetup.tearDown(self) + + + def buildFolders(self): + root = self.rootFolder = SampleContainer() + zope.interface.directlyProvides(root, IContainmentRoot) + root[u'folder1'] = SampleContainer() + root[u'folder1'][u'folder1_1'] = SampleContainer() + root[u'folder1'][u'folder1_1'][u'folder1_1_1'] = SampleContainer() + root[u'folder2'] = SampleContainer() + root[u'folder2'][u'folder2_1'] = SampleContainer() + root[u'folder2'][u'folder2_1'][u'folder2_1_1'] = SampleContainer() diff -Nru zope3-3.4.0/src/zope/container/tests/directory.txt zope3-3.5~bzr18/src/zope/container/tests/directory.txt --- zope3-3.4.0/src/zope/container/tests/directory.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/directory.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,43 @@ +=============================== +File representation for folders +=============================== + +Folders can be represented in file-system-like protocols (e.g. FTP). An +adapter abstracts some internals away and adds support for accessing the +'++etc++site' folder from those protocols. + + >>> from zope.container.folder import Folder + >>> from zope.container.directory import ReadDirectory + >>> folder = Folder() + +The new folder isn't a site manager and doesn't have any entries: + + >>> fs_folder = ReadDirectory(folder) + >>> list(fs_folder.keys()) + [] + >>> fs_folder.get('test', ) + >>> fs_folder['test'] + Traceback (most recent call last): + KeyError: 'test' + >>> list(fs_folder.__iter__()) + [] + >>> fs_folder.values() + [] + >>> len(fs_folder) + 0 + >>> fs_folder.items() + [] + >>> 'test' in fs_folder + False + +This is a short regression test for #728: we get a KeyError when trying to +access non-existing entries: + + >>> from zope.security.proxy import ProxyFactory + >>> from zope.security.checker import NamesChecker + >>> proxied_folder = ProxyFactory(fs_folder, NamesChecker(('get',))) + >>> proxied_fs_folder = ReadDirectory(proxied_folder) + >>> print proxied_fs_folder['i dont exist'] + Traceback (most recent call last): + KeyError: 'i dont exist' + diff -Nru zope3-3.4.0/src/zope/container/tests/ftest_zcml_dependencies.zcml zope3-3.5~bzr18/src/zope/container/tests/ftest_zcml_dependencies.zcml --- zope3-3.4.0/src/zope/container/tests/ftest_zcml_dependencies.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/ftest_zcml_dependencies.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/container/tests/__init__.py zope3-3.5~bzr18/src/zope/container/tests/__init__.py --- zope3-3.4.0/src/zope/container/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/container/tests/test_btree.py zope3-3.5~bzr18/src/zope/container/tests/test_btree.py --- zope3-3.4.0/src/zope/container/tests/test_btree.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_btree.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,177 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""BTree Container Tests + +$Id: test_btree.py 111713 2010-04-30 20:43:09Z hannosch $ +""" +from doctest import DocTestSuite +from unittest import TestCase, main, makeSuite, TestSuite +from zope.interface.verify import verifyObject +from zope.component.testing import setUp, tearDown +from zope.container.tests.test_icontainer import TestSampleContainer +from zope.container.btree import BTreeContainer +from zope.container.interfaces import IBTreeContainer + + +class TestBTreeContainer(TestSampleContainer, TestCase): + + def makeTestObject(self): + return BTreeContainer() + + +class TestBTreeSpecials(TestCase): + + def testStoredLength(self): + # This is lazy for backward compatibility. If the len is not + # stored already we set it to the length of the underlying + # btree. + bc = BTreeContainer() + self.assertEqual(bc.__dict__['_BTreeContainer__len'](), 0) + del bc.__dict__['_BTreeContainer__len'] + self.failIf(bc.__dict__.has_key('_BTreeContainer__len')) + bc['1'] = 1 + self.assertEqual(len(bc), 1) + self.assertEqual(bc.__dict__['_BTreeContainer__len'](), 1) + + # The tests which follow test the additional signatures and declarations + # for the BTreeContainer that allow it to provide the IBTreeContainer + # interface. + + def testBTreeContainerInterface(self): + bc = BTreeContainer() + self.assert_(verifyObject(IBTreeContainer, bc)) + self.checkIterable(bc.items()) + self.checkIterable(bc.keys()) + self.checkIterable(bc.values()) + + def testEmptyItemsWithArg(self): + bc = BTreeContainer() + self.assertEqual(list(bc.items(None)), list(bc.items())) + self.assertEqual(list(bc.items("")), []) + self.assertEqual(list(bc.items("not-there")), []) + self.checkIterable(bc.items(None)) + self.checkIterable(bc.items("")) + self.checkIterable(bc.items("not-there")) + + def testEmptyKeysWithArg(self): + bc = BTreeContainer() + self.assertEqual(list(bc.keys(None)), list(bc.keys())) + self.assertEqual(list(bc.keys("")), []) + self.assertEqual(list(bc.keys("not-there")), []) + self.checkIterable(bc.keys(None)) + self.checkIterable(bc.keys("")) + self.checkIterable(bc.keys("not-there")) + + def testEmptyValuesWithArg(self): + bc = BTreeContainer() + self.assertEqual(list(bc.values(None)), list(bc.values())) + self.assertEqual(list(bc.values("")), []) + self.assertEqual(list(bc.values("not-there")), []) + self.checkIterable(bc.values(None)) + self.checkIterable(bc.values("")) + self.checkIterable(bc.values("not-there")) + + def testNonemptyItemsWithArg(self): + bc = BTreeContainer() + bc["0"] = 1 + bc["1"] = 2 + bc["2"] = 3 + self.assertEqual(list(bc.items(None)), list(bc.items())) + self.assertEqual(list(bc.items("")), [("0", 1), ("1", 2), ("2", 3)]) + self.assertEqual(list(bc.items("3")), []) + self.assertEqual(list(bc.items("2.")), []) + self.assertEqual(list(bc.items("2")), [("2", 3)]) + self.assertEqual(list(bc.items("1.")), [("2", 3)]) + self.assertEqual(list(bc.items("1")), [("1", 2), ("2", 3)]) + self.assertEqual(list(bc.items("0.")), [("1", 2), ("2", 3)]) + self.assertEqual(list(bc.items("0")), [("0", 1), ("1", 2), ("2", 3)]) + self.checkIterable(bc.items(None)) + self.checkIterable(bc.items("")) + self.checkIterable(bc.items("0.")) + self.checkIterable(bc.items("3")) + + def testNonemptyKeysWithArg(self): + bc = BTreeContainer() + bc["0"] = 1 + bc["1"] = 2 + bc["2"] = 3 + self.assertEqual(list(bc.keys(None)), list(bc.keys())) + self.assertEqual(list(bc.keys("")), ["0", "1", "2"]) + self.assertEqual(list(bc.keys("3")), []) + self.assertEqual(list(bc.keys("2.")), []) + self.assertEqual(list(bc.keys("2")), ["2"]) + self.assertEqual(list(bc.keys("1.")), ["2"]) + self.assertEqual(list(bc.keys("1")), ["1", "2"]) + self.assertEqual(list(bc.keys("0.")), ["1", "2"]) + self.assertEqual(list(bc.keys("0")), ["0", "1", "2"]) + self.checkIterable(bc.keys(None)) + self.checkIterable(bc.keys("")) + self.checkIterable(bc.keys("0.")) + self.checkIterable(bc.keys("3")) + + def testNonemptyValueWithArg(self): + bc = BTreeContainer() + bc["0"] = 1 + bc["1"] = 2 + bc["2"] = 3 + self.assertEqual(list(bc.values(None)), list(bc.values())) + self.assertEqual(list(bc.values("")), [1, 2, 3]) + self.assertEqual(list(bc.values("3")), []) + self.assertEqual(list(bc.values("2.")), []) + self.assertEqual(list(bc.values("2")), [3]) + self.assertEqual(list(bc.values("1.")), [3]) + self.assertEqual(list(bc.values("1")), [2, 3]) + self.assertEqual(list(bc.values("0.")), [2, 3]) + self.assertEqual(list(bc.values("0")), [1, 2, 3]) + self.checkIterable(bc.values(None)) + self.checkIterable(bc.values("")) + self.checkIterable(bc.values("0.")) + self.checkIterable(bc.values("3")) + + def testCorrectLengthWhenAddingExistingItem(self): + """ + for bug #175388 + """ + bc = BTreeContainer() + bc[u'x'] = object() + self.assertEqual(len(bc), 1) + bc[u'x'] = bc[u'x'] + self.assertEqual(len(bc), 1) + self.assertEqual(list(bc), [u'x']) + + + def checkIterable(self, iterable): + it = iter(iterable) + self.assert_(callable(it.next)) + self.assert_(callable(it.__iter__)) + self.assert_(iter(it) is it) + # Exhaust the iterator: + first_time = list(it) + self.assertRaises(StopIteration, it.next) + # Subsequent iterations will return the same values: + self.assertEqual(list(iterable), first_time) + self.assertEqual(list(iterable), first_time) + + +def test_suite(): + return TestSuite(( + makeSuite(TestBTreeContainer), + makeSuite(TestBTreeSpecials), + DocTestSuite('zope.container.btree', + setUp=setUp, + tearDown=tearDown), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/container/tests/test_constraints.py zope3-3.5~bzr18/src/zope/container/tests/test_constraints.py --- zope3-3.4.0/src/zope/container/tests/test_constraints.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_constraints.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,36 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Container constraint tests + +$Id: test_constraints.py 111713 2010-04-30 20:43:09Z hannosch $ +""" + +import doctest +import unittest +from zope.testing import module + +def setUp(test): + module.setUp(test, 'zope.container.constraints_txt') + +def tearDown(test): + module.tearDown(test, 'zope.container.constraints_txt') + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite('zope.container.constraints'), + doctest.DocFileSuite('../constraints.txt', + setUp=setUp, tearDown=tearDown), + )) + +if __name__ == '__main__': unittest.main() diff -Nru zope3-3.4.0/src/zope/container/tests/test_contained.py zope3-3.5~bzr18/src/zope/container/tests/test_contained.py --- zope3-3.4.0/src/zope/container/tests/test_contained.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_contained.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,416 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Contained Tests + +$Id: test_contained.py 111713 2010-04-30 20:43:09Z hannosch $ +""" + +import doctest +import gc +import unittest + +from ZODB.DemoStorage import DemoStorage +from ZODB.DB import DB +import transaction +from persistent import Persistent + +import zope.interface +import zope.component + +from zope.container.contained import ContainedProxy, NameChooser +from zope.container.sample import SampleContainer +from zope.container import testing +from zope.container.interfaces import NameReserved, IContainer, IReservedNames + +class MyOb(Persistent): + pass + +def test_basic_proxy_attribute_management_and_picklability(): + """Contained-object proxy + + This is a picklable proxy that can be put around objects that + don't implement IContained. + + >>> l = [1, 2, 3] + >>> p = ContainedProxy(l) + >>> p.__parent__ = 'Dad' + >>> p.__name__ = 'p' + >>> p + [1, 2, 3] + >>> p.__parent__ + 'Dad' + >>> p.__name__ + 'p' + + >>> import pickle + >>> p2 = pickle.loads(pickle.dumps(p)) + >>> p2 + [1, 2, 3] + >>> p2.__parent__ + 'Dad' + >>> p2.__name__ + 'p' + """ + +def test_basic_persistent_w_non_persistent_proxied(): + """ + >>> p = ContainedProxy([1]) + >>> p.__parent__ = 2 + >>> p.__name__ = 'test' + >>> db = DB(DemoStorage('test_storage')) + >>> c = db.open() + >>> c.root()['p'] = p + >>> transaction.commit() + + >>> c2 = db.open() + >>> p2 = c2.root()['p'] + >>> p2 + [1] + >>> p2.__parent__ + 2 + >>> p2.__name__ + 'test' + + >>> p2._p_changed + 0 + >>> p2._p_deactivate() + >>> p2._p_changed + >>> p2.__name__ + 'test' + + >>> db.close() + """ + +def test_declarations_on_ContainedProxy(): + r""" + + It is possible to make declarations on ContainedProxy objects. + + >>> class I1(zope.interface.Interface): + ... pass + >>> class C(object): + ... zope.interface.implements(I1) + + >>> c = C() + >>> p = ContainedProxy(c) + + ContainedProxy provides no interfaces on it's own: + + >>> tuple(zope.interface.providedBy(ContainedProxy)) + () + + It implements IContained and IPersistent: + + >>> tuple(zope.interface.implementedBy(ContainedProxy)) + (, + ) + + A proxied object has IContainer, in addition to what the unproxied + object has: + + >>> tuple(zope.interface.providedBy(p)) + (, + , + ) + + >>> class I2(zope.interface.Interface): + ... pass + >>> zope.interface.directlyProvides(c, I2) + >>> tuple(zope.interface.providedBy(p)) + (, + , + , + ) + + We can declare interfaces through the proxy: + + >>> class I3(zope.interface.Interface): + ... pass + >>> zope.interface.directlyProvides(p, I3) + >>> tuple(zope.interface.providedBy(p)) + (, + , + , + ) + + """ + +def test_basic_persistent_w_persistent_proxied(): + """ + + Here, we'll verify that shared references work and + that updates to both the proxies and the proxied objects + are made correctly. + + ---------------------- + | | + parent other + | / + ob <-------------- + + Here we have an object, parent, that contains ob. There is another + object, other, that has a non-container reference to ob. + + >>> parent = MyOb() + >>> parent.ob = ContainedProxy(MyOb()) + >>> parent.ob.__parent__ = parent + >>> parent.ob.__name__ = 'test' + >>> other = MyOb() + >>> other.ob = parent.ob + + We can change ob through either parent or other + + >>> parent.ob.x = 1 + >>> other.ob.y = 2 + + Now we'll save the data: + + >>> db = DB(DemoStorage('test_storage')) + >>> c1 = db.open() + >>> c1.root()['parent'] = parent + >>> c1.root()['other'] = other + >>> transaction.commit() + + We'll open a second connection and verify that we have the data we + expect: + + >>> c2 = db.open() + >>> p2 = c2.root()['parent'] + >>> p2.ob.__parent__ is p2 + 1 + >>> p2.ob.x + 1 + >>> p2.ob.y + 2 + >>> o2 = c2.root()['other'] + >>> o2.ob is p2.ob + 1 + >>> o2.ob is p2.ob + 1 + >>> o2.ob.__name__ + 'test' + + Now we'll change things around a bit. We'll move things around + a bit. We'll also add an attribute to ob + + >>> o2.ob.__name__ = 'test 2' + >>> o2.ob.__parent__ = o2 + >>> o2.ob.z = 3 + + >>> p2.ob.__parent__ is p2 + 0 + >>> p2.ob.__parent__ is o2 + 1 + + And save the changes: + + >>> transaction.commit() + + Now we'll reopen the first connection and verify that we can see + the changes: + + >>> c1.close() + >>> c1 = db.open() + >>> p2 = c1.root()['parent'] + >>> p2.ob.__name__ + 'test 2' + >>> p2.ob.z + 3 + >>> p2.ob.__parent__ is c1.root()['other'] + 1 + + >>> db.close() + """ + +def test_proxy_cache_interaction(): + """Test to make sure the proxy properly interacts with the object cache + + Persistent objects are their own weak refs. Thier deallocators + need to notify their connection's cache that their object is being + deallocated, so that it is removed from the cache. + + >>> from ZODB.tests.util import DB + >>> db = DB() + >>> db.setCacheSize(5) + >>> conn = db.open() + >>> conn.root()['p'] = ContainedProxy(None) + + We need to create some filler objects to push our proxy out of the cache: + + >>> for i in range(10): + ... conn.root()[i] = MyOb() + + >>> transaction.commit() + + Let's get the oid of our proxy: + + >>> oid = conn.root()['p']._p_oid + + Now, we'll access the filler object's: + + >>> x = [getattr(conn.root()[i], 'x', 0) for i in range(10)] + + We've also accessed the root object. If we garbage-collect the + cache: + + >>> conn._cache.incrgc() + + Then the root object will still be active, because it was accessed + recently: + + >>> conn.root()._p_changed + 0 + + And the proxy will be in the cache, because it's refernced from + the root object: + + >>> conn._cache.get(oid) is not None + True + + But it's a ghost: + + >>> conn.root()['p']._p_changed + + If we deactivate the root object: + + >>> conn.root()._p_deactivate() + + Then we'll release the last reference to the proxy and it should + no longer be in the cache. To be sure, we'll call gc: + + >>> x = gc.collect() + >>> conn._cache.get(oid) is not None + False + + """ + +def test_ContainedProxy_instances_have_no_instance_dictionaries(): + """Make sure that proxies don't introduce extra instance dictionaries + + >>> from zope.container.contained import ContainedProxy + >>> class C: + ... pass + + >>> c = C() + >>> c.x = 1 + >>> c.__dict__ + {'x': 1} + + >>> p = ContainedProxy(c) + >>> p.__dict__ + {'x': 1} + >>> p.y = 3 + >>> p.__dict__ + {'y': 3, 'x': 1} + >>> c.__dict__ + {'y': 3, 'x': 1} + + >>> p.__dict__ is c.__dict__ + True + + """ + + +class TestNameChooser(unittest.TestCase): + def test_checkName(self): + container = SampleContainer() + container['foo'] = 'bar' + checkName = NameChooser(container).checkName + + # invalid type for the name + self.assertRaises(TypeError, checkName, 2, object()) + self.assertRaises(TypeError, checkName, [], object()) + self.assertRaises(TypeError, checkName, None, object()) + self.assertRaises(TypeError, checkName, None, None) + + # invalid names + self.assertRaises(ValueError, checkName, '+foo', object()) + self.assertRaises(ValueError, checkName, '@foo', object()) + self.assertRaises(ValueError, checkName, 'f/oo', object()) + self.assertRaises(ValueError, checkName, '', object()) + + # existing names + self.assertRaises(KeyError, checkName, 'foo', object()) + self.assertRaises(KeyError, checkName, u'foo', object()) + + # correct names + self.assertEqual(True, checkName('2', object())) + self.assertEqual(True, checkName(u'2', object())) + self.assertEqual(True, checkName('other', object())) + self.assertEqual(True, checkName(u'reserved', object())) + self.assertEqual(True, checkName(u'r\xe9served', object())) + + # reserved names + class ReservedNames(object): + zope.component.adapts(IContainer) + zope.interface.implements(IReservedNames) + def __init__(self, context): + self.reservedNames = set(('reserved', 'other')) + zope.component.getSiteManager().registerAdapter(ReservedNames) + + self.assertRaises(NameReserved, checkName, 'reserved', object()) + self.assertRaises(NameReserved, checkName, 'other', object()) + self.assertRaises(NameReserved, checkName, u'reserved', object()) + self.assertRaises(NameReserved, checkName, u'other', object()) + + def test_chooseName(self): + container = SampleContainer() + container['foo.old.rst'] = 'rst doc' + nc = NameChooser(container) + + # correct name without changes + self.assertEqual(nc.chooseName('foobar.rst', None), + u'foobar.rst') + self.assertEqual(nc.chooseName(u'\xe9', None), + u'\xe9') + + # automatically modified named + self.assertEqual(nc.chooseName('foo.old.rst', None), + u'foo.old-2.rst') + self.assertEqual(nc.chooseName('+@+@foo.old.rst', None), + u'foo.old-2.rst') + self.assertEqual(nc.chooseName('+@+@foo/foo+@', None), + u'foo-foo+@') + + # empty name + self.assertEqual(nc.chooseName('', None), u'NoneType') + self.assertEqual(nc.chooseName('@+@', []), u'list') + + # if the name is not a string it is converted + self.assertEqual(nc.chooseName(None, None), u'None') + self.assertEqual(nc.chooseName(2, None), u'2') + self.assertEqual(nc.chooseName([], None), u'[]') + container['None'] = 'something' + self.assertEqual(nc.chooseName(None, None), u'None-2') + container['None-2'] = 'something' + self.assertEqual(nc.chooseName(None, None), u'None-3') + + # even if the given name cannot be converted to unicode + class BadBoy: + def __unicode__(self): + raise Exception + + self.assertEqual(nc.chooseName(BadBoy(), set()), u'set') + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite('zope.container.contained', + setUp=testing.setUp, + tearDown=testing.tearDown), + doctest.DocTestSuite(optionflags=doctest.NORMALIZE_WHITESPACE), + unittest.makeSuite(TestNameChooser), + )) + +if __name__ == '__main__': unittest.main() diff -Nru zope3-3.4.0/src/zope/container/tests/test_containertraversable.py zope3-3.5~bzr18/src/zope/container/tests/test_containertraversable.py --- zope3-3.4.0/src/zope/container/tests/test_containertraversable.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_containertraversable.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,75 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Container Traverser tests. + +$Id: test_containertraversable.py 95341 2009-01-28 15:59:18Z wosc $ +""" +import unittest +from zope.testing.cleanup import CleanUp +from zope.interface import implements +from zope.traversing.interfaces import TraversalError + +from zope.container.traversal import ContainerTraversable +from zope.container.interfaces import IContainer + +class Container(object): + + implements(IContainer) + + def __init__(self, attrs={}, objs={}): + for attr,value in attrs.iteritems(): + setattr(self, attr, value) + + self.__objs = {} + for name,value in objs.iteritems(): + self.__objs[name] = value + + + def __getitem__(self, name): + return self.__objs[name] + + def get(self, name, default=None): + return self.__objs.get(name, default) + + def __contains__(self, name): + return self.__objs.has_key(name) + + +class Test(CleanUp, unittest.TestCase): + def testAttr(self): + # test container path traversal + foo = Container() + bar = Container() + baz = Container() + c = Container({'foo': foo}, {'bar': bar, 'foo': baz}) + + T = ContainerTraversable(c) + self.failUnless(T.traverse('foo', []) is baz) + self.failUnless(T.traverse('bar', []) is bar) + + self.assertRaises(TraversalError , T.traverse, 'morebar', []) + def test_unicode_attr(self): + # test traversal with unicode + voila = Container() + c = Container({}, {u'voil\xe0': voila}) + self.failUnless(ContainerTraversable(c).traverse(u'voil\xe0', []) is voila) + + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + + +if __name__ == '__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/container/tests/test_containertraverser.py zope3-3.5~bzr18/src/zope/container/tests/test_containertraverser.py --- zope3-3.4.0/src/zope/container/tests/test_containertraverser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_containertraverser.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,110 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Container Traverser Tests + +$Id: test_containertraverser.py 97680 2009-03-09 07:32:19Z wosc $ +""" +import unittest +from zope.interface import Interface, implements +from zope import component +from zope.publisher.interfaces import NotFound, IDefaultViewName +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces.browser import IDefaultBrowserLayer + +from zope.container.traversal import ContainerTraverser +from zope.container.interfaces import IReadContainer +from zope.container import testing + +class TestContainer(object): + implements(IReadContainer) + + def __init__(self, **kw): + for name, value in kw.items(): + setattr(self, name , value) + + def get(self, name, default=None): + return getattr(self, name, default) + + +class View(object): + def __init__(self, context, request): + self.context = context + self.request = request + + +class TraverserTest(testing.ContainerPlacelessSetup, unittest.TestCase): + + # The following two methods exist, so that other container traversers can + # use these tests as a base. + def _getTraverser(self, context, request): + return ContainerTraverser(context, request) + + def _getContainer(self, **kw): + return TestContainer(**kw) + + def setUp(self): + super(TraverserTest, self).setUp() + # Create a small object tree + self.container = self._getContainer() + self.subcontainer = self._getContainer(Foo=self.container) + # Initiate a request + self.request = TestRequest() + # Create the traverser + self.traverser = self._getTraverser(self.subcontainer, self.request) + # Define a simple view for the container + component.provideAdapter( + View, (IReadContainer, IDefaultBrowserLayer), Interface, + name='viewfoo') + + def test_itemTraversal(self): + self.assertEqual( + self.traverser.publishTraverse(self.request, 'Foo'), + self.container) + self.assertRaises( + NotFound, + self.traverser.publishTraverse, self.request, 'morebar') + + def test_viewTraversal(self): + self.assertEquals( + self.traverser.publishTraverse(self.request, 'viewfoo').__class__, + View) + self.assertEquals( + self.traverser.publishTraverse(self.request, 'Foo'), + self.container) + self.assertRaises( + NotFound, + self.traverser.publishTraverse, self.request, 'morebar') + self.assertRaises( + NotFound, + self.traverser.publishTraverse, self.request, '@@morebar') + + def test_browserDefault_without_registration_should_raise(self): + self.assertRaises(component.ComponentLookupError, + self.traverser.browserDefault, self.request) + + def test_browserDefault(self): + component.provideAdapter( + 'myDefaultView', (Interface, IDefaultBrowserLayer), + IDefaultViewName) + self.assertEquals((self.subcontainer, ('@@myDefaultView',)), + self.traverser.browserDefault(self.request)) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(TraverserTest), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/container/tests/test_dependencies.py zope3-3.5~bzr18/src/zope/container/tests/test_dependencies.py --- zope3-3.4.0/src/zope/container/tests/test_dependencies.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_dependencies.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,57 @@ +import unittest + +from zope.configuration.xmlconfig import XMLConfig +from zope.interface import implements +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces.browser import IBrowserPublisher + +from zope.container.interfaces import IItemContainer +from zope.container.interfaces import ISimpleReadContainer +from zope.container.traversal import ItemTraverser +from zope.container.testing import ContainerPlacelessSetup + + +class ZCMLDependencies(ContainerPlacelessSetup, unittest.TestCase): + + def test_zcml_can_load_with_only_zope_component_meta(self): + # this is just an example. It is supposed to show that the + # configure.zcml file has loaded successfully. + + import zope.component + XMLConfig('meta.zcml', zope.component)() + + import zope.security + XMLConfig('meta.zcml', zope.security)() + XMLConfig('permissions.zcml', zope.security)() + + import zope.container + XMLConfig('configure.zcml', zope.container)() + + request = TestRequest() + + class SampleItemContainer(object): + implements(IItemContainer) + + sampleitemcontainer = SampleItemContainer() + res = zope.component.getMultiAdapter( + (sampleitemcontainer, request), IBrowserPublisher) + self.failUnless(isinstance(res, ItemTraverser)) + self.failUnless(res.context is sampleitemcontainer) + + class SampleSimpleReadContainer(object): + implements(ISimpleReadContainer) + + samplesimplereadcontainer = SampleSimpleReadContainer() + res = zope.component.getMultiAdapter( + (samplesimplereadcontainer, request), IBrowserPublisher) + self.failUnless(isinstance(res, ItemTraverser)) + self.failUnless(res.context is samplesimplereadcontainer) + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(ZCMLDependencies)) + return suite + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/container/tests/test_directory.py zope3-3.5~bzr18/src/zope/container/tests/test_directory.py --- zope3-3.4.0/src/zope/container/tests/test_directory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_directory.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""FS-based directory implementation tests for containers + +$Id: test_directory.py 111713 2010-04-30 20:43:09Z hannosch $ +""" + +import doctest +from unittest import TestCase, TestSuite, main, makeSuite + +from zope.container import testing +import zope.container.directory + + +class Directory(object): + pass + +class Test(TestCase): + + def test_Cloner(self): + d = Directory() + d.a = 1 + clone = zope.container.directory.Cloner(d)('foo') + self.assert_(clone != d) + self.assertEqual(clone.__class__, d.__class__) + +def test_suite(): + flags = doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE + return TestSuite(( + makeSuite(Test), + doctest.DocFileSuite("directory.txt", + setUp=testing.setUp, tearDown=testing.tearDown, + optionflags=flags), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/container/tests/test_find.py zope3-3.5~bzr18/src/zope/container/tests/test_find.py --- zope3-3.4.0/src/zope/container/tests/test_find.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_find.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,174 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Find functionality tests + +$Id: test_find.py 95341 2009-01-28 15:59:18Z wosc $ +""" +from unittest import TestCase, main, makeSuite +from zope.container.interfaces import IReadContainer +from zope.container.interfaces import IObjectFindFilter +from zope.container.find import FindAdapter, SimpleIdFindFilter +from zope.container.find import SimpleInterfacesFindFilter +from zope.interface import implements, Interface, directlyProvides + +class FakeContainer(object): + implements(IReadContainer) + + def __init__(self, id, objects): + self._id = id + self._objects = objects + + def keys(self): + return [object._id for object in self._objects] + + def values(self): + return self._objects + + def items(self): + return [(object._id, object) for object in self._objects] + + def __getitem__(self, id): + for object in self._objects: + if object._id == id: + return object + raise KeyError("Could not find %s" % id) + + def get(self, id, default=None): + for object in self._objects: + if object._id == id: + return object + + return default + + def __contains__(self, id): + for object in self._objects: + if object.id == id: + return True + return False + + def __len__(self): + return len(self._objects) + +class FakeInterfaceFoo(Interface): + """Test interface Foo""" + +class FakeInterfaceBar(Interface): + """Test interface Bar""" + +class FakeInterfaceSpam(Interface): + """Test interface Spam""" + +class TestObjectFindFilter(object): + implements(IObjectFindFilter) + + def __init__(self, count): + self._count = count + + def matches(self, object): + if IReadContainer.providedBy(object): + return len(object) == self._count + else: + return False + +class Test(TestCase): + def test_idFind(self): + alpha = FakeContainer('alpha', []) + delta = FakeContainer('delta', []) + beta = FakeContainer('beta', [delta]) + gamma = FakeContainer('gamma', []) + tree = FakeContainer( + 'tree', + [alpha, beta, gamma]) + find = FindAdapter(tree) + # some simple searches + result = find.find([SimpleIdFindFilter(['beta'])]) + self.assertEquals([beta], result) + result = find.find([SimpleIdFindFilter(['gamma'])]) + self.assertEquals([gamma], result) + result = find.find([SimpleIdFindFilter(['delta'])]) + self.assertEquals([delta], result) + # we should not find the container we search on + result = find.find([SimpleIdFindFilter(['tree'])]) + self.assertEquals([], result) + # search for multiple ids + result = find.find([SimpleIdFindFilter(['alpha', 'beta'])]) + self.assertEquals([alpha, beta], result) + result = find.find([SimpleIdFindFilter(['beta', 'delta'])]) + self.assertEquals([beta, delta], result) + # search without any filters, find everything + result = find.find([]) + self.assertEquals([alpha, beta, delta, gamma], result) + # search for something that doesn't exist + result = find.find([SimpleIdFindFilter(['foo'])]) + self.assertEquals([], result) + # find for something that has two ids at the same time, + # can't ever be the case + result = find.find([SimpleIdFindFilter(['alpha']), + SimpleIdFindFilter(['beta'])]) + self.assertEquals([], result) + + def test_objectFind(self): + alpha = FakeContainer('alpha', []) + delta = FakeContainer('delta', []) + beta = FakeContainer('beta', [delta]) + gamma = FakeContainer('gamma', []) + tree = FakeContainer( + 'tree', + [alpha, beta, gamma]) + find = FindAdapter(tree) + result = find.find(object_filters=[TestObjectFindFilter(0)]) + self.assertEquals([alpha, delta, gamma], result) + result = find.find(object_filters=[TestObjectFindFilter(1)]) + self.assertEquals([beta], result) + result = find.find(object_filters=[TestObjectFindFilter(2)]) + self.assertEquals([], result) + + def test_combinedFind(self): + alpha = FakeContainer('alpha', []) + delta = FakeContainer('delta', []) + beta = FakeContainer('beta', [delta]) + gamma = FakeContainer('gamma', []) + tree = FakeContainer( + 'tree', + [alpha, beta, gamma]) + find = FindAdapter(tree) + result = find.find(id_filters=[SimpleIdFindFilter(['alpha'])], + object_filters=[TestObjectFindFilter(0)]) + self.assertEquals([alpha], result) + + result = find.find(id_filters=[SimpleIdFindFilter(['alpha'])], + object_filters=[TestObjectFindFilter(1)]) + self.assertEquals([], result) + + def test_interfaceFind(self): + alpha = FakeContainer('alpha', []) + directlyProvides(alpha, FakeInterfaceBar) + delta = FakeContainer('delta', []) + directlyProvides(delta, FakeInterfaceFoo) + beta = FakeContainer('beta', [delta]) + directlyProvides(beta, FakeInterfaceSpam) + gamma = FakeContainer('gamma', []) + tree = FakeContainer( + 'tree', + [alpha, beta, gamma]) + find = FindAdapter(tree) + result = find.find(object_filters=[ + SimpleInterfacesFindFilter(FakeInterfaceFoo, FakeInterfaceSpam)]) + self.assertEqual([beta, delta], result) + +def test_suite(): + return makeSuite(Test) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/container/tests/test_folder.py zope3-3.5~bzr18/src/zope/container/tests/test_folder.py --- zope3-3.4.0/src/zope/container/tests/test_folder.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_folder.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ +from unittest import TestCase, makeSuite + +from zope.container.folder import Folder + +from zope.container.tests.test_icontainer import BaseTestIContainer +from zope.container.tests.test_icontainer import DefaultTestData + + +class Test(BaseTestIContainer, TestCase): + + def makeTestObject(self): + return Folder() + + def makeTestData(self): + return DefaultTestData() + + def getUnknownKey(self): + return '10' + + def getBadKeyTypes(self): + return [None, ['foo'], 1, '\xf3abc'] + +def test_suite(): + return makeSuite(Test) + diff -Nru zope3-3.4.0/src/zope/container/tests/test_icontainer.py zope3-3.5~bzr18/src/zope/container/tests/test_icontainer.py --- zope3-3.4.0/src/zope/container/tests/test_icontainer.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_icontainer.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,319 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 the IContainer interface. + +$Id: test_icontainer.py 95417 2009-01-29 11:53:38Z faassen $ +""" +from unittest import TestCase, main, makeSuite + +from zope.interface.verify import verifyObject +from zope.container.interfaces import IContainer +from zope.container import testing + + +def DefaultTestData(): + return [('3', '0'), ('2', '1'), ('4', '2'), ('6', '3'), ('0', '4'), + ('5', '5'), ('1', '6'), ('8', '7'), ('7', '8'), ('9', '9')] + +class BaseTestIContainer(testing.ContainerPlacelessSetup): + """Base test cases for containers. + + Subclasses must define a makeTestObject that takes no + arguments and that returns a new empty test container, + and a makeTestData that also takes no arguments and returns + a sequence of (key, value) pairs that may be stored in + the test container. The list must be at least ten items long. + 'NoSuchKey' may not be used as a key value in the returned list. + """ + + def __setUp(self): + self.__container = container = self.makeTestObject() + self.__data = data = self.makeTestData() + for k, v in data: + container[k] = v + return container, data + + ############################################################ + # Interface-driven tests: + + def testIContainerVerify(self): + verifyObject(IContainer, self.makeTestObject()) + + def test_keys(self): + # See interface IReadContainer + container = self.makeTestObject() + keys = container.keys() + self.assertEqual(list(keys), []) + + container, data = self.__setUp() + keys = container.keys() + keys = list(keys); keys.sort() # convert to sorted list + ikeys = [ k for k, v in data ]; ikeys.sort() # sort input keys + self.assertEqual(keys, ikeys) + + def test_get(self): + # See interface IReadContainer + default = object() + data = self.makeTestData() + container = self.makeTestObject() + self.assertRaises(KeyError, container.__getitem__, data[0][0]) + self.assertEqual(container.get(data[0][0], default), default) + + container, data = self.__setUp() + self.assertRaises(KeyError, container.__getitem__, + self.getUnknownKey()) + self.assertEqual(container.get(self.getUnknownKey(), default), default) + for i in (1, 8, 7, 3, 4): + self.assertEqual(container.get(data[i][0], default), data[i][1]) + self.assertEqual(container.get(data[i][0]), data[i][1]) + + def test_values(self): + # See interface IReadContainer + container = self.makeTestObject() + values = container.values() + self.assertEqual(list(values), []) + + container, data = self.__setUp() + values = list(container.values()) + for k, v in data: + try: + values.remove(v) + except ValueError: + self.fail('Value not in list') + + self.assertEqual(values, []) + + def test_len(self): + # See interface IReadContainer + container = self.makeTestObject() + self.assertEqual(len(container), 0) + + container, data = self.__setUp() + self.assertEqual(len(container), len(data)) + + def test_items(self): + # See interface IReadContainer + container = self.makeTestObject() + items = container.items() + self.assertEqual(list(items), []) + + container, data = self.__setUp() + items = container.items() + items = list(items); items.sort() # convert to sorted list + data.sort() # sort input data + self.assertEqual(items, data) + + def test___contains__(self): + # See interface IReadContainer + container = self.makeTestObject() + data = self.makeTestData() + self.assertEqual(not not (data[6][0] in container), False) + + container, data = self.__setUp() + self.assertEqual(not not (data[6][0] in container), True) + for i in (1, 8, 7, 3, 4): + self.assertEqual(not not (data[i][0] in container), 1) + + def test_delObject(self): + # See interface IWriteContainer + default = object() + data = self.makeTestData() + container = self.makeTestObject() + self.assertRaises(KeyError, container.__delitem__, data[0][0]) + + container, data = self.__setUp() + self.assertRaises(KeyError, container.__delitem__, + self.getUnknownKey()) + for i in (1, 8, 7, 3, 4): + del container[data[i][0]] + for i in (1, 8, 7, 3, 4): + self.assertRaises(KeyError, container.__getitem__, data[i][0]) + self.assertEqual(container.get(data[i][0], default), default) + for i in (0, 2, 9, 6, 5): + self.assertEqual(container[data[i][0]], data[i][1]) + + ############################################################ + # Tests from Folder + + def testEmpty(self): + folder = self.makeTestObject() + data = self.makeTestData() + self.failIf(folder.keys()) + self.failIf(folder.values()) + self.failIf(folder.items()) + self.failIf(len(folder)) + self.failIf(data[6][0] in folder) + + self.assertEquals(folder.get(data[6][0], None), None) + self.assertRaises(KeyError, folder.__getitem__, data[6][0]) + + self.assertRaises(KeyError, folder.__delitem__, data[6][0]) + + def testBadKeyTypes(self): + folder = self.makeTestObject() + data = self.makeTestData() + value = data[1][1] + for name in self.getBadKeyTypes(): + self.assertRaises(TypeError, folder.__setitem__, name, value) + + def testOneItem(self): + folder = self.makeTestObject() + data = self.makeTestData() + + foo = data[0][1] + name = data[0][0] + folder[name] = foo + + self.assertEquals(len(folder.keys()), 1) + self.assertEquals(folder.keys()[0], name) + self.assertEquals(len(folder.values()), 1) + self.assertEquals(folder.values()[0], foo) + self.assertEquals(len(folder.items()), 1) + self.assertEquals(folder.items()[0], (name, foo)) + self.assertEquals(len(folder), 1) + + self.failUnless(name in folder) + # Use an arbitrary id frpm the data set; don;t just use any id, since + # there might be restrictions on their form + self.failIf(data[6][0] in folder) + + self.assertEquals(folder.get(name, None), foo) + self.assertEquals(folder[name], foo) + + self.assertRaises(KeyError, folder.__getitem__, data[6][0]) + + foo2 = data[1][1] + + name2 = data[1][0] + folder[name2] = foo2 + + self.assertEquals(len(folder.keys()), 2) + self.assertEquals(not not name2 in folder.keys(), True) + self.assertEquals(len(folder.values()), 2) + self.assertEquals(not not foo2 in folder.values(), True) + self.assertEquals(len(folder.items()), 2) + self.assertEquals(not not (name2, foo2) in folder.items(), True) + self.assertEquals(len(folder), 2) + + del folder[name] + del folder[name2] + + self.failIf(folder.keys()) + self.failIf(folder.values()) + self.failIf(folder.items()) + self.failIf(len(folder)) + self.failIf(name in folder) + + self.assertRaises(KeyError, folder.__getitem__, name) + self.assertEquals(folder.get(name, None), None) + self.assertRaises(KeyError, folder.__delitem__, name) + + def testManyItems(self): + folder = self.makeTestObject() + data = self.makeTestData() + objects = [ data[i][1] for i in range(4) ] + name0 = data[0][0] + name1 = data[1][0] + name2 = data[2][0] + name3 = data[3][0] + folder[name0] = objects[0] + folder[name1] = objects[1] + folder[name2] = objects[2] + folder[name3] = objects[3] + + self.assertEquals(len(folder.keys()), len(objects)) + self.failUnless(name0 in folder.keys()) + self.failUnless(name1 in folder.keys()) + self.failUnless(name2 in folder.keys()) + self.failUnless(name3 in folder.keys()) + + self.assertEquals(len(folder.values()), len(objects)) + self.failUnless(objects[0] in folder.values()) + self.failUnless(objects[1] in folder.values()) + self.failUnless(objects[2] in folder.values()) + self.failUnless(objects[3] in folder.values()) + + self.assertEquals(len(folder.items()), len(objects)) + self.failUnless((name0, objects[0]) in folder.items()) + self.failUnless((name1, objects[1]) in folder.items()) + self.failUnless((name2, objects[2]) in folder.items()) + self.failUnless((name3, objects[3]) in folder.items()) + + self.assertEquals(len(folder), len(objects)) + + self.failUnless(name0 in folder) + self.failUnless(name1 in folder) + self.failUnless(name2 in folder) + self.failUnless(name3 in folder) + self.failIf(data[5][0] in folder) + + self.assertEquals(folder.get(name0, None), objects[0]) + self.assertEquals(folder[name0], objects[0]) + self.assertEquals(folder.get(name1, None), objects[1]) + self.assertEquals(folder[name1], objects[1]) + self.assertEquals(folder.get(name2, None), objects[2]) + self.assertEquals(folder[name2], objects[2]) + self.assertEquals(folder.get(name3, None), objects[3]) + self.assertEquals(folder[name3], objects[3]) + + self.assertEquals(folder.get(data[5][0], None), None) + self.assertRaises(KeyError, folder.__getitem__, data[5][0]) + + del folder[name0] + self.assertEquals(len(folder), len(objects) - 1) + self.failIf(name0 in folder) + self.failIf(name0 in folder.keys()) + + self.failIf(objects[0] in folder.values()) + self.failIf((name0, objects[0]) in folder.items()) + + self.assertEquals(folder.get(name0, None), None) + self.assertRaises(KeyError, folder.__getitem__, name0) + + self.assertRaises(KeyError, folder.__delitem__, name0) + + del folder[name1] + del folder[name2] + del folder[name3] + + self.failIf(folder.keys()) + self.failIf(folder.values()) + self.failIf(folder.items()) + self.failIf(len(folder)) + self.failIf(name0 in folder) + self.failIf(name1 in folder) + self.failIf(name2 in folder) + self.failIf(name3 in folder) + + +class TestSampleContainer(BaseTestIContainer, TestCase): + + def makeTestObject(self): + from zope.container.sample import SampleContainer + return SampleContainer() + + def makeTestData(self): + return DefaultTestData() + + def getUnknownKey(self): + return '10' + + def getBadKeyTypes(self): + return [None, ['foo'], 1, '\xf3abc'] + +def test_suite(): + return makeSuite(TestSampleContainer) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/container/tests/test_ordered.py zope3-3.5~bzr18/src/zope/container/tests/test_ordered.py --- zope3-3.4.0/src/zope/container/tests/test_ordered.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_ordered.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,138 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 the OrderedContainer. + +$Id: test_ordered.py 111713 2010-04-30 20:43:09Z hannosch $ +""" +import unittest +from doctest import DocTestSuite + +from zope.component.eventtesting import getEvents, clearEvents +from zope.container import testing + + +def test_order_events(): + """ + Prepare the setup:: + + >>> from zope.container.sample import SampleContainer + >>> root = SampleContainer() + + Prepare some objects:: + + >>> from zope.container.ordered import OrderedContainer + >>> oc = OrderedContainer() + >>> oc['foo'] = 'bar' + >>> oc['baz'] = 'quux' + >>> oc['zork'] = 'grue' + >>> oc.keys() + ['foo', 'baz', 'zork'] + + Now change the order:: + + >>> clearEvents() + >>> oc.updateOrder(['baz', 'foo', 'zork']) + >>> oc.keys() + ['baz', 'foo', 'zork'] + + Check what events have been sent:: + + >>> events = getEvents() + >>> [event.__class__.__name__ for event in events] + ['ContainerModifiedEvent'] + + This is in fact a specialized modification event:: + + >>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent + >>> IObjectModifiedEvent.providedBy(events[0]) + True + + """ + +def test_all_items_available_at_object_added_event(): + """ + Prepare the setup:: + + >>> from zope.container.sample import SampleContainer + >>> root = SampleContainer() + + Now register an event subscriber to object added events. + + >>> import zope.component + >>> from zope.container import interfaces + >>> from zope.lifecycleevent.interfaces import IObjectAddedEvent + + >>> @zope.component.adapter(IObjectAddedEvent) + ... def printContainerKeys(event): + ... print event.newParent.keys() + + >>> zope.component.provideHandler(printContainerKeys) + + Now we are adding an object to the container. + + >>> from zope.container.ordered import OrderedContainer + >>> oc = OrderedContainer() + >>> oc['foo'] = 'FOO' + ['foo'] + + """ + +def test_exception_causes_order_fix(): + """ + Prepare the setup:: + + >>> from zope.container.sample import SampleContainer + >>> root = SampleContainer() + + Now register an event subscriber to object added events that + throws an error. + + >>> import zope.component + >>> from zope.container import interfaces + >>> from zope.lifecycleevent.interfaces import IObjectAddedEvent + + >>> @zope.component.adapter(IObjectAddedEvent) + ... def raiseException(event): + ... raise Exception() + + >>> zope.component.provideHandler(raiseException) + + Now we are adding an object to the container. + + >>> from zope.container.ordered import OrderedContainer + >>> oc = OrderedContainer() + >>> oc['foo'] = 'FOO' + Traceback (most recent call last): + ... + Exception + + The key 'foo' should not be around: + + >>> 'foo' in oc.keys() + False + + """ + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(DocTestSuite("zope.container.ordered", + setUp=testing.setUp, + tearDown=testing.tearDown)) + suite.addTest(DocTestSuite( + setUp=testing.ContainerPlacefulSetup().setUp, + tearDown=testing.ContainerPlacefulSetup().tearDown)) + return suite + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/container/tests/test_size.py zope3-3.5~bzr18/src/zope/container/tests/test_size.py --- zope3-3.4.0/src/zope/container/tests/test_size.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/tests/test_size.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,70 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 container ISized adapter. + +$Id: test_size.py 95341 2009-01-28 15:59:18Z wosc $ +""" +import unittest + +from zope.interface import implements +from zope.size.interfaces import ISized +from zope.container.interfaces import IContainer + +class DummyContainer(object): + + implements(IContainer) + + def __init__(self, numitems): + self._numitems = numitems + + def __len__(self): + return self._numitems + + +class Test(unittest.TestCase): + + def testImplementsISized(self): + from zope.container.size import ContainerSized + sized = ContainerSized(DummyContainer(23)) + self.assert_(ISized.providedBy(sized)) + + def testEmptyContainer(self): + from zope.container.size import ContainerSized + obj = DummyContainer(0) + sized = ContainerSized(obj) + self.assertEqual(sized.sizeForSorting(), ('item', 0)) + self.assertEqual(sized.sizeForDisplay(), u'${items} items') + self.assertEqual(sized.sizeForDisplay().mapping['items'], '0') + + def testOneItem(self): + from zope.container.size import ContainerSized + obj = DummyContainer(1) + sized = ContainerSized(obj) + self.assertEqual(sized.sizeForSorting(), ('item', 1)) + self.assertEqual(sized.sizeForDisplay(), u'1 item') + + def testSeveralItems(self): + from zope.container.size import ContainerSized + obj = DummyContainer(2) + sized = ContainerSized(obj) + self.assertEqual(sized.sizeForSorting(), ('item', 2)) + self.assertEqual(sized.sizeForDisplay(), u'${items} items') + self.assertEqual(sized.sizeForDisplay().mapping['items'], '2') + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/container/traversal.py zope3-3.5~bzr18/src/zope/container/traversal.py --- zope3-3.4.0/src/zope/container/traversal.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/traversal.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,115 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Traversal components for containers + +$Id: traversal.py 97680 2009-03-09 07:32:19Z wosc $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import implements, providedBy +from zope.component import queryMultiAdapter, getSiteManager +from zope.component import ComponentLookupError +from zope.traversing.interfaces import TraversalError, ITraversable +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.interfaces.xmlrpc import IXMLRPCPublisher +from zope.publisher.interfaces import IDefaultViewName, NotFound + +from zope.container.interfaces import ISimpleReadContainer, IItemContainer +from zope.container.interfaces import IReadContainer + +# Note that the next two classes are included here because they +# can be used for multiple view types. + +class ContainerTraverser(object): + """A traverser that knows how to look up objects by name in a container.""" + + implements(IBrowserPublisher, IXMLRPCPublisher) + __used_for__ = ISimpleReadContainer + + def __init__(self, container, request): + self.context = container + self.request = request + + def publishTraverse(self, request, name): + """See zope.publisher.interfaces.IPublishTraverse""" + subob = self.context.get(name, None) + if subob is None: + view = queryMultiAdapter((self.context, request), name=name) + if view is not None: + return view + + raise NotFound(self.context, name, request) + + return subob + + def browserDefault(self, request): + """See zope.publisher.browser.interfaces.IBrowserPublisher""" + # XXX this re-implements zope.app.publisher.browser.getDefaultViewName() + # to break our only dependency on it. + view_name = getSiteManager(None).adapters.lookup( + map(providedBy, (self.context, request)), IDefaultViewName) + if view_name is None: + raise ComponentLookupError("Couldn't find default view name", + self.context, request) + view_uri = "@@%s" %view_name + return self.context, (view_uri,) + + +class ItemTraverser(ContainerTraverser): + """A traverser that knows how to look up objects by name in an item + container.""" + + __used_for__ = IItemContainer + + def publishTraverse(self, request, name): + """See zope.publisher.interfaces.IPublishTraverse""" + try: + return self.context[name] + except KeyError: + view = queryMultiAdapter((self.context, request), name=name) + if view is not None: + return view + + raise NotFound(self.context, name, request) + + +_marker = object() + +class ContainerTraversable(object): + """Traverses containers via `getattr` and `get`.""" + + implements(ITraversable) + __used_for__ = IReadContainer + + def __init__(self, container): + self._container = container + + + def traverse(self, name, furtherPath): + container = self._container + + v = container.get(name, _marker) + if v is _marker: + try: + # Note that if name is a unicode string, getattr will + # implicitly try to encode it using the system + # encoding (usually ascii). Failure to encode means + # invalid attribute name. + v = getattr(container, name, _marker) + except UnicodeEncodeError: + raise TraversalError(container, name) + if v is _marker: + raise TraversalError(container, name) + + return v diff -Nru zope3-3.4.0/src/zope/container/_zope_container_contained.c zope3-3.5~bzr18/src/zope/container/_zope_container_contained.c --- zope3-3.4.0/src/zope/container/_zope_container_contained.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/_zope_container_contained.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,336 @@ +/*############################################################################ +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################*/ + +#define _ZOPE_CONTAINER_CONTAINED_C "$Id: _zope_container_contained.c 95350 2009-01-28 17:01:17Z wosc $\n" + +/* Contained Proxy Base class + + Contained proxies provide __parent__ and __name__ attributes for + objects without them. + + There is something strange and, possibly cool, going on here, wrt + persistence. To reuse the base proxy implementation we don't treat + the proxied object as part of the persistent state of the proxy. + This means that the proxy still operates as a proxy even if it is a + ghost. + + The proxy will only be unghostified if you need to access one of the + attributes provided by the proxy. + + */ + + +#include "Python.h" +#include "persistent/cPersistence.h" + +static PyObject *str_p_deactivate; + +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +typedef Py_ssize_t (*lenfunc)(PyObject *); +typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t); +typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t); +typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *); +typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *); +#endif + +typedef struct { + cPersistent_HEAD + PyObject *po_weaklist; + PyObject *proxy_object; + PyObject *__parent__; + PyObject *__name__; +} ProxyObject; + +typedef struct { + PyTypeObject *proxytype; + int (*check)(PyObject *obj); + PyObject *(*create)(PyObject *obj); + PyObject *(*getobject)(PyObject *proxy); +} ProxyInterface; + +#define OBJECT(O) ((PyObject*)(O)) +#define Proxy_GET_OBJECT(ob) (((ProxyObject *)(ob))->proxy_object) + +#define CLEAR(O) \ + if (O) {PyObject *clr__tmp = O; O = NULL; Py_DECREF(clr__tmp); } + +/* Supress inclusion of the original proxy.h */ +#define _proxy_H_ 1 + +/* Incude the proxy C source */ +#include "_zope_proxy_proxy.c" + +#define SPECIAL(NAME) ( \ + *(NAME) == '_' && \ + (((NAME)[1] == 'p' && (NAME)[2] == '_') \ + || \ + ((NAME)[1] == '_' && ( \ + strcmp((NAME), "__parent__") == 0 \ + || \ + strcmp((NAME), "__name__") == 0 \ + || \ + strcmp((NAME), "__getstate__") == 0 \ + || \ + strcmp((NAME), "__setstate__") == 0 \ + || \ + strcmp((NAME), "__getnewargs__") == 0 \ + || \ + strcmp((NAME), "__reduce__") == 0 \ + || \ + strcmp((NAME), "__reduce_ex__") == 0 \ + )) \ + )) + +static PyObject * +CP_getattro(PyObject *self, PyObject *name) +{ + char *cname; + + cname = PyString_AsString(name); + if (cname == NULL) + return NULL; + + if (SPECIAL(cname)) + /* delegate to persistent */ + return cPersistenceCAPI->pertype->tp_getattro(self, name); + + /* Use the wrapper version to delegate */ + return wrap_getattro(self, name); +} + +static int +CP_setattro(PyObject *self, PyObject *name, PyObject *v) +{ + char *cname; + + cname = PyString_AsString(name); + if (cname == NULL) + return -1; + + if (SPECIAL(cname)) + /* delegate to persistent */ + return cPersistenceCAPI->pertype->tp_setattro(self, name, v); + + /* Use the wrapper version to delegate */ + return wrap_setattro(self, name, v); +} + +static PyObject * +CP_getstate(ProxyObject *self) +{ + return Py_BuildValue("OO", + self->__parent__ ? self->__parent__ : Py_None, + self->__name__ ? self->__name__ : Py_None + ); +} + +static PyObject * +CP_getnewargs(ProxyObject *self) +{ + return Py_BuildValue("(O)", self->proxy_object); +} + +static PyObject * +CP_setstate(ProxyObject *self, PyObject *state) +{ + PyObject *parent, *name; + + if(! PyArg_ParseTuple(state, "OO", &parent, &name)) + return NULL; + + CLEAR(self->__parent__); + CLEAR(self->__name__); + + Py_INCREF(parent); + Py_INCREF(name); + + self->__parent__ = parent; + self->__name__ = name; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CP_reduce(ProxyObject *self) +{ + PyObject *result; + if (! PER_USE(self)) + return NULL; + result = Py_BuildValue("O(O)(OO)", + self->ob_type, + self->proxy_object, + self->__parent__ ? self->__parent__ : Py_None, + self->__name__ ? self->__name__ : Py_None + ); + PER_ALLOW_DEACTIVATION(self); + return result; +} + +static PyObject * +CP_reduce_ex(ProxyObject *self, PyObject *proto) +{ + return CP_reduce(self); +} + +static PyObject * +CP__p_deactivate(ProxyObject *self) +{ + PyObject *result; + + result = PyObject_CallMethodObjArgs(OBJECT(cPersistenceCAPI->pertype), + str_p_deactivate, + self, NULL); + if (result == NULL) + return NULL; + + if (self->jar && self->oid && self->state == cPersistent_UPTODATE_STATE) + { + Py_XDECREF(self->__parent__); + self->__parent__ = NULL; + Py_XDECREF(self->__name__); + self->__name__ = NULL; + } + + return result; +} + + +static PyMethodDef +CP_methods[] = { + {"__getstate__", (PyCFunction)CP_getstate, METH_NOARGS, + "Get the object state"}, + {"__setstate__", (PyCFunction)CP_setstate, METH_O, + "Set the object state"}, + {"__getnewargs__", (PyCFunction)CP_getnewargs, METH_NOARGS, + "Get the arguments that must be passed to __new__"}, + {"__reduce__", (PyCFunction)CP_reduce, METH_NOARGS, + "Reduce the object to constituent parts."}, + {"__reduce_ex__", (PyCFunction)CP_reduce_ex, METH_O, + "Reduce the object to constituent parts."}, + {"_p_deactivate", (PyCFunction)CP__p_deactivate, METH_NOARGS, + "Deactivate the object."}, + {NULL, NULL}, +}; + + +/* Code to access structure members by accessing attributes */ + +#include "structmember.h" + +static PyMemberDef CP_members[] = { + {"__parent__", T_OBJECT, offsetof(ProxyObject, __parent__)}, + {"__name__", T_OBJECT, offsetof(ProxyObject, __name__)}, + {NULL} /* Sentinel */ +}; + +static int +CP_traverse(ProxyObject *self, visitproc visit, void *arg) +{ + if (cPersistenceCAPI->pertype->tp_traverse((PyObject *)self, visit, arg) < 0) + return -1; + if (self->proxy_object != NULL && visit(self->proxy_object, arg) < 0) + return -1; + if (self->__parent__ != NULL && visit(self->__parent__, arg) < 0) + return -1; + if (self->__name__ != NULL && visit(self->__name__, arg) < 0) + return -1; + + return 0; +} + +static int +CP_clear(ProxyObject *self) +{ + /* Drop references that may have created reference + cycles. Immutable objects do not have to define this method + since they can never directly create reference cycles. Note + that the object must still be valid after calling this + method (don't just call Py_DECREF() on a reference). The + collector will call this method if it detects that this + object is involved in a reference cycle. + */ + if (cPersistenceCAPI->pertype->tp_clear != NULL) + cPersistenceCAPI->pertype->tp_clear((PyObject*)self); + + CLEAR(self->proxy_object); + CLEAR(self->__parent__); + CLEAR(self->__name__); + + return 0; +} + +static void +CP_dealloc(ProxyObject *self) +{ + if (self->po_weaklist != NULL) + PyObject_ClearWeakRefs((PyObject *)self); + + CLEAR(self->proxy_object); + CLEAR(self->__parent__); + CLEAR(self->__name__); + + cPersistenceCAPI->pertype->tp_dealloc((PyObject*)self); +} + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +init_zope_container_contained(void) +{ + PyObject *m; + + str_p_deactivate = PyString_FromString("_p_deactivate"); + if (str_p_deactivate == NULL) + return; + + /* Try to fake out compiler nag function */ + if (0) init_zope_proxy_proxy(); + + m = Py_InitModule3("_zope_container_contained", + module_functions, module___doc__); + + if (m == NULL) + return; + + if (empty_tuple == NULL) + empty_tuple = PyTuple_New(0); + + /* Initialize the PyPersist_C_API and the type objects. */ + cPersistenceCAPI = PyCObject_Import("persistent.cPersistence", "CAPI"); + if (cPersistenceCAPI == NULL) + return; + + ProxyType.tp_name = "zope.container.contained.ContainedProxyBase"; + ProxyType.ob_type = &PyType_Type; + ProxyType.tp_base = cPersistenceCAPI->pertype; + ProxyType.tp_getattro = CP_getattro; + ProxyType.tp_setattro = CP_setattro; + ProxyType.tp_members = CP_members; + ProxyType.tp_methods = CP_methods; + ProxyType.tp_traverse = (traverseproc) CP_traverse; + ProxyType.tp_clear = (inquiry) CP_clear; + ProxyType.tp_dealloc = (destructor) CP_dealloc; + ProxyType.tp_weaklistoffset = offsetof(ProxyObject, po_weaklist); + + if (PyType_Ready(&ProxyType) < 0) + return; + + Py_INCREF(&ProxyType); + PyModule_AddObject(m, "ContainedProxyBase", (PyObject *)&ProxyType); +} diff -Nru zope3-3.4.0/src/zope/container/_zope_container_contained.py zope3-3.5~bzr18/src/zope/container/_zope_container_contained.py --- zope3-3.4.0/src/zope/container/_zope_container_contained.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/_zope_container_contained.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_zope_container_contained.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/zope/container/_zope_proxy_proxy.c zope3-3.5~bzr18/src/zope/container/_zope_proxy_proxy.c --- zope3-3.4.0/src/zope/container/_zope_proxy_proxy.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/container/_zope_proxy_proxy.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1098 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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 file is also used as a really extensive macro in + * ../app/container/_zope_container_contained.c. If you need to + * change this file, you need to "svn copy" it to ../container/. + * + * This approach is taken to allow the sources for the two packages + * to be compilable when the relative locations of these aren't + * related in the same way as they are in a checkout. + * + * This will be revisited in the future, but works for now. + */ + +#include "Python.h" +#include "modsupport.h" + +#define PROXY_MODULE +#include "zope.proxy/proxy.h" + +static PyTypeObject ProxyType; + +#define Proxy_Check(wrapper) (PyObject_TypeCheck((wrapper), &ProxyType)) + +static PyObject * +empty_tuple = NULL; + + +/* + * Slot methods. + */ + +static PyObject * +wrap_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *result = NULL; + PyObject *object; + + if (PyArg_UnpackTuple(args, "__new__", 1, 1, &object)) { + if (kwds != NULL && PyDict_Size(kwds) != 0) { + PyErr_SetString(PyExc_TypeError, + "proxy.__new__ does not accept keyword args"); + return NULL; + } + result = PyType_GenericNew(type, args, kwds); + if (result != NULL) { + ProxyObject *wrapper = (ProxyObject *) result; + Py_INCREF(object); + wrapper->proxy_object = object; + } + } + return result; +} + +static int +wrap_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result = -1; + PyObject *object; + + if (PyArg_UnpackTuple(args, "__init__", 1, 1, &object)) { + ProxyObject *wrapper = (ProxyObject *)self; + if (kwds != NULL && PyDict_Size(kwds) != 0) { + PyErr_SetString(PyExc_TypeError, + "proxy.__init__ does not accept keyword args"); + return -1; + } + /* If the object in this proxy is not the one we + * received in args, replace it with the new one. + */ + if (wrapper->proxy_object != object) { + PyObject *temp = wrapper->proxy_object; + Py_INCREF(object); + wrapper->proxy_object = object; + Py_DECREF(temp); + } + result = 0; + } + return result; +} + +static int +wrap_traverse(PyObject *self, visitproc visit, void *arg) +{ + PyObject *ob = Proxy_GET_OBJECT(self); + if (ob != NULL) + return visit(ob, arg); + else + return 0; +} + +static int +wrap_clear(PyObject *self) +{ + ProxyObject *proxy = (ProxyObject *)self; + PyObject *temp = proxy->proxy_object; + + if (temp != NULL) { + proxy->proxy_object = NULL; + Py_DECREF(temp); + } + return 0; +} + +static PyObject * +wrap_richcompare(PyObject* self, PyObject* other, int op) +{ + if (Proxy_Check(self)) { + self = Proxy_GET_OBJECT(self); + } + else { + other = Proxy_GET_OBJECT(other); + } + return PyObject_RichCompare(self, other, op); +} + +static PyObject * +wrap_iter(PyObject *self) +{ + return PyObject_GetIter(Proxy_GET_OBJECT(self)); +} + +static PyObject * +wrap_iternext(PyObject *self) +{ + return PyIter_Next(Proxy_GET_OBJECT(self)); +} + +static void +wrap_dealloc(PyObject *self) +{ + (void) wrap_clear(self); + self->ob_type->tp_free(self); +} + +/* A variant of _PyType_Lookup that doesn't look in ProxyType. + * + * If argument search_wrappertype is nonzero, we can look in WrapperType. + */ +PyObject * +WrapperType_Lookup(PyTypeObject *type, PyObject *name) +{ + int i, n; + PyObject *mro, *res, *base, *dict; + + /* Look in tp_dict of types in MRO */ + mro = type->tp_mro; + + /* If mro is NULL, the type is either not yet initialized + by PyType_Ready(), or already cleared by type_clear(). + Either way the safest thing to do is to return NULL. */ + if (mro == NULL) + return NULL; + + assert(PyTuple_Check(mro)); + + n = PyTuple_GET_SIZE(mro) + - 1; /* We don't want to look at the last item, which is object. */ + + for (i = 0; i < n; i++) { + base = PyTuple_GET_ITEM(mro, i); + + if (((PyTypeObject *)base) != &ProxyType) { + if (PyClass_Check(base)) + dict = ((PyClassObject *)base)->cl_dict; + else { + assert(PyType_Check(base)); + dict = ((PyTypeObject *)base)->tp_dict; + } + assert(dict && PyDict_Check(dict)); + res = PyDict_GetItem(dict, name); + if (res != NULL) + return res; + } + } + return NULL; +} + + +static PyObject * +wrap_getattro(PyObject *self, PyObject *name) +{ + PyObject *wrapped; + PyObject *descriptor; + PyObject *res = NULL; + char *name_as_string; + int maybe_special_name; + +#ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_getattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return NULL; + } + else +#endif + if (!PyString_Check(name)){ + PyErr_SetString(PyExc_TypeError, "attribute name must be string"); + return NULL; + } + else + Py_INCREF(name); + + name_as_string = PyString_AS_STRING(name); + wrapped = Proxy_GET_OBJECT(self); + if (wrapped == NULL) { + PyErr_Format(PyExc_RuntimeError, + "object is NULL; requested to get attribute '%s'", + name_as_string); + goto finally; + } + + maybe_special_name = name_as_string[0] == '_' && name_as_string[1] == '_'; + + if (!(maybe_special_name && strcmp(name_as_string, "__class__") == 0)) { + + descriptor = WrapperType_Lookup(self->ob_type, name); + + if (descriptor != NULL) { + if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) + && descriptor->ob_type->tp_descr_get != NULL) { + res = descriptor->ob_type->tp_descr_get( + descriptor, + self, + (PyObject *)self->ob_type); + } else { + Py_INCREF(descriptor); + res = descriptor; + } + goto finally; + } + } + res = PyObject_GetAttr(wrapped, name); + +finally: + Py_DECREF(name); + return res; +} + +static int +wrap_setattro(PyObject *self, PyObject *name, PyObject *value) +{ + PyObject *wrapped; + PyObject *descriptor; + int res = -1; + +#ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_setattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return -1; + } + else +#endif + if (!PyString_Check(name)){ + PyErr_SetString(PyExc_TypeError, "attribute name must be string"); + return -1; + } + else + Py_INCREF(name); + + descriptor = WrapperType_Lookup(self->ob_type, name); + if (descriptor != NULL) { + if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) && + descriptor->ob_type->tp_descr_set != NULL) { + res = descriptor->ob_type->tp_descr_set(descriptor, self, value); + } else { + PyErr_Format(PyExc_TypeError, + "Tried to set attribute '%s' on wrapper, but it is not" + " a data descriptor", PyString_AS_STRING(name)); + } + goto finally; + } + + wrapped = Proxy_GET_OBJECT(self); + if (wrapped == NULL) { + PyErr_Format(PyExc_RuntimeError, + "object is NULL; requested to set attribute '%s'", + PyString_AS_STRING(name)); + goto finally; + } + res = PyObject_SetAttr(wrapped, name, value); + +finally: + Py_DECREF(name); + return res; +} + +static int +wrap_print(PyObject *wrapper, FILE *fp, int flags) +{ + return PyObject_Print(Proxy_GET_OBJECT(wrapper), fp, flags); +} + +static PyObject * +wrap_str(PyObject *wrapper) { + return PyObject_Str(Proxy_GET_OBJECT(wrapper)); +} + +static PyObject * +wrap_repr(PyObject *wrapper) +{ + return PyObject_Repr(Proxy_GET_OBJECT(wrapper)); +} + + +static int +wrap_compare(PyObject *wrapper, PyObject *v) +{ + return PyObject_Compare(Proxy_GET_OBJECT(wrapper), v); +} + +static long +wrap_hash(PyObject *self) +{ + return PyObject_Hash(Proxy_GET_OBJECT(self)); +} + +static PyObject * +wrap_call(PyObject *self, PyObject *args, PyObject *kw) +{ + if (kw) + return PyEval_CallObjectWithKeywords(Proxy_GET_OBJECT(self), + args, kw); + else + return PyObject_CallObject(Proxy_GET_OBJECT(self), args); +} + +/* + * Number methods + */ + +/* + * Number methods. + */ + +static PyObject * +call_int(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_int == NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to int"); + return NULL; + } + return nb->nb_int(self); +} + +static PyObject * +call_long(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_long == NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to long"); + return NULL; + } + return nb->nb_long(self); +} + +static PyObject * +call_float(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_float== NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to float"); + return NULL; + } + return nb->nb_float(self); +} + +static PyObject * +call_oct(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_oct== NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to oct"); + return NULL; + } + return nb->nb_oct(self); +} + +static PyObject * +call_hex(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_hex == NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to hex"); + return NULL; + } + return nb->nb_hex(self); +} + +static PyObject * +call_ipow(PyObject *self, PyObject *other) +{ + /* PyNumber_InPlacePower has three args. How silly. :-) */ + return PyNumber_InPlacePower(self, other, Py_None); +} + +typedef PyObject *(*function1)(PyObject *); + +static PyObject * +check1(ProxyObject *self, char *opname, function1 operation) +{ + PyObject *result = NULL; + + result = operation(Proxy_GET_OBJECT(self)); +#if 0 + if (result != NULL) + /* ??? create proxy for result? */ + ; +#endif + return result; +} + +static PyObject * +check2(PyObject *self, PyObject *other, + char *opname, char *ropname, binaryfunc operation) +{ + PyObject *result = NULL; + PyObject *object; + + if (Proxy_Check(self)) { + object = Proxy_GET_OBJECT(self); + result = operation(object, other); + } + else if (Proxy_Check(other)) { + object = Proxy_GET_OBJECT(other); + result = operation(self, object); + } + else { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } +#if 0 + if (result != NULL) + /* ??? create proxy for result? */ + ; +#endif + return result; +} + +static PyObject * +check2i(ProxyObject *self, PyObject *other, + char *opname, binaryfunc operation) +{ + PyObject *result = NULL; + PyObject *object = Proxy_GET_OBJECT(self); + + result = operation(object, other); + if (result == object) { + /* If the operation was really carried out inplace, + don't create a new proxy, but use the old one. */ + Py_INCREF(self); + Py_DECREF(object); + result = (PyObject *)self; + } +#if 0 + else if (result != NULL) + /* ??? create proxy for result? */ + ; +#endif + return result; +} + +#define UNOP(NAME, CALL) \ + static PyObject *wrap_##NAME(PyObject *self) \ + { return check1((ProxyObject *)self, "__"#NAME"__", CALL); } + +#define BINOP(NAME, CALL) \ + static PyObject *wrap_##NAME(PyObject *self, PyObject *other) \ + { return check2(self, other, "__"#NAME"__", "__r"#NAME"__", CALL); } + +#define INPLACE(NAME, CALL) \ + static PyObject *wrap_i##NAME(PyObject *self, PyObject *other) \ + { return check2i((ProxyObject *)self, other, "__i"#NAME"__", CALL); } + +BINOP(add, PyNumber_Add) +BINOP(sub, PyNumber_Subtract) +BINOP(mul, PyNumber_Multiply) +BINOP(div, PyNumber_Divide) +BINOP(mod, PyNumber_Remainder) +BINOP(divmod, PyNumber_Divmod) + +static PyObject * +wrap_pow(PyObject *self, PyObject *other, PyObject *modulus) +{ + PyObject *result = NULL; + PyObject *object; + + if (Proxy_Check(self)) { + object = Proxy_GET_OBJECT(self); + result = PyNumber_Power(object, other, modulus); + } + else if (Proxy_Check(other)) { + object = Proxy_GET_OBJECT(other); + result = PyNumber_Power(self, object, modulus); + } + else if (modulus != NULL && Proxy_Check(modulus)) { + object = Proxy_GET_OBJECT(modulus); + result = PyNumber_Power(self, other, modulus); + } + else { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return result; +} + +BINOP(lshift, PyNumber_Lshift) +BINOP(rshift, PyNumber_Rshift) +BINOP(and, PyNumber_And) +BINOP(xor, PyNumber_Xor) +BINOP(or, PyNumber_Or) + +static int +wrap_coerce(PyObject **p_self, PyObject **p_other) +{ + PyObject *self = *p_self; + PyObject *other = *p_other; + PyObject *object; + PyObject *left; + PyObject *right; + int r; + + assert(Proxy_Check(self)); + object = Proxy_GET_OBJECT(self); + + left = object; + right = other; + r = PyNumber_CoerceEx(&left, &right); + if (r != 0) + return r; + /* Now left and right have been INCREF'ed. Any new value that + comes out is proxied; any unchanged value is left unchanged. */ + if (left == object) { + /* Keep the old proxy */ + Py_INCREF(self); + Py_DECREF(left); + left = self; + } +#if 0 + else { + /* ??? create proxy for left? */ + } + if (right != other) { + /* ??? create proxy for right? */ + } +#endif + *p_self = left; + *p_other = right; + return 0; +} + +UNOP(neg, PyNumber_Negative) +UNOP(pos, PyNumber_Positive) +UNOP(abs, PyNumber_Absolute) +UNOP(invert, PyNumber_Invert) + +UNOP(int, call_int) +UNOP(long, call_long) +UNOP(float, call_float) +UNOP(oct, call_oct) +UNOP(hex, call_hex) + +INPLACE(add, PyNumber_InPlaceAdd) +INPLACE(sub, PyNumber_InPlaceSubtract) +INPLACE(mul, PyNumber_InPlaceMultiply) +INPLACE(div, PyNumber_InPlaceDivide) +INPLACE(mod, PyNumber_InPlaceRemainder) +INPLACE(pow, call_ipow) +INPLACE(lshift, PyNumber_InPlaceLshift) +INPLACE(rshift, PyNumber_InPlaceRshift) +INPLACE(and, PyNumber_InPlaceAnd) +INPLACE(xor, PyNumber_InPlaceXor) +INPLACE(or, PyNumber_InPlaceOr) + +BINOP(floordiv, PyNumber_FloorDivide) +BINOP(truediv, PyNumber_TrueDivide) +INPLACE(floordiv, PyNumber_InPlaceFloorDivide) +INPLACE(truediv, PyNumber_InPlaceTrueDivide) + +static int +wrap_nonzero(PyObject *self) +{ + return PyObject_IsTrue(Proxy_GET_OBJECT(self)); +} + +/* + * Sequence methods + */ + +static Py_ssize_t +wrap_length(PyObject *self) +{ + return PyObject_Length(Proxy_GET_OBJECT(self)); +} + +static PyObject * +wrap_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end) +{ + return PySequence_GetSlice(Proxy_GET_OBJECT(self), start, end); +} + +static int +wrap_ass_slice(PyObject *self, Py_ssize_t i, Py_ssize_t j, PyObject *value) +{ + return PySequence_SetSlice(Proxy_GET_OBJECT(self), i, j, value); +} + +static int +wrap_contains(PyObject *self, PyObject *value) +{ + return PySequence_Contains(Proxy_GET_OBJECT(self), value); +} + +/* + * Mapping methods + */ + +static PyObject * +wrap_getitem(PyObject *wrapper, PyObject *v) { + return PyObject_GetItem(Proxy_GET_OBJECT(wrapper), v); +} + +static int +wrap_setitem(PyObject *self, PyObject *key, PyObject *value) +{ + if (value == NULL) + return PyObject_DelItem(Proxy_GET_OBJECT(self), key); + else + return PyObject_SetItem(Proxy_GET_OBJECT(self), key, value); +} + +/* + * Normal methods + */ + +static char +reduce__doc__[] = +"__reduce__()\n" +"Raise an exception; this prevents proxies from being picklable by\n" +"default, even if the underlying object is picklable."; + +static PyObject * +wrap_reduce(PyObject *self) +{ + PyObject *pickle_error = NULL; + PyObject *pickle = PyImport_ImportModule("pickle"); + + if (pickle == NULL) + PyErr_Clear(); + else { + pickle_error = PyObject_GetAttrString(pickle, "PicklingError"); + if (pickle_error == NULL) + PyErr_Clear(); + } + if (pickle_error == NULL) { + pickle_error = PyExc_RuntimeError; + Py_INCREF(pickle_error); + } + PyErr_SetString(pickle_error, + "proxy instances cannot be pickled"); + Py_DECREF(pickle_error); + return NULL; +} + +static PyNumberMethods +wrap_as_number = { + wrap_add, /* nb_add */ + wrap_sub, /* nb_subtract */ + wrap_mul, /* nb_multiply */ + wrap_div, /* nb_divide */ + wrap_mod, /* nb_remainder */ + wrap_divmod, /* nb_divmod */ + wrap_pow, /* nb_power */ + wrap_neg, /* nb_negative */ + wrap_pos, /* nb_positive */ + wrap_abs, /* nb_absolute */ + wrap_nonzero, /* nb_nonzero */ + wrap_invert, /* nb_invert */ + wrap_lshift, /* nb_lshift */ + wrap_rshift, /* nb_rshift */ + wrap_and, /* nb_and */ + wrap_xor, /* nb_xor */ + wrap_or, /* nb_or */ + wrap_coerce, /* nb_coerce */ + wrap_int, /* nb_int */ + wrap_long, /* nb_long */ + wrap_float, /* nb_float */ + wrap_oct, /* nb_oct */ + wrap_hex, /* nb_hex */ + + /* Added in release 2.0 */ + /* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */ + wrap_iadd, /* nb_inplace_add */ + wrap_isub, /* nb_inplace_subtract */ + wrap_imul, /* nb_inplace_multiply */ + wrap_idiv, /* nb_inplace_divide */ + wrap_imod, /* nb_inplace_remainder */ + (ternaryfunc)wrap_ipow, /* nb_inplace_power */ + wrap_ilshift, /* nb_inplace_lshift */ + wrap_irshift, /* nb_inplace_rshift */ + wrap_iand, /* nb_inplace_and */ + wrap_ixor, /* nb_inplace_xor */ + wrap_ior, /* nb_inplace_or */ + + /* Added in release 2.2 */ + /* These require the Py_TPFLAGS_HAVE_CLASS flag */ + wrap_floordiv, /* nb_floor_divide */ + wrap_truediv, /* nb_true_divide */ + wrap_ifloordiv, /* nb_inplace_floor_divide */ + wrap_itruediv, /* nb_inplace_true_divide */ +}; + +static PySequenceMethods +wrap_as_sequence = { + wrap_length, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + wrap_slice, /* sq_slice */ + 0, /* sq_ass_item */ + wrap_ass_slice, /* sq_ass_slice */ + wrap_contains, /* sq_contains */ +}; + +static PyMappingMethods +wrap_as_mapping = { + wrap_length, /* mp_length */ + wrap_getitem, /* mp_subscript */ + wrap_setitem, /* mp_ass_subscript */ +}; + +static PyMethodDef +wrap_methods[] = { + {"__reduce__", (PyCFunction)wrap_reduce, METH_NOARGS, reduce__doc__}, + {NULL, NULL}, +}; + +/* + * Note that the numeric methods are not supported. This is primarily + * because of the way coercion-less operations are performed with + * new-style numbers; since we can't tell which side of the operation + * is 'self', we can't ensure we'd unwrap the right thing to perform + * the actual operation. We also can't afford to just unwrap both + * sides the way weakrefs do, since we don't know what semantics will + * be associated with the wrapper itself. + */ + +statichere PyTypeObject +ProxyType = { + PyObject_HEAD_INIT(NULL) /* PyObject_HEAD_INIT(&PyType_Type) */ + 0, + "zope.proxy.ProxyBase", + sizeof(ProxyObject), + 0, + wrap_dealloc, /* tp_dealloc */ + wrap_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + wrap_compare, /* tp_compare */ + wrap_repr, /* tp_repr */ + &wrap_as_number, /* tp_as_number */ + &wrap_as_sequence, /* tp_as_sequence */ + &wrap_as_mapping, /* tp_as_mapping */ + wrap_hash, /* tp_hash */ + wrap_call, /* tp_call */ + wrap_str, /* tp_str */ + wrap_getattro, /* tp_getattro */ + wrap_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + wrap_traverse, /* tp_traverse */ + wrap_clear, /* tp_clear */ + wrap_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + wrap_iter, /* tp_iter */ + wrap_iternext, /* tp_iternext */ + wrap_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + wrap_init, /* tp_init */ + 0, /* tp_alloc */ + wrap_new, /* tp_new */ + 0, /*_PyObject_GC_Del,*/ /* tp_free */ +}; + +static PyObject * +create_proxy(PyObject *object) +{ + PyObject *result = NULL; + PyObject *args; + + args = PyTuple_New(1); + if (args != NULL) { + Py_INCREF(object); + PyTuple_SET_ITEM(args, 0, object); + result = PyObject_CallObject((PyObject *)&ProxyType, args); + Py_DECREF(args); + } + return result; +} + +static int +api_check(PyObject *obj) +{ + return obj ? Proxy_Check(obj) : 0; +} + +static PyObject * +api_create(PyObject *object) +{ + if (object == NULL) { + PyErr_SetString(PyExc_ValueError, + "cannot create proxy around NULL"); + return NULL; + } + return create_proxy(object); +} + +static PyObject * +api_getobject(PyObject *proxy) +{ + if (proxy == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "cannot pass NULL to ProxyAPI.getobject()"); + return NULL; + } + if (Proxy_Check(proxy)) + return Proxy_GET_OBJECT(proxy); + else { + PyErr_Format(PyExc_TypeError, "expected proxy object, got %s", + proxy->ob_type->tp_name); + return NULL; + } +} + +static ProxyInterface +wrapper_capi = { + &ProxyType, + api_check, + api_create, + api_getobject, +}; + +static PyObject *api_object = NULL; + + +static char +getobject__doc__[] = +"getProxiedObject(proxy) --> object\n" +"\n" +"Get the underlying object for proxy, or the object itself, if it is\n" +"not a proxy."; + +static PyObject * +wrapper_getobject(PyObject *unused, PyObject *obj) +{ + if (Proxy_Check(obj)) + obj = Proxy_GET_OBJECT(obj); + + if (obj == NULL) + obj = Py_None; + + Py_INCREF(obj); + return obj; +} + +static char +isProxy__doc__[] = +"Check whether the given object is a proxy\n" +"\n" +"If proxytype is not None, checkes whether the object is\n" +"proxied by the given proxytype.\n" +; + +static PyObject * +wrapper_isProxy(PyObject *unused, PyObject *args) +{ + PyObject *obj, *result; + PyTypeObject *proxytype=&ProxyType; + + if (! PyArg_ParseTuple(args, "O|O!:isProxy", + &obj, &PyType_Type, &proxytype) + ) + return NULL; + + while (obj && Proxy_Check(obj)) + { + if (PyObject_TypeCheck(obj, proxytype)) + { + result = Py_True; + Py_INCREF(result); + return result; + } + obj = Proxy_GET_OBJECT(obj); + } + result = Py_False; + Py_INCREF(result); + return result; +} + +static char +removeAllProxies__doc__[] = +"removeAllProxies(proxy) --> object\n" +"\n" +"Get the proxied object with no proxies\n" +"\n" +"If obj is not a proxied object, return obj.\n" +"\n" +"The returned object has no proxies.\n" +; + +static PyObject * +wrapper_removeAllProxies(PyObject *unused, PyObject *obj) +{ + while (obj && Proxy_Check(obj)) + obj = Proxy_GET_OBJECT(obj); + + if (obj == NULL) + obj = Py_None; + + Py_INCREF(obj); + return obj; +} + +static char +sameProxiedObjects__doc__[] = +"Check whether two objects are the same or proxies of the same object"; + +static PyObject * +wrapper_sameProxiedObjects(PyObject *unused, PyObject *args) +{ + PyObject *ob1, *ob2; + + if (! PyArg_ParseTuple(args, "OO:sameProxiedObjects", &ob1, &ob2)) + return NULL; + + while (ob1 && Proxy_Check(ob1)) + ob1 = Proxy_GET_OBJECT(ob1); + + while (ob2 && Proxy_Check(ob2)) + ob2 = Proxy_GET_OBJECT(ob2); + + if (ob1 == ob2) + ob1 = Py_True; + else + ob1 = Py_False; + + Py_INCREF(ob1); + return ob1; +} + + +static char +queryProxy__doc__[] = +"Look for a proxy of the given type around the object\n" +"\n" +"If no such proxy can be found, return the default.\n" +; + +static PyObject * +wrapper_queryProxy(PyObject *unused, PyObject *args) +{ + PyObject *obj, *result=Py_None; + PyTypeObject *proxytype=&ProxyType; + + if (! PyArg_ParseTuple(args, "O|O!O:queryProxy", + &obj, &PyType_Type, &proxytype, &result) + ) + return NULL; + + while (obj && Proxy_Check(obj)) + { + if (PyObject_TypeCheck(obj, proxytype)) + { + Py_INCREF(obj); + return obj; + } + obj = Proxy_GET_OBJECT(obj); + } + + Py_INCREF(result); + return result; +} + +static char +queryInnerProxy__doc__[] = +"Look for the inner-most proxy of the given type around the object\n" +"\n" +"If no such proxy can be found, return the default.\n" +"\n" +"If there is such a proxy, return the inner-most one.\n" +; + +static PyObject * +wrapper_queryInnerProxy(PyObject *unused, PyObject *args) +{ + PyObject *obj, *result=Py_None; + PyTypeObject *proxytype=&ProxyType; + + if (! PyArg_ParseTuple(args, "O|O!O:queryInnerProxy", + &obj, &PyType_Type, &proxytype, &result) + ) + return NULL; + + while (obj && Proxy_Check(obj)) + { + if (PyObject_TypeCheck(obj, proxytype)) + result = obj; + obj = Proxy_GET_OBJECT(obj); + } + + Py_INCREF(result); + return result; +} + +static char +module___doc__[] = +"Association between an object, a context object, and a dictionary.\n\ +\n\ +The context object and dictionary give additional context information\n\ +associated with a reference to the basic object. The wrapper objects\n\ +act as proxies for the original object."; + + +static PyMethodDef +module_functions[] = { + {"getProxiedObject", wrapper_getobject, METH_O, getobject__doc__}, + {"isProxy", wrapper_isProxy, METH_VARARGS, isProxy__doc__}, + {"sameProxiedObjects", wrapper_sameProxiedObjects, METH_VARARGS, + sameProxiedObjects__doc__}, + {"queryProxy", wrapper_queryProxy, METH_VARARGS, queryProxy__doc__}, + {"queryInnerProxy", wrapper_queryInnerProxy, METH_VARARGS, + queryInnerProxy__doc__}, + {"removeAllProxies", wrapper_removeAllProxies, METH_O, + removeAllProxies__doc__}, + {NULL} +}; + +void +init_zope_proxy_proxy(void) +{ + PyObject *m = Py_InitModule3("_zope_proxy_proxy", + module_functions, module___doc__); + + if (m == NULL) + return; + + if (empty_tuple == NULL) + empty_tuple = PyTuple_New(0); + + ProxyType.tp_free = _PyObject_GC_Del; + + if (PyType_Ready(&ProxyType) < 0) + return; + + Py_INCREF(&ProxyType); + PyModule_AddObject(m, "ProxyBase", (PyObject *)&ProxyType); + + if (api_object == NULL) { + api_object = PyCObject_FromVoidPtr(&wrapper_capi, NULL); + if (api_object == NULL) + return; + } + Py_INCREF(api_object); + PyModule_AddObject(m, "_CAPI", api_object); +} diff -Nru zope3-3.4.0/src/zope/contentprovider/configure.zcml zope3-3.5~bzr18/src/zope/contentprovider/configure.zcml --- zope3-3.4.0/src/zope/contentprovider/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contentprovider/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/contentprovider/__init__.py zope3-3.5~bzr18/src/zope/contentprovider/__init__.py --- zope3-3.4.0/src/zope/contentprovider/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contentprovider/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,16 @@ +############################################################################## +# +# 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. +# +############################################################################## +""" +$Id: __init__.py 112004 2010-05-05 17:54:28Z tseaver $ +""" diff -Nru zope3-3.4.0/src/zope/contentprovider/interfaces.py zope3-3.5~bzr18/src/zope/contentprovider/interfaces.py --- zope3-3.4.0/src/zope/contentprovider/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contentprovider/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,150 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Content provider interfaces + +$Id: interfaces.py 112004 2010-05-05 17:54:28Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +import zope.interface +from zope.component.interfaces import ObjectEvent, IObjectEvent +from zope.publisher.interfaces import browser +from zope.tales import interfaces + +class IUpdateNotCalled(zope.interface.common.interfaces.IRuntimeError): + """Update Not Called + + An error that is raised when any content provider method is called before + the ``update()`` method. + """ + +class UpdateNotCalled(RuntimeError): + pass + +# Make it a singelton +UpdateNotCalled = UpdateNotCalled('``update()`` was not called yet.') + +class IBeforeUpdateEvent(IObjectEvent): + + """A Contentprovider will be updated""" + + request = zope.interface.Attribute( + """The request in which the object is udpated, might also be + None""") + +class BeforeUpdateEvent(ObjectEvent): + + """A Contentprovider will be updated""" + + zope.interface.implements(IBeforeUpdateEvent) + + def __init__(self, provider, request=None): + super(BeforeUpdateEvent, self).__init__(provider) + self.request = request + +class IContentProvider(browser.IBrowserView): + """A piece of content to be shown on a page. + + Objects implementing this interface are providing HTML content when they + are rendered. It is up to the implementation to decide how to lookup + necessary data to complete the job. + + Content Providers use a two-stage process to fulfill their contract: + + (1) The first stage is responsible to calculate the state of the content + provider and, if applicable, edit the data. This stage is executed + using the ``update()`` method. + + (2) During the second stage the provider constructs/renders its HTML + output based on the state that was calculated in the first stage. This + stage is executed using the ``render()`` method. + + Content Providers are discriminated by three components: the context, the + request and the view. This allows great control over the selection of the + provider. + """ + + __parent__ = zope.interface.Attribute( + """The view the provider appears in. + + The view is the third discriminator of the content provider. It allows + that the content can be controlled for different views. + + Having it stored as the parent is also very important for the security + context to be kept. + """) + + def update(): + """Initialize the content provider. + + This method should perform all state calculation and *not* refer it to + the rendering stage. + + In this method, all state must be calculated from the current + interaction (e.g., the browser request); all contained or managed + content providers must have ``update()`` be called as well; any + additional stateful API for contained or managed content providers + must be handled; and persistent objects should be modified, if the + provider is going to do it. + + Do *not* store state about persistent objects: the rendering process + should actually use the persistent objects for the data, in case other + components modify the object between the update and render stages. + + This method *must* be called before any other method that mutates the + instance (besides the class constructor). Non-mutating methods and + attributes may raise an error if used before ``update()`` is + called. The view may rely on this order but is *not required* to + explicitly enforce this. Implementations *may* enforce it as a + developer aid. + """ + + def render(*args, **kw): + """Return the content provided by this content provider. + + Calling this method before ``update()`` *may* (but is not required to) + raise an ``UpdateNotCalled`` error. + """ + +class IContentProviderType(zope.interface.interfaces.IInterface): + """Type interface for content provider types (interfaces derived from + IContentProvider). + """ + + +class ITALNamespaceData(zope.interface.interfaces.IInterface): + """A type interface that marks an interface as a TAL data specification. + + All fields specified in an interface that provides `ITALNamespaceData` + will be looked up in the TAL context and stored on the content provider. A + content provider can have multiple interfaces that are of this type. + """ + + +class ContentProviderLookupError(zope.component.ComponentLookupError): + """No content provider was found.""" + + +class ITALESProviderExpression(interfaces.ITALESExpression): + """Return the HTML content of the named provider. + + To call a content provider in a view use the the following syntax in a page + template:: + + + + The content provider is looked up by the (context, request, view) objects + and the name (`provider.name`). + """ diff -Nru zope3-3.4.0/src/zope/contentprovider/provider.py zope3-3.5~bzr18/src/zope/contentprovider/provider.py --- zope3-3.4.0/src/zope/contentprovider/provider.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contentprovider/provider.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ +############################################################################## +# +# Copyright (c) 2009 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. +# +############################################################################## +"""Simple base class for implementing content providers + +$Id: provider.py 98173 2009-03-16 22:47:55Z nadako $ +""" +from zope.component import adapts +from zope.interface import Interface, implements +from zope.publisher.browser import BrowserView +from zope.publisher.interfaces.browser import IBrowserRequest + +from zope.contentprovider.interfaces import IContentProvider + +class ContentProviderBase(BrowserView): + """Base class for content providers""" + + implements(IContentProvider) + adapts(Interface, IBrowserRequest, Interface) + + def __init__(self, context, request, view): + super(ContentProviderBase, self).__init__(context, request) + self.__parent__ = view + + def update(self): + pass + + def render(self, *args, **kwargs): + raise NotImplementedError( + '``render`` method must be implemented by subclass') diff -Nru zope3-3.4.0/src/zope/contentprovider/README.txt zope3-3.5~bzr18/src/zope/contentprovider/README.txt --- zope3-3.4.0/src/zope/contentprovider/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contentprovider/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,572 @@ +================= +Content Providers +================= + +This package provides a framework to develop componentized Web GUI +applications. Instead of describing the content of a page using a single +template or static system of templates and METAL macros, content provider +objects are dynamically looked up based on the setup/configuration of the +application. + +.. contents:: + +Motivation and Design Goals +--------------------------- + +Before diving into the features of this package let me take up a few bytes of +text to explain the use cases that drove us to develop this package (also +others) and how the API documented below fulfills/solves those use cases. When +we started developing Zope 3, it was from a desire to decentralize +functionality and thus the complexity of the Python code. And we were +successful! The component architecture is a marvelous piece of software that +hopefully will allow us to build scalable solutions for a very long +time. However, when it comes to user interface design, in this case +specifically HTML pages, we have failed to provide the features and patterns +of assembeling a page from configured components. + +Looking up views for a particular content component and a request just simply +does not work by itself. The content inside the page is still monolithic. One +attempt to solve this problem are METAL macros, which allow you to insert +other TAL snippets into your main template. But macros have two shortcomings. +For one there is a "hard-coded" one-to-one mapping between a slot and the +macro that fills that slot, which makes it impossible to register several +macros for a given location. The second problem is that macros are not views +in their own right; thus they cannot provide functionality that is independent +of the main template's view. + +A second approach to modular UI design are rendering pipes. Rendering pipes +have the great advantage that they can reach all regions of the page during +every step of the rendering process. For example, if we have a widget in the +middle of the page that requires some additional Javascript, then it is easy +for a rendering unit to insert the Javascript file link in the HTML header of +the page. This type of use case is very hard to solve using page +templates. However, pipes are not the answer to componentized user interface, +since they cannot simply deal with registering random content for a given page +region. In fact, I propose that pipelines are orthogonal to content providers, +the concept introducted below. A pipeline framework could easily use +functionality provided by this and other packages to provide component-driven +UI design. + +So our goal is clear: Bring the pluggability of the component architecture +into page templates and user interface design. Zope is commonly known to +reinvent the wheel, develop its own terminology and misuse other's terms. For +example, the Plone community has a very different understanding of what a +"portlet" is compared to the commonly accepted meaning in the corporate world, +which derives its definition from JSR 168. Therefore an additional use case of +the design of this package was to stick with common terms and use them in +their original meaning -- well, given a little extra twist. + +The most basic user interface component in the Web application Java world is +the "content provider" [1]_. A content provider is simply responsible for +providing HTML content for a page. This is equivalent to a view that does not +provide a full page, but just a snippet, much like widgets or macros. Once +there is a way to configure those content providers, we need a way to +insert them into our page templates. In our implementation this is +accomplished using a new TALES namespace that allows to insert content +providers by name. But how, you might wonder, does this provide a +componentized user interface? On the Zope 3 level, each content provider is +registered as a presentation component discriminated by the context, request +and view it will appear in. Thus different content providers will be picked +for different configurations. + +Okay, that's pretty much everything there is to say about content +providers. What, we are done? Hold on, what about defining regions of pages +and filling them configured UI snippets. The short answer is: See the +``zope.viewlet`` pacakge. But let me also give you the long answer. This and +the other pacakges were developed using real world use cases. While doing +this, we noticed that not every project would need, for example, all the +features of a portlet, but would still profit from lower-level features. Thus +we decided to declare clear boundaries of functionality and providing each +level in a different package. This particualr package is only meant to provide +the interface between the content provider world and page templates. + +.. [1] Note that this is a bit different from the role named content provider, + which refers to a service that provides content; the content provider + we are talking about here are the software components the service would + provide to an application. + + +Content Providers +----------------- + +Content Provider is a term from the Java world that refers to components that +can provide HTML content. It means nothing more! How the content is found and +returned is totally up to the implementation. The Zope 3 touch to the concept +is that content providers are multi-adapters that are looked up by the +context, request (and thus the layer/skin), and view they are displayed in. + +The second important concept of content providers are their two-phase +rendering design. In the first phase the state of the content provider is +prepared and, if applicable, any data the provider is responsible for is +updated. + + >>> from zope.contentprovider import interfaces + +So let's create a simple content provider: + + >>> import zope.interface + >>> import zope.component + >>> from zope.publisher.interfaces import browser + + >>> class MessageBox(object): + ... zope.interface.implements(interfaces.IContentProvider) + ... zope.component.adapts(zope.interface.Interface, + ... browser.IDefaultBrowserLayer, + ... zope.interface.Interface) + ... message = u'My Message' + ... + ... def __init__(self, context, request, view): + ... self.__parent__ = view + ... + ... def update(self): + ... pass + ... + ... def render(self): + ... return u'
%s
' %self.message + +The ``update()`` method is executed during phase one. Since no state needs to +be calculated and no data is modified by this simple content provider, it is +an empty implementation. The ``render()`` method implements phase 2 of the +process. We can now instantiate the content provider (manually) and render it: + + >>> box = MessageBox(None, None, None) + >>> box.render() + u'
My Message
' + +Since our content provider did not require the context, request or view to +create its HTML content, we were able to pass trivial dummy values into the +constructor. Also note that the provider must have a parent (using the +``__parent__`` attribute) specified at all times. The parent must be the view +the provider appears in. + +I agree, this functionally does not seem very useful now. The constructor and +the ``update()`` method seem useless and the returned content is totally +static. However, we implemented a contract for content providers that other +code can rely on. Content providers are (commonly) instantiated using the +context, request and view they appear in and are required to always generate +its HTML using those three components. + + +Two-Phased Content Providers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let's now have a look at a content provider that actively uses the two-phase +rendering process. The simpler scenario is the case where the content provider +updates a content component without affecting anything else. So let's create a +content component to be updated, + + >>> class Article(object): + ... title = u'initial' + >>> article = Article() + +and the content provider that is updating the title: + + >>> class ChangeTitle(object): + ... zope.interface.implements(interfaces.IContentProvider) + ... zope.component.adapts(zope.interface.Interface, + ... browser.IDefaultBrowserLayer, + ... zope.interface.Interface) + ... fieldName = 'ChangeTitle.title' + ... + ... def __init__(self, context, request, view): + ... self.__parent__ = view + ... self.context, self.request = context, request + ... + ... def update(self): + ... if self.fieldName in self.request: + ... self.context.title = self.request[self.fieldName] + ... + ... def render(self): + ... return u'' % (self.fieldName, + ... self.context.title) + +Using a request, let's now instantiate the content provider and go through the +two-phase rendering process: + + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + >>> changer = ChangeTitle(article, request, None) + >>> changer.update() + >>> changer.render() + u'' + +Let's now enter a new title and render the provider: + + >>> request = TestRequest(form={'ChangeTitle.title': u'new title'}) + >>> changer = ChangeTitle(article, request, None) + >>> changer.update() + >>> changer.render() + u'' + >>> article.title + u'new title' + +So this was easy. Let's now look at a case where one content provider's update +influences the content of another. Let's say we have a content provider that +displays the article's title: + + >>> class ViewTitle(object): + ... zope.interface.implements(interfaces.IContentProvider) + ... zope.component.adapts(zope.interface.Interface, + ... browser.IDefaultBrowserLayer, + ... zope.interface.Interface) + ... + ... def __init__(self, context, request, view): + ... self.context, self.__parent__ = context, view + ... + ... def update(self): + ... pass + ... + ... def render(self): + ... return u'

Title: %s

' % self.context.title + +Let's now say that the `ShowTitle` content provider is shown on a page +*before* the `ChangeTitle` content provider. If we do the full rendering +process for each provider in sequence, we get the wrong result: + + >>> request = TestRequest(form={'ChangeTitle.title': u'newer title'}) + + >>> viewer = ViewTitle(article, request, None) + >>> viewer.update() + >>> viewer.render() + u'

Title: new title

' + + >>> changer = ChangeTitle(article, request, None) + >>> changer.update() + >>> changer.render() + u'' + +So the correct way of doing this is to first complete phase 1 (update) for all +providers, before executing phase 2 (render): + + >>> request = TestRequest(form={'ChangeTitle.title': u'newest title'}) + + >>> viewer = ViewTitle(article, request, None) + >>> changer = ChangeTitle(article, request, None) + + >>> viewer.update() + >>> changer.update() + + >>> viewer.render() + u'

Title: newest title

' + + >>> changer.render() + u'' + + +``UpdateNotCalled`` Errors +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since calling ``update()`` before any other method that mutates the provider +or any other data is so important to the correct functioning of the API, the +developer has the choice to raise the ``UpdateNotCalled`` error, if any method +is called before ``update()`` (with exception of the constructor): + + >>> class InfoBox(object): + ... zope.interface.implements(interfaces.IContentProvider) + ... zope.component.adapts(zope.interface.Interface, + ... browser.IDefaultBrowserLayer, + ... zope.interface.Interface) + ... + ... def __init__(self, context, request, view): + ... self.__parent__ = view + ... self.__updated = False + ... + ... def update(self): + ... self.__updated = True + ... + ... def render(self): + ... if not self.__updated: + ... raise interfaces.UpdateNotCalled + ... return u'
Some information
' + + >>> info = InfoBox(None, None, None) + + >>> info.render() + Traceback (most recent call last): + ... + UpdateNotCalled: ``update()`` was not called yet. + + >>> info.update() + + >>> info.render() + u'
Some information
' + + +The TALES ``provider`` Expression +--------------------------------- + +The ``provider`` expression will look up the name of the content provider, +call it and return the HTML content. The first step, however, will be to +register our content provider with the component architecture: + + >>> zope.component.provideAdapter(MessageBox, name='mypage.MessageBox') + +The content provider must be registered by name, since the TALES expression +uses the name to look up the provider at run time. + +Let's now create a view using a page template: + + >>> import os, tempfile + >>> temp_dir = tempfile.mkdtemp() + >>> templateFileName = os.path.join(temp_dir, 'template.pt') + >>> open(templateFileName, 'w').write(''' + ... + ... + ...

My Web Page

+ ...
+ ... + ...
+ ...
+ ... Content here + ...
+ ... + ... + ... ''') + +As you can see, we exprect the ``provider`` expression to simply look up the +content provider and insert the HTML content at this place. + +Next we register the template as a view (browser page) for all objects: + + >>> from zope.browserpage.simpleviewclass import SimpleViewClass + >>> FrontPage = SimpleViewClass(templateFileName, name='main.html') + + >>> zope.component.provideAdapter( + ... FrontPage, + ... (zope.interface.Interface, browser.IDefaultBrowserLayer), + ... zope.interface.Interface, + ... name='main.html') + +Let's create a content object that can be viewed: + + >>> class Content(object): + ... zope.interface.implements(zope.interface.Interface) + + >>> content = Content() + +Finally we look up the view and render it. Note that a +BeforeUpdateEvent is fired - this event should always be fired before +any contentprovider is updated. + + >>> from zope.publisher.browser import TestRequest + >>> events = [] + >>> zope.component.provideHandler(events.append, (None, )) + >>> request = TestRequest() + + >>> view = zope.component.getMultiAdapter((content, request), + ... name='main.html') + >>> print view().strip() + + +

My Web Page

+
+
My Message
+
+
+ Content here +
+ + + + >>> events + [] + +The event holds the provider and the request. + + >>> events[0].request + + >>> events[0].object + + +Failure to lookup a Content Provider +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the name is not found, an error is raised. To demonstrate this behavior +let's create another template: + + >>> errorFileName = os.path.join(temp_dir, 'error.pt') + >>> open(errorFileName, 'w').write(''' + ... + ... + ... + ... + ... + ... ''') + + >>> ErrorPage = SimpleViewClass(errorFileName, name='error.html') + >>> zope.component.provideAdapter( + ... ErrorPage, + ... (zope.interface.Interface, browser.IDefaultBrowserLayer), + ... zope.interface.Interface, + ... name='main.html') + + >>> errorview = zope.component.getMultiAdapter((content, request), + ... name='main.html') + >>> print errorview() + Traceback (most recent call last): + ... + ContentProviderLookupError: mypage.UnknownName + + +Additional Data from TAL +~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``provider`` expression allows also for transferring data from the TAL +context into the content provider. This is accomplished by having the content +provider implement an interface that specifies the attributes and provides +``ITALNamespaceData``: + + >>> import zope.schema + >>> class IMessageText(zope.interface.Interface): + ... message = zope.schema.Text(title=u'Text of the message box') + + >>> zope.interface.directlyProvides(IMessageText, + ... interfaces.ITALNamespaceData) + +Now the message box can receive its text from the TAL environment: + + >>> class DynamicMessageBox(MessageBox): + ... zope.interface.implements(IMessageText) + + >>> zope.component.provideAdapter( + ... DynamicMessageBox, provides=interfaces.IContentProvider, + ... name='mypage.DynamicMessageBox') + +We are now updating our original template to provide the message text: + + >>> open(templateFileName, 'w').write(''' + ... + ... + ...

My Web Page

+ ...
+ ... + ... + ...
+ ...
+ ... Content here + ...
+ ... + ... + ... ''') + +Now we should get two message boxes with different text: + + >>> print view().strip() + + +

My Web Page

+
+
Hello World!
+
Hello World again!
+
+
+ Content here +
+ + + +Finally, a content provider can also implement several ``ITALNamespaceData``: + + >>> class IMessageType(zope.interface.Interface): + ... type = zope.schema.TextLine(title=u'The type of the message box') + + >>> zope.interface.directlyProvides(IMessageType, + ... interfaces.ITALNamespaceData) + +We'll change our message box content provider implementation a bit, so the new +information is used: + + >>> class BetterDynamicMessageBox(DynamicMessageBox): + ... zope.interface.implements(IMessageType) + ... type = None + ... + ... def render(self): + ... return u'
%s
' %(self.type, self.message) + + >>> zope.component.provideAdapter( + ... BetterDynamicMessageBox, provides=interfaces.IContentProvider, + ... name='mypage.MessageBox') + +Of course, we also have to make our tempalte a little bit more dynamic as +well: + + >>> open(templateFileName, 'w').write(''' + ... + ... + ...

My Web Page

+ ...
+ ... + ... + ...
+ ...
+ ... Content here + ...
+ ... + ... + ... ''') + +Now we should get two message boxes with different text and types: + + >>> print view().strip() + + +

My Web Page

+
+
Hello World!
+
Hello World again!
+
+
+ Content here +
+ + + + +Base class +---------- + +The ``zope.contentprovider.provider`` module provides an useful base +class for implementing content providers. It has all boilerplate code +and it's only required to override the ``render`` method to make it +work: + + >>> from zope.contentprovider.provider import ContentProviderBase + >>> class MyProvider(ContentProviderBase): + ... def render(self, *args, **kwargs): + ... return 'Hi there' + + >>> provider = MyProvider(None, None, None) + >>> interfaces.IContentProvider.providedBy(provider) + True + + >>> provider.update() + >>> print provider.render() + Hi there + +Note, that it can't be used as is, without providing the ``render`` method: + + >>> bad = ContentProviderBase(None, None, None) + >>> bad.update() + >>> print bad.render() + Traceback (most recent call last): + ... + NotImplementedError: ``render`` method must be implemented by subclass + +You can add the update logic into the ``update`` method as with any content +provider and you can implement more complex rendering patterns, based on +templates, using this ContentProviderBase class as a base. + + +You might also want to look at the ``zope.viewlet`` package for a more +featureful API. + +Let's remove all temporary data we created during this README. + + >>> import shutil + >>> shutil.rmtree(temp_dir) diff -Nru zope3-3.4.0/src/zope/contentprovider/tales.py zope3-3.5~bzr18/src/zope/contentprovider/tales.py --- zope3-3.4.0/src/zope/contentprovider/tales.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contentprovider/tales.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,80 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Provider TALES expression + +$Id: tales.py 112004 2010-05-05 17:54:28Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +import zope.interface +import zope.schema +import zope.event +from zope.location.interfaces import ILocation +from zope.tales import expressions + +from zope.contentprovider import interfaces + +def addTALNamespaceData(provider, context): + """Add the requested TAL attributes to the provider""" + data = {} + + for interface in zope.interface.providedBy(provider): + if interfaces.ITALNamespaceData.providedBy(interface): + for name, field in zope.schema.getFields(interface).items(): + data[name] = context.vars.get(name, field.default) + + provider.__dict__.update(data) + + +class TALESProviderExpression(expressions.StringExpr): + """Collect content provider via a TAL namespace. + + Note that this implementation of the TALES `provider` namespace does not + work with interdependent content providers, since each content-provider's + stage one call is made just before the second stage is executed. If you + want to implement interdependent content providers, you need to consider a + TAL-independent view implementation that will complete all content + providers' stage one before rendering any of them. + """ + + zope.interface.implements(interfaces.ITALESProviderExpression) + + def __call__(self, econtext): + name = super(TALESProviderExpression, self).__call__(econtext) + context = econtext.vars['context'] + request = econtext.vars['request'] + view = econtext.vars['view'] + + # Try to look up the provider. + provider = zope.component.queryMultiAdapter( + (context, request, view), interfaces.IContentProvider, name) + + # Provide a useful error message, if the provider was not found. + if provider is None: + raise interfaces.ContentProviderLookupError(name) + + # add the __name__ attribute if it implements ILocation + if ILocation.providedBy(provider): + provider.__name__ = name + + # Insert the data gotten from the context + addTALNamespaceData(provider, econtext) + + # Stage 1: Do the state update. + zope.event.notify(interfaces.BeforeUpdateEvent(provider, request)) + provider.update() + + # Stage 2: Render the HTML content. + return provider.render() diff -Nru zope3-3.4.0/src/zope/contentprovider/tests.py zope3-3.5~bzr18/src/zope/contentprovider/tests.py --- zope3-3.4.0/src/zope/contentprovider/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contentprovider/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,62 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Content provider tests + +$Id: tests.py 112004 2010-05-05 17:54:28Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import doctest +import os.path +import unittest + +from zope.component import eventtesting +from zope.testing import cleanup + +counter = 0 +mtime_func = None + +def setUp(test): + cleanup.setUp() + eventtesting.setUp() + + from zope.browserpage.metaconfigure import registerType + from zope.contentprovider import tales + registerType('provider', tales.TALESProviderExpression) + + # Make sure we are always reloading page template files ;-) + global mtime_func + mtime_func = os.path.getmtime + def number(x): + global counter + counter += 1 + return counter + os.path.getmtime = number + + +def tearDown(test): + cleanup.tearDown() + os.path.getmtime = mtime_func + global counter + counter = 0 + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('README.txt', + setUp=setUp, tearDown=tearDown, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, + globs = {'__file__': os.path.join( + os.path.dirname(__file__), 'README.txt')} + ), + )) diff -Nru zope3-3.4.0/src/zope/contenttype/__init__.py zope3-3.5~bzr18/src/zope/contenttype/__init__.py --- zope3-3.4.0/src/zope/contenttype/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contenttype/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,113 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 +# +############################################################################## + +"""A utility module for content-type handling. + +$Id: __init__.py 110128 2010-03-24 01:51:17Z tseaver $ +""" + +import string +import re +import os.path +import mimetypes + + +find_binary = re.compile('[\0-\7]').search + + +def text_type(s): + """Given an unnamed piece of text, try to guess its content type. + + Detects HTML, XML, and plain text. Returns a MIME type string + such as 'text/html'. + """ + # at least the maximum length of any tags we look for + max_tags = 14 + s = s.strip()[:max_tags] + s2 = s.lower() + + if len(s) == max_tags: + + if s2.startswith(''): + return 'text/html' + + if s2.startswith('@,;:\"/?=\\\\]+$").match + return _token_match(string) + +def _unescape(string): + assert string[0] == '"' + assert string[-1] == '"' + string = string[1:-1] + if "\\" in string: + string = re.sub(r"\\(.)", r"\1", string) + return string + + +def join((major, minor, params)): + pstr = "" + try: + params.items + except AttributeError: + pass + else: + params = params.items() + # ensure a predictable order: + params.sort() + for name, value in params: + pstr += ";%s=%s" % (name, _escape(value)) + return "%s/%s%s" % (major, minor, pstr) + +def _escape(string): + try: + return _check_token(string) + except ValueError: + # '\\' must be first + for c in '\\"\n\r': + string = string.replace(c, "\\" + c) + return '"%s"' % string diff -Nru zope3-3.4.0/src/zope/contenttype/tests/mime.types-1 zope3-3.5~bzr18/src/zope/contenttype/tests/mime.types-1 --- zope3-3.4.0/src/zope/contenttype/tests/mime.types-1 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contenttype/tests/mime.types-1 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,5 @@ +# This is a sample mime.types file. +# It contains a single bogus MIME type and extention for testing +# purposes. It is not loaded during normal Zope operation. + +text/x-vnd.zope.test-mime-type-1 ztmt-1 diff -Nru zope3-3.4.0/src/zope/contenttype/tests/mime.types-2 zope3-3.5~bzr18/src/zope/contenttype/tests/mime.types-2 --- zope3-3.4.0/src/zope/contenttype/tests/mime.types-2 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contenttype/tests/mime.types-2 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,5 @@ +# This is a sample mime.types file. +# It contains a single bogus MIME type and extention for testing +# purposes. It is not loaded during normal Zope operation. + +text/x-vnd.zope.test-mime-type-2 ztmt-2 diff -Nru zope3-3.4.0/src/zope/contenttype/tests/testContentTypes.py zope3-3.5~bzr18/src/zope/contenttype/tests/testContentTypes.py --- zope3-3.4.0/src/zope/contenttype/tests/testContentTypes.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contenttype/tests/testContentTypes.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,94 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 of the contenttypes extension mechanism. + +$Id: testContentTypes.py 110128 2010-03-24 01:51:17Z tseaver $ +""" +import unittest + +class ContentTypesTestCase(unittest.TestCase): + + def setUp(self): + import mimetypes + mimetypes.init() + self._old_state = mimetypes.__dict__.copy() + + def tearDown(self): + import mimetypes + mimetypes.__dict__.clear() + mimetypes.__dict__.update(self._old_state) + + def _check_types_count(self, delta): + import mimetypes + self.assertEqual(len(mimetypes.types_map), + len(self._old_state["types_map"]) + delta) + + def _getFilename(self, name): + import os.path + here = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(here, name) + + def test_add_one_file(self): + from zope.contenttype import add_files + from zope.contenttype import guess_content_type + filename = self._getFilename('mime.types-1') + add_files([filename]) + ctype, encoding = guess_content_type("foo.ztmt-1") + self.assert_(encoding is None) + self.assertEqual(ctype, "text/x-vnd.zope.test-mime-type-1") + ctype, encoding = guess_content_type("foo.ztmt-1.gz") + self.assertEqual(encoding, "gzip") + self.assertEqual(ctype, "text/x-vnd.zope.test-mime-type-1") + self._check_types_count(1) + + def test_add_two_files(self): + from zope.contenttype import add_files + from zope.contenttype import guess_content_type + filename1 = self._getFilename('mime.types-1') + filename2 = self._getFilename('mime.types-2') + add_files([filename1, filename2]) + ctype, encoding = guess_content_type("foo.ztmt-1") + self.assert_(encoding is None) + self.assertEqual(ctype, "text/x-vnd.zope.test-mime-type-1") + ctype, encoding = guess_content_type("foo.ztmt-2") + self.assert_(encoding is None) + self.assertEqual(ctype, "text/x-vnd.zope.test-mime-type-2") + self._check_types_count(2) + + def test_text_type(self): + HTML = 'hello world' + from zope.contenttype import text_type + self.assertEqual(text_type(HTML), + 'text/html') + self.assertEqual(text_type(''), + 'text/xml') + self.assertEqual(text_type(''), + 'text/plain') + self.assertEqual(text_type('foo bar'), + 'text/plain') + self.assertEqual(text_type(''), + 'text/html') + # See https://bugs.launchpad.net/bugs/487998 + self.assertEqual(text_type(' ' * 14 + HTML), + 'text/html') + self.assertEqual(text_type(' ' * 14 + 'abc'), + 'text/plain') + self.assertEqual(text_type(' ' * 14), + 'text/plain') + + +def test_suite(): + return unittest.makeSuite(ContentTypesTestCase) diff -Nru zope3-3.4.0/src/zope/contenttype/tests/test_parse.py zope3-3.5~bzr18/src/zope/contenttype/tests/test_parse.py --- zope3-3.4.0/src/zope/contenttype/tests/test_parse.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/contenttype/tests/test_parse.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,199 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 of the contenttype helpers. + +""" +__docformat__ = "reStructuredText" + +import re +import unittest + +from zope.contenttype import parse + + +class ParseOrderedTestCase(unittest.TestCase): + + empty_params = [] + + def setUp(self): + self.parse = parse.parseOrdered + + def oneParam(self, name, value): + return [(name, value)] + + def test_without_params(self): + self.assertEqual(self.parse("text/plain"), + ("text", "plain", self.empty_params)) + self.assertEqual(self.parse("TEXT/PLAIN"), + ("text", "plain", self.empty_params)) + self.assertEqual(self.parse("TeXt / PlaIN"), + ("text", "plain", self.empty_params)) + self.assertEqual(self.parse("text / vnd.wap.wml"), + ("text", "vnd.wap.wml", self.empty_params)) + + def test_with_empty_params(self): + self.assertEqual(self.parse("text/plain ; "), + ("text", "plain", self.empty_params)) + self.assertEqual(self.parse("TEXT/PLAIN ; "), + ("text", "plain", self.empty_params)) + self.assertEqual(self.parse("TeXt / PlaIN ; "), + ("text", "plain", self.empty_params)) + + def test_bad_tokens(self): + self.assertRaises(ValueError, + self.parse, "text stuff/plain") + self.assertRaises(ValueError, + self.parse, "text/plain stuff") + self.assertRaises(ValueError, + self.parse, "text/plain;some stuff=foo") + self.assertRaises(ValueError, + self.parse, "text/plain;a=b;c d=e") + + def test_missing_parts(self): + self.assertRaises(ValueError, + self.parse, "text ; params") + self.assertRaises(ValueError, + self.parse, "text/ ; params") + self.assertRaises(ValueError, + self.parse, "/plain ; params") + self.assertRaises(ValueError, + self.parse, "text/plain ; params") + self.assertRaises(ValueError, + self.parse, "text/plain ; params=") + self.assertRaises(ValueError, + self.parse, "text/plain ; =params") + self.assertRaises(ValueError, + self.parse, "text/plain ; a=b; params") + self.assertRaises(ValueError, + self.parse, "text/plain ; a=b; params=") + self.assertRaises(ValueError, + self.parse, "text/plain ; a=b; =params") + + def test_single_parameter(self): + self.assertEqual(self.parse("text/plain;charset=UTF-8"), + ("text", "plain", self.oneParam("charset", "UTF-8"))) + self.assertEqual(self.parse("text/plain ;\tcharset = UTF-8"), + ("text", "plain", self.oneParam("charset", "UTF-8"))) + # quoted-string parameter values + self.assertEqual(self.parse('text/plain;charset="UTF-8"'), + ("text", "plain", self.oneParam("charset", "UTF-8"))) + self.assertEqual(self.parse('text/plain ;\tcharset = "UTF-8"'), + ("text", "plain", self.oneParam("charset", "UTF-8"))) + + def test_multiple_parameters(self): + self.assertEqual( + self.parse("text/plain;charset=utf-8;format=flowed"), + ("text", "plain", [("charset", "utf-8"), ("format", "flowed")])) + self.assertEqual( + self.parse('text/plain;charset=utf-8;format="flowed"'), + ("text", "plain", [("charset", "utf-8"), ("format", "flowed")])) + + def test_quoted_strings(self): + p = self.oneParam("c", " This [] has <> ? other () chars\t") + self.assertEqual( + self.parse('a/b;c= " This [] has <> ? other () chars\t" '), + ("a", "b", p)) + self.assertEqual( + self.parse('a/b;c=""'), + ("a", "b", self.oneParam("c", ""))) + self.assertEqual( + self.parse(r'a/b;c="\\\""'), + ("a", "b", self.oneParam("c", r'\"'))) + +class ParseTestCase(ParseOrderedTestCase): + + empty_params = {} + + def setUp(self): + self.parse = parse.parse + + def oneParam(self, name, value): + return {name: value} + + def test_multiple_parameters(self): + self.assertEqual( + self.parse("text/plain;charset=utf-8;format=flowed"), + ("text", "plain", {"charset": "utf-8", "format": "flowed"})) + self.assertEqual( + self.parse('text/plain;charset=utf-8;format="flowed"'), + ("text", "plain", {"charset": "utf-8", "format": "flowed"})) + + +class JoinTestCase(unittest.TestCase): + + def test_without_params(self): + self.assertEqual(parse.join(("text", "plain", [])), + "text/plain") + self.assertEqual(parse.join(("text", "plain", {})), + "text/plain") + + def test_single_token_param(self): + self.assertEqual( + parse.join(("text", "plain", [("charset", "UTF-8")])), + "text/plain;charset=UTF-8") + self.assertEqual( + parse.join(("text", "plain", {"charset": "UTF-8"})), + "text/plain;charset=UTF-8") + + def test_multi_params_list_maintains_order(self): + # multiple parameters given as a list maintain order: + self.assertEqual( + parse.join(("text", "plain", + [("charset", "UTF-8"), ("format", "flowed")])), + "text/plain;charset=UTF-8;format=flowed") + self.assertEqual( + parse.join(("text", "plain", + [("format", "flowed"), ("charset", "UTF-8")])), + "text/plain;format=flowed;charset=UTF-8") + + def test_multi_params_dict_sorted_order(self): + # multiple parameters given as a dict are sorted by param name: + self.assertEqual( + parse.join(("text", "plain", + {"charset": "UTF-8", "format": "flowed"})), + "text/plain;charset=UTF-8;format=flowed") + + def test_params_list_quoted(self): + # parameter values are quoted automatically: + self.assertEqual(parse.join(("a", "b", [("c", "")])), + 'a/b;c=""') + self.assertEqual(parse.join(("a", "b", [("c", "ab cd")])), + 'a/b;c="ab cd"') + self.assertEqual(parse.join(("a", "b", [("c", " \t")])), + 'a/b;c=" \t"') + self.assertEqual(parse.join(("a", "b", [("c", '"')])), + r'a/b;c="\""') + self.assertEqual(parse.join(("a", "b", [("c", "\n")])), + 'a/b;c="\\\n"') + + def test_params_dict_quoted(self): + # parameter values are quoted automatically: + self.assertEqual(parse.join(("a", "b", {"c": ""})), + 'a/b;c=""') + self.assertEqual(parse.join(("a", "b", {"c": "ab cd"})), + 'a/b;c="ab cd"') + self.assertEqual(parse.join(("a", "b", {"c": " \t"})), + 'a/b;c=" \t"') + self.assertEqual(parse.join(("a", "b", {"c": '"'})), + r'a/b;c="\""') + self.assertEqual(parse.join(("a", "b", {"c": "\n"})), + 'a/b;c="\\\n"') + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(ParseOrderedTestCase)) + suite.addTest(unittest.makeSuite(ParseTestCase)) + suite.addTest(unittest.makeSuite(JoinTestCase)) + return suite diff -Nru zope3-3.4.0/src/zope/copy/__init__.py zope3-3.5~bzr18/src/zope/copy/__init__.py --- zope3-3.4.0/src/zope/copy/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copy/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,97 @@ +############################################################################## +# +# Copyright (c) 2009 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. +# +############################################################################## +""" +$Id: __init__.py 96250 2009-02-08 16:15:43Z nadako $ +""" +import tempfile +import cPickle + +from zope.copy import interfaces + +def clone(obj): + """Clone an object by pickling and unpickling it""" + tmp = tempfile.TemporaryFile() + persistent = CopyPersistent(obj) + + # Pickle the object to a temporary file + pickler = cPickle.Pickler(tmp, 2) + pickler.persistent_id = persistent.id + pickler.dump(obj) + + # Now load it back + tmp.seek(0) + unpickler = cPickle.Unpickler(tmp) + unpickler.persistent_load = persistent.load + + res = unpickler.load() + # run the registered cleanups + def convert(obj): + return unpickler.memo[pickler.memo[id(obj)][0]] + for call in persistent.registered: + call(convert) + return res + +def copy(obj): + """Clone an object, clearing the __name__ and __parent__ attribute + values of the copy.""" + res = clone(obj) + if getattr(res, '__parent__', None) is not None: + try: + res.__parent__ = None + except AttributeError: + pass + if getattr(res, '__name__', None) is not None: + try: + res.__name__ = None + except AttributeError: + pass + return res + +class CopyPersistent(object): + """A helper class providing the persisntent_id and persistent_load + functions for pickling and unpickling respectively. + + It uses the adaptation to ICopyHook to allow control over object + copying. See README.txt for more information on that mechanism. + """ + + def __init__(self, obj): + self.toplevel = obj + self.pids_by_id = {} + self.others_by_pid = {} + self.load = self.others_by_pid.get + self.registered = [] + + def id(self, obj): + hook = interfaces.ICopyHook(obj, None) + if hook is not None: + oid = id(obj) + if oid in self.pids_by_id: + return self.pids_by_id[oid] + try: + res = hook(self.toplevel, self.registered.append) + except interfaces.ResumeCopy: + pass + else: + pid = len(self.others_by_pid) + + # The following is needed to overcome a bug + # in pickle.py. The pickle checks the boolean value + # of the id, rather than whether it is None. + pid += 1 + + self.pids_by_id[oid] = pid + self.others_by_pid[pid] = res + return pid + return None diff -Nru zope3-3.4.0/src/zope/copy/interfaces.py zope3-3.5~bzr18/src/zope/copy/interfaces.py --- zope3-3.4.0/src/zope/copy/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copy/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2009 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. +# +############################################################################## +""" +$Id: interfaces.py 96280 2009-02-08 22:27:11Z nadako $ +""" +import zope.interface + +class ResumeCopy(Exception): + """Don't use the hook, resume the copy. + + This is a special exception, raised from the copy hook to signal + copier that it should continue copying the object recursively. + + See ICopyHook.__call__ method documentation. + """ + +class ICopyHook(zope.interface.Interface): + """An adapter to an object that is being copied""" + + def __call__(toplevel, register): + """Given the top-level object that is being copied, return the + version of the adapted object that should be used in the new copy. + + Raising ResumeCopy means that you are foregoing the hook: the + adapted object will continue to be recursively copied as usual. + + If you need to have a post-copy actions executed, register a + callable with ``register``. This callable must take a single + argument: a callable that, given an object from the original, + returns the equivalent in the copy. + + See README.txt for more explanation. + """ diff -Nru zope3-3.4.0/src/zope/copy/README.txt zope3-3.5~bzr18/src/zope/copy/README.txt --- zope3-3.4.0/src/zope/copy/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copy/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,321 @@ +============== +Object copying +============== + +This package provides a pluggable way to copy persistent objects. It +was once extracted from the zc.copy package to contain much less +dependencies. In fact, we only depend on zope.interface to provide +pluggability. + +The package provides a ``clone`` function that does the object cloning +and the ``copy`` wrapper that sets __parent__ and __name__ attributes +of object's copy to None. This is useful, when working with Zope's +located objects (see zope.location package). The ``copy`` function +actually calls the ``clone`` function so we'll use the first one in +the examples below. We'll also look a bit at their differences in the +end of this document. + +The ``clone`` function (and thus the ``copy`` function that wraps it) +uses pickling to copy the object and all its subobjects recursively. +As each object and subobject is pickled, the function tries to adapt it +to ``zope.copy.interfaces.ICopyHook``. If a copy hook is found, +the recursive copy is halted. The hook is called with two values: the +main, top-level object that is being copied; and a callable that supports +registering functions to be called after the copy is made. The copy hook +should return the exact object or subobject that should be used at this +point in the copy, or raise ``zope.copy.interfaces.ResumeCopy`` +exception to resume copying the object or subobject recursively after +all. + +Note that we use zope's component architecture provided by the +``zope.component`` package in this document, but the +``zope.copy`` package itself doesn't use or depend on it, so +you can provide another adaptation mechanism as described in zope.interface's +adapter documentation. + +Simple hooks +------------ + +First let's examine a simple use. A hook is to support the use case of +resetting the state of data that should be changed in a copy -- for +instance, a log, or freezing or versioning data. The canonical way to +do this is by storing the changable data on a special sub-object of the +object that is to be copied. We'll look at a simple case of a subobject +that should be converted to None when it is copied -- the way that the +zc.freeze copier hook works. Also see the zc.objectlog copier module +for a similar example. + +So, here is a simple object that stores a boolean on a special object. + + >>> class Demo(object): + ... _frozen = None + ... def isFrozen(self): + ... return self._frozen is not None + ... def freeze(self): + ... self._frozen = Data() + ... + >>> class Data(object): + ... pass + ... + +Here's what happens if we copy one of these objects without a copy hook. + + >>> original = Demo() + >>> original.isFrozen() + False + >>> original.freeze() + >>> original.isFrozen() + True + >>> import zope.copy + >>> copy = zope.copy.copy(original) + >>> copy is original + False + >>> copy.isFrozen() + True + +Now let's make a super-simple copy hook that always returns None, no +matter what the top-level object being copied is. We'll register it and +make another copy. + + >>> import zope.component + >>> import zope.interface + >>> import zope.copy.interfaces + >>> def _factory(obj, register): + ... return None + >>> @zope.component.adapter(Data) + ... @zope.interface.implementer(zope.copy.interfaces.ICopyHook) + ... def data_copyfactory(obj): + ... return _factory + ... + + >>> zope.component.provideAdapter(data_copyfactory) + >>> copy2 = zope.copy.copy(original) + >>> copy2 is original + False + >>> copy2.isFrozen() + False + +Much better. + +Post-copy functions +------------------- + +Now, let's look at the registration function that the hook can use. It +is useful for resetting objects within the new copy -- for instance, back +references such as __parent__ pointers. This is used concretely in the +zc.objectlog.copier module; we will come up with a similar but artificial +example here. + +Imagine an object with a subobject that is "located" (i.e., zope.location) on +the parent and should be replaced whenever the main object is copied. + + >>> import zope.location.location + >>> class Subobject(zope.location.location.Location): + ... def __init__(self): + ... self.counter = 0 + ... def __call__(self): + ... res = self.counter + ... self.counter += 1 + ... return res + ... + >>> o = zope.location.location.Location() + >>> s = Subobject() + >>> o.subobject = s + >>> zope.location.location.locate(s, o, 'subobject') + >>> s.__parent__ is o + True + >>> o.subobject() + 0 + >>> o.subobject() + 1 + >>> o.subobject() + 2 + +Without an ICopyHook, this will simply duplicate the subobject, with correct +new pointers. + + >>> c = zope.copy.copy(o) + >>> c.subobject.__parent__ is c + True + +Note that the subobject has also copied state. + + >>> c.subobject() + 3 + >>> o.subobject() + 3 + +Our goal will be to make the counters restart when they are copied. We'll do +that with a copy hook. + +This copy hook is different: it provides an object to replace the old object, +but then it needs to set it up further after the copy is made. This is +accomplished by registering a callable, ``reparent`` here, that sets up the +__parent__. The callable is passed a function that can translate something +from the original object into the equivalent on the new object. We use this +to find the new parent, so we can set it. + + >>> import zope.component + >>> import zope.interface + >>> import zope.copy.interfaces + >>> @zope.component.adapter(Subobject) + ... @zope.interface.implementer(zope.copy.interfaces.ICopyHook) + ... def subobject_copyfactory(original): + ... def factory(obj, register): + ... obj = Subobject() + ... def reparent(translate): + ... obj.__parent__ = translate(original.__parent__) + ... register(reparent) + ... return obj + ... return factory + ... + >>> zope.component.provideAdapter(subobject_copyfactory) + +Now when we copy, the new subobject will have the correct, revised __parent__, +but will be otherwise reset (here, just the counter) + + >>> c = zope.copy.copy(o) + >>> c.subobject.__parent__ is c + True + >>> c.subobject() + 0 + >>> o.subobject() + 4 + +Resuming recursive copy +----------------------- + +One thing we didn't examine yet is the use of ResumeCopy exception in +the copy hooks. For example, when copying located objects we don't want +to copy referenced subobjects that are not located in the object that +is being copied. Imagine, we have a content object that has an image object, +referenced by the ``cover`` attribute, but located in an independent +place. + + >>> root = zope.location.location.Location() + + >>> content = zope.location.location.Location() + >>> zope.location.location.locate(content, root, 'content') + + >>> image = zope.location.location.Location() + >>> zope.location.location.locate(image, root, 'image.jpg') + + >>> content.cover = image + +Without any hooks, the image object will be cloned as well: + + >>> new = zope.copy.copy(content) + >>> new.cover is image + False + +That's not what we'd expect though, so, let's provide a copy hook +to deal with that. The copy hook for this case is provided by zope.location +package, but we'll create one from scratch as we want to check out the +usage of the ResumeCopy. + + >>> @zope.component.adapter(zope.location.interfaces.ILocation) + ... @zope.interface.implementer(zope.copy.interfaces.ICopyHook) + ... def location_copyfactory(obj): + ... def factory(location, register): + ... if not zope.location.location.inside(obj, location): + ... return obj + ... raise zope.copy.interfaces.ResumeCopy + ... return factory + ... + >>> zope.component.provideAdapter(location_copyfactory) + +This hook returns objects as they are if they are not located inside +object that's being copied, or raises ResumeCopy to signal that the +recursive copy should be continued and used for the object. + + >>> new = zope.copy.copy(content) + >>> new.cover is image + True + +Much better :-) + +``clone`` vs ``copy`` +--------------------- + +As we stated before, there's two functions that is used for copying +objects. The ``clone`` - that does the job, and its wrapper, ``copy`` +that calls ``clone`` and then clears copy's __parent__ and __name__ +attribute values. + +Let's create a location object with __name__ and __parent__ set. + + >>> root = zope.location.location.Location() + >>> folder = zope.location.location.Location() + >>> folder.__name__ = 'files' + >>> folder.__parent__ = root + +The ``clone`` function will leave those attributes as is. Note that the +referenced __parent__ won't be cloned, as we registered a hook for locations +in the previous section. + + >>> folder_clone = zope.copy.clone(folder) + >>> folder_clone.__parent__ is root + True + >>> folder_clone.__name__ == 'files' + True + +However, the ``copy`` function will reset those attributes to None, as +we will probably want to place our object into another container with +another name. + + >>> folder_clone = zope.copy.copy(folder) + >>> folder_clone.__parent__ is None + True + >>> folder_clone.__name__ is None + True + +Notice, that if your object doesn't have __parent__ and __name__ +attributes at all, or these attributes could'nt be got or set because of +some protections (as with zope.security's proxies, for example), you still +can use the ``copy`` function, because it works for objects that don't +have those attributes. + +It won't set them if original object doesn't have them: + + >>> class Something(object): + ... pass + + >>> s = Something() + >>> s_copy = zope.copy.copy(s) + >>> s_copy.__parent__ + Traceback (most recent call last): + ... + AttributeError: ... + >>> s_copy.__name__ + Traceback (most recent call last): + ... + AttributeError: ... + +And it won't fail if original object has them but doesn't allow to set +them. + + >>> root = object() + >>> class Something(object): + ... + ... @apply + ... def __name__(): + ... def fget(self): + ... return 'something' + ... def fset(self, value): + ... raise AttributeError + ... return property(fget, fset) + ... + ... @apply + ... def __parent__(): + ... def fget(self): + ... return root + ... def fset(self, value): + ... raise AttributeError + ... return property(fget, fset) + + >>> s = Something() + >>> s_copy = zope.copy.copy(s) + >>> s_copy.__parent__ is root + True + >>> s_copy.__name__ == 'something' + True diff -Nru zope3-3.4.0/src/zope/copy/tests.py zope3-3.5~bzr18/src/zope/copy/tests.py --- zope3-3.4.0/src/zope/copy/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copy/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,33 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Tests for the zope.copy package. + +$Id: tests.py 96251 2009-02-08 16:17:47Z nadako $ +""" +import unittest +from zope.testing import doctest, module + +def setUp(test): + module.setUp(test, 'zope.copy.doctest') + +def tearDown(test): + module.tearDown(test) + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('README.txt', + setUp=setUp, + tearDown=tearDown, + optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE), + )) diff -Nru zope3-3.4.0/src/zope/copypastemove/configure.zcml zope3-3.5~bzr18/src/zope/copypastemove/configure.zcml --- zope3-3.4.0/src/zope/copypastemove/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copypastemove/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/copypastemove/__init__.py zope3-3.5~bzr18/src/zope/copypastemove/__init__.py --- zope3-3.4.0/src/zope/copypastemove/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copypastemove/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,671 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Copy, Paste and Move support for content components + +$Id: __init__.py 97525 2009-03-05 11:50:36Z nadako $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +from zope.interface import implements, Invalid +from zope.exceptions import DuplicationError +from zope.component import adapts +from zope.copy import copy +from zope.event import notify +from zope.location.interfaces import ISublocations +from zope.annotation.interfaces import IAnnotations +from zope.annotation.interfaces import IAnnotations +from zope.lifecycleevent import ObjectCopiedEvent + +from zope.copypastemove.interfaces import IObjectMover +from zope.copypastemove.interfaces import IObjectCopier +from zope.copypastemove.interfaces import IContainerItemRenamer +from zope.copypastemove.interfaces import IPrincipalClipboard +from zope.copypastemove.interfaces import ItemNotFoundError + +from zope.container.sample import SampleContainer +from zope.container.interfaces import IContainer, IOrderedContainer +from zope.container.interfaces import IContained +from zope.container.interfaces import INameChooser +from zope.container.constraints import checkObject + +class ObjectMover(object): + """Adapter for moving objects between containers + + To use an object mover, pass a contained `object` to the class. + The contained `object` should implement `IContained`. It should be + contained in a container that has an adapter to `INameChooser`. + + + >>> from zope.container.contained import Contained + >>> ob = Contained() + >>> container = ExampleContainer() + >>> container[u'foo'] = ob + >>> mover = ObjectMover(ob) + + In addition to moving objects, object movers can tell you if the + object is movable: + + >>> mover.moveable() + True + + which, at least for now, they always are. A better question to + ask is whether we can move to a particular container. Right now, + we can always move to a container of the same class: + + >>> container2 = ExampleContainer() + >>> mover.moveableTo(container2) + True + >>> mover.moveableTo({}) + Traceback (most recent call last): + ... + TypeError: Container is not a valid Zope container. + + Of course, once we've decided we can move an object, we can use + the mover to do so: + + >>> mover.moveTo(container2) + u'foo' + >>> list(container) + [] + >>> list(container2) + [u'foo'] + >>> ob.__parent__ is container2 + True + + We can also specify a name: + + >>> mover.moveTo(container2, u'bar') + u'bar' + >>> list(container2) + [u'bar'] + >>> ob.__parent__ is container2 + True + >>> ob.__name__ + u'bar' + + But we may not use the same name given, if the name is already in + use: + + >>> container2[u'splat'] = 1 + >>> mover.moveTo(container2, u'splat') + u'splat_' + >>> l = list(container2) + >>> l.sort() + >>> l + [u'splat', u'splat_'] + >>> ob.__name__ + u'splat_' + + + If we try to move to an invalid container, we'll get an error: + + >>> mover.moveTo({}) + Traceback (most recent call last): + ... + TypeError: Container is not a valid Zope container. + + + Do a test for preconditions: + + >>> import zope.interface + >>> import zope.schema + >>> def preNoZ(container, name, ob): + ... "Silly precondition example" + ... if name.startswith("Z"): + ... raise zope.interface.Invalid("Invalid name.") + + >>> class I1(zope.interface.Interface): + ... def __setitem__(name, on): + ... "Add an item" + ... __setitem__.precondition = preNoZ + + >>> from zope.container.interfaces import IContainer + >>> class C1(object): + ... zope.interface.implements(I1, IContainer) + ... def __repr__(self): + ... return 'C1' + + >>> from zope.container.constraints import checkObject + >>> container3 = C1() + >>> mover.moveableTo(container3, 'ZDummy') + False + >>> mover.moveableTo(container3, 'newName') + True + + And a test for constraints: + + >>> def con1(container): + ... "silly container constraint" + ... if not hasattr(container, 'x'): + ... return False + ... return True + ... + >>> class I2(zope.interface.Interface): + ... __parent__ = zope.schema.Field(constraint=con1) + ... + >>> class constrainedObject(object): + ... zope.interface.implements(I2) + ... def __init__(self): + ... self.__name__ = 'constrainedObject' + ... + >>> cO = constrainedObject() + >>> mover2 = ObjectMover(cO) + >>> mover2.moveableTo(container) + False + >>> container.x = 1 + >>> mover2.moveableTo(container) + True + + """ + + adapts(IContained) + + implements(IObjectMover) + + def __init__(self, object): + self.context = object + self.__parent__ = object # TODO: see if we can automate this + + def moveTo(self, target, new_name=None): + """Move this object to the `target` given. + + Returns the new name within the `target` + """ + + obj = self.context + container = obj.__parent__ + + orig_name = obj.__name__ + if new_name is None: + new_name = orig_name + + checkObject(target, new_name, obj) + + if target is container and new_name == orig_name: + # Nothing to do + return + + chooser = INameChooser(target) + new_name = chooser.chooseName(new_name, obj) + + target[new_name] = obj + del container[orig_name] + return new_name + + def moveable(self): + """Returns ``True`` if the object is moveable, otherwise ``False``.""" + return True + + def moveableTo(self, target, name=None): + """Say whether the object can be moved to the given target. + + Returns ``True`` if it can be moved there. Otherwise, returns + ``False``. + """ + if name is None: + name = self.context.__name__ + try: + checkObject(target, name, self.context) + except Invalid: + return False + return True + +class ObjectCopier(object): + """Adapter for copying objects between containers + + To use an object copier, pass a contained `object` to the class. + The contained `object` should implement `IContained`. It should be + contained in a container that has an adapter to `INameChooser`. + + >>> from zope.container.contained import Contained + >>> ob = Contained() + >>> container = ExampleContainer() + >>> container[u'foo'] = ob + >>> copier = ObjectCopier(ob) + + In addition to moving objects, object copiers can tell you if the + object is movable: + + >>> copier.copyable() + True + + which, at least for now, they always are. A better question to + ask is whether we can copy to a particular container. Right now, + we can always copy to a container of the same class: + + >>> container2 = ExampleContainer() + >>> copier.copyableTo(container2) + True + >>> copier.copyableTo({}) + Traceback (most recent call last): + ... + TypeError: Container is not a valid Zope container. + + Of course, once we've decided we can copy an object, we can use + the copier to do so: + + >>> copier.copyTo(container2) + u'foo' + >>> list(container) + [u'foo'] + >>> list(container2) + [u'foo'] + >>> ob.__parent__ is container + True + >>> container2[u'foo'] is ob + False + >>> container2[u'foo'].__parent__ is container2 + True + >>> container2[u'foo'].__name__ + u'foo' + + We can also specify a name: + + >>> copier.copyTo(container2, u'bar') + u'bar' + >>> l = list(container2) + >>> l.sort() + >>> l + [u'bar', u'foo'] + + >>> ob.__parent__ is container + True + >>> container2[u'bar'] is ob + False + >>> container2[u'bar'].__parent__ is container2 + True + >>> container2[u'bar'].__name__ + u'bar' + + But we may not use the same name given, if the name is already in + use: + + >>> copier.copyTo(container2, u'bar') + u'bar_' + >>> l = list(container2) + >>> l.sort() + >>> l + [u'bar', u'bar_', u'foo'] + >>> container2[u'bar_'].__name__ + u'bar_' + + + If we try to copy to an invalid container, we'll get an error: + + >>> copier.copyTo({}) + Traceback (most recent call last): + ... + TypeError: Container is not a valid Zope container. + + Do a test for preconditions: + + >>> import zope.interface + >>> import zope.schema + >>> def preNoZ(container, name, ob): + ... "Silly precondition example" + ... if name.startswith("Z"): + ... raise zope.interface.Invalid("Invalid name.") + + >>> class I1(zope.interface.Interface): + ... def __setitem__(name, on): + ... "Add an item" + ... __setitem__.precondition = preNoZ + + >>> from zope.container.interfaces import IContainer + >>> class C1(object): + ... zope.interface.implements(I1, IContainer) + ... def __repr__(self): + ... return 'C1' + + >>> from zope.container.constraints import checkObject + >>> container3 = C1() + >>> copier.copyableTo(container3, 'ZDummy') + False + >>> copier.copyableTo(container3, 'newName') + True + + And a test for constraints: + + >>> def con1(container): + ... "silly container constraint" + ... if not hasattr(container, 'x'): + ... return False + ... return True + ... + >>> class I2(zope.interface.Interface): + ... __parent__ = zope.schema.Field(constraint=con1) + ... + >>> class constrainedObject(object): + ... zope.interface.implements(I2) + ... def __init__(self): + ... self.__name__ = 'constrainedObject' + ... + >>> cO = constrainedObject() + >>> copier2 = ObjectCopier(cO) + >>> copier2.copyableTo(container) + False + >>> container.x = 1 + >>> copier2.copyableTo(container) + True + + """ + + adapts(IContained) + + implements(IObjectCopier) + + def __init__(self, object): + self.context = object + self.__parent__ = object # TODO: see if we can automate this + + def copyTo(self, target, new_name=None): + """Copy this object to the `target` given. + + Returns the new name within the `target`. After the copy + is created and before adding it to the target container, + an `IObjectCopied` event is published. + """ + obj = self.context + container = obj.__parent__ + + orig_name = obj.__name__ + if new_name is None: + new_name = orig_name + + checkObject(target, new_name, obj) + + chooser = INameChooser(target) + new_name = chooser.chooseName(new_name, obj) + + new = copy(obj) + notify(ObjectCopiedEvent(new, obj)) + + target[new_name] = new + return new_name + + def copyable(self): + """Returns True if the object is copyable, otherwise False.""" + return True + + def copyableTo(self, target, name=None): + """Say whether the object can be copied to the given `target`. + + Returns ``True`` if it can be copied there. Otherwise, returns + ``False``. + """ + if name is None: + name = self.context.__name__ + try: + checkObject(target, name, self.context) + except Invalid: + return False + return True + + +class ContainerItemRenamer(object): + """An IContainerItemRenamer adapter for containers. + + This adapter uses IObjectMover to move an item within the same container + to a different name. We need to first setup an adapter for IObjectMover: + + >>> from zope.container.interfaces import IContained + >>> gsm = zope.component.getGlobalSiteManager() + >>> gsm.registerAdapter(ObjectMover, (IContained, ), IObjectMover) + + To rename an item in a container, instantiate a ContainerItemRenamer + with the container: + + >>> container = SampleContainer() + >>> renamer = ContainerItemRenamer(container) + + For this example, we'll rename an item 'foo': + + >>> from zope.container.contained import Contained + >>> foo = Contained() + >>> container['foo'] = foo + >>> container['foo'] is foo + True + + to 'bar': + + >>> renamer.renameItem('foo', 'bar') + >>> container['foo'] is foo + Traceback (most recent call last): + KeyError: 'foo' + >>> container['bar'] is foo + True + + If the item being renamed isn't in the container, a NotFoundError is raised: + + >>> renamer.renameItem('foo', 'bar') # doctest:+ELLIPSIS + Traceback (most recent call last): + ItemNotFoundError: (<...SampleContainer...>, 'foo') + + If the new item name already exists, a DuplicationError is raised: + + >>> renamer.renameItem('bar', 'bar') + Traceback (most recent call last): + DuplicationError: bar is already in use + + """ + + adapts(IContainer) + + implements(IContainerItemRenamer) + + def __init__(self, container): + self.container = container + + def renameItem(self, oldName, newName): + object = self.container.get(oldName) + if object is None: + raise ItemNotFoundError(self.container, oldName) + mover = IObjectMover(object) + + if newName in self.container: + raise DuplicationError("%s is already in use" % newName) + + mover.moveTo(self.container, newName) + + +class OrderedContainerItemRenamer(ContainerItemRenamer): + """Renames items within an ordered container. + + This renamer preserves the original order of the contained items. + + To illustrate, we need to setup an IObjectMover, which is used in the + renaming: + + >>> from zope.container.interfaces import IContained + >>> gsm = zope.component.getGlobalSiteManager() + >>> gsm.registerAdapter(ObjectMover, (IContained, ), IObjectMover) + + To rename an item in an ordered container, we instantiate a + OrderedContainerItemRenamer with the container: + + >>> from zope.container.ordered import OrderedContainer + >>> container = OrderedContainer() + >>> renamer = OrderedContainerItemRenamer(container) + + We'll add three items to the container: + + >>> container['1'] = 'Item 1' + >>> container['2'] = 'Item 2' + >>> container['3'] = 'Item 3' + >>> container.items() + [('1', 'Item 1'), ('2', 'Item 2'), ('3', 'Item 3')] + + When we rename one of the items: + + >>> renamer.renameItem('1', 'I') + + the order is preserved: + + >>> container.items() + [('I', 'Item 1'), ('2', 'Item 2'), ('3', 'Item 3')] + + Renaming the other two items also preserves the origina order: + + >>> renamer.renameItem('2', 'II') + >>> renamer.renameItem('3', 'III') + >>> container.items() + [('I', 'Item 1'), ('II', 'Item 2'), ('III', 'Item 3')] + + As with the standard renamer, trying to rename a non-existent item raises + an error: + + >>> renamer.renameItem('IV', '4') # doctest:+ELLIPSIS + Traceback (most recent call last): + ItemNotFoundError: (<...OrderedContainer...>, 'IV') + + And if the new item name already exists, a DuplicationError is raised: + + >>> renamer.renameItem('III', 'I') + Traceback (most recent call last): + DuplicationError: I is already in use + + """ + + adapts(IOrderedContainer) + + implements(IContainerItemRenamer) + + def renameItem(self, oldName, newName): + order = list(self.container.keys()) + super(OrderedContainerItemRenamer, self).renameItem(oldName, newName) + order[order.index(oldName)] = newName + self.container.updateOrder(order) + + +class PrincipalClipboard(object): + """Principal clipboard + + Clipboard information consists of mappings of + ``{'action':action, 'target':target}``. + """ + + adapts(IAnnotations) + + implements(IPrincipalClipboard) + + def __init__(self, annotation): + self.context = annotation + + def clearContents(self): + """Clear the contents of the clipboard""" + self.context['clipboard'] = () + + def addItems(self, action, targets): + """Add new items to the clipboard""" + contents = self.getContents() + actions = [] + for target in targets: + actions.append({'action':action, 'target':target}) + self.context['clipboard'] = contents + tuple(actions) + + def setContents(self, clipboard): + """Replace the contents of the clipboard by the given value""" + self.context['clipboard'] = clipboard + + def getContents(self): + """Return the contents of the clipboard""" + return self.context.get('clipboard', ()) + + +class ExampleContainer(SampleContainer): + # Sample container used for examples in doc stringss in this module + + implements(INameChooser) + + def chooseName(self, name, ob): + while name in self: + name += '_' + return name + + +def dispatchToSublocations(object, event): + """Dispatches an event to sublocations of a given object. + + This handler is used to dispatch copy events to sublocations. + + To illustrate, we'll first create a hierarchy of objects: + + >>> class L(object): + ... def __init__(self, name): + ... self.__name__ = name + ... self.__parent__ = None + ... def __repr__(self): + ... return '%s(%s)' % (self.__class__.__name__, self.__name__) + + >>> class C(L): + ... implements(ISublocations) + ... def __init__(self, name, *subs): + ... L.__init__(self, name) + ... self.subs = subs + ... for sub in subs: + ... sub.__parent__ = self + ... def sublocations(self): + ... return self.subs + + >>> c = C(1, + ... C(11, + ... L(111), + ... L(112), + ... ), + ... C(12, + ... L(121), + ... L(122), + ... L(123), + ... L(124), + ... ), + ... L(13), + ... ) + + and a handler for copy events that records the objects it's seen: + + >>> seen = [] + >>> def handler(ob, event): + ... seen.append((ob, event.object)) + + Finally, we need to register our handler for copy events: + + >>> from zope.lifecycleevent.interfaces import IObjectCopiedEvent + >>> gsm = zope.component.getGlobalSiteManager() + >>> gsm.registerHandler(handler, [None, IObjectCopiedEvent]) + + and this function as a dispatcher: + + >>> gsm.registerHandler(dispatchToSublocations, + ... [None, IObjectCopiedEvent]) + + When we notify that our root object has been copied: + + >>> notify(ObjectCopiedEvent(c, L(''))) + + we see that our handler has seen all of the subobjects: + + >>> seenreprs = map(repr, seen) + >>> seenreprs.sort() + >>> seenreprs #doctest: +NORMALIZE_WHITESPACE + ['(C(1), C(1))', '(C(11), C(1))', '(C(12), C(1))', + '(L(111), C(1))', '(L(112), C(1))', '(L(121), C(1))', + '(L(122), C(1))', '(L(123), C(1))', '(L(124), C(1))', + '(L(13), C(1))'] + """ + subs = ISublocations(object, None) + if subs is not None: + for sub in subs.sublocations(): + for ignored in zope.component.subscribers((sub, event), None): + pass # They do work in the adapter fetch diff -Nru zope3-3.4.0/src/zope/copypastemove/interfaces.py zope3-3.5~bzr18/src/zope/copypastemove/interfaces.py --- zope3-3.4.0/src/zope/copypastemove/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copypastemove/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,95 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Copy and Move support + +$Id: interfaces.py 97525 2009-03-05 11:50:36Z nadako $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface, implements + +class IObjectMover(Interface): + """Use `IObjectMover(obj)` to move an object somewhere.""" + + def moveTo(target, new_name=None): + """Move this object to the target given. + + Returns the new name within the target. + """ + + def moveable(): + """Returns ``True`` if the object is moveable, otherwise ``False``.""" + + def moveableTo(target, name=None): + """Say whether the object can be moved to the given `target`. + + Returns ``True`` if it can be moved there. Otherwise, returns + ``False``. + """ + +class IObjectCopier(Interface): + + def copyTo(target, new_name=None): + """Copy this object to the `target` given. + + Returns the new name within the `target`. After the copy + is created and before adding it to the target container, + an `IObjectCopied` event is published. + """ + + def copyable(): + """Returns ``True`` if the object is copyable, otherwise ``False``.""" + + def copyableTo(target, name=None): + """Say whether the object can be copied to the given `target`. + + Returns ``True`` if it can be copied there. Otherwise, returns + ``False``. + """ + +class IContainerItemRenamer(Interface): + + def renameItem(oldName, newName): + """Renames an object in the container from oldName to newName. + + Raises ItemNotFoundError if oldName doesn't exist in the container. + + Raises DuplicationError if newName is already used in the container. + """ + +class IPrincipalClipboard(Interface): + """Interface for adapters that store/retrieve clipboard information + for a principal. + + Clipboard information consists of mappings of + ``{'action':action, 'target':target}``. + """ + + def clearContents(): + """Clear the contents of the clipboard""" + + def addItems(action, targets): + """Add new items to the clipboard""" + + def setContents(clipboard): + """Replace the contents of the clipboard by the given value""" + + def getContents(): + """Return the contents of the clipboard""" + +class IItemNotFoundError(Interface): + pass + +class ItemNotFoundError(LookupError): + implements(IItemNotFoundError) diff -Nru zope3-3.4.0/src/zope/copypastemove/tests/__init__.py zope3-3.5~bzr18/src/zope/copypastemove/tests/__init__.py --- zope3-3.4.0/src/zope/copypastemove/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copypastemove/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Import this. diff -Nru zope3-3.4.0/src/zope/copypastemove/tests/test_clipboard.py zope3-3.5~bzr18/src/zope/copypastemove/tests/test_clipboard.py --- zope3-3.4.0/src/zope/copypastemove/tests/test_clipboard.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copypastemove/tests/test_clipboard.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,93 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Clipboard tests + +$Id: test_clipboard.py 106679 2009-12-16 23:25:52Z hannosch $ +""" +import unittest + +import zope.component +from zope.annotation.interfaces import IAnnotations +from zope.component.testing import PlacelessSetup +from zope.principalannotation.utility import PrincipalAnnotationUtility +from zope.principalannotation.interfaces import IPrincipalAnnotationUtility + +from zope.copypastemove import PrincipalClipboard +from zope.copypastemove.interfaces import IPrincipalClipboard + + +class PrincipalStub(object): + + def __init__(self, id): + self.id = id + + +class PrincipalClipboardTest(PlacelessSetup, unittest.TestCase): + + def setUp(self): + gsm = zope.component.getGlobalSiteManager() + gsm.registerAdapter(PrincipalClipboard, (IAnnotations, ), + IPrincipalClipboard) + gsm.registerUtility(PrincipalAnnotationUtility(), + IPrincipalAnnotationUtility) + + def testAddItems(self): + user = PrincipalStub('srichter') + + annotationutil = zope.component.getUtility(IPrincipalAnnotationUtility) + annotations = annotationutil.getAnnotations(user) + clipboard = IPrincipalClipboard(annotations) + clipboard.addItems('move', ['bla', 'bla/foo', 'bla/bar']) + expected = ({'action':'move', 'target':'bla'}, + {'action':'move', 'target':'bla/foo'}, + {'action':'move', 'target':'bla/bar'}) + + self.failUnless(clipboard.getContents() == expected) + clipboard.addItems('copy', ['bla']) + expected = expected + ({'action':'copy', 'target':'bla'},) + self.failUnless(clipboard.getContents() == expected) + + def testSetContents(self): + user = PrincipalStub('srichter') + + annotationutil = zope.component.getUtility(IPrincipalAnnotationUtility) + annotations = annotationutil.getAnnotations(user) + clipboard = IPrincipalClipboard(annotations) + + expected = ({'action':'move', 'target':'bla'}, + {'action':'move', 'target':'bla/foo'}, + {'action':'move', 'target':'bla/bar'}) + clipboard.setContents(expected) + self.failUnless(clipboard.getContents() == expected) + clipboard.addItems('copy', ['bla']) + expected = expected + ({'action':'copy', 'target':'bla'},) + self.failUnless(clipboard.getContents() == expected) + + def testClearContents(self): + user = PrincipalStub('srichter') + + annotationutil = zope.component.getUtility(IPrincipalAnnotationUtility) + annotations = annotationutil.getAnnotations(user) + clipboard = IPrincipalClipboard(annotations) + clipboard.clearContents() + self.failUnless(clipboard.getContents() == ()) + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(PrincipalClipboardTest), + )) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') + diff -Nru zope3-3.4.0/src/zope/copypastemove/tests/test_rename.py zope3-3.5~bzr18/src/zope/copypastemove/tests/test_rename.py --- zope3-3.4.0/src/zope/copypastemove/tests/test_rename.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/copypastemove/tests/test_rename.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,38 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 renaming of components + +$Id: test_rename.py 95443 2009-01-29 14:55:57Z wosc $ +""" +import unittest + +from zope.testing.doctestunit import DocTestSuite +from zope.component import testing, eventtesting +from zope.container.testing import PlacelessSetup + +container_setup = PlacelessSetup() + +def setUp(test): + testing.setUp() + eventtesting.setUp() + container_setup.setUp() + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite('zope.copypastemove', + setUp=setUp, tearDown=testing.tearDown), + )) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/datetime/__init__.py zope3-3.5~bzr18/src/zope/datetime/__init__.py --- zope3-3.4.0/src/zope/datetime/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/datetime/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,954 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Commonly used utility functions. + +Encapsulation of date/time values + +$Id: __init__.py 68931 2006-06-30 19:25:53Z hdima $ +""" +import math +import re +import time as _time # there is a method definition that makes just "time" + # problematic while executing a class definition + +from types import StringTypes + +try: + from time import tzname +except ImportError: + tzname = ('UNKNOWN', 'UNKNOWN') + + +# These are needed because the various date formats below must +# be in english per the RFCs. That means we can't use strftime, +# which is affected by different locale settings. +weekday_abbr = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] +weekday_full = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', + 'Friday', 'Saturday', 'Sunday'] +monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + + +def iso8601_date(ts=None): + # Return an ISO 8601 formatted date string, required + # for certain DAV properties. + # '2000-11-10T16:21:09-08:00 + if ts is None: + ts = _time.time() + return _time.strftime('%Y-%m-%dT%H:%M:%SZ', _time.gmtime(ts)) + +def rfc850_date(ts=None): + # Return an HTTP-date formatted date string. + # 'Friday, 10-Nov-00 16:21:09 GMT' + if ts is None: + ts = _time.time() + year, month, day, hh, mm, ss, wd, y, z = _time.gmtime(ts) + return "%s, %02d-%3s-%2s %02d:%02d:%02d GMT" % ( + weekday_full[wd], + day, monthname[month], + str(year)[2:], + hh, mm, ss) + +def rfc1123_date(ts=None): + # Return an RFC 1123 format date string, required for + # use in HTTP Date headers per the HTTP 1.1 spec. + # 'Fri, 10 Nov 2000 16:21:09 GMT' + if ts is None: + ts = _time.time() + year, month, day, hh, mm, ss, wd, y, z = _time.gmtime(ts) + return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (weekday_abbr[wd], + day, monthname[month], + year, + hh, mm, ss) + + + +from zope.datetime.timezones import historical_zone_info as _data + +class DateTimeError(Exception): "Date-time error" +class DateError(DateTimeError): 'Invalid Date Components' +class TimeError(DateTimeError): 'Invalid Time Components' +class SyntaxError(DateTimeError): 'Invalid Date-Time String' + +# Determine machine epoch +tm=((0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334), + (0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)) +yr,mo,dy,hr,mn,sc = _time.gmtime(0)[:6] +i=int(yr-1) +to_year =int(i*365+i/4-i/100+i/400-693960.0) +to_month=tm[yr%4==0 and (yr%100!=0 or yr%400==0)][mo] +EPOCH =(to_year+to_month+dy+(hr/24.0+mn/1440.0+sc/86400.0))*86400 +jd1901 =2415385L + + +numericTimeZoneMatch=re.compile(r'[+-][0-9][0-9][0-9][0-9]').match #TS + +class _timezone: + def __init__(self,data): + self.name,self.timect,self.typect, \ + self.ttrans,self.tindex,self.tinfo,self.az=data + + def default_index(self): + if self.timect == 0: return 0 + for i in range(self.typect): + if self.tinfo[i][1] == 0: return i + return 0 + + def index(self, t=None): + t = t or _time.time() + if self.timect == 0: + idx = (0, 0, 0) + elif t < self.ttrans[0]: + i = self.default_index() + idx = (i, ord(self.tindex[0]),i) + elif t >= self.ttrans[-1]: + if self.timect > 1: + idx=(ord(self.tindex[-1]),ord(self.tindex[-1]), + ord(self.tindex[-2])) + else: + idx=(ord(self.tindex[-1]),ord(self.tindex[-1]), + self.default_index()) + else: + for i in range(self.timect-1): + if t < self.ttrans[i+1]: + if i==0: idx=(ord(self.tindex[0]),ord(self.tindex[1]), + self.default_index()) + else: idx=(ord(self.tindex[i]),ord(self.tindex[i+1]), + ord(self.tindex[i-1])) + break + return idx + + def info(self,t=None): + idx=self.index(t)[0] + zs =self.az[self.tinfo[idx][2]:] + return self.tinfo[idx][0],self.tinfo[idx][1],zs[: zs.find('\000')] + + + + +class _cache: + + _zlst=['Brazil/Acre','Brazil/DeNoronha','Brazil/East', + 'Brazil/West','Canada/Atlantic','Canada/Central', + 'Canada/Eastern','Canada/East-Saskatchewan', + 'Canada/Mountain','Canada/Newfoundland', + 'Canada/Pacific','Canada/Yukon', + 'Chile/Continental','Chile/EasterIsland','CST','Cuba', + 'Egypt','EST','GB-Eire','Greenwich','Hongkong','Iceland', + 'Iran','Israel','Jamaica','Japan','Mexico/BajaNorte', + 'Mexico/BajaSur','Mexico/General','MST','Poland','PST', + 'Singapore','Turkey','Universal','US/Alaska','US/Aleutian', + 'US/Arizona','US/Central','US/Eastern','US/East-Indiana', + 'US/Hawaii','US/Indiana-Starke','US/Michigan', + 'US/Mountain','US/Pacific','US/Samoa','UTC','UCT','GMT', + + 'GMT+0100','GMT+0200','GMT+0300','GMT+0400','GMT+0500', + 'GMT+0600','GMT+0700','GMT+0800','GMT+0900','GMT+1000', + 'GMT+1100','GMT+1200','GMT+1300','GMT-0100','GMT-0200', + 'GMT-0300','GMT-0400','GMT-0500','GMT-0600','GMT-0700', + 'GMT-0800','GMT-0900','GMT-1000','GMT-1100','GMT-1200', + 'GMT+1', + + 'GMT+0130', 'GMT+0230', 'GMT+0330', 'GMT+0430', 'GMT+0530', + 'GMT+0630', 'GMT+0730', 'GMT+0830', 'GMT+0930', 'GMT+1030', + 'GMT+1130', 'GMT+1230', + + 'GMT-0130', 'GMT-0230', 'GMT-0330', 'GMT-0430', 'GMT-0530', + 'GMT-0630', 'GMT-0730', 'GMT-0830', 'GMT-0930', 'GMT-1030', + 'GMT-1130', 'GMT-1230', + + 'UT','BST','MEST','SST','FST','WADT','EADT','NZDT', + 'WET','WAT','AT','AST','NT','IDLW','CET','MET', + 'MEWT','SWT','FWT','EET','EEST','BT','ZP4','ZP5','ZP6', + 'WAST','CCT','JST','EAST','GST','NZT','NZST','IDLE'] + + + _zmap={'aest':'GMT+1000', 'aedt':'GMT+1100', + 'aus eastern standard time':'GMT+1000', + 'sydney standard time':'GMT+1000', + 'tasmania standard time':'GMT+1000', + 'e. australia standard time':'GMT+1000', + 'aus central standard time':'GMT+0930', + 'cen. australia standard time':'GMT+0930', + 'w. australia standard time':'GMT+0800', + + 'brazil/acre':'Brazil/Acre', + 'brazil/denoronha':'Brazil/Denoronha', + 'brazil/east':'Brazil/East','brazil/west':'Brazil/West', + 'canada/atlantic':'Canada/Atlantic', + 'canada/central':'Canada/Central', + 'canada/eastern':'Canada/Eastern', + 'canada/east-saskatchewan':'Canada/East-Saskatchewan', + 'canada/mountain':'Canada/Mountain', + 'canada/newfoundland':'Canada/Newfoundland', + 'canada/pacific':'Canada/Pacific','canada/yukon':'Canada/Yukon', + 'central europe standard time':'GMT+0100', + 'chile/continental':'Chile/Continental', + 'chile/easterisland':'Chile/EasterIsland', + 'cst':'US/Central','cuba':'Cuba','est':'US/Eastern','egypt':'Egypt', + 'eastern standard time':'US/Eastern', + 'us eastern standard time':'US/Eastern', + 'central standard time':'US/Central', + 'mountain standard time':'US/Mountain', + 'pacific standard time':'US/Pacific', + 'gb-eire':'GB-Eire','gmt':'GMT', + + 'gmt+0000':'GMT+0', 'gmt+0':'GMT+0', + + + 'gmt+0100':'GMT+1', 'gmt+0200':'GMT+2', 'gmt+0300':'GMT+3', + 'gmt+0400':'GMT+4', 'gmt+0500':'GMT+5', 'gmt+0600':'GMT+6', + 'gmt+0700':'GMT+7', 'gmt+0800':'GMT+8', 'gmt+0900':'GMT+9', + 'gmt+1000':'GMT+10','gmt+1100':'GMT+11','gmt+1200':'GMT+12', + 'gmt+1300':'GMT+13', + 'gmt-0100':'GMT-1', 'gmt-0200':'GMT-2', 'gmt-0300':'GMT-3', + 'gmt-0400':'GMT-4', 'gmt-0500':'GMT-5', 'gmt-0600':'GMT-6', + 'gmt-0700':'GMT-7', 'gmt-0800':'GMT-8', 'gmt-0900':'GMT-9', + 'gmt-1000':'GMT-10','gmt-1100':'GMT-11','gmt-1200':'GMT-12', + + 'gmt+1': 'GMT+1', 'gmt+2': 'GMT+2', 'gmt+3': 'GMT+3', + 'gmt+4': 'GMT+4', 'gmt+5': 'GMT+5', 'gmt+6': 'GMT+6', + 'gmt+7': 'GMT+7', 'gmt+8': 'GMT+8', 'gmt+9': 'GMT+9', + 'gmt+10':'GMT+10','gmt+11':'GMT+11','gmt+12':'GMT+12', + 'gmt+13':'GMT+13', + 'gmt-1': 'GMT-1', 'gmt-2': 'GMT-2', 'gmt-3': 'GMT-3', + 'gmt-4': 'GMT-4', 'gmt-5': 'GMT-5', 'gmt-6': 'GMT-6', + 'gmt-7': 'GMT-7', 'gmt-8': 'GMT-8', 'gmt-9': 'GMT-9', + 'gmt-10':'GMT-10','gmt-11':'GMT-11','gmt-12':'GMT-12', + + 'gmt+130':'GMT+0130', 'gmt+0130':'GMT+0130', + 'gmt+230':'GMT+0230', 'gmt+0230':'GMT+0230', + 'gmt+330':'GMT+0330', 'gmt+0330':'GMT+0330', + 'gmt+430':'GMT+0430', 'gmt+0430':'GMT+0430', + 'gmt+530':'GMT+0530', 'gmt+0530':'GMT+0530', + 'gmt+630':'GMT+0630', 'gmt+0630':'GMT+0630', + 'gmt+730':'GMT+0730', 'gmt+0730':'GMT+0730', + 'gmt+830':'GMT+0830', 'gmt+0830':'GMT+0830', + 'gmt+930':'GMT+0930', 'gmt+0930':'GMT+0930', + 'gmt+1030':'GMT+1030', + 'gmt+1130':'GMT+1130', + 'gmt+1230':'GMT+1230', + + 'gmt-130':'GMT-0130', 'gmt-0130':'GMT-0130', + 'gmt-230':'GMT-0230', 'gmt-0230':'GMT-0230', + 'gmt-330':'GMT-0330', 'gmt-0330':'GMT-0330', + 'gmt-430':'GMT-0430', 'gmt-0430':'GMT-0430', + 'gmt-530':'GMT-0530', 'gmt-0530':'GMT-0530', + 'gmt-630':'GMT-0630', 'gmt-0630':'GMT-0630', + 'gmt-730':'GMT-0730', 'gmt-0730':'GMT-0730', + 'gmt-830':'GMT-0830', 'gmt-0830':'GMT-0830', + 'gmt-930':'GMT-0930', 'gmt-0930':'GMT-0930', + 'gmt-1030':'GMT-1030', + 'gmt-1130':'GMT-1130', + 'gmt-1230':'GMT-1230', + + 'greenwich':'Greenwich','hongkong':'Hongkong', + 'iceland':'Iceland','iran':'Iran','israel':'Israel', + 'jamaica':'Jamaica','japan':'Japan', + 'mexico/bajanorte':'Mexico/BajaNorte', + 'mexico/bajasur':'Mexico/BajaSur','mexico/general':'Mexico/General', + 'mst':'US/Mountain','pst':'US/Pacific','poland':'Poland', + 'singapore':'Singapore','turkey':'Turkey','universal':'Universal', + 'utc':'Universal','uct':'Universal','us/alaska':'US/Alaska', + 'us/aleutian':'US/Aleutian','us/arizona':'US/Arizona', + 'us/central':'US/Central','us/eastern':'US/Eastern', + 'us/east-indiana':'US/East-Indiana','us/hawaii':'US/Hawaii', + 'us/indiana-starke':'US/Indiana-Starke','us/michigan':'US/Michigan', + 'us/mountain':'US/Mountain','us/pacific':'US/Pacific', + 'us/samoa':'US/Samoa', + + 'ut':'Universal', + 'bst':'GMT+1', 'mest':'GMT+2', 'sst':'GMT+2', + 'fst':'GMT+2', 'wadt':'GMT+8', 'eadt':'GMT+11', 'nzdt':'GMT+13', + 'wet':'GMT', 'wat':'GMT-1', 'at':'GMT-2', 'ast':'GMT-4', + 'nt':'GMT-11', 'idlw':'GMT-12', 'cet':'GMT+1', 'cest':'GMT+2', + 'met':'GMT+1', + 'mewt':'GMT+1', 'swt':'GMT+1', 'fwt':'GMT+1', 'eet':'GMT+2', + 'eest':'GMT+3', + 'bt':'GMT+3', 'zp4':'GMT+4', 'zp5':'GMT+5', 'zp6':'GMT+6', + 'wast':'GMT+7', 'cct':'GMT+8', 'jst':'GMT+9', 'east':'GMT+10', + 'gst':'GMT+10', 'nzt':'GMT+12', 'nzst':'GMT+12', 'idle':'GMT+12', + 'ret':'GMT+4' + } + + def __init__(self): + self._db = _data + self._d, self._zidx= {}, self._zmap.keys() + + def __getitem__(self,k): + try: n=self._zmap[k.lower()] + except KeyError: + if numericTimeZoneMatch(k) == None: + raise DateTimeError('Unrecognized timezone: %s' % k) + return k + try: + return self._d[n] + except KeyError: + z = self._d[n] = _timezone(self._db[n]) + return z + +def _findLocalTimeZoneName(isDST): + if not _time.daylight: + # Daylight savings does not occur in this time zone. + isDST = 0 + try: + # Get the name of the current time zone depending + # on DST. + _localzone = _cache._zmap[tzname[isDST].lower()] + except KeyError: + try: + # Generate a GMT-offset zone name. + if isDST: + localzone = _time.altzone + else: + localzone = _time.timezone + offset=(-localzone/(60*60)) + majorOffset=int(offset) + if majorOffset != 0 : + minorOffset=abs(int((offset % majorOffset) * 60.0)) + else: minorOffset = 0 + m=majorOffset >= 0 and '+' or '' + lz='%s%0.02d%0.02d' % (m, majorOffset, minorOffset) + _localzone = _cache._zmap[('GMT%s' % lz).lower()] + except: + _localzone = '' + return _localzone + +# Some utility functions for calculating dates: + +def _calcSD(t): + # Returns timezone-independent days since epoch and the fractional + # part of the days. + dd = t + EPOCH - 86400.0 + d = dd / 86400.0 + s = d - math.floor(d) + return s, d + +def _calcDependentSecond(tz, t): + # Calculates the timezone-dependent second (integer part only) + # from the timezone-independent second. + fset = _tzoffset(tz, t) + return fset + long(math.floor(t)) + long(EPOCH) - 86400L + +def _calcDependentSecond2(yr,mo,dy,hr,mn,sc): + # Calculates the timezone-dependent second (integer part only) + # from the date given. + ss = int(hr) * 3600 + int(mn) * 60 + int(sc) + x = long(_julianday(yr,mo,dy)-jd1901) * 86400 + ss + return x + +def _calcIndependentSecondEtc(tz, x, ms): + # Derive the timezone-independent second from the timezone + # dependent second. + fsetAtEpoch = _tzoffset(tz, 0.0) + nearTime = x - fsetAtEpoch - long(EPOCH) + 86400L + ms + # nearTime is now within an hour of being correct. + # Recalculate t according to DST. + fset = long(_tzoffset(tz, nearTime)) + x_adjusted = x - fset + ms + d = x_adjusted / 86400.0 + t = x_adjusted - long(EPOCH) + 86400L + millis = (x + 86400 - fset) * 1000 + \ + long(ms * 1000.0) - long(EPOCH * 1000.0) + s = d - math.floor(d) + return s,d,t,millis + +def _calcHMS(x, ms): + # hours, minutes, seconds from integer and float. + hr = x / 3600 + x = x - hr * 3600 + mn = x / 60 + sc = x - mn * 60 + ms + return hr,mn,sc + +def _calcYMDHMS(x, ms): + # x is a timezone-dependent integer of seconds. + # Produces yr,mo,dy,hr,mn,sc. + yr,mo,dy=_calendarday(x / 86400 + jd1901) + x = int(x - (x / 86400) * 86400) + hr = x / 3600 + x = x - hr * 3600 + mn = x / 60 + sc = x - mn * 60 + ms + return yr,mo,dy,hr,mn,sc + +def _julianday(yr,mo,dy): + y,m,d=long(yr),long(mo),long(dy) + if m > 12L: + y=y+m/12L + m=m%12L + elif m < 1L: + m=-m + y=y-m/12L-1L + m=12L-m%12L + if y > 0L: yr_correct=0L + else: yr_correct=3L + if m < 3L: y, m=y-1L,m+12L + if y*10000L+m*100L+d > 15821014L: b=2L-y/100L+y/400L + else: b=0L + return (1461L*y-yr_correct)/4L+306001L*(m+1L)/10000L+d+1720994L+b + +def _calendarday(j): + j=long(j) + if(j < 2299160L): + b=j+1525L + else: + a=(4L*j-7468861L)/146097L + b=j+1526L+a-a/4L + c=(20L*b-2442L)/7305L + d=1461L*c/4L + e=10000L*(b-d)/306001L + dy=int(b-d-306001L*e/10000L) + mo=(e < 14L) and int(e-1L) or int(e-13L) + yr=(mo > 2) and (c-4716L) or (c-4715L) + return int(yr),int(mo),int(dy) + +def _tzoffset(tz, t): + try: + return DateTimeParser._tzinfo[tz].info(t)[0] + except: + if numericTimeZoneMatch(tz) is not None: + offset = int(tz[1:3])*3600+int(tz[3:5])*60 + if tz[0] == '-': + return -offset + else: + return offset + else: + return 0 # Assume UTC + +def _correctYear(year): + # Y2K patch. + if year >= 0 and year < 100: + # 00-69 means 2000-2069, 70-99 means 1970-1999. + if year < 70: + year = 2000 + year + else: + year = 1900 + year + return year + +def safegmtime(t): + '''gmtime with a safety zone.''' + try: + t_int = int(t) + except OverflowError: + raise TimeError('The time %f is beyond the range ' + 'of this Python implementation.' % float(t)) + return _time.gmtime(t_int) + +def safelocaltime(t): + '''localtime with a safety zone.''' + try: + t_int = int(t) + except OverflowError: + raise TimeError('The time %f is beyond the range ' + 'of this Python implementation.' % float(t)) + return _time.localtime(t_int) + +class DateTimeParser: + + def parse(self, arg, local=True): + """Parse a string containing some sort of date-time data. + + This function returns a tuple (year, month, day, hour, minute, + second, timezone_string). + + As a general rule, any date-time representation that is + recognized and unambigous to a resident of North America is + acceptable.(The reason for this qualification is that + in North America, a date like: 2/1/1994 is interpreted + as February 1, 1994, while in some parts of the world, + it is interpreted as January 2, 1994.) A date/time + string consists of two components, a date component and + an optional time component, separated by one or more + spaces. If the time component is omited, 12:00am is + assumed. Any recognized timezone name specified as the + final element of the date/time string will be used for + computing the date/time value. (If you create a DateTime + with the string 'Mar 9, 1997 1:45pm US/Pacific', the + value will essentially be the same as if you had captured + time.time() at the specified date and time on a machine in + that timezone) + + x=parse('1997/3/9 1:45pm') + # returns specified time, represented in local machine zone. + + y=parse('Mar 9, 1997 13:45:00') + # y is equal to x + + The function automatically detects and handles + ISO8601 compliant dates (YYYY-MM-DDThh:ss:mmTZD). + See http://www.w3.org/TR/NOTE-datetime for full specs. + + The date component consists of year, month, and day + values. The year value must be a one-, two-, or + four-digit integer. If a one- or two-digit year is + used, the year is assumed to be in the twentieth + century. The month may an integer, from 1 to 12, a + month name, or a month abreviation, where a period may + optionally follow the abreviation. The day must be an + integer from 1 to the number of days in the month. The + year, month, and day values may be separated by + periods, hyphens, forward, shashes, or spaces. Extra + spaces are permitted around the delimiters. Year, + month, and day values may be given in any order as long + as it is possible to distinguish the components. If all + three components are numbers that are less than 13, + then a a month-day-year ordering is assumed. + + The time component consists of hour, minute, and second + values separated by colons. The hour value must be an + integer between 0 and 23 inclusively. The minute value + must be an integer between 0 and 59 inclusively. The + second value may be an integer value between 0 and + 59.999 inclusively. The second value or both the minute + and second values may be ommitted. The time may be + followed by am or pm in upper or lower case, in which + case a 12-hour clock is assumed. + + If a string argument passed to the DateTime constructor cannot be + parsed, it will raise SyntaxError. Invalid date components + will raise a DateError, while invalid time or timezone components + will raise a DateTimeError. + """ + if not isinstance(arg, StringTypes): + raise TypeError('Expected a string argument') + + if not arg: + raise SyntaxError(arg) + + if arg.find(' ')==-1 and len(arg) >= 5 and arg[4]=='-': + yr,mo,dy,hr,mn,sc,tz=self._parse_iso8601(arg) + else: + yr,mo,dy,hr,mn,sc,tz=self._parse(arg, local) + + + if not self._validDate(yr,mo,dy): + raise DateError(arg, yr, mo, dy) + if not self._validTime(hr,mn,int(sc)): + raise TimeError(arg) + + return yr, mo, dy, hr, mn, sc, tz + + def time(self, arg): + """Parse a string containing some sort of date-time data. + + This function returns the time in seconds since the Epoch (in UTC). + + See date() for the description of allowed input values. + """ + + yr, mo, dy, hr, mn, sc, tz = self.parse(arg) + + ms = sc - math.floor(sc) + x = _calcDependentSecond2(yr,mo,dy,hr,mn,sc) + + if tz: + try: + tz=self._tzinfo._zmap[tz.lower()] + except KeyError: + if numericTimeZoneMatch(tz) is None: + raise DateTimeError('Unknown time zone in date: %s' % arg) + else: + tz = self._calcTimezoneName(x, ms) + s,d,t,millisecs = _calcIndependentSecondEtc(tz, x, ms) + + return t + + + int_pattern =re.compile(r'([0-9]+)') #AJ + flt_pattern =re.compile(r':([0-9]+\.[0-9]+)') #AJ + name_pattern =re.compile(r'([a-zA-Z]+)', re.I) #AJ + space_chars =' \t\n' + delimiters ='-/.:,+' + _month_len =((0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), + (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)) + _until_month=((0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334), + (0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)) + _monthmap ={'january': 1, 'jan': 1, + 'february': 2, 'feb': 2, + 'march': 3, 'mar': 3, + 'april': 4, 'apr': 4, + 'may': 5, + 'june': 6, 'jun': 6, + 'july': 7, 'jul': 7, + 'august': 8, 'aug': 8, + 'september': 9, 'sep': 9, 'sept': 9, + 'october': 10, 'oct': 10, + 'november': 11, 'nov': 11, + 'december': 12, 'dec': 12} + _daymap ={'sunday': 1, 'sun': 1, + 'monday': 2, 'mon': 2, + 'tuesday': 3, 'tues': 3, 'tue': 3, + 'wednesday': 4, 'wed': 4, + 'thursday': 5, 'thurs': 5, 'thur': 5, 'thu': 5, + 'friday': 6, 'fri': 6, + 'saturday': 7, 'sat': 7} + + _localzone0 = _findLocalTimeZoneName(0) + _localzone1 = _findLocalTimeZoneName(1) + _multipleZones = (_localzone0 != _localzone1) + # For backward compatibility only: + _isDST = _time.localtime()[8] + _localzone = _isDST and _localzone1 or _localzone0 + + _tzinfo = _cache() + + def localZone(self, ltm=None): + '''Returns the time zone on the given date. The time zone + can change according to daylight savings.''' + if not self._multipleZones: + return self._localzone0 + if ltm == None: + ltm = _time.localtime() + isDST = ltm[8] + lz = isDST and self._localzone1 or self._localzone0 + return lz + + def _calcTimezoneName(self, x, ms): + # Derive the name of the local time zone at the given + # timezone-dependent second. + if not self._multipleZones: + return self._localzone0 + fsetAtEpoch = _tzoffset(self._localzone0, 0.0) + nearTime = x - fsetAtEpoch - long(EPOCH) + 86400L + ms + # nearTime is within an hour of being correct. + try: + ltm = safelocaltime(nearTime) + except: + # We are beyond the range of Python's date support. + # Hopefully we can assume that daylight savings schedules + # repeat every 28 years. Calculate the name of the + # time zone using a supported range of years. + yr,mo,dy,hr,mn,sc = _calcYMDHMS(x, 0) + yr = ((yr - 1970) % 28) + 1970 + x = _calcDependentSecond2(yr,mo,dy,hr,mn,sc) + nearTime = x - fsetAtEpoch - long(EPOCH) + 86400L + ms + ltm = safelocaltime(nearTime) + tz = self.localZone(ltm) + return tz + + def _parse(self, string, local=True): + # Parse date-time components from a string + month = year = tz = tm = None + spaces = self.space_chars + intpat = self.int_pattern + fltpat = self.flt_pattern + wordpat = self.name_pattern + delimiters = self.delimiters + MonthNumbers = self._monthmap + DayOfWeekNames = self._daymap + ValidZones = self._tzinfo._zidx + TimeModifiers = ['am','pm'] + + string = string.strip() + + # Find timezone first, since it should always be the last + # element, and may contain a slash, confusing the parser. + + + # First check for time zone of form +dd:dd + tz = _iso_tz_re.search(string) + if tz: + tz = tz.start(0) + tz, string = string[tz:], string[:tz].strip() + tz = tz[:3]+tz[4:] + else: + # Look at last token + sp=string.split() + tz = sp[-1] + if tz and (tz.lower() in ValidZones): + string=' '.join(sp[:-1]) + else: + tz = None # Decide later, since the default time zone + # could depend on the date. + + ints,dels=[],[] + i,l=0,len(string) + while i < l: + while i < l and string[i] in spaces : i=i+1 + if i < l and string[i] in delimiters: + d=string[i] + i=i+1 + else: d='' + while i < l and string[i] in spaces : i=i+1 + + # The float pattern needs to look back 1 character, because it + # actually looks for a preceding colon like ':33.33'. This is + # needed to avoid accidentally matching the date part of a + # dot-separated date string such as '1999.12.31'. + if i > 0: b=i-1 + else: b=i + + ts_results = fltpat.match(string, b) + if ts_results: + s=ts_results.group(1) + i=i+len(s) + ints.append(float(s)) + continue + + #AJ + ts_results = intpat.match(string, i) + if ts_results: + s=ts_results.group(0) + + ls=len(s) + i=i+ls + if (ls==4 and d and d in '+-' and + (len(ints) + (not not month) >= 3)): + tz='%s%s' % (d,s) + else: + v=int(s) + ints.append(v) + continue + + + ts_results = wordpat.match(string, i) + if ts_results: + o,s=ts_results.group(0),ts_results.group(0).lower() + i=i+len(s) + if i < l and string[i]=='.': i=i+1 + # Check for month name: + if s in MonthNumbers: + v=MonthNumbers[s] + if month is None: month=v + else: raise SyntaxError(string) + continue + # Check for time modifier: + if s in TimeModifiers: + if tm is None: tm=s + else: raise SyntaxError(string) + continue + # Check for and skip day of week: + if s in DayOfWeekNames: + continue + raise SyntaxError(string) + + day=None + if ints[-1] > 60 and d not in ['.',':'] and len(ints) > 2: + year=ints[-1] + del ints[-1] + if month: + day=ints[0] + del ints[:1] + else: + month=ints[0] + day=ints[1] + del ints[:2] + elif month: + if len(ints) > 1: + if ints[0] > 31: + year=ints[0] + day=ints[1] + else: + year=ints[1] + day=ints[0] + del ints[:2] + elif len(ints) > 2: + if ints[0] > 31: + year=ints[0] + if ints[1] > 12: + day=ints[1] + month=ints[2] + else: + day=ints[2] + month=ints[1] + if ints[1] > 31: + year=ints[1] + if ints[0] > 12 and ints[2] <= 12: + day=ints[0] + month=ints[2] + elif ints[2] > 12 and ints[0] <= 12: + day=ints[2] + month=ints[0] + elif ints[2] > 31: + year=ints[2] + if ints[0] > 12: + day=ints[0] + month=ints[1] + else: + day=ints[1] + month=ints[0] + elif ints[0] <= 12: + month=ints[0] + day=ints[1] + year=ints[2] + del ints[:3] + + if day is None: + # Use today's date. + year,month,day = _time.localtime()[:3] + + year = _correctYear(year) + if year < 1000: raise SyntaxError(string) + + leap = year%4==0 and (year%100!=0 or year%400==0) + try: + if not day or day > self._month_len[leap][month]: + raise DateError(string) + except IndexError: + raise DateError(string) + tod=0 + if ints: + i=ints[0] + # Modify hour to reflect am/pm + if tm and (tm=='pm') and i<12: i=i+12 + if tm and (tm=='am') and i==12: i=0 + if i > 24: raise DateTimeError(string) + tod = tod + int(i) * 3600 + del ints[0] + if ints: + i=ints[0] + if i > 60: raise DateTimeError(string) + tod = tod + int(i) * 60 + del ints[0] + if ints: + i=ints[0] + if i > 60: raise DateTimeError(string) + tod = tod + i + del ints[0] + if ints: raise SyntaxError(string) + + + tod_int = int(math.floor(tod)) + ms = tod - tod_int + hr,mn,sc = _calcHMS(tod_int, ms) + + if local and not tz: + # Figure out what time zone it is in the local area + # on the given date. + x = _calcDependentSecond2(year,month,day,hr,mn,sc) + tz = self._calcTimezoneName(x, ms) + + return year,month,day,hr,mn,sc,tz + + def _validDate(self,y,m,d): + if m<1 or m>12 or y<0 or d<1 or d>31: return 0 + return d<=self._month_len[(y%4==0 and (y%100!=0 or y%400==0))][m] + + def _validTime(self,h,m,s): + return h>=0 and h<=23 and m>=0 and m<=59 and s>=0 and s < 60 + + def _parse_iso8601(self,s): + try: + return self.__parse_iso8601(s) + except IndexError: + raise DateError( + 'Not an ISO 8601 compliant date string: "%s"' % s) + + + def __parse_iso8601(self,s): + """Parse an ISO 8601 compliant date. + + TODO: Not all allowed formats are recognized (for some examples see + http://www.cl.cam.ac.uk/~mgk25/iso-time.html). + """ + year=0 + month=day=1 + hour=minute=seconds=hour_off=min_off=0 + tzsign='+' + + datereg = re.compile( + '([0-9]{4})(-([0-9][0-9]))?(-([0-9][0-9]))?') + timereg = re.compile( + '([0-9]{2})(:([0-9][0-9]))?(:([0-9][0-9]))?(\.[0-9]{1,20})?' + '(\s*([-+])([0-9]{2})(:?([0-9]{2}))?)?') + + # Date part + + fields = datereg.split(s.strip()) + if fields[1]: year = int(fields[1]) + if fields[3]: month = int(fields[3]) + if fields[5]: day = int(fields[5]) + + if s.find('T')>-1: + fields = timereg.split(s[s.find('T')+1:]) + + if fields[1]: hour = int(fields[1]) + if fields[3]: minute = int(fields[3]) + if fields[5]: seconds = int(fields[5]) + if fields[6]: seconds = seconds+float(fields[6]) + + if fields[8]: tzsign = fields[8] + if fields[9]: hour_off = int(fields[9]) + if fields[11]: min_off = int(fields[11]) + + return (year,month,day,hour,minute,seconds, + '%s%02d%02d' % (tzsign,hour_off,min_off)) + +parser = DateTimeParser() +parse = parser.parse +time = parser.time + +###################################################################### +# Time-zone info based soley on offsets +# +# Share tzinfos for the same offset + +from datetime import tzinfo as _tzinfo, timedelta as _timedelta + +class _tzinfo(_tzinfo): + + def __init__(self, minutes): + if abs(minutes) > 1439: + raise ValueError("Time-zone offset is too large,", minutes) + self.__minutes = minutes + self.__offset = _timedelta(minutes=minutes) + + def utcoffset(self, dt): + return self.__offset + + def __reduce__(self): + return tzinfo, (self.__minutes, ) + + def dst(self, dt): + return None + + def tzname(self, dt): + return None + + def __repr__(self): + return 'tzinfo(%d)' % self.__minutes + + +def tzinfo(offset, _tzinfos = {}): + + info = _tzinfos.get(offset) + if info is None: + # We haven't seen this one before. we need to save it. + + # Use setdefault to avoid a race condition and make sure we have + # only one + info = _tzinfos.setdefault(offset, _tzinfo(offset)) + + return info + +tzinfo.__safe_for_unpickling__ = True + +# +###################################################################### + +from datetime import datetime as _datetime + +def parseDatetimetz(string, local=True): + y, mo, d, h, m, s, tz = parse(string, local) + s, micro = divmod(s, 1.0) + micro = round(micro * 1000000) + if tz: + offset = _tzoffset(tz, None) / 60 + _tzinfo = tzinfo(offset) + else: + _tzinfo = None + return _datetime(y, mo, d, h, m, int(s), int(micro), _tzinfo) + +_iso_tz_re = re.compile("[-+]\d\d:\d\d$") diff -Nru zope3-3.4.0/src/zope/datetime/tests/__init__.py zope3-3.5~bzr18/src/zope/datetime/tests/__init__.py --- zope3-3.4.0/src/zope/datetime/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/datetime/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# make this directory a package diff -Nru zope3-3.4.0/src/zope/datetime/tests/test_datetimeparse.py zope3-3.5~bzr18/src/zope/datetime/tests/test_datetimeparse.py --- zope3-3.4.0/src/zope/datetime/tests/test_datetimeparse.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/datetime/tests/test_datetimeparse.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,106 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 datetime parser + +$Id: test_datetimeparse.py 68931 2006-06-30 19:25:53Z hdima $ +""" +import unittest +from time import gmtime +from datetime import datetime + +from zope.datetime import parse, time, DateTimeError +from zope.datetime import parseDatetimetz, tzinfo +from zope.datetime import _tzoffset + + +class Test(unittest.TestCase): + + def testParse(self): + self.assertEqual(parse('1999 12 31')[:6], + (1999, 12, 31, 0, 0, 0)) + self.assertEqual(parse('1999 12 31 EST'), + (1999, 12, 31, 0, 0, 0, 'EST')) + self.assertEqual(parse('Dec 31, 1999')[:6], + (1999, 12, 31, 0, 0, 0)) + self.assertEqual(parse('Dec 31 1999')[:6], + (1999, 12, 31, 0, 0, 0)) + self.assertEqual(parse('Dec 31 1999')[:6], + (1999, 12, 31, 0, 0, 0)) + self.assertEqual(parse('1999/12/31 1:2:3')[:6], + (1999, 12, 31, 1, 2, 3)) + self.assertEqual(parse('1999-12-31 1:2:3')[:6], + (1999, 12, 31, 1, 2, 3)) + self.assertEqual(parse('1999-12-31T01:02:03')[:6], + (1999, 12, 31, 1, 2, 3)) + self.assertEqual(parse('1999-31-12 1:2:3')[:6], + (1999, 12, 31, 1, 2, 3)) + self.assertEqual(parse('1999-31-12 1:2:3.456')[:5], + (1999, 12, 31, 1, 2)) + self.assertEqual(int(parse('1999-31-12 1:2:3.456')[5]*1000+.000001), + 3456) + self.assertEqual(parse('1999-12-31T01:02:03.456')[:5], + (1999, 12, 31, 1, 2)) + self.assertEqual(int(parse('1999-12-31T01:02:03.456')[5]*1000+.000001), + 3456) + self.assertEqual(parse('Tue, 24 Jul 2001 09:41:03 -0400'), + (2001, 7, 24, 9, 41, 3, '-0400')) + self.assertEqual(parse('1999-12-31T01:02:03.456-12')[6], '-1200') + self.assertEqual(parse('1999-12-31T01:02:03.456+0030')[6], '+0030') + self.assertEqual(parse('1999-12-31T01:02:03.456-00:30')[6], '-0030') + + def testTime(self): + self.assertEqual(gmtime(time('1999 12 31 GMT'))[:6], + (1999, 12, 31, 0, 0, 0)) + self.assertEqual(gmtime(time('1999 12 31 EST'))[:6], + (1999, 12, 31, 5, 0, 0)) + self.assertEqual(gmtime(time('1999 12 31 -0500'))[:6], + (1999, 12, 31, 5, 0, 0)) + self.assertEqual(gmtime(time('1999-12-31T00:11:22Z'))[:6], + (1999, 12, 31, 0, 11, 22)) + self.assertEqual(gmtime(time('1999-12-31T01:11:22+01:00'))[:6], + (1999, 12, 31, 0, 11, 22)) + + def testBad(self): + self.assertRaises(DateTimeError, parse, '1999') + self.assertRaises(DateTimeError, parse, '1999-31-12 1:2:63.456') + self.assertRaises(DateTimeError, parse, '1999-31-13 1:2:3.456') + self.assertRaises(DateTimeError, parse, '1999-2-30 1:2:3.456') + self.assertRaises(DateTimeError, parse, 'April 31, 1999 1:2:3.456') + + def testLeap(self): + self.assertRaises(DateTimeError, parse, '1999-2-29 1:2:3.456') + self.assertRaises(DateTimeError, parse, '1900-2-29 1:2:3.456') + self.assertEqual(parse('2000-02-29 1:2:3')[:6], + (2000, 2, 29, 1, 2, 3)) + self.assertEqual(parse('2004-02-29 1:2:3')[:6], + (2004, 2, 29, 1, 2, 3)) + + def test_tzoffset(self): + self.assertEqual(_tzoffset('-0400', None), -4*60*60) + self.assertEqual(_tzoffset('-0030', None), -30*60) + self.assertEqual(_tzoffset('+0200', None), 2*60*60) + self.assertEqual(_tzoffset('EET', None), 2*60*60) + + def testParseDatetimetz(self): + self.assertEqual(parseDatetimetz('1999-12-31T01:02:03.037-00:30'), + datetime(1999, 12, 31, 1, 2, 3, 37000, tzinfo(-30))) + self.assertEqual(parseDatetimetz('2003 6 4 00:00:00 ', local=False), + datetime(2003, 6, 4)) + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/datetime/tests/test_standard_dates.py zope3-3.5~bzr18/src/zope/datetime/tests/test_standard_dates.py --- zope3-3.4.0/src/zope/datetime/tests/test_standard_dates.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/datetime/tests/test_standard_dates.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 standard date parsing + +$Id: test_standard_dates.py 68457 2006-06-02 11:03:06Z hdima $ +""" +import unittest + +from zope.datetime import time + +class Test(unittest.TestCase): + + def testiso8601_date(self): + from zope.datetime import iso8601_date + self.assertEqual(iso8601_date(time("2000-01-01T01:01:01.234Z")), + "2000-01-01T01:01:01Z") + + def testrfc850_date(self): + from zope.datetime import rfc850_date + self.assertEqual(rfc850_date(time("2002-01-12T01:01:01.234Z")), + "Saturday, 12-Jan-02 01:01:01 GMT") + + def testrfc1123_date(self): + from zope.datetime import rfc1123_date + self.assertEqual(rfc1123_date(time("2002-01-12T01:01:01.234Z")), + "Sat, 12 Jan 2002 01:01:01 GMT") + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/datetime/tests/test_tzinfo.py zope3-3.5~bzr18/src/zope/datetime/tests/test_tzinfo.py --- zope3-3.4.0/src/zope/datetime/tests/test_tzinfo.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/datetime/tests/test_tzinfo.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,54 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 for the 'tzinfo() function + +$Id: test_tzinfo.py 66343 2006-04-03 04:59:49Z philikon $ +""" + +from unittest import TestCase, TestSuite, main, makeSuite +import pickle +import datetime + +from zope.datetime import tzinfo +class Test(TestCase): + + def test(self): + + for minutes in 1439, 600, 1, 0, -1, -600, -1439: + info1 = tzinfo(minutes) + info2 = tzinfo(minutes) + + self.assertEqual(info1, info2) + self.assert_(info1 is info2) + self.assert_(pickle.loads(pickle.dumps(info1)) is info1) + + + self.assertEqual(info1.utcoffset(None), + datetime.timedelta(minutes=minutes)) + + self.assertEqual(info1.dst(None), None) + self.assertEqual(info1.tzname(None), None) + + for minutes in 900000, 1440*60, -1440*60, -900000: + self.assertRaises(ValueError, tzinfo, minutes) + + + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/datetime/timezones.py zope3-3.5~bzr18/src/zope/datetime/timezones.py --- zope3-3.4.0/src/zope/datetime/timezones.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/datetime/timezones.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1202 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +""" Historical timezone data, ported from Zope2's DateTime.DateTimeZone. + +$Id: timezones.py 66343 2006-04-03 04:59:49Z philikon $ +""" + +historical_zone_info = { +'Brazil/Acre': ('Brazil/Acre', 101, 2, +[ '561970800', '571644000', '593420400', '603093600', '625561200', +'634543200', '656924400', '665992800', '688374000', '697442400', +'719823600', '729496800', '751273200', '760946400', '782722800', +'792396000', '814863600', '823845600', '846226800', '855295200', +'877676400', '887436000', '909126000', '918799200', '940575600', +'950248800', '972716400', '981698400', '1004079600', '1013148000', +'1035529200', '1044597600', '1066978800', '1076738400', '1098428400', +'1108101600', '1129878000', '1139551200', '1162018800', '1171000800', +'1193382000', '1202450400', '1224831600', '1234591200', '1256281200', +'1265954400', '1287730800', '1297404000', '1319180400', '1328853600', +'1351234800', '1360303200', '1382684400', '1391752800', '1414134000', +'1423893600', '1445583600', '1455256800', '1477033200', '1486706400', +'1509174000', '1518156000', '1540537200', '1549605600', '1571986800', +'1581055200', '1603436400', '1613109600', '1634886000', '1644559200', +'1666335600', '1676008800', '1698476400', '1707458400', '1729839600', +'1738908000', '1761289200', '1771048800', '1792738800', '1802412000', +'1824188400', '1833861600', '1856329200', '1865311200', '1887692400', +'1896760800', '1919142000', '1928210400', '1950591600', '1960351200', +'1982041200', '1991714400', '2013490800', '2023164000', '2045631600', +'2054613600', '2076994800', '2086063200', '2108444400', '2118204000', +'2139894000', +], +'\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-18000, 0, 0), (-14400, 1, 4)], 'AST\x00ADT\x00'), +'Brazil/DeNoronha': ('Brazil/DeNoronha', 101, 2, +[ '561960000', '571633200', '593409600', '603082800', '625550400', +'634532400', '656913600', '665982000', '688363200', '697431600', +'719812800', '729486000', '751262400', '760935600', '782712000', +'792385200', '814852800', '823834800', '846216000', '855284400', +'877665600', '887425200', '909115200', '918788400', '940564800', +'950238000', '972705600', '981687600', '1004068800', '1013137200', +'1035518400', '1044586800', '1066968000', '1076727600', '1098417600', +'1108090800', '1129867200', '1139540400', '1162008000', '1170990000', +'1193371200', '1202439600', '1224820800', '1234580400', '1256270400', +'1265943600', '1287720000', '1297393200', '1319169600', '1328842800', +'1351224000', '1360292400', '1382673600', '1391742000', '1414123200', +'1423882800', '1445572800', '1455246000', '1477022400', '1486695600', +'1509163200', '1518145200', '1540526400', '1549594800', '1571976000', +'1581044400', '1603425600', '1613098800', '1634875200', '1644548400', +'1666324800', '1675998000', '1698465600', '1707447600', '1729828800', +'1738897200', '1761278400', '1771038000', '1792728000', '1802401200', +'1824177600', '1833850800', '1856318400', '1865300400', '1887681600', +'1896750000', '1919131200', '1928199600', '1950580800', '1960340400', +'1982030400', '1991703600', '2013480000', '2023153200', '2045620800', +'2054602800', '2076984000', '2086052400', '2108433600', '2118193200', +'2139883200', +], +'\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-7200, 0, 0), (-3600, 1, 4)], 'FST\x00FDT\x00'), +'Brazil/East': ('Brazil/East', 101, 2, +[ '561963600', '571636800', '593413200', '603086400', '625554000', +'634536000', '656917200', '665985600', '688366800', '697435200', +'719816400', '729489600', '751266000', '760939200', '782715600', +'792388800', '814856400', '823838400', '846219600', '855288000', +'877669200', '887428800', '909118800', '918792000', '940568400', +'950241600', '972709200', '981691200', '1004072400', '1013140800', +'1035522000', '1044590400', '1066971600', '1076731200', '1098421200', +'1108094400', '1129870800', '1139544000', '1162011600', '1170993600', +'1193374800', '1202443200', '1224824400', '1234584000', '1256274000', +'1265947200', '1287723600', '1297396800', '1319173200', '1328846400', +'1351227600', '1360296000', '1382677200', '1391745600', '1414126800', +'1423886400', '1445576400', '1455249600', '1477026000', '1486699200', +'1509166800', '1518148800', '1540530000', '1549598400', '1571979600', +'1581048000', '1603429200', '1613102400', '1634878800', '1644552000', +'1666328400', '1676001600', '1698469200', '1707451200', '1729832400', +'1738900800', '1761282000', '1771041600', '1792731600', '1802404800', +'1824181200', '1833854400', '1856322000', '1865304000', '1887685200', +'1896753600', '1919134800', '1928203200', '1950584400', '1960344000', +'1982034000', '1991707200', '2013483600', '2023156800', '2045624400', +'2054606400', '2076987600', '2086056000', '2108437200', '2118196800', +'2139886800', +], +'\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-10800, 0, 0), (-7200, 1, 4)], 'EST\x00EDT\x00'), +'Brazil/West': ('Brazil/West', 101, 2, +[ '561967200', '571640400', '593416800', '603090000', '625557600', +'634539600', '656920800', '665989200', '688370400', '697438800', +'719820000', '729493200', '751269600', '760942800', '782719200', +'792392400', '814860000', '823842000', '846223200', '855291600', +'877672800', '887432400', '909122400', '918795600', '940572000', +'950245200', '972712800', '981694800', '1004076000', '1013144400', +'1035525600', '1044594000', '1066975200', '1076734800', '1098424800', +'1108098000', '1129874400', '1139547600', '1162015200', '1170997200', +'1193378400', '1202446800', '1224828000', '1234587600', '1256277600', +'1265950800', '1287727200', '1297400400', '1319176800', '1328850000', +'1351231200', '1360299600', '1382680800', '1391749200', '1414130400', +'1423890000', '1445580000', '1455253200', '1477029600', '1486702800', +'1509170400', '1518152400', '1540533600', '1549602000', '1571983200', +'1581051600', '1603432800', '1613106000', '1634882400', '1644555600', +'1666332000', '1676005200', '1698472800', '1707454800', '1729836000', +'1738904400', '1761285600', '1771045200', '1792735200', '1802408400', +'1824184800', '1833858000', '1856325600', '1865307600', '1887688800', +'1896757200', '1919138400', '1928206800', '1950588000', '1960347600', +'1982037600', '1991710800', '2013487200', '2023160400', '2045628000', +'2054610000', '2076991200', '2086059600', '2108440800', '2118200400', +'2139890400', +], +'\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-14400, 0, 0), (-10800, 1, 4)], 'WST\x00WDT\x00'), +'Canada/Atlantic': ('Canada/Atlantic', 138, 2, +[ '-21492000', '-5770800', '9957600', '25678800', '41407200', +'57733200', '73461600', '89182800', '104911200', '120632400', +'136360800', '152082000', '167810400', '183531600', '199260000', +'215586000', '230709600', '247035600', '262764000', '278485200', +'294213600', '309934800', '325663200', '341384400', '357112800', +'372834000', '388562400', '404888400', '420012000', '436338000', +'452066400', '467787600', '483516000', '499237200', '514965600', +'530686800', '544600800', '562136400', '576050400', '594190800', +'607500000', '625640400', '638949600', '657090000', '671004000', +'688539600', '702453600', '719989200', '733903200', '752043600', +'765352800', '783493200', '796802400', '814942800', '828856800', +'846392400', '860306400', '877842000', '891756000', '909291600', +'923205600', '941346000', '954655200', '972795600', '986104800', +'1004245200', '1018159200', '1035694800', '1049608800', '1067144400', +'1081058400', '1099198800', '1112508000', '1130648400', '1143957600', +'1162098000', '1175407200', '1193547600', '1207461600', '1224997200', +'1238911200', '1256446800', '1270360800', '1288501200', '1301810400', +'1319950800', '1333260000', '1351400400', '1365314400', '1382850000', +'1396764000', '1414299600', '1428213600', '1445749200', '1459663200', +'1477803600', '1491112800', '1509253200', '1522562400', '1540702800', +'1554616800', '1572152400', '1586066400', '1603602000', '1617516000', +'1635656400', '1648965600', '1667106000', '1680415200', '1698555600', +'1712469600', '1730005200', '1743919200', '1761454800', '1775368800', +'1792904400', '1806818400', '1824958800', '1838268000', '1856408400', +'1869717600', '1887858000', '1901772000', '1919307600', '1933221600', +'1950757200', '1964671200', '1982811600', '1996120800', '2014261200', +'2027570400', '2045710800', '2059020000', '2077160400', '2091074400', +'2108610000', '2122524000', '2140059600', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-10800, 1, 0), (-14400, 0, 4)], 'ADT\x00AST\x00'), +'Canada/Central': ('Canada/Central', 138, 2, +[ '-21484800', '-5763600', '9964800', '25686000', '41414400', +'57740400', '73468800', '89190000', '104918400', '120639600', +'136368000', '152089200', '167817600', '183538800', '199267200', +'215593200', '230716800', '247042800', '262771200', '278492400', +'294220800', '309942000', '325670400', '341391600', '357120000', +'372841200', '388569600', '404895600', '420019200', '436345200', +'452073600', '467794800', '483523200', '499244400', '514972800', +'530694000', '544608000', '562143600', '576057600', '594198000', +'607507200', '625647600', '638956800', '657097200', '671011200', +'688546800', '702460800', '719996400', '733910400', '752050800', +'765360000', '783500400', '796809600', '814950000', '828864000', +'846399600', '860313600', '877849200', '891763200', '909298800', +'923212800', '941353200', '954662400', '972802800', '986112000', +'1004252400', '1018166400', '1035702000', '1049616000', '1067151600', +'1081065600', '1099206000', '1112515200', '1130655600', '1143964800', +'1162105200', '1175414400', '1193554800', '1207468800', '1225004400', +'1238918400', '1256454000', '1270368000', '1288508400', '1301817600', +'1319958000', '1333267200', '1351407600', '1365321600', '1382857200', +'1396771200', '1414306800', '1428220800', '1445756400', '1459670400', +'1477810800', '1491120000', '1509260400', '1522569600', '1540710000', +'1554624000', '1572159600', '1586073600', '1603609200', '1617523200', +'1635663600', '1648972800', '1667113200', '1680422400', '1698562800', +'1712476800', '1730012400', '1743926400', '1761462000', '1775376000', +'1792911600', '1806825600', '1824966000', '1838275200', '1856415600', +'1869724800', '1887865200', '1901779200', '1919314800', '1933228800', +'1950764400', '1964678400', '1982818800', '1996128000', '2014268400', +'2027577600', '2045718000', '2059027200', '2077167600', '2091081600', +'2108617200', '2122531200', '2140066800', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-18000, 1, 0), (-21600, 0, 4)], 'CDT\x00CST\x00'), +'Canada/East-Saskatchewan': ('Canada/East-Saskatchewan', 0, 1, +[ ], +'', +[(-21600, 0, 0)], 'CST\x00'), +'Canada/Eastern': ('Canada/Eastern', 138, 2, +[ '-21488400', '-5767200', '9961200', '25682400', '41410800', +'57736800', '73465200', '89186400', '104914800', '120636000', +'136364400', '152085600', '167814000', '183535200', '199263600', +'215589600', '230713200', '247039200', '262767600', '278488800', +'294217200', '309938400', '325666800', '341388000', '357116400', +'372837600', '388566000', '404892000', '420015600', '436341600', +'452070000', '467791200', '483519600', '499240800', '514969200', +'530690400', '544604400', '562140000', '576054000', '594194400', +'607503600', '625644000', '638953200', '657093600', '671007600', +'688543200', '702457200', '719992800', '733906800', '752047200', +'765356400', '783496800', '796806000', '814946400', '828860400', +'846396000', '860310000', '877845600', '891759600', '909295200', +'923209200', '941349600', '954658800', '972799200', '986108400', +'1004248800', '1018162800', '1035698400', '1049612400', '1067148000', +'1081062000', '1099202400', '1112511600', '1130652000', '1143961200', +'1162101600', '1175410800', '1193551200', '1207465200', '1225000800', +'1238914800', '1256450400', '1270364400', '1288504800', '1301814000', +'1319954400', '1333263600', '1351404000', '1365318000', '1382853600', +'1396767600', '1414303200', '1428217200', '1445752800', '1459666800', +'1477807200', '1491116400', '1509256800', '1522566000', '1540706400', +'1554620400', '1572156000', '1586070000', '1603605600', '1617519600', +'1635660000', '1648969200', '1667109600', '1680418800', '1698559200', +'1712473200', '1730008800', '1743922800', '1761458400', '1775372400', +'1792908000', '1806822000', '1824962400', '1838271600', '1856412000', +'1869721200', '1887861600', '1901775600', '1919311200', '1933225200', +'1950760800', '1964674800', '1982815200', '1996124400', '2014264800', +'2027574000', '2045714400', '2059023600', '2077164000', '2091078000', +'2108613600', '2122527600', '2140063200', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-14400, 1, 0), (-18000, 0, 4)], 'EDT\x00EST\x00'), +'Canada/Mountain': ('Canada/Mountain', 138, 2, +[ '-21481200', '-5760000', '9968400', '25689600', '41418000', +'57744000', '73472400', '89193600', '104922000', '120643200', +'136371600', '152092800', '167821200', '183542400', '199270800', +'215596800', '230720400', '247046400', '262774800', '278496000', +'294224400', '309945600', '325674000', '341395200', '357123600', +'372844800', '388573200', '404899200', '420022800', '436348800', +'452077200', '467798400', '483526800', '499248000', '514976400', +'530697600', '544611600', '562147200', '576061200', '594201600', +'607510800', '625651200', '638960400', '657100800', '671014800', +'688550400', '702464400', '720000000', '733914000', '752054400', +'765363600', '783504000', '796813200', '814953600', '828867600', +'846403200', '860317200', '877852800', '891766800', '909302400', +'923216400', '941356800', '954666000', '972806400', '986115600', +'1004256000', '1018170000', '1035705600', '1049619600', '1067155200', +'1081069200', '1099209600', '1112518800', '1130659200', '1143968400', +'1162108800', '1175418000', '1193558400', '1207472400', '1225008000', +'1238922000', '1256457600', '1270371600', '1288512000', '1301821200', +'1319961600', '1333270800', '1351411200', '1365325200', '1382860800', +'1396774800', '1414310400', '1428224400', '1445760000', '1459674000', +'1477814400', '1491123600', '1509264000', '1522573200', '1540713600', +'1554627600', '1572163200', '1586077200', '1603612800', '1617526800', +'1635667200', '1648976400', '1667116800', '1680426000', '1698566400', +'1712480400', '1730016000', '1743930000', '1761465600', '1775379600', +'1792915200', '1806829200', '1824969600', '1838278800', '1856419200', +'1869728400', '1887868800', '1901782800', '1919318400', '1933232400', +'1950768000', '1964682000', '1982822400', '1996131600', '2014272000', +'2027581200', '2045721600', '2059030800', '2077171200', '2091085200', +'2108620800', '2122534800', '2140070400', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-21600, 1, 0), (-25200, 0, 4)], 'MDT\x00MST\x00'), +'Canada/Newfoundland': ('Canada/Newfoundland', 138, 2, +[ '-21493800', '-5772600', '9955800', '25677000', '41405400', +'57731400', '73459800', '89181000', '104909400', '120630600', +'136359000', '152080200', '167808600', '183529800', '199258200', +'215584200', '230707800', '247033800', '262762200', '278483400', +'294211800', '309933000', '325661400', '341382600', '357111000', +'372832200', '388560600', '404886600', '420010200', '436336200', +'452064600', '467785800', '483514200', '499235400', '514963800', +'530685000', '544599000', '562134600', '576048600', '594189000', +'607498200', '625638600', '638947800', '657088200', '671002200', +'688537800', '702451800', '719987400', '733901400', '752041800', +'765351000', '783491400', '796800600', '814941000', '828855000', +'846390600', '860304600', '877840200', '891754200', '909289800', +'923203800', '941344200', '954653400', '972793800', '986103000', +'1004243400', '1018157400', '1035693000', '1049607000', '1067142600', +'1081056600', '1099197000', '1112506200', '1130646600', '1143955800', +'1162096200', '1175405400', '1193545800', '1207459800', '1224995400', +'1238909400', '1256445000', '1270359000', '1288499400', '1301808600', +'1319949000', '1333258200', '1351398600', '1365312600', '1382848200', +'1396762200', '1414297800', '1428211800', '1445747400', '1459661400', +'1477801800', '1491111000', '1509251400', '1522560600', '1540701000', +'1554615000', '1572150600', '1586064600', '1603600200', '1617514200', +'1635654600', '1648963800', '1667104200', '1680413400', '1698553800', +'1712467800', '1730003400', '1743917400', '1761453000', '1775367000', +'1792902600', '1806816600', '1824957000', '1838266200', '1856406600', +'1869715800', '1887856200', '1901770200', '1919305800', '1933219800', +'1950755400', '1964669400', '1982809800', '1996119000', '2014259400', +'2027568600', '2045709000', '2059018200', '2077158600', '2091072600', +'2108608200', '2122522200', '2140057800', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-9000, 1, 0), (-12600, 0, 4)], 'NDT\x00NST\x00'), +'Canada/Pacific': ('Canada/Pacific', 138, 2, +[ '-21477600', '-5756400', '9972000', '25693200', '41421600', +'57747600', '73476000', '89197200', '104925600', '120646800', +'136375200', '152096400', '167824800', '183546000', '199274400', +'215600400', '230724000', '247050000', '262778400', '278499600', +'294228000', '309949200', '325677600', '341398800', '357127200', +'372848400', '388576800', '404902800', '420026400', '436352400', +'452080800', '467802000', '483530400', '499251600', '514980000', +'530701200', '544615200', '562150800', '576064800', '594205200', +'607514400', '625654800', '638964000', '657104400', '671018400', +'688554000', '702468000', '720003600', '733917600', '752058000', +'765367200', '783507600', '796816800', '814957200', '828871200', +'846406800', '860320800', '877856400', '891770400', '909306000', +'923220000', '941360400', '954669600', '972810000', '986119200', +'1004259600', '1018173600', '1035709200', '1049623200', '1067158800', +'1081072800', '1099213200', '1112522400', '1130662800', '1143972000', +'1162112400', '1175421600', '1193562000', '1207476000', '1225011600', +'1238925600', '1256461200', '1270375200', '1288515600', '1301824800', +'1319965200', '1333274400', '1351414800', '1365328800', '1382864400', +'1396778400', '1414314000', '1428228000', '1445763600', '1459677600', +'1477818000', '1491127200', '1509267600', '1522576800', '1540717200', +'1554631200', '1572166800', '1586080800', '1603616400', '1617530400', +'1635670800', '1648980000', '1667120400', '1680429600', '1698570000', +'1712484000', '1730019600', '1743933600', '1761469200', '1775383200', +'1792918800', '1806832800', '1824973200', '1838282400', '1856422800', +'1869732000', '1887872400', '1901786400', '1919322000', '1933236000', +'1950771600', '1964685600', '1982826000', '1996135200', '2014275600', +'2027584800', '2045725200', '2059034400', '2077174800', '2091088800', +'2108624400', '2122538400', '2140074000', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-25200, 1, 0), (-28800, 0, 4)], 'PDT\x00PST\x00'), +'Canada/Yukon': ('Canada/Yukon', 138, 2, +[ '-21474000', '-5752800', '9975600', '25696800', '41425200', +'57751200', '73479600', '89200800', '104929200', '120650400', +'136378800', '152100000', '167828400', '183549600', '199278000', +'215604000', '230727600', '247053600', '262782000', '278503200', +'294231600', '309952800', '325681200', '341402400', '357130800', +'372852000', '388580400', '404906400', '420030000', '436356000', +'452084400', '467805600', '483534000', '499255200', '514983600', +'530704800', '544618800', '562154400', '576068400', '594208800', +'607518000', '625658400', '638967600', '657108000', '671022000', +'688557600', '702471600', '720007200', '733921200', '752061600', +'765370800', '783511200', '796820400', '814960800', '828874800', +'846410400', '860324400', '877860000', '891774000', '909309600', +'923223600', '941364000', '954673200', '972813600', '986122800', +'1004263200', '1018177200', '1035712800', '1049626800', '1067162400', +'1081076400', '1099216800', '1112526000', '1130666400', '1143975600', +'1162116000', '1175425200', '1193565600', '1207479600', '1225015200', +'1238929200', '1256464800', '1270378800', '1288519200', '1301828400', +'1319968800', '1333278000', '1351418400', '1365332400', '1382868000', +'1396782000', '1414317600', '1428231600', '1445767200', '1459681200', +'1477821600', '1491130800', '1509271200', '1522580400', '1540720800', +'1554634800', '1572170400', '1586084400', '1603620000', '1617534000', +'1635674400', '1648983600', '1667124000', '1680433200', '1698573600', +'1712487600', '1730023200', '1743937200', '1761472800', '1775386800', +'1792922400', '1806836400', '1824976800', '1838286000', '1856426400', +'1869735600', '1887876000', '1901790000', '1919325600', '1933239600', +'1950775200', '1964689200', '1982829600', '1996138800', '2014279200', +'2027588400', '2045728800', '2059038000', '2077178400', '2091092400', +'2108628000', '2122542000', '2140077600', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-28800, 1, 0), (-32400, 0, 4)], 'YDT\x00YST\x00'), +'Chile/Continental': ('Chile/Continental', 121, 2, +[ '245217600', '258519600', '276667200', '289969200', '308721600', +'321418800', '340171200', '352868400', '371620800', '384922800', +'403070400', '416372400', '434520000', '447822000', '466574400', +'479271600', '498024000', '510721200', '529473600', '542170800', +'560923200', '574225200', '592372800', '605674800', '623822400', +'637124400', '655876800', '668574000', '687326400', '700023600', +'718776000', '732078000', '750225600', '763527600', '781675200', +'794977200', '813124800', '826426800', '845179200', '857876400', +'876628800', '889326000', '908078400', '921380400', '939528000', +'952830000', '970977600', '984279600', '1003032000', '1015729200', +'1034481600', '1047178800', '1065931200', '1079233200', '1097380800', +'1110682800', '1128830400', '1142132400', '1160280000', '1173582000', +'1192334400', '1205031600', '1223784000', '1236481200', '1255233600', +'1268535600', '1286683200', '1299985200', '1318132800', '1331434800', +'1350187200', '1362884400', '1381636800', '1394334000', '1413086400', +'1425783600', '1444536000', '1457838000', '1475985600', '1489287600', +'1507435200', '1520737200', '1539489600', '1552186800', '1570939200', +'1583636400', '1602388800', '1615690800', '1633838400', '1647140400', +'1665288000', '1678590000', '1696737600', '1710039600', '1728792000', +'1741489200', '1760241600', '1772938800', '1791691200', '1804993200', +'1823140800', '1836442800', '1854590400', '1867892400', '1886644800', +'1899342000', '1918094400', '1930791600', '1949544000', '1962846000', +'1980993600', '1994295600', '2012443200', '2025745200', '2043892800', +'2057194800', '2075947200', '2088644400', '2107396800', '2120094000', +'2138846400', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00', +[(-10800, 1, 0), (-14400, 0, 4)], 'CDT\x00CST\x00'), +'Chile/EasterIsland': ('Chile/EasterIsland', 121, 2, +[ '245224800', '258526800', '276674400', '289976400', '308728800', +'321426000', '340178400', '352875600', '371628000', '384930000', +'403077600', '416379600', '434527200', '447829200', '466581600', +'479278800', '498031200', '510728400', '529480800', '542178000', +'560930400', '574232400', '592380000', '605682000', '623829600', +'637131600', '655884000', '668581200', '687333600', '700030800', +'718783200', '732085200', '750232800', '763534800', '781682400', +'794984400', '813132000', '826434000', '845186400', '857883600', +'876636000', '889333200', '908085600', '921387600', '939535200', +'952837200', '970984800', '984286800', '1003039200', '1015736400', +'1034488800', '1047186000', '1065938400', '1079240400', '1097388000', +'1110690000', '1128837600', '1142139600', '1160287200', '1173589200', +'1192341600', '1205038800', '1223791200', '1236488400', '1255240800', +'1268542800', '1286690400', '1299992400', '1318140000', '1331442000', +'1350194400', '1362891600', '1381644000', '1394341200', '1413093600', +'1425790800', '1444543200', '1457845200', '1475992800', '1489294800', +'1507442400', '1520744400', '1539496800', '1552194000', '1570946400', +'1583643600', '1602396000', '1615698000', '1633845600', '1647147600', +'1665295200', '1678597200', '1696744800', '1710046800', '1728799200', +'1741496400', '1760248800', '1772946000', '1791698400', '1805000400', +'1823148000', '1836450000', '1854597600', '1867899600', '1886652000', +'1899349200', '1918101600', '1930798800', '1949551200', '1962853200', +'1981000800', '1994302800', '2012450400', '2025752400', '2043900000', +'2057202000', '2075954400', '2088651600', '2107404000', '2120101200', +'2138853600', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00', +[(-18000, 1, 0), (-21600, 0, 4)], 'EDT\x00EST\x00'), +'Cuba': ('Cuba', 118, 2, +[ '290581200', '308721600', '322030800', '340171200', '358318800', +'371620800', '389768400', '403070400', '421218000', '434520000', +'453272400', '466574400', '484722000', '498024000', '516171600', +'529473600', '547621200', '560923200', '579070800', '592372800', +'611125200', '623822400', '642574800', '655876800', '674024400', +'687326400', '705474000', '718776000', '736923600', '750225600', +'768373200', '781675200', '800427600', '813124800', '831877200', +'845179200', '863326800', '876628800', '894776400', '908078400', +'926226000', '939528000', '958280400', '970977600', '989730000', +'1003032000', '1021179600', '1034481600', '1052629200', '1065931200', +'1084078800', '1097380800', '1115528400', '1128830400', '1147582800', +'1160280000', '1179032400', '1192334400', '1210482000', '1223784000', +'1241931600', '1255233600', '1273381200', '1286683200', '1304830800', +'1318132800', '1336885200', '1350187200', '1368334800', '1381636800', +'1399784400', '1413086400', '1431234000', '1444536000', '1462683600', +'1475985600', '1494738000', '1507435200', '1526187600', '1539489600', +'1557637200', '1570939200', '1589086800', '1602388800', '1620536400', +'1633838400', '1651986000', '1665288000', '1684040400', '1696737600', +'1715490000', '1728792000', '1746939600', '1760241600', '1778389200', +'1791691200', '1809838800', '1823140800', '1841893200', '1854590400', +'1873342800', '1886644800', '1904792400', '1918094400', '1936242000', +'1949544000', '1967691600', '1980993600', '1999141200', '2012443200', +'2031195600', '2043892800', '2062645200', '2075947200', '2094094800', +'2107396800', '2125544400', '2138846400', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-14400, 1, 0), (-18000, 0, 4)], 'CDT\x00CST\x00'), +'Egypt': ('Egypt', 152, 2, +[ '-305164800', '-291949200', '-273628800', '-260413200', '-242092800', +'-228877200', '-210556800', '-197341200', '-178934400', '-165718800', +'-147398400', '-134182800', '-115862400', '-102646800', '-84326400', +'-71110800', '-52704000', '-39488400', '-21168000', '-7952400', +'10368000', '23583600', '41904000', '55119600', '73526400', +'86742000', '105062400', '118278000', '136598400', '149814000', +'168134400', '181350000', '199756800', '212972400', '231292800', +'244508400', '262828800', '276044400', '294364800', '307580400', +'325987200', '339202800', '420595200', '433810800', '452217600', +'465433200', '483753600', '496969200', '515289600', '528505200', +'546825600', '560041200', '578448000', '591663600', '609984000', +'623199600', '641520000', '654735600', '673056000', '686271600', +'704678400', '717894000', '736214400', '749430000', '767750400', +'780966000', '799286400', '812502000', '830908800', '844124400', +'862444800', '875660400', '893980800', '907196400', '925516800', +'938732400', '957139200', '970354800', '988675200', '1001890800', +'1020211200', '1033426800', '1051747200', '1064962800', '1083369600', +'1096585200', '1114905600', '1128121200', '1146441600', '1159657200', +'1177977600', '1191193200', '1209600000', '1222815600', '1241136000', +'1254351600', '1272672000', '1285887600', '1304208000', '1317423600', +'1335830400', '1349046000', '1367366400', '1380582000', '1398902400', +'1412118000', '1430438400', '1443654000', '1462060800', '1475276400', +'1493596800', '1506812400', '1525132800', '1538348400', '1556668800', +'1569884400', '1588291200', '1601506800', '1619827200', '1633042800', +'1651363200', '1664578800', '1682899200', '1696114800', '1714521600', +'1727737200', '1746057600', '1759273200', '1777593600', '1790809200', +'1809129600', '1822345200', '1840752000', '1853967600', '1872288000', +'1885503600', '1903824000', '1917039600', '1935360000', '1948575600', +'1966982400', '1980198000', '1998518400', '2011734000', '2030054400', +'2043270000', '2061590400', '2074806000', '2093212800', '2106428400', +'2124748800', '2137964400', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(10800, 1, 0), (7200, 0, 8)], 'EET DST\x00EET\x00'), +'GB-Eire': ('GB-Eire', 241, 4, +[ '-1697238000', '-1680476400', '-1664146800', '-1650150000', '-1633906800', +'-1617490800', '-1601852400', '-1586041200', '-1570402800', '-1552172400', +'-1538348400', '-1522537200', '-1507503600', '-1490569200', '-1473634800', +'-1458342000', '-1441321200', '-1428879600', '-1410735600', '-1396220400', +'-1379286000', '-1364770800', '-1347836400', '-1333321200', '-1316386800', +'-1301266800', '-1284332400', '-1269817200', '-1252882800', '-1238367600', +'-1221433200', '-1206918000', '-1189983600', '-1175468400', '-1158534000', +'-1144018800', '-1127084400', '-1111964400', '-1095030000', '-1080514800', +'-1063580400', '-1049065200', '-1032130800', '-1017615600', '-1000681200', +'-986166000', '-969231600', '-950482800', '-942015600', '-904518000', +'-896050800', '-875487600', '-864601200', '-844038000', '-832546800', +'-812588400', '-798073200', '-781052400', '-772066800', '-764809200', +'-748479600', '-733359600', '-719449200', '-717030000', '-706748400', +'-699490800', '-687999600', '-668041200', '-654735600', '-636591600', +'-622076400', '-605746800', '-590626800', '-574297200', '-558572400', +'-542242800', '-527122800', '-512607600', '-496278000', '-481158000', +'-464223600', '-449708400', '-432774000', '-417654000', '-401324400', +'-386204400', '-369270000', '-354754800', '-337820400', '-323305200', +'-306975600', '-291855600', '-276735600', '-257986800', '-245286000', +'-226537200', '-213231600', '-195087600', '-182386800', '-163638000', +'-150937200', '-132188400', '-119487600', '-100738800', '-88038000', +'-68684400', '-59007600', '-37238400', '57715200', '69814800', +'89168400', '101264400', '120618000', '132714000', '152067600', +'164163600', '183517200', '196218000', '214966800', '227667600', +'246416400', '259117200', '278470800', '290566800', '309920400', +'322016400', '341370000', '354675600', '372819600', '386125200', +'404269200', '417574800', '435718800', '449024400', '467773200', +'481078800', '499222800', '512528400', '530672400', '543978000', +'562122000', '575427600', '593571600', '606877200', '625626000', +'638326800', '657075600', '670381200', '688525200', '701830800', +'719974800', '733280400', '751424400', '764730000', '782874000', +'796179600', '814928400', '828234000', '846378000', '859683600', +'877827600', '891133200', '909277200', '922582800', '940726800', +'954032400', '972781200', '985482000', '1004230800', '1017536400', +'1035680400', '1048986000', '1067130000', '1080435600', '1098579600', +'1111885200', '1130029200', '1143334800', '1162083600', '1174784400', +'1193533200', '1206838800', '1224982800', '1238288400', '1256432400', +'1269738000', '1287882000', '1301187600', '1319331600', '1332637200', +'1351386000', '1364691600', '1382835600', '1396141200', '1414285200', +'1427590800', '1445734800', '1459040400', '1477184400', '1490490000', +'1509238800', '1521939600', '1540688400', '1553994000', '1572138000', +'1585443600', '1603587600', '1616893200', '1635037200', '1648342800', +'1666486800', '1679792400', '1698541200', '1711846800', '1729990800', +'1743296400', '1761440400', '1774746000', '1792890000', '1806195600', +'1824339600', '1837645200', '1856394000', '1869094800', '1887843600', +'1901149200', '1919293200', '1932598800', '1950742800', '1964048400', +'1982192400', '1995498000', '2013642000', '2026947600', '2045696400', +'2058397200', '2077146000', '2090451600', '2108595600', '2121901200', +'2140045200', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x02\x00\x02\x00\x02\x00\x02\x00\x02\x00\x01\x00\x01\x00\x02\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x03\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(3600, 1, 0), (0, 0, 4), (7200, 1, 8), (3600, 0, 0)], 'BST\x00GMT\x00DST\x00'), +'GMT': ('GMT', 0, 1, +[ ], +'', +[(0, 0, 0)], 'GMT\x00'), +'GMT+0': ('GMT+0', 0, 1, +[ ], +'', +[(0, 0, 0)], 'GMT+0000\x00'), +'GMT+0130': ('GMT+0130', 0, 1, +[ ], +'', +[(5400, 0, 0)], 'GMT+0130\x00'), +'GMT+0230': ('GMT+0230', 0, 1, +[ ], +'', +[(9000, 0, 0)], 'GMT+0230\x00'), +'GMT+0330': ('GMT+0330', 0, 1, +[ ], +'', +[(12600, 0, 0)], 'GMT+0330\x00'), +'GMT+0430': ('GMT+0430', 0, 1, +[ ], +'', +[(16200, 0, 0)], 'GMT+0430\x00'), +'GMT+0530': ('GMT+0530', 0, 1, +[ ], +'', +[(19800, 0, 0)], 'GMT+0530\x00'), +'GMT+0630': ('GMT+0630', 0, 1, +[ ], +'', +[(23400, 0, 0)], 'GMT+0630\x00'), +'GMT+0730': ('GMT+0730', 0, 1, +[ ], +'', +[(27000, 0, 0)], 'GMT+0730\x00'), +'GMT+0830': ('GMT+0830', 0, 1, +[ ], +'', +[(30600, 0, 0)], 'GMT+0830\x00'), +'GMT+0930': ('GMT+0930', 0, 1, +[ ], +'', +[(34200, 0, 0)], 'GMT+0930\x00'), +'GMT+1': ('GMT+1', 0, 1, +[ ], +'', +[(3600, 0, 0)], 'GMT+0100\x00'), +'GMT+10': ('GMT+10', 0, 1, +[ ], +'', +[(36000, 0, 0)], 'GMT+1000\x00'), +'GMT+1030': ('GMT+1030', 0, 1, +[ ], +'', +[(37800, 0, 0)], 'GMT+1030\x00'), +'GMT+11': ('GMT+11', 0, 1, +[ ], +'', +[(39600, 0, 0)], 'GMT+1100\x00'), +'GMT+1130': ('GMT+1130', 0, 1, +[ ], +'', +[(41400, 0, 0)], 'GMT+1130\x00'), +'GMT+12': ('GMT+12', 0, 1, +[ ], +'', +[(43200, 0, 0)], 'GMT+1200\x00'), +'GMT+1230': ('GMT+1230', 0, 1, +[ ], +'', +[(45000, 0, 0)], 'GMT+1230\x00'), +'GMT+13': ('GMT+13', 0, 1, +[ ], +'', +[(46800, 0, 0)], 'GMT+1300\x00'), +'GMT+2': ('GMT+2', 0, 1, +[ ], +'', +[(7200, 0, 0)], 'GMT+0200\x00'), +'GMT+3': ('GMT+3', 0, 1, +[ ], +'', +[(10800, 0, 0)], 'GMT+0300\x00'), +'GMT+4': ('GMT+4', 0, 1, +[ ], +'', +[(14400, 0, 0)], 'GMT+0400\x00'), +'GMT+5': ('GMT+5', 0, 1, +[ ], +'', +[(18000, 0, 0)], 'GMT+0500\x00'), +'GMT+6': ('GMT+6', 0, 1, +[ ], +'', +[(21600, 0, 0)], 'GMT+0600\x00'), +'GMT+7': ('GMT+7', 0, 1, +[ ], +'', +[(25200, 0, 0)], 'GMT+0700\x00'), +'GMT+8': ('GMT+8', 0, 1, +[ ], +'', +[(28800, 0, 0)], 'GMT+0800\x00'), +'GMT+9': ('GMT+9', 0, 1, +[ ], +'', +[(32400, 0, 0)], 'GMT+0900\x00'), +'GMT-0130': ('GMT-0130', 0, 1, +[ ], +'', +[(-5400, 0, 0)], 'GMT-0130\x00'), +'GMT-0230': ('GMT-0230', 0, 1, +[ ], +'', +[(-9000, 0, 0)], 'GMT-0230\x00'), +'GMT-0330': ('GMT-0330', 0, 1, +[ ], +'', +[(-12600, 0, 0)], 'GMT-0330\x00'), +'GMT-0430': ('GMT-0430', 0, 1, +[ ], +'', +[(-16200, 0, 0)], 'GMT-0430\x00'), +'GMT-0530': ('GMT-0530', 0, 1, +[ ], +'', +[(-19800, 0, 0)], 'GMT-0530\x00'), +'GMT-0630': ('GMT-0630', 0, 1, +[ ], +'', +[(-23400, 0, 0)], 'GMT-0630\x00'), +'GMT-0730': ('GMT-0730', 0, 1, +[ ], +'', +[(-27000, 0, 0)], 'GMT-0730\x00'), +'GMT-0830': ('GMT-0830', 0, 1, +[ ], +'', +[(-30600, 0, 0)], 'GMT-0830\x00'), +'GMT-0930': ('GMT-0930', 0, 1, +[ ], +'', +[(-34200, 0, 0)], 'GMT-0930\x00'), +'GMT-1': ('GMT-1', 0, 1, +[ ], +'', +[(-3600, 0, 0)], 'GMT-0100\x00'), +'GMT-10': ('GMT-10', 0, 1, +[ ], +'', +[(-36000, 0, 0)], 'GMT-1000\x00'), +'GMT-1030': ('GMT-1030', 0, 1, +[ ], +'', +[(-37800, 0, 0)], 'GMT-1030\x00'), +'GMT-11': ('GMT-11', 0, 1, +[ ], +'', +[(-39600, 0, 0)], 'GMT-1100\x00'), +'GMT-1130': ('GMT-1130', 0, 1, +[ ], +'', +[(-41400, 0, 0)], 'GMT-1130\x00'), +'GMT-12': ('GMT-12', 0, 1, +[ ], +'', +[(-43200, 0, 0)], 'GMT-1200\x00'), +'GMT-1230': ('GMT-1230', 0, 1, +[ ], +'', +[(-45000, 0, 0)], 'GMT-1230\x00'), +'GMT-2': ('GMT-2', 0, 1, +[ ], +'', +[(-7200, 0, 0)], 'GMT-0200\x00'), +'GMT-3': ('GMT-3', 0, 1, +[ ], +'', +[(-10800, 0, 0)], 'GMT-0300\x00'), +'GMT-4': ('GMT-4', 0, 1, +[ ], +'', +[(-14400, 0, 0)], 'GMT-0400\x00'), +'GMT-5': ('GMT-5', 0, 1, +[ ], +'', +[(-18000, 0, 0)], 'GMT-0500\x00'), +'GMT-6': ('GMT-6', 0, 1, +[ ], +'', +[(-21600, 0, 0)], 'GMT-0600\x00'), +'GMT-7': ('GMT-7', 0, 1, +[ ], +'', +[(-25200, 0, 0)], 'GMT-0700\x00'), +'GMT-8': ('GMT-8', 0, 1, +[ ], +'', +[(-28800, 0, 0)], 'GMT-0800\x00'), +'GMT-9': ('GMT-9', 0, 1, +[ ], +'', +[(-32400, 0, 0)], 'GMT-0900\x00'), +'Greenwich': ('Greenwich', 0, 1, +[ ], +'', +[(0, 0, 0)], 'GMT\x00'), +'Hongkong': ('Hongkong', 0, 1, +[ ], +'', +[(28800, 0, 0)], 'HKT\x00'), +'Iceland': ('Iceland', 0, 1, +[ ], +'', +[(0, 0, 0)], 'WET\x00'), +'Iran': ('Iran', 100, 2, +[ '575418600', '590535000', '606868200', '621984600', '638317800', +'653434200', '670372200', '684883800', '701821800', '716938200', +'733271400', '748387800', '764721000', '779837400', '796170600', +'811287000', '828225000', '842736600', '859674600', '874791000', +'891124200', '906240600', '922573800', '937690200', '954023400', +'969139800', '985473000', '1000589400', '1017527400', '1032039000', +'1048977000', '1064093400', '1080426600', '1095543000', '1111876200', +'1126992600', '1143325800', '1158442200', '1174775400', '1189891800', +'1206829800', '1221946200', '1238279400', '1253395800', '1269729000', +'1284845400', '1301178600', '1316295000', '1332628200', '1347744600', +'1364682600', '1379194200', '1396132200', '1411248600', '1427581800', +'1442698200', '1459031400', '1474147800', '1490481000', '1505597400', +'1521930600', '1537047000', '1553985000', '1568496600', '1585434600', +'1600551000', '1616884200', '1632000600', '1648333800', '1663450200', +'1679783400', '1694899800', '1711837800', '1726349400', '1743287400', +'1758403800', '1774737000', '1789853400', '1806186600', '1821303000', +'1837636200', '1852752600', '1869085800', '1884202200', '1901140200', +'1915651800', '1932589800', '1947706200', '1964039400', '1979155800', +'1995489000', '2010605400', '2026938600', '2042055000', '2058388200', +'2073504600', '2090442600', '2105559000', '2121892200', '2137008600', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(16200, 1, 0), (12600, 0, 4)], 'IDT\x00IST\x00'), +'Israel': ('Israel', 42, 2, +[ '609890400', '622587600', '640735200', '653432400', '670975200', +'683672400', '704239200', '716936400', '735084000', '747781200', +'765324000', '778021200', '798588000', '811285200', '829432800', +'842130000', '862696800', '875394000', '892936800', '905634000', +'923781600', '936478800', '957045600', '969742800', '987285600', +'999982800', '1018130400', '1030827600', '1051394400', '1064091600', +'1082239200', '1094936400', '1114898400', '1127595600', '1145743200', +'1158440400', '1176588000', '1189285200', '1209247200', '1221944400', +'1240092000', '1252789200', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(10800, 1, 0), (7200, 0, 4)], 'IDT\x00IST\x00'), +'Jamaica': ('Jamaica', 148, 3, +[ '-1633280400', '-1615140000', '-1601830800', '-1583690400', '-880218000', +'-765396000', '-84387600', '-68666400', '-52938000', '-37216800', +'-21488400', '-5767200', '9961200', '25682400', '41410800', +'57736800', '73465200', '89186400', '104914800', '120636000', +'126687600', '152085600', '162370800', '183535200', '199263600', +'215589600', '230713200', '247039200', '262767600', '278488800', +'294217200', '309938400', '325666800', '341388000', '357116400', +'372837600', '388566000', '404892000', '420015600', '436341600', +'452070000', '467791200', '483519600', '499240800', '514969200', +'530690400', '544604400', '562140000', '576054000', '594194400', +'607503600', '625644000', '638953200', '657093600', '671007600', +'688543200', '702457200', '719992800', '733906800', '752047200', +'765356400', '783496800', '796806000', '814946400', '828860400', +'846396000', '860310000', '877845600', '891759600', '909295200', +'923209200', '941349600', '954658800', '972799200', '986108400', +'1004248800', '1018162800', '1035698400', '1049612400', '1067148000', +'1081062000', '1099202400', '1112511600', '1130652000', '1143961200', +'1162101600', '1175410800', '1193551200', '1207465200', '1225000800', +'1238914800', '1256450400', '1270364400', '1288504800', '1301814000', +'1319954400', '1333263600', '1351404000', '1365318000', '1382853600', +'1396767600', '1414303200', '1428217200', '1445752800', '1459666800', +'1477807200', '1491116400', '1509256800', '1522566000', '1540706400', +'1554620400', '1572156000', '1586070000', '1603605600', '1617519600', +'1635660000', '1648969200', '1667109600', '1680418800', '1698559200', +'1712473200', '1730008800', '1743922800', '1761458400', '1775372400', +'1792908000', '1806822000', '1824962400', '1838271600', '1856412000', +'1869721200', '1887861600', '1901775600', '1919311200', '1933225200', +'1950760800', '1964674800', '1982815200', '1996124400', '2014264800', +'2027574000', '2045714400', '2059023600', '2077164000', '2091078000', +'2108613600', '2122527600', '2140063200', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-14400, 1, 0), (-18000, 0, 4), (-14400, 1, 8)], 'EDT\x00EST\x00EWT\x00'), +'Japan': ('Japan', 0, 1, +[ ], +'', +[(32400, 0, 0)], 'JST\x00'), +'Mexico/BajaNorte': ('Mexico/BajaNorte', 102, 2, +[ '544615200', '562150800', '576064800', '594205200', '607514400', +'625654800', '638964000', '657104400', '671018400', '688554000', +'702468000', '720003600', '733917600', '752058000', '765367200', +'783507600', '796816800', '814957200', '828871200', '846406800', +'860320800', '877856400', '891770400', '909306000', '923220000', +'941360400', '954669600', '972810000', '986119200', '1004259600', +'1018173600', '1035709200', '1049623200', '1067158800', '1081072800', +'1099213200', '1112522400', '1130662800', '1143972000', '1162112400', +'1175421600', '1193562000', '1207476000', '1225011600', '1238925600', +'1256461200', '1270375200', '1288515600', '1301824800', '1319965200', +'1333274400', '1351414800', '1365328800', '1382864400', '1396778400', +'1414314000', '1428228000', '1445763600', '1459677600', '1477818000', +'1491127200', '1509267600', '1522576800', '1540717200', '1554631200', +'1572166800', '1586080800', '1603616400', '1617530400', '1635670800', +'1648980000', '1667120400', '1680429600', '1698570000', '1712484000', +'1730019600', '1743933600', '1761469200', '1775383200', '1792918800', +'1806832800', '1824973200', '1838282400', '1856422800', '1869732000', +'1887872400', '1901786400', '1919322000', '1933236000', '1950771600', +'1964685600', '1982826000', '1996135200', '2014275600', '2027584800', +'2045725200', '2059034400', '2077174800', '2091088800', '2108624400', +'2122538400', '2140074000', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-25200, 1, 0), (-28800, 0, 4)], 'PDT\x00PST\x00'), +'Mexico/BajaSur': ('Mexico/BajaSur', 0, 1, +[ ], +'', +[(-25200, 0, 0)], 'MST\x00'), +'Mexico/General': ('Mexico/General', 0, 1, +[ ], +'', +[(-21600, 0, 0)], 'CST\x00'), +'Poland': ('Poland', 104, 2, +[ '512524800', '528249600', '543974400', '559699200', '575424000', +'591148800', '606873600', '622598400', '638323200', '654652800', +'670377600', '686102400', '701827200', '717552000', '733276800', +'749001600', '764726400', '780451200', '796176000', '811900800', +'828230400', '843955200', '859680000', '875404800', '891129600', +'906854400', '922579200', '938304000', '954028800', '969753600', +'985478400', '1001808000', '1017532800', '1033257600', '1048982400', +'1064707200', '1080432000', '1096156800', '1111881600', '1127606400', +'1143331200', '1159056000', '1174780800', '1191110400', '1206835200', +'1222560000', '1238284800', '1254009600', '1269734400', '1285459200', +'1301184000', '1316908800', '1332633600', '1348963200', '1364688000', +'1380412800', '1396137600', '1411862400', '1427587200', '1443312000', +'1459036800', '1474761600', '1490486400', '1506211200', '1521936000', +'1538265600', '1553990400', '1569715200', '1585440000', '1601164800', +'1616889600', '1632614400', '1648339200', '1664064000', '1679788800', +'1695513600', '1711843200', '1727568000', '1743292800', '1759017600', +'1774742400', '1790467200', '1806192000', '1821916800', '1837641600', +'1853366400', '1869091200', '1885420800', '1901145600', '1916870400', +'1932595200', '1948320000', '1964044800', '1979769600', '1995494400', +'2011219200', '2026944000', '2042668800', '2058393600', '2074723200', +'2090448000', '2106172800', '2121897600', '2137622400', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(7200, 1, 0), (3600, 0, 8)], 'MET DST\x00MET\x00'), +'Singapore': ('Singapore', 0, 1, +[ ], +'', +[(28800, 0, 0)], 'SST\x00'), +'Turkey': ('Turkey', 104, 2, +[ '512517600', '528238800', '543967200', '559688400', '575416800', +'591138000', '606866400', '622587600', '638316000', '654642000', +'670370400', '686091600', '701820000', '717541200', '733269600', +'748990800', '764719200', '780440400', '796168800', '811890000', +'828223200', '843944400', '859672800', '875394000', '891122400', +'906843600', '922572000', '938293200', '954021600', '969742800', +'985471200', '1001797200', '1017525600', '1033246800', '1048975200', +'1064696400', '1080424800', '1096146000', '1111874400', '1127595600', +'1143324000', '1159045200', '1174773600', '1191099600', '1206828000', +'1222549200', '1238277600', '1253998800', '1269727200', '1285448400', +'1301176800', '1316898000', '1332626400', '1348952400', '1364680800', +'1380402000', '1396130400', '1411851600', '1427580000', '1443301200', +'1459029600', '1474750800', '1490479200', '1506200400', '1521928800', +'1538254800', '1553983200', '1569704400', '1585432800', '1601154000', +'1616882400', '1632603600', '1648332000', '1664053200', '1679781600', +'1695502800', '1711836000', '1727557200', '1743285600', '1759006800', +'1774735200', '1790456400', '1806184800', '1821906000', '1837634400', +'1853355600', '1869084000', '1885410000', '1901138400', '1916859600', +'1932588000', '1948309200', '1964037600', '1979758800', '1995487200', +'2011208400', '2026936800', '2042658000', '2058386400', '2074712400', +'2090440800', '2106162000', '2121890400', '2137611600', +], +'\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(14400, 1, 0), (10800, 0, 8)], 'EET DST\x00EET\x00'), +'US/Alaska': ('US/Alaska', 148, 3, +[ '-1633266000', '-1615125600', '-1601816400', '-1583676000', '-880203600', +'-765381600', '-84373200', '-68652000', '-52923600', '-37202400', +'-21474000', '-5752800', '9975600', '25696800', '41425200', +'57751200', '73479600', '89200800', '104929200', '120650400', +'126702000', '152100000', '162385200', '183549600', '199278000', +'215604000', '230727600', '247053600', '262782000', '278503200', +'294231600', '309952800', '325681200', '341402400', '357130800', +'372852000', '388580400', '404906400', '420030000', '436356000', +'452084400', '467805600', '483534000', '499255200', '514983600', +'530704800', '544618800', '562154400', '576068400', '594208800', +'607518000', '625658400', '638967600', '657108000', '671022000', +'688557600', '702471600', '720007200', '733921200', '752061600', +'765370800', '783511200', '796820400', '814960800', '828874800', +'846410400', '860324400', '877860000', '891774000', '909309600', +'923223600', '941364000', '954673200', '972813600', '986122800', +'1004263200', '1018177200', '1035712800', '1049626800', '1067162400', +'1081076400', '1099216800', '1112526000', '1130666400', '1143975600', +'1162116000', '1175425200', '1193565600', '1207479600', '1225015200', +'1238929200', '1256464800', '1270378800', '1288519200', '1301828400', +'1319968800', '1333278000', '1351418400', '1365332400', '1382868000', +'1396782000', '1414317600', '1428231600', '1445767200', '1459681200', +'1477821600', '1491130800', '1509271200', '1522580400', '1540720800', +'1554634800', '1572170400', '1586084400', '1603620000', '1617534000', +'1635674400', '1648983600', '1667124000', '1680433200', '1698573600', +'1712487600', '1730023200', '1743937200', '1761472800', '1775386800', +'1792922400', '1806836400', '1824976800', '1838286000', '1856426400', +'1869735600', '1887876000', '1901790000', '1919325600', '1933239600', +'1950775200', '1964689200', '1982829600', '1996138800', '2014279200', +'2027588400', '2045728800', '2059038000', '2077178400', '2091092400', +'2108628000', '2122542000', '2140077600', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-28800, 1, 0), (-32400, 0, 5), (-28800, 1, 10)], 'AKDT\x00AKST\x00AKWT\x00'), +'US/Aleutian': ('US/Aleutian', 149, 5, +[ '-1633262400', '-1615122000', '-1601812800', '-1583672400', '-880200000', +'-765378000', '-84369600', '-68648400', '-52920000', '-37198800', +'-21470400', '-5749200', '9979200', '25700400', '41428800', +'57754800', '73483200', '89204400', '104932800', '120654000', +'126705600', '152103600', '162388800', '183553200', '199281600', +'215607600', '230731200', '247057200', '262785600', '278506800', +'294235200', '309956400', '325684800', '341406000', '357134400', +'372855600', '388584000', '404910000', '420033600', '436359600', +'439034400', '452088000', '467809200', '483537600', '499258800', +'514987200', '530708400', '544622400', '562158000', '576072000', +'594212400', '607521600', '625662000', '638971200', '657111600', +'671025600', '688561200', '702475200', '720010800', '733924800', +'752065200', '765374400', '783514800', '796824000', '814964400', +'828878400', '846414000', '860328000', '877863600', '891777600', +'909313200', '923227200', '941367600', '954676800', '972817200', +'986126400', '1004266800', '1018180800', '1035716400', '1049630400', +'1067166000', '1081080000', '1099220400', '1112529600', '1130670000', +'1143979200', '1162119600', '1175428800', '1193569200', '1207483200', +'1225018800', '1238932800', '1256468400', '1270382400', '1288522800', +'1301832000', '1319972400', '1333281600', '1351422000', '1365336000', +'1382871600', '1396785600', '1414321200', '1428235200', '1445770800', +'1459684800', '1477825200', '1491134400', '1509274800', '1522584000', +'1540724400', '1554638400', '1572174000', '1586088000', '1603623600', +'1617537600', '1635678000', '1648987200', '1667127600', '1680436800', +'1698577200', '1712491200', '1730026800', '1743940800', '1761476400', +'1775390400', '1792926000', '1806840000', '1824980400', '1838289600', +'1856430000', '1869739200', '1887879600', '1901793600', '1919329200', +'1933243200', '1950778800', '1964692800', '1982833200', '1996142400', +'2014282800', '2027592000', '2045732400', '2059041600', '2077182000', +'2091096000', '2108631600', '2122545600', '2140081200', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03', +[(-32400, 1, 0), (-36000, 0, 5), (-32400, 1, 10), (-36000, 0, 15), (-32400, 1, 20)], 'AHDT\x00AHST\x00AHWT\x00HAST\x00HADT\x00'), +'US/Arizona': ('US/Arizona', 6, 3, +[ '-1633273200', '-1615132800', '-1601823600', '-1583683200', '-880210800', +'-765388800', +], +'\x00\x01\x00\x01\x02\x01', +[(-21600, 1, 0), (-25200, 0, 4), (-21600, 1, 8)], 'MDT\x00MST\x00MWT\x00'), +'US/Central': ('US/Central', 148, 3, +[ '-1633276800', '-1615136400', '-1601827200', '-1583686800', '-880214400', +'-765392400', '-84384000', '-68662800', '-52934400', '-37213200', +'-21484800', '-5763600', '9964800', '25686000', '41414400', +'57740400', '73468800', '89190000', '104918400', '120639600', +'126691200', '152089200', '162374400', '183538800', '199267200', +'215593200', '230716800', '247042800', '262771200', '278492400', +'294220800', '309942000', '325670400', '341391600', '357120000', +'372841200', '388569600', '404895600', '420019200', '436345200', +'452073600', '467794800', '483523200', '499244400', '514972800', +'530694000', '544608000', '562143600', '576057600', '594198000', +'607507200', '625647600', '638956800', '657097200', '671011200', +'688546800', '702460800', '719996400', '733910400', '752050800', +'765360000', '783500400', '796809600', '814950000', '828864000', +'846399600', '860313600', '877849200', '891763200', '909298800', +'923212800', '941353200', '954662400', '972802800', '986112000', +'1004252400', '1018166400', '1035702000', '1049616000', '1067151600', +'1081065600', '1099206000', '1112515200', '1130655600', '1143964800', +'1162105200', '1175414400', '1193554800', '1207468800', '1225004400', +'1238918400', '1256454000', '1270368000', '1288508400', '1301817600', +'1319958000', '1333267200', '1351407600', '1365321600', '1382857200', +'1396771200', '1414306800', '1428220800', '1445756400', '1459670400', +'1477810800', '1491120000', '1509260400', '1522569600', '1540710000', +'1554624000', '1572159600', '1586073600', '1603609200', '1617523200', +'1635663600', '1648972800', '1667113200', '1680422400', '1698562800', +'1712476800', '1730012400', '1743926400', '1761462000', '1775376000', +'1792911600', '1806825600', '1824966000', '1838275200', '1856415600', +'1869724800', '1887865200', '1901779200', '1919314800', '1933228800', +'1950764400', '1964678400', '1982818800', '1996128000', '2014268400', +'2027577600', '2045718000', '2059027200', '2077167600', '2091081600', +'2108617200', '2122531200', '2140066800', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-18000, 1, 0), (-21600, 0, 4), (-18000, 1, 8)], 'CDT\x00CST\x00CWT\x00'), +'US/East-Indiana': ('US/East-Indiana', 6, 3, +[ '-1633280400', '-1615140000', '-1601830800', '-1583690400', '-880218000', +'-765396000', +], +'\x00\x01\x00\x01\x02\x01', +[(-14400, 1, 0), (-18000, 0, 4), (-14400, 1, 8)], 'EDT\x00EST\x00EWT\x00'), +'US/Eastern': ('US/Eastern', 148, 3, +[ '-1633280400', '-1615140000', '-1601830800', '-1583690400', '-880218000', +'-765396000', '-84387600', '-68666400', '-52938000', '-37216800', +'-21488400', '-5767200', '9961200', '25682400', '41410800', +'57736800', '73465200', '89186400', '104914800', '120636000', +'126687600', '152085600', '162370800', '183535200', '199263600', +'215589600', '230713200', '247039200', '262767600', '278488800', +'294217200', '309938400', '325666800', '341388000', '357116400', +'372837600', '388566000', '404892000', '420015600', '436341600', +'452070000', '467791200', '483519600', '499240800', '514969200', +'530690400', '544604400', '562140000', '576054000', '594194400', +'607503600', '625644000', '638953200', '657093600', '671007600', +'688543200', '702457200', '719992800', '733906800', '752047200', +'765356400', '783496800', '796806000', '814946400', '828860400', +'846396000', '860310000', '877845600', '891759600', '909295200', +'923209200', '941349600', '954658800', '972799200', '986108400', +'1004248800', '1018162800', '1035698400', '1049612400', '1067148000', +'1081062000', '1099202400', '1112511600', '1130652000', '1143961200', +'1162101600', '1175410800', '1193551200', '1207465200', '1225000800', +'1238914800', '1256450400', '1270364400', '1288504800', '1301814000', +'1319954400', '1333263600', '1351404000', '1365318000', '1382853600', +'1396767600', '1414303200', '1428217200', '1445752800', '1459666800', +'1477807200', '1491116400', '1509256800', '1522566000', '1540706400', +'1554620400', '1572156000', '1586070000', '1603605600', '1617519600', +'1635660000', '1648969200', '1667109600', '1680418800', '1698559200', +'1712473200', '1730008800', '1743922800', '1761458400', '1775372400', +'1792908000', '1806822000', '1824962400', '1838271600', '1856412000', +'1869721200', '1887861600', '1901775600', '1919311200', '1933225200', +'1950760800', '1964674800', '1982815200', '1996124400', '2014264800', +'2027574000', '2045714400', '2059023600', '2077164000', '2091078000', +'2108613600', '2122527600', '2140063200', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-14400, 1, 0), (-18000, 0, 4), (-14400, 1, 8)], 'EDT\x00EST\x00EWT\x00'), +'US/Hawaii': ('US/Hawaii', 9, 4, +[ '-1633260600', '-1615120200', '-1601811000', '-1583670600', '-1157283000', +'-1157200200', '-880198200', '-765376200', '-712150200', +], +'\x00\x01\x00\x01\x00\x01\x02\x01\x03', +[(-34200, 1, 0), (-37800, 0, 4), (-34200, 1, 8), (-36000, 0, 4)], 'HDT\x00HST\x00HWT\x00'), +'US/Indiana-Starke': ('US/Indiana-Starke', 56, 4, +[ '-1633276800', '-1615136400', '-1601827200', '-1583686800', '-880214400', +'-765392400', '-84384000', '-68662800', '-52934400', '-37213200', +'-21484800', '-5763600', '9964800', '25686000', '41414400', +'57740400', '73468800', '89190000', '104918400', '120639600', +'126691200', '152089200', '162374400', '183538800', '199267200', +'215593200', '230716800', '247042800', '262771200', '278492400', +'294220800', '309942000', '325670400', '341391600', '357120000', +'372841200', '388569600', '404895600', '420019200', '436345200', +'452073600', '467794800', '483523200', '499244400', '514972800', +'530694000', '544608000', '562143600', '576057600', '594198000', +'607507200', '625647600', '638956800', '657097200', '671011200', +'688546800', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x03', +[(-18000, 1, 0), (-21600, 0, 4), (-18000, 1, 8), (-18000, 0, 12)], 'CDT\x00CST\x00CWT\x00EST\x00'), +'US/Michigan': ('US/Michigan', 138, 3, +[ '-1633280400', '-1615140000', '-1601830800', '-1583690400', '-880218000', +'-765396000', '-84387600', '-68666400', '104914800', '120636000', +'126687600', '152085600', '162370800', '183535200', '199263600', +'215589600', '230713200', '247039200', '262767600', '278488800', +'294217200', '309938400', '325666800', '341388000', '357116400', +'372837600', '388566000', '404892000', '420015600', '436341600', +'452070000', '467791200', '483519600', '499240800', '514969200', +'530690400', '544604400', '562140000', '576054000', '594194400', +'607503600', '625644000', '638953200', '657093600', '671007600', +'688543200', '702457200', '719992800', '733906800', '752047200', +'765356400', '783496800', '796806000', '814946400', '828860400', +'846396000', '860310000', '877845600', '891759600', '909295200', +'923209200', '941349600', '954658800', '972799200', '986108400', +'1004248800', '1018162800', '1035698400', '1049612400', '1067148000', +'1081062000', '1099202400', '1112511600', '1130652000', '1143961200', +'1162101600', '1175410800', '1193551200', '1207465200', '1225000800', +'1238914800', '1256450400', '1270364400', '1288504800', '1301814000', +'1319954400', '1333263600', '1351404000', '1365318000', '1382853600', +'1396767600', '1414303200', '1428217200', '1445752800', '1459666800', +'1477807200', '1491116400', '1509256800', '1522566000', '1540706400', +'1554620400', '1572156000', '1586070000', '1603605600', '1617519600', +'1635660000', '1648969200', '1667109600', '1680418800', '1698559200', +'1712473200', '1730008800', '1743922800', '1761458400', '1775372400', +'1792908000', '1806822000', '1824962400', '1838271600', '1856412000', +'1869721200', '1887861600', '1901775600', '1919311200', '1933225200', +'1950760800', '1964674800', '1982815200', '1996124400', '2014264800', +'2027574000', '2045714400', '2059023600', '2077164000', '2091078000', +'2108613600', '2122527600', '2140063200', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-14400, 1, 0), (-18000, 0, 4), (-14400, 1, 8)], 'EDT\x00EST\x00EWT\x00'), +'US/Mountain': ('US/Mountain', 148, 3, +[ '-1633273200', '-1615132800', '-1601823600', '-1583683200', '-880210800', +'-765388800', '-84380400', '-68659200', '-52930800', '-37209600', +'-21481200', '-5760000', '9968400', '25689600', '41418000', +'57744000', '73472400', '89193600', '104922000', '120643200', +'126694800', '152092800', '162378000', '183542400', '199270800', +'215596800', '230720400', '247046400', '262774800', '278496000', +'294224400', '309945600', '325674000', '341395200', '357123600', +'372844800', '388573200', '404899200', '420022800', '436348800', +'452077200', '467798400', '483526800', '499248000', '514976400', +'530697600', '544611600', '562147200', '576061200', '594201600', +'607510800', '625651200', '638960400', '657100800', '671014800', +'688550400', '702464400', '720000000', '733914000', '752054400', +'765363600', '783504000', '796813200', '814953600', '828867600', +'846403200', '860317200', '877852800', '891766800', '909302400', +'923216400', '941356800', '954666000', '972806400', '986115600', +'1004256000', '1018170000', '1035705600', '1049619600', '1067155200', +'1081069200', '1099209600', '1112518800', '1130659200', '1143968400', +'1162108800', '1175418000', '1193558400', '1207472400', '1225008000', +'1238922000', '1256457600', '1270371600', '1288512000', '1301821200', +'1319961600', '1333270800', '1351411200', '1365325200', '1382860800', +'1396774800', '1414310400', '1428224400', '1445760000', '1459674000', +'1477814400', '1491123600', '1509264000', '1522573200', '1540713600', +'1554627600', '1572163200', '1586077200', '1603612800', '1617526800', +'1635667200', '1648976400', '1667116800', '1680426000', '1698566400', +'1712480400', '1730016000', '1743930000', '1761465600', '1775379600', +'1792915200', '1806829200', '1824969600', '1838278800', '1856419200', +'1869728400', '1887868800', '1901782800', '1919318400', '1933232400', +'1950768000', '1964682000', '1982822400', '1996131600', '2014272000', +'2027581200', '2045721600', '2059030800', '2077171200', '2091085200', +'2108620800', '2122534800', '2140070400', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-21600, 1, 0), (-25200, 0, 4), (-21600, 1, 8)], 'MDT\x00MST\x00MWT\x00'), +'US/Pacific': ('US/Pacific', 148, 3, +[ '-1633269600', '-1615129200', '-1601820000', '-1583679600', '-880207200', +'-765385200', '-84376800', '-68655600', '-52927200', '-37206000', +'-21477600', '-5756400', '9972000', '25693200', '41421600', +'57747600', '73476000', '89197200', '104925600', '120646800', +'126698400', '152096400', '162381600', '183546000', '199274400', +'215600400', '230724000', '247050000', '262778400', '278499600', +'294228000', '309949200', '325677600', '341398800', '357127200', +'372848400', '388576800', '404902800', '420026400', '436352400', +'452080800', '467802000', '483530400', '499251600', '514980000', +'530701200', '544615200', '562150800', '576064800', '594205200', +'607514400', '625654800', '638964000', '657104400', '671018400', +'688554000', '702468000', '720003600', '733917600', '752058000', +'765367200', '783507600', '796816800', '814957200', '828871200', +'846406800', '860320800', '877856400', '891770400', '909306000', +'923220000', '941360400', '954669600', '972810000', '986119200', +'1004259600', '1018173600', '1035709200', '1049623200', '1067158800', +'1081072800', '1099213200', '1112522400', '1130662800', '1143972000', +'1162112400', '1175421600', '1193562000', '1207476000', '1225011600', +'1238925600', '1256461200', '1270375200', '1288515600', '1301824800', +'1319965200', '1333274400', '1351414800', '1365328800', '1382864400', +'1396778400', '1414314000', '1428228000', '1445763600', '1459677600', +'1477818000', '1491127200', '1509267600', '1522576800', '1540717200', +'1554631200', '1572166800', '1586080800', '1603616400', '1617530400', +'1635670800', '1648980000', '1667120400', '1680429600', '1698570000', +'1712484000', '1730019600', '1743933600', '1761469200', '1775383200', +'1792918800', '1806832800', '1824973200', '1838282400', '1856422800', +'1869732000', '1887872400', '1901786400', '1919322000', '1933236000', +'1950771600', '1964685600', '1982826000', '1996135200', '2014275600', +'2027584800', '2045725200', '2059034400', '2077174800', '2091088800', +'2108624400', '2122538400', '2140074000', +], +'\x00\x01\x00\x01\x02\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01', +[(-25200, 1, 0), (-28800, 0, 4), (-25200, 1, 8)], 'PDT\x00PST\x00PWT\x00'), +'US/Samoa': ('US/Samoa', 2, 3, +[ '-86878800', '439038000', +], +'\x01\x02', +[(-39600, 0, 0), (-39600, 0, 4), (-39600, 0, 8)], 'NST\x00BST\x00SST\x00'), +'Universal': ('Universal', 0, 1, +[ ], +'', +[(0, 0, 0)], 'GMT\x00'), +} + +def dumpTimezoneInfo(_data): + + print "historical_zone_info = {" + + items = _data.items() + items.sort() + for key, value in items: + v1, v2, v3, ilist, bitmap, two_by_three, two_nullterm = value + print "'%s': ('%s', %s, %s," % (key, v1, v2, v3) + print "[", + while ilist: + next_5, ilist = ilist[:5], ilist[5:] + line = ", ".join(["'%s'" % x for x in next_5]) + print "%s," % line + print "], " + print "%s," % repr(bitmap) + print "%s, %s)," % (repr(two_by_three), repr(two_nullterm)) + + print "}" + +if __name__ == '__main__': + dumpTimezoneInfo(historical_zone_info) diff -Nru zope3-3.4.0/src/zope/deferredimport/deferredmodule.py zope3-3.5~bzr18/src/zope/deferredimport/deferredmodule.py --- zope3-3.4.0/src/zope/deferredimport/deferredmodule.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deferredimport/deferredmodule.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,158 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Modules with defered attributes + +$Id: deferredmodule.py 112008 2010-05-05 18:01:12Z tseaver $ +""" +import sys +import warnings +import zope.proxy + + +class Deferred(object): + + def __init__(self, name, specifier): + self.__name__ = name + self.specifier = specifier + + _import_chicken = {}, {}, ['*'] + + def get(self): + + specifier = self.specifier + if ':' in specifier: + module, name = specifier.split(':') + else: + module, name = specifier, '' + + v = __import__(module, *self._import_chicken) + if name: + for n in name.split('.'): + v = getattr(v, n) + return v + +class DeferredAndDeprecated(Deferred): + + def __init__(self, name, specifier, message): + super(DeferredAndDeprecated, self).__init__(name, specifier) + self.message = message + + def get(self): + warnings.warn( + self.__name__ + " is deprecated. " + self.message, + DeprecationWarning, stacklevel=3) + + return super(DeferredAndDeprecated, self).get() + + +class ModuleProxy(zope.proxy.ProxyBase): + __slots__ = ('__deferred_definitions__', ) + + def __init__(self, module): + super(ModuleProxy, self).__init__(module) + self.__deferred_definitions__ = {} + + def __getattr__(self, name): + try: + get = self.__deferred_definitions__.pop(name) + except KeyError: + raise AttributeError, name + v = get.get() + setattr(self, name, v) + return v + +def initialize(level=1): + """Prepare a module to support deferred imports. + + Modules do not need to call this directly, because the + `define*` and `deprecated*` functions call it. + + This is intended to be called from the module to be prepared. + The implementation wraps a proxy around the module and replaces + the entry in sys.modules with the proxy. It does no harm to + call this function more than once for a given module, because + this function does not re-wrap a proxied module. + + The level parameter specifies a relative stack depth. + When this function is called directly by the module, level should be 1. + When this function is called by a helper function, level should + increase with the depth of the stack. + + Returns nothing when level is 1; otherwise returns the proxied module. + """ + __name__ = sys._getframe(level).f_globals['__name__'] + module = sys.modules[__name__] + if not (type(module) is ModuleProxy): + module = ModuleProxy(module) + sys.modules[__name__] = module + + if level == 1: + return + return module + +def define(**names): + """Define deferred imports using keyword parameters. + + Each parameter specifies the importable name and how to import it. + Use `module:name` syntax to import a name from a module, or `module` + (no colon) to import a module. + """ + module = initialize(2) + __deferred_definitions__ = module.__deferred_definitions__ + for name, specifier in names.iteritems(): + __deferred_definitions__[name] = Deferred(name, specifier) + +def defineFrom(from_name, *names): + """Define deferred imports from a particular module. + + The from_name specifies which module to import. + The rest of the parameters specify names to import from that module. + """ + module = initialize(2) + __deferred_definitions__ = module.__deferred_definitions__ + for name in names: + specifier = from_name + ':' + name + __deferred_definitions__[name] = Deferred(name, specifier) + +def deprecated(message, **names): + """Define deferred and deprecated imports using keyword parameters. + + The first use of each name will generate a deprecation warning with + the given message. + + Each parameter specifies the importable name and how to import it. + Use `module:name` syntax to import a name from a module, or `module` + (no colon) to import a module. + """ + module = initialize(2) + __deferred_definitions__ = module.__deferred_definitions__ + for name, specifier in names.iteritems(): + __deferred_definitions__[name] = DeferredAndDeprecated( + name, specifier, message) + +def deprecatedFrom(message, from_name, *names): + """Define deferred and deprecated imports from a particular module. + + The first use of each name will generate a deprecation warning with + the given message. + + The from_name specifies which module to import. + The rest of the parameters specify names to import from that module. + """ + module = initialize(2) + __deferred_definitions__ = module.__deferred_definitions__ + for name in names: + specifier = from_name + ':' + name + __deferred_definitions__[name] = DeferredAndDeprecated( + name, specifier, message) diff -Nru zope3-3.4.0/src/zope/deferredimport/__init__.py zope3-3.5~bzr18/src/zope/deferredimport/__init__.py --- zope3-3.4.0/src/zope/deferredimport/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deferredimport/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,5 @@ +from zope.deferredimport.deferredmodule import initialize +from zope.deferredimport.deferredmodule import define, defineFrom +from zope.deferredimport.deferredmodule import deprecated, deprecatedFrom + +__all__ = tuple(name for name in globals() if not name.startswith('_')) diff -Nru zope3-3.4.0/src/zope/deferredimport/README.txt zope3-3.5~bzr18/src/zope/deferredimport/README.txt --- zope3-3.4.0/src/zope/deferredimport/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deferredimport/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,285 @@ +Deferred Import +=============== + +Often, especially for package modules, you want to import names for +convenience, but not actually perform the imports until necessary. +The zope.deferredimport package provided facilities for defining names +in modules that will be imported from somewhere else when used. You +can also cause deprecation warnings to be issued when a variable is +used, but we'll get to that later. + +The zope.deferredimport.define function can be used to define one or +more names to be imported when they are accessed. Simply provide +names as keyword arguments with import specifiers as values. The +import specifiers are given as strings of the form "module:name", +where module is the dotted name of the module and name is a, possibly +dotted, name of an object within the module. + +To see how this works, we'll create some sample modules within the +zope.deferredimport package. We'll actually use a helper function +specific to this document to define the modules inline so we can +easily see what's in them. Let's start by defining a module, sample1, +that defined some things to be imported: + + >>> create_module(sample1 = ''' + ... + ... print "Sampe 1 imported!" + ... + ... x = 1 + ... + ... class C: + ... y = 2 + ... + ... z = 3 + ... q = 4 + ... + ... ''') + +Note that the module starts by printing a message. This allows us to +see when the module is actually imported. Now, let's define a module +that imports some names from this module: + + + >>> create_module(sample2 = ''' + ... + ... import zope.deferredimport + ... + ... zope.deferredimport.define( + ... sample1 = 'zope.deferredimport.sample1', + ... one = 'zope.deferredimport.sample1:x', + ... two = 'zope.deferredimport.sample1:C.y', + ... ) + ... + ... three = 3 + ... x = 4 + ... def getx(): + ... return x + ... + ... ''') + + +In this example, we defined the name 'sample1' as the module +zope.deferredimport.sample1. The module isn't imported immediately, +but will be imported when needed. Similarly, the name 'one' is +defined as the 'x' attribute of sample1. + +The sample1 module prints a message when it is +imported. When we import sample2, we don't see a message until we +access a variable: + + >>> import zope.deferredimport.sample2 + >>> print zope.deferredimport.sample2.one + Sampe 1 imported! + 1 + + >>> import zope.deferredimport.sample1 + + >>> zope.deferredimport.sample2.sample1 is zope.deferredimport.sample1 + True + +Note that a deferred attribute appears in a module's dictionary *after* +it is accessed the first time: + + >>> 'two' in zope.deferredimport.sample2.__dict__ + False + + >>> zope.deferredimport.sample2.two + 2 + + >>> 'two' in zope.deferredimport.sample2.__dict__ + True + +When deferred imports are used, the original module is replaced with a +proxy. + + >>> type(zope.deferredimport.sample2) + + +But we can use the proxy just like the original. We can even update +it. + + >>> zope.deferredimport.sample2.x=5 + >>> zope.deferredimport.sample2.getx() + 5 + +And the inspect module thinks it's a module: + + >>> import inspect + >>> inspect.ismodule(zope.deferredimport.sample2) + True + + +In the example above, the modules were fairly simple. Let's look at a +more complicated example. + + >>> create_module(sample3 = ''' + ... + ... import zope.deferredimport + ... import zope.deferredimport.sample4 + ... + ... zope.deferredimport.define( + ... sample1 = 'zope.deferredimport.sample1', + ... one = 'zope.deferredimport.sample1:x', + ... two = 'zope.deferredimport.sample1:C.y', + ... ) + ... + ... x = 1 + ... + ... ''') + + >>> create_module(sample4 = ''' + ... + ... import sample3 + ... + ... def getone(): + ... return sample3.one + ... + ... ''') + +Here, we have a circular import between sample3 and sample4. When +sample3 is imported, it imports sample 4, which then imports sample3. +Let's see what happens when we use these modules in an unfortunate +order: + + >>> import zope.deferredimport.sample3 + >>> import zope.deferredimport.sample4 + + >>> zope.deferredimport.sample4.getone() + Traceback (most recent call last): + ... + AttributeError: 'module' object has no attribute 'one' + +Hm. Let's try accessing one through sample3: + + >>> zope.deferredimport.sample3.one + 1 + +Funny, let's try getone again: + + >>> zope.deferredimport.sample4.getone() + 1 + +The problem is that sample4 obtained sample3 before sample4 was +replaced by a proxy. This example is slightly pathological because it +requires a circular import and a relative import, but the bug +introduced is very subtle. To guard against this, you should define +deferred imports before importing any other modules. Alternatively, +you can call the initialize function before importing any other +modules, as in: + + + >>> create_module(sample5 = ''' + ... + ... import zope.deferredimport + ... zope.deferredimport.initialize() + ... + ... import zope.deferredimport.sample6 + ... + ... zope.deferredimport.define( + ... sample1 = 'zope.deferredimport.sample1', + ... one = 'zope.deferredimport.sample1:x', + ... two = 'zope.deferredimport.sample1:C.y', + ... ) + ... + ... x = 1 + ... + ... ''') + + >>> create_module(sample6 = ''' + ... + ... import sample5 + ... + ... def getone(): + ... return sample5.one + ... + ... ''') + + >>> import zope.deferredimport.sample5 + >>> import zope.deferredimport.sample6 + + >>> zope.deferredimport.sample6.getone() + 1 + + +Deprecation +----------- + +Deferred attributes can also be marked as deprecated, in which case, a +message will be printed the first time they are accessed. + +Lets define a module that has deprecated attributes defined as +deferred imports: + + >>> create_module(sample7 = ''' + ... + ... import zope.deferredimport + ... zope.deferredimport.initialize() + ... + ... zope.deferredimport.deprecated( + ... "Import from sample1 instead", + ... x = 'zope.deferredimport.sample1:x', + ... y = 'zope.deferredimport.sample1:C.y', + ... z = 'zope.deferredimport.sample1:z', + ... ) + ... + ... ''') + +Now, if we use one of these variables, we'll get a deprecation +warning: + + >>> import zope.deferredimport.sample7 + >>> zope.deferredimport.sample7.x + ... doctest: +NORMALIZE_WHITESPACE + zope/deferredimport/README.txt:1: DeprecationWarning: + x is deprecated. Import from sample1 instead + Deferred Import + 1 + +but only the first time: + + >>> zope.deferredimport.sample7.x + 1 + +Importing multiple names from the same module +--------------------------------------------- + +Sometimes, you want to get multiple things from the same module. You +can use defineFrom or deprecatedFrom to do that: + + + >>> create_module(sample8 = ''' + ... + ... import zope.deferredimport + ... + ... zope.deferredimport.deprecatedFrom( + ... "Import from sample1 instead", + ... 'zope.deferredimport.sample1', + ... 'x', 'z', 'q', + ... ) + ... + ... zope.deferredimport.defineFrom( + ... 'zope.deferredimport.sample9', + ... 'a', 'b', 'c', + ... ) + ... + ... ''') + + >>> create_module(sample9 = ''' + ... print 'Imported sample 9' + ... a, b, c = range(10,13) + ... ''') + + >>> import zope.deferredimport.sample8 + >>> zope.deferredimport.sample8.q + ... doctest: +NORMALIZE_WHITESPACE + zope/deferredimport/README.txt:1: DeprecationWarning: + q is deprecated. Import from sample1 instead + Deferred Import + 4 + + >>> zope.deferredimport.sample8.c + Imported sample 9 + 12 + +Note, as in the example above, that you can make multiple +deferred-import calls in a module. diff -Nru zope3-3.4.0/src/zope/deferredimport/tests.py zope3-3.5~bzr18/src/zope/deferredimport/tests.py --- zope3-3.4.0/src/zope/deferredimport/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deferredimport/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,93 @@ +############################################################################## +# +# Copyright (c) 2006 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 doctest +import os +import re +import shutil +import sys +import tempfile +import warnings + +from zope.testing import renormalizing +import zope.deferredimport + + +class OutErr: + + @staticmethod + def write(message): + sys.stdout.write(message) + + +def warn(message, type_, stacklevel): + frame = sys._getframe(stacklevel) + path = frame.f_globals['__file__'] + file = open(path) + lineno = frame.f_lineno + for i in range(lineno): + line = file.readline() + + print "%s:%s: %s: %s\n %s" % ( + path, + frame.f_lineno, + type_.__name__, + message, + line.strip(), + ) + + +def setUp(test): + d = test.globs['tmp_d'] = tempfile.mkdtemp('deferredimport') + + def create_module(**modules): + for name, src in modules.iteritems(): + f = open(os.path.join(d, name+'.py'), 'w') + f.write(src) + f.close() + test.globs['created_modules'].append(name) + + test.globs['created_modules'] = [] + test.globs['create_module'] = create_module + + zope.deferredimport.__path__.append(d) + + test.globs['oldstderr'] = sys.stderr + sys.stderr = OutErr + + test.globs['saved_warn'] = warnings.warn + warnings.warn = warn + + +def tearDown(test): + sys.stderr = test.globs['oldstderr'] + + zope.deferredimport.__path__.pop() + shutil.rmtree(test.globs['tmp_d']) + for name in test.globs['created_modules']: + sys.modules.pop(name, None) + warnings.warn = test.globs['saved_warn'] + + +def test_suite(): + checker = renormalizing.RENormalizing(( + (re.compile(r'.+[/\\]README.txt'), 'README.txt'), + )) + return doctest.DocFileSuite( + 'README.txt', + setUp=setUp, tearDown=tearDown, + optionflags=doctest.NORMALIZE_WHITESPACE, + checker=checker, + globs = {'__file__': os.path.join(os.path.dirname(__file__), 'README.txt')} + ) diff -Nru zope3-3.4.0/src/zope/deprecation/DEPENDENCIES.cfg zope3-3.5~bzr18/src/zope/deprecation/DEPENDENCIES.cfg --- zope3-3.4.0/src/zope/deprecation/DEPENDENCIES.cfg 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deprecation/DEPENDENCIES.cfg 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +zope.testing diff -Nru zope3-3.4.0/src/zope/deprecation/deprecation.py zope3-3.5~bzr18/src/zope/deprecation/deprecation.py --- zope3-3.4.0/src/zope/deprecation/deprecation.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deprecation/deprecation.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,216 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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. +# +############################################################################## +"""Deprecation Support + +This module provides utilities to ease the development of backward-compatible +code. + +$Id: deprecation.py 70794 2006-10-19 04:29:42Z baijum $ +""" +__docformat__ = "reStructuredText" +import sys +import types +import warnings + +import zope.deprecation + + +class ShowSwitch(object): + """Simple stack-based switch.""" + + def __init__(self): + self.stack = [] + + def on(self): + self.stack.pop() + + def off(self): + self.stack.append(False) + + def reset(self): + self.stack = [] + + def __call__(self): + return self.stack == [] + + def __repr__(self): + return '' %(self() and 'on' or 'off') + + +ogetattr = object.__getattribute__ +class DeprecationProxy(object): + + def __init__(self, module): + self.__original_module = module + self.__deprecated = {} + + def deprecate(self, names, message): + """Deprecate the given names.""" + if not isinstance(names, (tuple, list)): + names = (names,) + for name in names: + self.__deprecated[name] = message + + def __getattribute__(self, name): + if name == 'deprecate' or name.startswith('_DeprecationProxy__'): + return ogetattr(self, name) + + if name == '__class__': + return types.ModuleType + + if name in ogetattr(self, '_DeprecationProxy__deprecated'): + if zope.deprecation.__show__(): + warnings.warn( + name + ': ' + self.__deprecated[name], + DeprecationWarning, 2) + + return getattr(ogetattr(self, '_DeprecationProxy__original_module'), + name) + + def __setattr__(self, name, value): + if name.startswith('_DeprecationProxy__'): + return object.__setattr__(self, name, value) + + setattr(self.__original_module, name, value) + + def __delattr__(self, name): + if name.startswith('_DeprecationProxy__'): + return object.__delattr__(self, name) + + delattr(self.__original_module, name) + +class DeprecatedModule(object): + + def __init__(self, module, msg): + self.__original_module = module + self.__msg = msg + + def __getattribute__(self, name): + if name.startswith('_DeprecatedModule__'): + return ogetattr(self, name) + + if name == '__class__': + return types.ModuleType + + if zope.deprecation.__show__(): + warnings.warn(self.__msg, DeprecationWarning, 2) + + return getattr(ogetattr(self, '_DeprecatedModule__original_module'), + name) + + def __setattr__(self, name, value): + if name.startswith('_DeprecatedModule__'): + return object.__setattr__(self, name, value) + setattr(self.__original_module, name, value) + + def __delattr__(self, name): + if name.startswith('_DeprecatedModule__'): + return object.__delattr__(self, name) + delattr(self.__original_module, name) + +class DeprecatedGetProperty(object): + + def __init__(self, prop, message): + self.message = message + self.prop = prop + + def __get__(self, inst, klass): + if zope.deprecation.__show__(): + warnings.warn(self.message, DeprecationWarning, 2) + return self.prop.__get__(inst, klass) + +class DeprecatedGetSetProperty(DeprecatedGetProperty): + + def __set__(self, inst, prop): + if zope.deprecation.__show__(): + warnings.warn(self.message, DeprecationWarning, 2) + self.prop.__set__(inst, prop) + +class DeprecatedGetSetDeleteProperty(DeprecatedGetSetProperty): + + def __delete__(self, inst): + if zope.deprecation.__show__(): + warnings.warn(self.message, DeprecationWarning, 2) + self.prop.__delete__(inst) + +def DeprecatedMethod(method, message): + + def deprecated_method(self, *args, **kw): + if zope.deprecation.__show__(): + warnings.warn(message, DeprecationWarning, 2) + return method(self, *args, **kw) + + return deprecated_method + + +def deprecated(specifier, message): + """Deprecate the given names.""" + + # A string specifier (or list of strings) means we're called + # top-level in a module and are to deprecate things inside this + # module + if isinstance(specifier, (str, unicode, list, tuple)): + globals = sys._getframe(1).f_globals + modname = globals['__name__'] + + if not isinstance(sys.modules[modname], DeprecationProxy): + sys.modules[modname] = DeprecationProxy(sys.modules[modname]) + sys.modules[modname].deprecate(specifier, message) + + + # Anything else can mean the specifier is a function/method, + # module, or just an attribute of a class + elif isinstance(specifier, types.FunctionType): + return DeprecatedMethod(specifier, message) + elif isinstance(specifier, types.ModuleType): + return DeprecatedModule(specifier, message) + else: + prop = specifier + if hasattr(prop, '__get__') and hasattr(prop, '__set__') and \ + hasattr(prop, '__delete__'): + return DeprecatedGetSetDeleteProperty(prop, message) + elif hasattr(prop, '__get__') and hasattr(prop, '__set__'): + return DeprecatedGetSetProperty(prop, message) + elif hasattr(prop, '__get__'): + return DeprecatedGetProperty(prop, message) + +class deprecate(object): + """Deprecation decorator""" + + def __init__(self, msg): + self.msg = msg + + def __call__(self, func): + return DeprecatedMethod(func, self.msg) + +def moved(to_location, unsupported_in=None): + old = sys._getframe(1).f_globals['__name__'] + message = '%s has moved to %s.' % (old, to_location) + if unsupported_in: + message += " Import of %s will become unsupported in %s" % ( + old, unsupported_in) + + warnings.warn(message, DeprecationWarning, 3) + __import__(to_location) + + fromdict = sys.modules[to_location].__dict__ + tomod = sys.modules[old] + tomod.__doc__ = message + todict = tomod.__dict__ + + for name, v in fromdict.iteritems(): + if name not in tomod.__dict__: + setattr(tomod, name, v) + + diff -Nru zope3-3.4.0/src/zope/deprecation/__init__.py zope3-3.5~bzr18/src/zope/deprecation/__init__.py --- zope3-3.4.0/src/zope/deprecation/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deprecation/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Corporation 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. +# +############################################################################## +"""Deprecation Package + +$Id: __init__.py 70794 2006-10-19 04:29:42Z baijum $ +""" +__docformat__ = "reStructuredText" + +from zope.deprecation.deprecation import deprecated, deprecate, ShowSwitch +from zope.deprecation.deprecation import moved + +# This attribute can be used to temporarly deactivate deprecation +# warnings, so that backward-compatibility code can import other +# backward-compatiblity components without warnings being produced. + +__show__ = ShowSwitch() diff -Nru zope3-3.4.0/src/zope/deprecation/README.txt zope3-3.5~bzr18/src/zope/deprecation/README.txt --- zope3-3.4.0/src/zope/deprecation/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deprecation/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,362 @@ +=============== +Deprecation API +=============== + +When we started working on Zope 3.1, we noticed that the hardest part of the +development process was to ensure backward-compatibility and correctly mark +deprecated modules, classes, functions, methods and properties. This module +provides a simple function called `deprecated(names, reason)` to deprecate the +previously mentioned Python objects. + +Deprecating objects inside a module +----------------------------------- + +Let's start with a demonstration of deprecating any name inside a module. To +demonstrate the functionality, I have placed the following code inside the +`tests.py` file of this package: + + from zope.deprecation import deprecated + demo1 = 1 + deprecated('demo1', 'demo1 is no more.') + + demo2 = 2 + deprecated('demo2', 'demo2 is no more.') + + demo3 = 3 + deprecated('demo3', 'demo3 is no more.') + +The first argument to the `deprecated()` function is a list of names that +should be declared deprecated. If the first argument is a string, it is +interpreted as one name. The second argument is the reason the particular name +has been deprecated. It is good practice to also list the version in which the +name will be removed completely. + +Let's now see how the deprecation warnings are displayed. + + >>> from zope.deprecation import tests + >>> tests.demo1 + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: demo1: demo1 is no more. + ... + 1 + + >>> import zope.deprecation.tests + >>> zope.deprecation.tests.demo2 + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: demo2: demo2 is no more. + ... + 2 + +You can see that merely importing the affected module or one of its parents +does not cause a deprecation warning. Only when we try to access the name in +the module, we get a deprecation warning. On the other hand, if we import the +name directly, the deprecation warning will be raised immediately. + + >>> from zope.deprecation.tests import demo3 + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: demo3: demo3 is no more. + ... + +Deprecation can also happen inside a function. When we first access +``demo4``, it can be accessed without problems, then we call a +function that sets the deprecation message and we get the message upon +the next access: + + >>> tests.demo4 + 4 + >>> tests.deprecatedemo4() + >>> tests.demo4 + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: demo4: demo4 is no more. + ... + 4 + +Deprecating methods and properties +---------------------------------- + +New let's see how properties and methods can be deprecated. We are going to +use the same function as before, except that this time, we do not pass in names +as first argument, but the method or attribute itself. The function then +returns a wrapper that sends out a deprecation warning when the attribute or +method is accessed. + + >>> from zope.deprecation import deprecation + >>> class MyComponent(object): + ... foo = property(lambda self: 1) + ... foo = deprecation.deprecated(foo, 'foo is no more.') + ... + ... bar = 2 + ... + ... def blah(self): + ... return 3 + ... blah = deprecation.deprecated(blah, 'blah() is no more.') + ... + ... def splat(self): + ... return 4 + ... + ... @deprecation.deprecate("clap() is no more.") + ... def clap(self): + ... return 5 + +And here is the result: + + >>> my = MyComponent() + >>> my.foo + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: foo is no more. + ... + 1 + >>> my.bar + 2 + >>> my.blah() + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: blah() is no more. + ... + 3 + >>> my.splat() + 4 + >>> my.clap() + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: clap() is no more. + ... + 5 + +Deprecating modules +------------------- + +It is also possible to deprecate whole modules. This is useful when +creating module aliases for backward compatibility. Let's imagine, +the ``zope.deprecation`` module used to be called ``zope.wanda`` and +we'd like to retain backward compatibility: + + >>> import zope.deprecation + >>> import sys + >>> sys.modules['zope.wanda'] = deprecation.deprecated( + ... zope.deprecation, 'A module called Wanda is now zope.deprecation.') + +Now we can import ``wanda``, but when accessing things from it, we get +our deprecation message as expected: + + >>> from zope.wanda import deprecated + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: A module called Wanda is now zope.deprecation. + ... + +Before we move on, we should clean up: + + >>> del deprecated + >>> del sys.modules['zope.wanda'] + +Moving modules +-------------- + +When a module is moved, you often want to support importing from the +old location for a while, generating a deprecation warning when +someone uses the old location. This can be done using the moved +function. + +To see how this works, we'll use a helper function to create two fake +modules in the zope.deprecation package. First will create a module +in the "old" location that used the moved function to indicate the a +module on the new location should be used: + + >>> create_module(old_location= + ... ''' + ... import zope.deprecation + ... zope.deprecation.moved('zope.deprecation.new_location', 'version 2') + ... ''') + +and we define the module in the new location: + + >>> create_module(new_location= + ... '''\ + ... print "new module imported" + ... x = 42 + ... ''') + +Now, if we import the old location, we'll see the output of importing +the old location: + + >>> import zope.deprecation.old_location + ... # doctest: +NORMALIZE_WHITESPACE + From tests.py's showwarning(): + ...zope/deprecation/README.txt:1: + DeprecationWarning: zope.deprecation.old_location has moved to + zope.deprecation.new_location. + Import of zope.deprecation.old_location will become unsupported + in version 2 + =============== + new module imported + + >>> zope.deprecation.old_location.x + 42 + +Moving packages +--------------- + +When moving packages, you need to leave placeholders for each +module. Let's look at an example: + + >>> create_module({ + ... 'new_package.__init__': '''\ + ... print __name__, 'imported' + ... x=0 + ... ''', + ... 'new_package.m1': '''\ + ... print __name__, 'imported' + ... x=1 + ... ''', + ... 'new_package.m2': '''\ + ... print __name__, 'imported' + ... def x(): + ... pass + ... ''', + ... 'new_package.m3': '''\ + ... print __name__, 'imported' + ... x=3 + ... ''', + ... 'old_package.__init__': '''\ + ... import zope.deprecation + ... zope.deprecation.moved('zope.deprecation.new_package', 'version 2') + ... ''', + ... 'old_package.m1': '''\ + ... import zope.deprecation + ... zope.deprecation.moved('zope.deprecation.new_package.m1', 'version 2') + ... ''', + ... 'old_package.m2': '''\ + ... import zope.deprecation + ... zope.deprecation.moved('zope.deprecation.new_package.m2', 'version 2') + ... ''', + ... }) + + + +Now, if we import the old modules, we'll get warnings: + + >>> import zope.deprecation.old_package + ... # doctest: +NORMALIZE_WHITESPACE + From tests.py's showwarning(): + ...zope/deprecation/README.txt:1: DeprecationWarning: + zope.deprecation.old_package has moved to zope.deprecation.new_package. + Import of zope.deprecation.old_package will become unsupported in version 2 + =============== + zope.deprecation.new_package imported + + >>> zope.deprecation.old_package.x + 0 + + >>> import zope.deprecation.old_package.m1 + ... # doctest: +NORMALIZE_WHITESPACE + From tests.py's showwarning(): + ...zope/deprecation/README.txt:1: DeprecationWarning: + zope.deprecation.old_package.m1 has moved to zope.deprecation.new_package.m1. + Import of zope.deprecation.old_package.m1 will become unsupported in + version 2 + =============== + zope.deprecation.new_package.m1 imported + + >>> zope.deprecation.old_package.m1.x + 1 + + >>> import zope.deprecation.old_package.m2 + ... # doctest: +NORMALIZE_WHITESPACE + From tests.py's showwarning(): + ...zope/deprecation/README.txt:1: DeprecationWarning: + zope.deprecation.old_package.m2 has moved to zope.deprecation.new_package.m2. + Import of zope.deprecation.old_package.m2 will become unsupported in + version 2 + =============== + zope.deprecation.new_package.m2 imported + + >>> zope.deprecation.old_package.m2.x is zope.deprecation.new_package.m2.x + True + + >>> (zope.deprecation.old_package.m2.x.func_globals + ... is zope.deprecation.new_package.m2.__dict__) + True + + >>> zope.deprecation.old_package.m2.x.__module__ + 'zope.deprecation.new_package.m2' + +We'll get an error if we try to import m3, because we didn't create a +placeholder for it: + + >>> import zope.deprecation.old_package.m3 + Traceback (most recent call last): + ... + ImportError: No module named m3 + + + +Temporarily turning off deprecation warnings +-------------------------------------------- + +In some cases it is desireable to turn off the deprecation warnings for a +short time. To support such a feature, the `zope.deprecation` package provides +an attribute called `__show__`. One can ask for its status by calling it: + + >>> from zope.deprecation import __show__ + >>> __show__() + True + + >>> class Foo(object): + ... bar = property(lambda self: 1) + ... bar = deprecation.deprecated(bar, 'bar is no more.') + ... blah = property(lambda self: 1) + ... blah = deprecation.deprecated(blah, 'blah is no more.') + >>> foo = Foo() + + >>> foo.bar + From tests.py's showwarning(): + ...README.txt:1: DeprecationWarning: bar is no more. + ... + 1 + +You can turn off the depraction warnings using + + >>> __show__.off() + >>> __show__() + False + + >>> foo.blah + 1 + +Now, you can also nest several turn-offs, so that calling `off()` multiple +times is meaningful: + + >>> __show__.stack + [False] + + >>> __show__.off() + >>> __show__.stack + [False, False] + + >>> __show__.on() + >>> __show__.stack + [False] + >>> __show__() + False + + >>> __show__.on() + >>> __show__.stack + [] + >>> __show__() + True + +You can also reset `__show__` to `True`: + + >>> __show__.off() + >>> __show__.off() + >>> __show__() + False + + >>> __show__.reset() + >>> __show__() + True + +Finally, you cannot call `on()` without having called `off()` before: + + >>> __show__.on() + Traceback (most recent call last): + ... + IndexError: pop from empty list diff -Nru zope3-3.4.0/src/zope/deprecation/tests.py zope3-3.5~bzr18/src/zope/deprecation/tests.py --- zope3-3.4.0/src/zope/deprecation/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/deprecation/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,115 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Component Architecture Tests + +$Id: tests.py 70794 2006-10-19 04:29:42Z baijum $ +""" + +from zope.testing import doctest +from zope.testing import renormalizing +import os +import re +import shutil +import sys +import tempfile +import unittest +import warnings +import zope.deprecation + +# Used in doctests +from deprecation import deprecated +demo1 = 1 +deprecated('demo1', 'demo1 is no more.') + +demo2 = 2 +deprecated('demo2', 'demo2 is no more.') + +demo3 = 3 +deprecated('demo3', 'demo3 is no more.') + +demo4 = 4 +def deprecatedemo4(): + """Demonstrate that deprecate() also works in a local scope.""" + deprecated('demo4', 'demo4 is no more.') + +def warn(message, type_, stacklevel): + print "From tests.py's showwarning():" + + frame = sys._getframe(stacklevel) + path = frame.f_globals['__file__'] + file = open(path) + lineno = frame.f_lineno + for i in range(lineno): + line = file.readline() + + print "%s:%s: %s: %s\n %s" % ( + path, + frame.f_lineno, + type_.__name__, + message, + line.strip(), + ) + + +def setUpCreateModule(test): + d = test.globs['tmp_d'] = tempfile.mkdtemp('deprecation') + + def create_module(modules=(), **kw): + modules = dict(modules) + modules.update(kw) + for name, src in modules.iteritems(): + pname = name.split('.') + if pname[-1] == '__init__': + os.mkdir(os.path.join(d, *pname[:-1])) + name = '.'.join(pname[:-1]) + open(os.path.join(d, *pname)+'.py', 'w').write(src) + test.globs['created_modules'].append(name) + + test.globs['created_modules'] = [] + test.globs['create_module'] = create_module + + zope.deprecation.__path__.append(d) + +def tearDownCreateModule(test): + zope.deprecation.__path__.pop() + shutil.rmtree(test.globs['tmp_d']) + for name in test.globs['created_modules']: + sys.modules.pop(name, None) + +def setUp(test): + test.globs['saved_warn'] = warnings.warn + warnings.warn = warn + setUpCreateModule(test) + +def tearDown(test): + tearDownCreateModule(test) + warnings.warn = test.globs['saved_warn'] + del object.__getattribute__(sys.modules['zope.deprecation.tests'], + '_DeprecationProxy__deprecated')['demo4'] + +def test_suite(): + checker = renormalizing.RENormalizing([ + (re.compile('\\\\'), '/'), # convert Windows paths to Unix paths + ]) + + return unittest.TestSuite(( + doctest.DocFileSuite('README.txt', + setUp=setUp, tearDown=tearDown, + optionflags=doctest.ELLIPSIS, + checker=checker, + ), + )) + +if __name__ == "__main__": + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/dottedname/__init__.py zope3-3.5~bzr18/src/zope/dottedname/__init__.py --- zope3-3.4.0/src/zope/dottedname/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dottedname/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# diff -Nru zope3-3.4.0/src/zope/dottedname/README.txt zope3-3.5~bzr18/src/zope/dottedname/README.txt --- zope3-3.4.0/src/zope/dottedname/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dottedname/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,61 @@ +====================== +Dotted Name Resolution +====================== + +The ``zope.dottedname`` module provides one function, ``resolve`` that +resolves strings containing dotted names into the appropriate python +object. + +Dotted names are resolved by importing modules and by getting +attributes from imported modules. Names may be relative, provided the +module they are relative to is supplied. + +Here are some examples of importing absolute names:: + + >>> from zope.dottedname.resolve import resolve + + >>> resolve('unittest') + + + >>> resolve('datetime.datetime') + + + >>> resolve('datetime.datetime.now') + + + >>> resolve('non existent module') + Traceback (most recent call last): + ... + ImportError: No module named non existent module + + >>> resolve('__doc__') + Traceback (most recent call last): + ... + ImportError: No module named __doc__ + + >>> resolve('datetime.foo') + Traceback (most recent call last): + ... + ImportError: No module named foo + + >>> resolve('os.path.split').__name__ + 'split' + +Here are some examples of importing relative names:: + + >>> resolve('.split', 'os.path') + + + >>> resolve('..system', 'os.path') + + + >>> resolve('...datetime', 'os.path') + + +NB: When relative names are imported, a module the name is relative to +**must** be supplied:: + + >>> resolve('.split').__name__ + Traceback (most recent call last): + ... + ValueError: relative name without base module diff -Nru zope3-3.4.0/src/zope/dottedname/resolve.py zope3-3.5~bzr18/src/zope/dottedname/resolve.py --- zope3-3.4.0/src/zope/dottedname/resolve.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dottedname/resolve.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Dotted name support + +$Id: resolve.py 38682 2005-09-29 09:12:45Z jim $ +""" + +def resolve(name, module=None): + name = name.split('.') + if not name[0]: + if module is None: + raise ValueError("relative name without base module") + module = module.split('.') + name.pop(0) + while not name[0]: + module.pop() + name.pop(0) + name = module + name + + used = name.pop(0) + found = __import__(used) + for n in name: + used += '.' + n + try: + found = getattr(found, n) + except AttributeError: + __import__(used) + found = getattr(found, n) + + return found + + + diff -Nru zope3-3.4.0/src/zope/dottedname/tests.py zope3-3.5~bzr18/src/zope/dottedname/tests.py --- zope3-3.4.0/src/zope/dottedname/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dottedname/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,32 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 resolution of dotted names + +$Id: tests.py 95202 2009-01-27 14:15:28Z thefunny42 $ +""" +import os,unittest +from zope.testing.doctest import DocFileSuite,REPORT_NDIFF,ELLIPSIS + +def test_suite(): + return unittest.TestSuite(( + DocFileSuite( + os.path.abspath(os.path.join(os.path.dirname(__file__), 'README.txt')), + optionflags=REPORT_NDIFF|ELLIPSIS, + module_relative=False, + ), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') + diff -Nru zope3-3.4.0/src/zope/dublincore/annotatableadapter.py zope3-3.5~bzr18/src/zope/dublincore/annotatableadapter.py --- zope3-3.4.0/src/zope/dublincore/annotatableadapter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/annotatableadapter.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,124 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Dublin Core Annotatable Adapter + +$Id: annotatableadapter.py 101365 2009-06-30 23:12:00Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +from persistent.dict import PersistentDict + +from zope.annotation.interfaces import IAnnotatable +from zope.annotation.interfaces import IAnnotations +from zope.component import adapts +from zope.interface import implements +from zope.location import Location +from zope.dublincore.interfaces import IWriteZopeDublinCore +from zope.dublincore.zopedublincore import DateProperty +from zope.dublincore.zopedublincore import ScalarProperty +from zope.dublincore.zopedublincore import ZopeDublinCore + +DCkey = "zope.app.dublincore.ZopeDublinCore" + + +class ZDCAnnotatableAdapter(ZopeDublinCore, Location): + """Adapt annotatable objects to Zope Dublin Core.""" + implements(IWriteZopeDublinCore) + adapts(IAnnotatable) + + annotations = None + + def __init__(self, context): + annotations = IAnnotations(context) + dcdata = annotations.get(DCkey) + if dcdata is None: + self.annotations = annotations + dcdata = ZDCAnnotationData() + + super(ZDCAnnotatableAdapter, self).__init__(dcdata) + + def _changed(self): + if self.annotations is not None: + self.annotations[DCkey] = self._mapping + self.annotations = None + + +class ZDCAnnotationData(PersistentDict): + """Data for a Dublin Core annotation. + + A specialized class is used to allow an alternate fssync + serialization to be registered. See the + zope.dublincore.fssync package. + """ + + +# Hybrid adapters. +# +# Adapter factories created using this support the Dublin Core using a +# mixture of annotations and data on the context object. + + +class DirectProperty(object): + + def __init__(self, name, attrname): + self.__name__ = name + self.__attrname = attrname + + def __get__(self, inst, klass): + if inst is None: + return self + context = inst._ZDCPartialAnnotatableAdapter__context + return getattr(context, self.__attrname, u"") + + def __set__(self, inst, value): + if not isinstance(value, unicode): + raise TypeError("Element must be unicode") + context = inst._ZDCPartialAnnotatableAdapter__context + oldvalue = getattr(context, self.__attrname, None) + if oldvalue != value: + setattr(context, self.__attrname, value) + +def partialAnnotatableAdapterFactory(direct_fields): + if not direct_fields: + raise ValueError("only use partialAnnotatableAdapterFactory()" + " if at least one DC field is implemented directly") + fieldmap = {} + try: + # is direct_fields a sequence or a mapping? + direct_fields[0] + except KeyError: + # direct_fields: { dc_name: attribute_name } + fieldmap.update(direct_fields) + else: + for attrname in direct_fields: + fieldmap[attrname] = attrname + + class ZDCPartialAnnotatableAdapter(ZDCAnnotatableAdapter): + + def __init__(self, context): + self.__context = context + # can't use super() since this isn't a globally available class + ZDCAnnotatableAdapter.__init__(self, context) + + for dcname, attrname in fieldmap.iteritems(): + oldprop = ZopeDublinCore.__dict__.get(dcname) + if oldprop is None: + raise ValueError("%r is not a valid DC field" % dcname) + if (isinstance(oldprop, DateProperty) + or not isinstance(oldprop, ScalarProperty)): + raise ValueError("%r is not a supported DC field" % dcname) + prop = DirectProperty(dcname, attrname) + setattr(ZDCPartialAnnotatableAdapter, dcname, prop) + + return ZDCPartialAnnotatableAdapter diff -Nru zope3-3.4.0/src/zope/dublincore/browser/box.pt zope3-3.5~bzr18/src/zope/dublincore/browser/box.pt --- zope3-3.4.0/src/zope/dublincore/browser/box.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/browser/box.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,24 @@ + + +
+ Title: + +
+ +
+ Description: + +
+
+ Created: + 2000-01-01 01:01:01 +
+ +
+ Modified: + 2000-01-01 01:01:01 +
+ +
+ diff -Nru zope3-3.4.0/src/zope/dublincore/browser/configure.zcml zope3-3.5~bzr18/src/zope/dublincore/browser/configure.zcml --- zope3-3.4.0/src/zope/dublincore/browser/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/browser/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,29 @@ + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/dublincore/browser/edit.pt zope3-3.5~bzr18/src/zope/dublincore/browser/edit.pt --- zope3-3.4.0/src/zope/dublincore/browser/edit.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/browser/edit.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,61 @@ + + +
+ +
+ +

Message here

+ +
+
Title
+
+ +
+
+ +
+
Description
+
+ +
+
+ +
+
Created
+
2000-01-01 01:01:01
+
+
+
Content Last Modified
+
2000-01-01 01:01:01
+
+
+
Creator
+
+ Bart Simpson +
+
+ +
+
+ + +
+
+ +
+ +
+ + diff -Nru zope3-3.4.0/src/zope/dublincore/browser/__init__.py zope3-3.5~bzr18/src/zope/dublincore/browser/__init__.py --- zope3-3.4.0/src/zope/dublincore/browser/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/browser/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/dublincore/browser/metadataedit.py zope3-3.5~bzr18/src/zope/dublincore/browser/metadataedit.py --- zope3-3.4.0/src/zope/dublincore/browser/metadataedit.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/browser/metadataedit.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,53 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Dublin Core Meta Data View + +$Id: metadataedit.py 66902 2006-04-12 20:16:30Z philikon $ +""" +__docformat__ = 'restructuredtext' + +from datetime import datetime +from zope.event import notify +from zope.dublincore.interfaces import IZopeDublinCore +from zope.lifecycleevent import ObjectModifiedEvent, Attributes +from zope.i18nmessageid import MessageFactory +_ = MessageFactory('zope') + + +class MetaDataEdit(object): + """Provide view for editing basic dublin-core meta-data.""" + + def edit(self): + request = self.request + formatter = self.request.locale.dates.getFormatter('dateTime', 'medium') + dc = IZopeDublinCore(self.context) + message='' + + if 'dctitle' in request: + dc.title = unicode(request['dctitle']) + dc.description = unicode(request['dcdescription']) + description = Attributes(IZopeDublinCore, 'title', 'description') + notify(ObjectModifiedEvent(self.context, description)) + message = _("Changed data ${datetime}", + mapping={'datetime': formatter.format(datetime.utcnow())}) + + return { + 'message': message, + 'dctitle': dc.title, + 'dcdescription': dc.description, + 'modified': (dc.modified or dc.created) and \ + formatter.format(dc.modified or dc.created) or '', + 'created': dc.created and formatter.format(dc.created) or '', + 'creators': dc.creators + } diff -Nru zope3-3.4.0/src/zope/dublincore/configure.zcml zope3-3.5~bzr18/src/zope/dublincore/configure.zcml --- zope3-3.4.0/src/zope/dublincore/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/dublincore/creatorannotator.py zope3-3.5~bzr18/src/zope/dublincore/creatorannotator.py --- zope3-3.4.0/src/zope/dublincore/creatorannotator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/creatorannotator.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Object that takes care of annotating the dublin core creator field. + +$Id: creatorannotator.py 93841 2008-12-10 13:14:39Z nadako $ +""" +__docformat__ = 'restructuredtext' + +from zope.dublincore.interfaces import IZopeDublinCore +from zope.security.management import queryInteraction +from zope.security.proxy import removeSecurityProxy + +def CreatorAnnotator(event): + """Update Dublin-Core creator property""" + dc = IZopeDublinCore(event.object, None) + # Principals that can create objects do not necessarily have + # 'zope.app.dublincore.change' permission. + # https://bugs.launchpad.net/zope3/+bug/98124 + dc = removeSecurityProxy(dc) + if dc is None: + return + + # Try to find a principal for that one. If there + # is no principal then we don't touch the list + # of creators. + interaction = queryInteraction() + if interaction is not None: + for participation in interaction.participations: + if participation.principal is None: + continue + principalid = participation.principal.id + if not principalid in dc.creators: + dc.creators = dc.creators + (unicode(principalid), ) diff -Nru zope3-3.4.0/src/zope/dublincore/dcsv.py zope3-3.5~bzr18/src/zope/dublincore/dcsv.py --- zope3-3.4.0/src/zope/dublincore/dcsv.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/dcsv.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,138 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Functions for working with Dublin Core Structured Values (DCSV) scheme. + +DCSV is specified in 'DCMI DCSV: A syntax for writing a list of +labelled values in a text string', at: + +http://dublincore.org/documents/dcmi-dcsv/ + +$Id: dcsv.py 26734 2004-07-23 21:55:49Z pruggera $ +""" +__docformat__ = 'restructuredtext' + +import re + +__all__ = "encode", "decode" + +try: + basestring +except NameError: + # define basestring in Python 2.2.x: + try: + unicode + except NameError: + basestring = str + else: + basestring = str, unicode + + +def encode(items): + L = [] + for item in items: + if isinstance(item, basestring): + L.append(_encode_string(item, "values") + ";") + else: + k, v = item + if not isinstance(v, basestring): + raise TypeError("values must be strings; found %r" % v) + v = _encode_string(v, "values") + if k: + if not isinstance(k, basestring): + raise TypeError("labels must be strings; found %r" % k) + k = _encode_string(k, "labels") + s = "%s=%s;" % (k, v) + else: + s = v + ";" + L.append(s) + return " ".join(L) + +def _encode_string(s, what): + if s.strip() != s: + raise ValueError("%s may not include leading or trailing spaces: %r" + % (what, s)) + return s.replace("\\", r"\\").replace(";", r"\;").replace("=", r"\=") + + +def decode(text): + items = [] + text = text.strip() + while text: + m = _find_interesting(text) + if m: + prefix, char = m.group(1, 2) + prefix = _decode_string(prefix).rstrip() + if char == ";": + items.append(('', prefix)) + text = text[m.end():].lstrip() + continue + else: # char == "=" + text = text[m.end():].lstrip() + # else we have a label + m = _find_value(text) + if m: + value = m.group(1) + text = text[m.end():].lstrip() + else: + value = text + text = '' + items.append((prefix, _decode_string(value))) + else: + items.append(('', _decode_string(text))) + break + return items + +_prefix = r"((?:[^;\\=]|\\.)*)" +_find_interesting = re.compile(_prefix + "([;=])").match +_find_value = re.compile(_prefix + ";").match + +def _decode_string(s): + if "\\" not in s: + return s.rstrip() + r = "" + while s: + c1 = s[0] + if c1 == "\\": + c2 = s[1:2] + if not c2: + return r + c1 + r += c2 + s = s[2:] + else: + r += c1 + s = s[1:] + return r.rstrip() + + +def createMapping(items, allow_duplicates=False): + mapping = {} + for item in items: + if isinstance(item, basestring): + raise ValueError("can't create mapping with unlabelled data") + k, v = item + if not isinstance(k, basestring): + raise TypeError("labels must be strings; found %r" % k) + if not isinstance(v, basestring): + raise TypeError("values must be strings; found %r" % v) + if k in mapping: + if allow_duplicates: + mapping[k].append(v) + else: + raise ValueError("labels may not have more than one value") + else: + if allow_duplicates: + mapping[k] = [v] + else: + mapping[k] = v + return mapping diff -Nru zope3-3.4.0/src/zope/dublincore/dcterms.py zope3-3.5~bzr18/src/zope/dublincore/dcterms.py --- zope3-3.4.0/src/zope/dublincore/dcterms.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/dcterms.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,192 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 information for qualified Dublin Core Metadata. + +$Id: dcterms.py 66902 2006-04-12 20:16:30Z philikon $ +""" +__docformat__ = 'restructuredtext' + +from zope.dublincore import dcsv + +# useful namespace URIs +DC_NS = "http://purl.org/dc/elements/1.1/" +DCTERMS_NS = "http://purl.org/dc/terms/" +XSI_NS = "http://www.w3.org/2001/XMLSchema-instance" + +W3CDTF = "W3CDTF" + + +def splitEncoding(name): + if "." not in name: + return name, None + parts = name.split(".") + if parts[-1] in encodings: + if len(parts) == 2: + return parts + else: + return ".".join(parts[:-1]), parts[-1] + else: + return name, None + + +# The type validator function must raise an exception if the value +# passed isn't valid for the type being check, other just return. + +_dcmitypes = {} +for x in ("Collection Dataset Event Image InteractiveResource" + " Service Software Sound Text PhysicalObject").split(): + _dcmitypes[x.lower()] = x +del x + +def check_dcmitype(value): + if value.lower() not in _dcmitypes: + raise ValueError("%r not a valid DCMIType") + +def check_imt(value): + pass + +def check_iso639_2(value): + pass + +def check_rfc1766(value): + pass + +def check_uri(value): + pass + +def check_point(value): + pass + +def check_iso3166(value): + pass + +def check_box(value): + pass + +def check_tgn(value): + pass + +_period_fields = "name start end scheme".split() + +def check_period(value): + # checks a Period in DCSV format; see: + # http://dublincore.org/documents/dcmi-period/ + items = dcsv.decode(value) + d = dcsv.createMapping(items) + for k in d: + if k not in _period_fields: + raise ValueError("unknown field label %r" % k) + if d.get("scheme", W3CDTF).upper() == W3CDTF: + if "start" in d: + check_w3cdtf(d["start"]) + if "end" in d: + check_w3cdtf(d["end"]) + +def check_w3cdtf(value): + pass + +def check_rfc3066(value): + pass + +encodings = { + # name --> (allowed for, validator|None), + "LCSH": (("Subject",), None), + "MESH": (("Subject",), None), + "DDC": (("Subject",), None), + "LCC": (("Subject",), None), + "UDC": (("Subject",), None), + "DCMIType": (("Type",), check_dcmitype), + "IMT": (("Format",), check_imt), + "ISO639-2": (("Language",), check_iso639_2), + "RFC1766": (("Language",), check_rfc1766), + "URI": (("Identifier", "Relation", "Source",), check_uri), + "Point": (("Coverage.Spatial",), check_point), + "ISO3166": (("Coverage.Spatial",), check_iso3166), + "Box": (("Coverage.Spatial",), check_box), + "TGN": (("Coverage.Spatial",), check_tgn), + "Period": (("Coverage.Temporal",), check_period), + W3CDTF: (("Coverage.Temporal", "Date",), check_w3cdtf), + "RFC3066": (("Language",), check_rfc3066), + } + + +name_to_element = { + # unqualified DCMES 1.1 + "Title": ("dc:title", ""), + "Creator": ("dc:creator", ""), + "Subject": ("dc:subject", ""), + "Description": ("dc:description", ""), + "Publisher": ("dc:publisher", ""), + "Contributor": ("dc:contributor", ""), + "Date": ("dc:date", "dcterms:"+W3CDTF), + "Type": ("dc:type", ""), + "Format": ("dc:format", ""), + "Identifier": ("dc:identifier", ""), + "Source": ("dc:source", ""), + "Language": ("dc:language", ""), + "Relation": ("dc:relation", ""), + "Coverage": ("dc:coverage", ""), + "Rights": ("dc:rights", ""), + + # qualified DCMES 1.1 (directly handled by Zope) + "Date.Created": ("dcterms:created", "dcterms:"+W3CDTF), + "Date.Modified": ("dcterms:modified", "dcterms:"+W3CDTF), + + # qualified DCMES 1.1 (not used by Zope) + "Audience": ("dcterms:audience", ""), + "Audience.Education Level": ("dcterms:educationLevel", ""), + "Audience.Mediator": ("dcterms:mediator", ""), + "Coverage.Spatial": ("dcterms:spatial", ""), + "Coverage.Temporal": ("dcterms:temporal", ""), + "Date.Accepted": ("dcterms:accepted", "dcterms:"+W3CDTF), + "Date.Available": ("dcterms:available", "dcterms:"+W3CDTF), + "Date.Copyrighted": ("dcterms:copyrighted","dcterms:"+W3CDTF), + "Date.Issued": ("dcterms:issued", "dcterms:"+W3CDTF), + "Date.Submitted": ("dcterms:submitted", "dcterms:"+W3CDTF), + "Date.Valid": ("dcterms:valid", "dcterms:"+W3CDTF), + "Description.Abstract": ("dcterms:abstract", ""), + "Description.Table Of Contents": ("dcterms:tableOfContents", ""), + "Format": ("dc:format", ""), + "Format.Extent": ("dcterms:extent", ""), + "Format.Medium": ("dcterms:medium", ""), + "Identifier.Bibliographic Citation": ("dcterms:bibliographicCitation", ""), + "Relation.Is Version Of": ("dcterms:isVersionOf", ""), + "Relation.Has Version": ("dcterms:hasVersion", ""), + "Relation.Is Replaced By": ("dcterms:isReplacedBy", ""), + "Relation.Replaces": ("dcterms:replaces", ""), + "Relation.Is Required By": ("dcterms:isRequiredBy", ""), + "Relation.Requires": ("dcterms:requires", ""), + "Relation.Is Part Of": ("dcterms:isPartOf", ""), + "Relation.Has Part": ("dcterms:hasPart", ""), + "Relation.Is Referenced By": ("dcterms:isReferencedBy", ""), + "Relation.References": ("dcterms:references", ""), + "Relation.Is Format Of": ("dcterms:isFormatOf", ""), + "Relation.Has Format": ("dcterms:hasFormat", ""), + "Relation.Conforms To": ("dcterms:conformsTo", ""), + "Rights.Access Rights": ("dcterms:accessRights", ""), + "Title.Alternative": ("dcterms:alternative", ""), + } + +_prefix_to_ns = { + "dc": DC_NS, + "dcterms": DCTERMS_NS, + # "xsi": XSI_NS, dont' use this for element names, only attrs + } + +element_to_name = {} +for name, (qname, attrs) in name_to_element.iteritems(): + prefix, localname = qname.split(":") + elem_name = _prefix_to_ns[prefix], localname + element_to_name[elem_name] = name + name_to_element[name] = (elem_name, attrs) diff -Nru zope3-3.4.0/src/zope/dublincore/__init__.py zope3-3.5~bzr18/src/zope/dublincore/__init__.py --- zope3-3.4.0/src/zope/dublincore/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/dublincore/interfaces.py zope3-3.5~bzr18/src/zope/dublincore/interfaces.py --- zope3-3.4.0/src/zope/dublincore/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,465 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Dublin Core interfaces + +$Id: interfaces.py 106178 2009-12-02 16:31:15Z tlotze $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface +from zope.schema import Text, TextLine, Datetime, Tuple + +class IDublinCoreElementItem(Interface): + """A qualified dublin core element""" + + qualification = TextLine( + title = u"Qualification", + description = u"The element qualification" + ) + + value = Text( + title = u"Value", + description = u"The element value", + ) + +class IGeneralDublinCore(Interface): + """Dublin-core data access interface + + The Dublin Core, http://dublincore.org/, is a meta data standard + that specifies a set of standard data elements. It provides + flexibility of interpretation of these elements by providing for + element qualifiers that specialize the meaning of specific + elements. For example, a date element might have a qualifier, like + "creation" to indicate that the date is a creation date. In + addition, any element may be repeated. For some elements, like + subject, and contributor, this is obviously necessary, but for + other elements, like title and description, allowing repetitions + is not very useful and adds complexity. + + This interface provides methods for retrieving data in full + generality, to be compliant with the Dublin Core standard. + Other interfaces will provide more convenient access methods + tailored to specific element usage patterns. + """ + + def getQualifiedTitles(): + """Return a sequence of Title IDublinCoreElementItem. + """ + + def getQualifiedCreators(): + """Return a sequence of Creator IDublinCoreElementItem. + """ + + def getQualifiedSubjects(): + """Return a sequence of Subject IDublinCoreElementItem. + """ + + def getQualifiedDescriptions(): + """Return a sequence of Description IDublinCoreElementItem. + """ + + def getQualifiedPublishers(): + """Return a sequence of Publisher IDublinCoreElementItem. + """ + + def getQualifiedContributors(): + """Return a sequence of Contributor IDublinCoreElementItem. + """ + + def getQualifiedDates(): + """Return a sequence of Date IDublinCoreElementItem. + """ + + def getQualifiedTypes(): + """Return a sequence of Type IDublinCoreElementItem. + """ + + def getQualifiedFormats(): + """Return a sequence of Format IDublinCoreElementItem. + """ + + def getQualifiedIdentifiers(): + """Return a sequence of Identifier IDublinCoreElementItem. + """ + + def getQualifiedSources(): + """Return a sequence of Source IDublinCoreElementItem. + """ + + def getQualifiedLanguages(): + """Return a sequence of Language IDublinCoreElementItem. + """ + + def getQualifiedRelations(): + """Return a sequence of Relation IDublinCoreElementItem. + """ + + def getQualifiedCoverages(): + """Return a sequence of Coverage IDublinCoreElementItem. + """ + + def getQualifiedRights(): + """Return a sequence of Rights IDublinCoreElementItem. + """ + +class IWritableGeneralDublinCore(Interface): + """Provide write access to dublin core data + + This interface augments `IStandardDublinCore` with methods for + writing elements. + """ + + def setQualifiedTitles(qualified_titles): + """Set the qualified Title elements. + + The argument must be a sequence of `IDublinCoreElementItem`. + """ + + def setQualifiedCreators(qualified_creators): + """Set the qualified Creator elements. + + The argument must be a sequence of Creator `IDublinCoreElementItem`. + """ + + def setQualifiedSubjects(qualified_subjects): + """Set the qualified Subjects elements. + + The argument must be a sequence of Subject `IDublinCoreElementItem`. + """ + + def setQualifiedDescriptions(qualified_descriptions): + """Set the qualified Descriptions elements. + + The argument must be a sequence of Description `IDublinCoreElementItem`. + """ + + def setQualifiedPublishers(qualified_publishers): + """Set the qualified Publishers elements. + + The argument must be a sequence of Publisher `IDublinCoreElementItem`. + """ + + def setQualifiedContributors(qualified_contributors): + """Set the qualified Contributors elements. + + The argument must be a sequence of Contributor `IDublinCoreElementItem`. + """ + + def setQualifiedDates(qualified_dates): + """Set the qualified Dates elements. + + The argument must be a sequence of Date `IDublinCoreElementItem`. + """ + + def setQualifiedTypes(qualified_types): + """Set the qualified Types elements. + + The argument must be a sequence of Type `IDublinCoreElementItem`. + """ + + def setQualifiedFormats(qualified_formats): + """Set the qualified Formats elements. + + The argument must be a sequence of Format `IDublinCoreElementItem`. + """ + + def setQualifiedIdentifiers(qualified_identifiers): + """Set the qualified Identifiers elements. + + The argument must be a sequence of Identifier `IDublinCoreElementItem`. + """ + + def setQualifiedSources(qualified_sources): + """Set the qualified Sources elements. + + The argument must be a sequence of Source `IDublinCoreElementItem`. + """ + + def setQualifiedLanguages(qualified_languages): + """Set the qualified Languages elements. + + The argument must be a sequence of Language `IDublinCoreElementItem`. + """ + + def setQualifiedRelations(qualified_relations): + """Set the qualified Relations elements. + + The argument must be a sequence of Relation `IDublinCoreElementItem`. + """ + + def setQualifiedCoverages(qualified_coverages): + """Set the qualified Coverages elements. + + The argument must be a sequence of Coverage `IDublinCoreElementItem`. + """ + + def setQualifiedRights(qualified_rights): + """Set the qualified Rights elements. + + The argument must be a sequence of Rights `IDublinCoreElementItem`. + """ + +class IDCDescriptiveProperties(Interface): + """Basic descriptive meta-data properties + """ + + title = TextLine( + title = u'Title', + description = + u"The first unqualified Dublin Core 'Title' element value." + ) + + description = Text( + title = u'Description', + description = + u"The first unqualified Dublin Core 'Description' element value.", + ) + +class IDCTimes(Interface): + """Time properties + """ + + created = Datetime( + title = u'Creation Date', + description = + u"The date and time that an object is created. " + u"\nThis is normally set automatically." + ) + + modified = Datetime( + title = u'Modification Date', + description = + u"The date and time that the object was last modified in a\n" + u"meaningful way." + ) + +class IDCPublishing(Interface): + """Publishing properties + """ + + effective = Datetime( + title = u'Effective Date', + description = + u"The date and time that an object should be published. " + ) + + + expires = Datetime( + title = u'Expiration Date', + description = + u"The date and time that the object should become unpublished." + ) + +class IDCExtended(Interface): + """Extended properties + + This is a mixed bag of properties we want but that we probably haven't + quite figured out yet. + """ + + + creators = Tuple( + title = u'Creators', + description = u"The unqualified Dublin Core 'Creator' element values", + value_type = TextLine(), + ) + + subjects = Tuple( + title = u'Subjects', + description = u"The unqualified Dublin Core 'Subject' element values", + value_type = TextLine(), + ) + + publisher = Text( + title = u'Publisher', + description = + u"The first unqualified Dublin Core 'Publisher' element value.", + ) + + contributors = Tuple( + title = u'Contributors', + description = + u"The unqualified Dublin Core 'Contributor' element values", + value_type = TextLine(), + ) + +class ICMFDublinCore(Interface): + """This interface duplicates the CMF dublin core interface. + """ + + def Title(): + """Return the resource title. + + The first unqualified Dublin Core `Title` element value is + returned as a unicode string if an unqualified element is + defined, otherwise, an empty unicode string is returned. + """ + + def Creator(): + """Return the resource creators. + + Return the full name(s) of the author(s) of the content + object. + + The unqualified Dublin Core `Creator` element values are + returned as a sequence of unicode strings. + """ + + def Subject(): + """Return the resource subjects. + + The unqualified Dublin Core `Subject` element values are + returned as a sequence of unicode strings. + """ + + def Description(): + """Return the resource description + + Return a natural language description of this object. + + The first unqualified Dublin Core `Description` element value is + returned as a unicode string if an unqualified element is + defined, otherwise, an empty unicode string is returned. + """ + + def Publisher(): + """Dublin Core element - resource publisher + + Return full formal name of the entity or person responsible + for publishing the resource. + + The first unqualified Dublin Core `Publisher` element value is + returned as a unicode string if an unqualified element is + defined, otherwise, an empty unicode string is returned. + """ + + def Contributors(): + """Return the resource contributors + + Return any additional collaborators. + + The unqualified Dublin Core `Contributor` element values are + returned as a sequence of unicode strings. + """ + + def Date(): + """Return the default date + + The first unqualified Dublin Core `Date` element value is + returned as a unicode string if an unqualified element is + defined, otherwise, an empty unicode string is returned. The + string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. + """ + + def CreationDate(): + """Return the creation date. + + The value of the first Dublin Core `Date` element qualified by + 'creation' is returned as a unicode string if a qualified + element is defined, otherwise, an empty unicode string is + returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. + """ + + def EffectiveDate(): + """Return the effective date + + The value of the first Dublin Core `Date` element qualified by + 'effective' is returned as a unicode string if a qualified + element is defined, otherwise, an empty unicode string is + returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. + """ + + def ExpirationDate(): + """Date resource expires. + + The value of the first Dublin Core `Date` element qualified by + 'expiration' is returned as a unicode string if a qualified + element is defined, otherwise, an empty unicode string is + returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. + """ + + def ModificationDate(): + """Date resource last modified. + + The value of the first Dublin Core `Date` element qualified by + 'modification' is returned as a unicode string if a qualified + element is defined, otherwise, an empty unicode string is + returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. + """ + + def Type(): + """Return the resource type + + Return a human-readable type name for the resource. + + The first unqualified Dublin Core `Type` element value is + returned as a unicode string if an unqualified element is + defined, otherwise, an empty unicode string is returned. + """ + + def Format(): + """Return the resource format. + + Return the resource's MIME type (e.g., 'text/html', + 'image/png', etc.). + + The first unqualified Dublin Core `Format` element value is + returned as a unicode string if an unqualified element is + defined, otherwise, an empty unicode string is returned. + """ + + def Identifier(): + """Return the URL of the resource. + + This value is computed. It is included in the output of + qualifiedIdentifiers with the qualification 'url'. + """ + + def Language(): + """Return the resource language. + + Return the RFC language code (e.g., 'en-US', 'pt-BR') + for the resource. + + The first unqualified Dublin Core `Language` element value is + returned as a unicode string if an unqualified element is + defined, otherwise, an empty unicode string is returned. + """ + + def Rights(): + """Return the resource rights. + + Return a string describing the intellectual property status, + if any, of the resource. for the resource. + + The first unqualified Dublin Core `Rights` element value is + returned as a unicode string if an unqualified element is + defined, otherwise, an empty unicode string is returned. + """ + +class IZopeDublinCore( + IGeneralDublinCore, + ICMFDublinCore, + IDCDescriptiveProperties, + IDCTimes, + IDCPublishing, + IDCExtended, + ): + """Zope Dublin Core properties""" + +class IWriteZopeDublinCore( + IZopeDublinCore, + IWritableGeneralDublinCore, + ): + """Zope Dublin Core properties with generate update support""" diff -Nru zope3-3.4.0/src/zope/dublincore/property.py zope3-3.5~bzr18/src/zope/dublincore/property.py --- zope3-3.4.0/src/zope/dublincore/property.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/property.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,105 @@ +############################################################################## +# +# Copyright (c) 2005 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. +# +############################################################################## +""" +$Id: property.py 104029 2009-09-15 10:29:47Z nadako $ +""" +__docformat__ = 'restructuredtext' +from zope import schema + +from zope.dublincore.interfaces import IZopeDublinCore +from zope.dublincore.zopedublincore import SequenceProperty + +_marker = object() + + +class DCProperty(object): + """Adapt to a dublin core property + + Handles DC list properties as scalar property. + """ + + def __init__(self, name): + self.__name = name + + def __get__(self, inst, klass): + if inst is None: + return self + name = self.__name + inst = IZopeDublinCore(inst) + value = getattr(inst, name, _marker) + if value is _marker: + field = IZopeDublinCore[name].bind(inst) + value = getattr(field, 'default', _marker) + if value is _marker: + raise AttributeError(name) + if isinstance(value, (list, tuple)): + value = value[0] + return value + + def __set__(self, inst, value): + name = self.__name + inst = IZopeDublinCore(inst) + field = IZopeDublinCore[name].bind(inst) + if isinstance(field, schema.List): + if isinstance(value, tuple): + value = list(value) + else: + value = [value] + elif isinstance(field, schema.Tuple): + if isinstance(value, list): + value = tuple(value) + else: + value = (value,) + field.validate(value) + if field.readonly and inst.__dict__.has_key(name): + raise ValueError(name, 'field is readonly') + setattr(inst, name, value) + + def __getattr__(self, name): + return getattr(IZopeDublinCore[self.__name], name) + + +class DCListProperty(DCProperty): + """Adapt to a dublin core list property + + Returns the DC property unchanged. + """ + + def __init__(self, name): + self.__name = name + + def __get__(self, inst, klass): + if inst is None: + return self + name = self.__name + inst = IZopeDublinCore(inst) + value = getattr(inst, name, _marker) + if value is _marker: + field = IZopeDublinCore[name].bind(inst) + value = getattr(field, 'default', _marker) + if value is _marker: + raise AttributeError(name) + return value + + def __set__(self, inst, value): + name = self.__name + inst = IZopeDublinCore(inst) + field = IZopeDublinCore[name].bind(inst) + if isinstance(field, schema.Tuple): + value = tuple(value) + field.validate(value) + if field.readonly and inst.__dict__.has_key(name): + raise ValueError(name, 'field is readonly') + setattr(inst, name, value) + diff -Nru zope3-3.4.0/src/zope/dublincore/property.txt zope3-3.5~bzr18/src/zope/dublincore/property.txt --- zope3-3.4.0/src/zope/dublincore/property.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/property.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,45 @@ +====================== +Dublin Core Properties +====================== + +A dublin core property allows us to use properties from dublin core +by simply defining a property as DCProperty. + + >>> from zope.dublincore import property + + >>> from zope import interface + >>> from zope.annotation.interfaces import IAttributeAnnotatable + >>> class DC(object): + ... interface.implements(IAttributeAnnotatable) + ... title = property.DCProperty('title') + ... author = property.DCProperty('creators') + ... authors = property.DCListProperty('creators') + + >>> obj = DC() + >>> obj.title = u'My title' + >>> obj.title + u'My title' + +Let's see if the title is really stored in dublin core : + + >>> from zope.dublincore.interfaces import IZopeDublinCore + >>> IZopeDublinCore(obj).title + u'My title' + +Even if a dublin core property is a list property we can set and get the +property as scalar type : + + >>> obj.author = u'me' + >>> obj.author + u'me' + +DCListProperty acts on the list : + + >>> obj.authors + (u'me',) + >>> obj.authors = [u'I', u'others'] + >>> obj.authors + (u'I', u'others') + >>> obj.author + u'I' + diff -Nru zope3-3.4.0/src/zope/dublincore/security.zcml zope3-3.5~bzr18/src/zope/dublincore/security.zcml --- zope3-3.4.0/src/zope/dublincore/security.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/security.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,15 @@ + + + + + + + + diff -Nru zope3-3.4.0/src/zope/dublincore/testing.py zope3-3.5~bzr18/src/zope/dublincore/testing.py --- zope3-3.4.0/src/zope/dublincore/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2005 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. +# +############################################################################## +""" +$Id: testing.py 70826 2006-10-20 03:41:16Z baijum $ +""" +__docformat__ = 'restructuredtext' + +from zope import component + +from annotatableadapter import ZDCAnnotatableAdapter +from interfaces import IWriteZopeDublinCore + +def setUpDublinCore(): + component.provideAdapter(ZDCAnnotatableAdapter, + provides=IWriteZopeDublinCore, + ) diff -Nru zope3-3.4.0/src/zope/dublincore/tests/__init__.py zope3-3.5~bzr18/src/zope/dublincore/tests/__init__.py --- zope3-3.4.0/src/zope/dublincore/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/dublincore/tests/partial.txt zope3-3.5~bzr18/src/zope/dublincore/tests/partial.txt --- zope3-3.4.0/src/zope/dublincore/tests/partial.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/partial.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,138 @@ +==================================== +Dublin Core metadata as content data +==================================== + +Sometimes we want to include data in content objects which mirrors one +or more Dublin Core fields. In these cases, we want the Dublin Core +structures to use the data in the content object rather than keeping a +separate value in the annotations typically used. What fields we want +to do this with can vary, however, and we may not want the Dublin Core +APIs to constrain our choices of field names for our content objects. + +To deal with this, we can use speciallized adapter implementations +tailored to specific content objects. To make this a bit easier, +there is a factory for such adapters. + +Let's take a look at the simplest case of this to start with. We have +some content object with a `title` attribute that should mirror the +Dublin Core `title` field:: + + >>> import zope.interface + >>> import zope.annotation.interfaces + + >>> class Content(object): + ... + ... zope.interface.implements( + ... zope.annotation.interfaces.IAttributeAnnotatable) + ... + ... title = u"" + ... description = u"" + +To avoid having a discrepency between the `title` attribute of our +content object and the equivalent Dublin Core field, we can provide a +specific adapter for our object:: + + >>> from zope.dublincore import annotatableadapter + + >>> factory = annotatableadapter.partialAnnotatableAdapterFactory( + ... ["title"]) + +This creates an adapter factory that maps the Dublin Core `title` +field to the `title` attribute on instances of our `Content` class. +Multiple mappings may be specified by naming the additional fields in +the sequence passed to `partialAnnotatableAdapterFactory()`. (We'll +see later how to use different attribute names for Dublin Core +fields.) + +Let's see what happens when we use the adapter. + +When using the adapter to retrieve a field set to use the content +object, the value stored on the content object is used:: + + >>> content = Content() + >>> adapter = factory(content) + + >>> adapter.title + u'' + + >>> content.title = u"New Title" + >>> adapter.title + u'New Title' + +If we set the relevant Dublin Core field using the adapter, the +content object is updated:: + + >>> adapter.title = u"Adapted Title" + >>> content.title + u'Adapted Title' + +Dublin Core fields which are not specifically mapped to the content +object do not affect the content object:: + + >>> adapter.description = u"Some long description." + >>> content.description + u'' + >>> adapter.description + u'Some long description.' + + +Using arbitrary field names +--------------------------- + +We've seen the simple approach, allowing a Dublin Core field to be +stored on the content object using an attribute of the same name as +the DC field. However, we may want to use a different name for some +reason. The `partialAnnotatableAdapterFactory()` supports this as +well. + +If we call `partialAnnotatableAdapterFactory()` with a mapping instead +of a sequence, the mapping is used to map Dublin Core field names to +attribute names on the content object. + +Let's look at an example where we want the `abstract` attribute on the +content object to be used for the `description` Dublin Core field:: + + >>> class Content(object): + ... + ... zope.interface.implements( + ... zope.annotation.interfaces.IAttributeAnnotatable) + ... + ... abstract = u"" + +We can create the adapter factory by passing a mapping to +`partialAnnotatableAdapterFactory()`:: + + >>> factory = annotatableadapter.partialAnnotatableAdapterFactory( + ... {"description": "abstract"}) + +We can check the effects of the adapter as before:: + + >>> content = Content() + >>> adapter = factory(content) + + >>> adapter.description + u'' + + >>> content.abstract = u"What it's about." + >>> adapter.description + u"What it's about." + + >>> adapter.description = u"Change of plans." + >>> content.abstract + u'Change of plans.' + + +Limitations +----------- + +The current implementation has a number of limitations to be aware of; +hopefully these can be removed in the future. + +- Only simple string properties, like `title`, are supported. This is + largely because other field types have not been given sufficient + thought. Attempting to use this for other fields will cause a + `ValueError` to be raised by `partialAnnotatableAdapterFactory()`. + +- The CMF-like APIs are not supported in the generated adapters. It + is not clear that these APIs are used, but content object + implementations should be aware of this limitation. diff -Nru zope3-3.4.0/src/zope/dublincore/tests/test_annotatableadapter.py zope3-3.5~bzr18/src/zope/dublincore/tests/test_annotatableadapter.py --- zope3-3.4.0/src/zope/dublincore/tests/test_annotatableadapter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/test_annotatableadapter.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,229 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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 annotatableadapter. + +$Id: test_creatorannotator.py 101331 2009-06-29 20:58:01Z tseaver $ +""" +import unittest + +_marker = object() + +class ZDCAnnotatableAdapterTests(unittest.TestCase): + + _registered = False + + def setUp(self): + from zope.testing.cleanup import cleanUp + cleanUp() + + def tearDown(self): + from zope.testing.cleanup import cleanUp + cleanUp() + + def _getTargetClass(self): + from zope.dublincore.annotatableadapter import ZDCAnnotatableAdapter + return ZDCAnnotatableAdapter + + def _registerAnnotations(self, dcdata=None): + from zope.component import provideAdapter + from zope.interface import Interface + from zope.annotation.interfaces import IAnnotations + from zope.dublincore.annotatableadapter import DCkey + class _Annotations(dict): + pass + instance = _Annotations({DCkey: dcdata}) + def _factory(context): + return instance + if not self._registered: + provideAdapter(_factory, (Interface, ), IAnnotations) + self._registered = True + return instance + + def _makeOne(self, context=_marker): + if context is _marker: + context = self._makeContext() + return self._getTargetClass()(context) + + def _makeContext(self): + class DummyContext(object): + pass + return DummyContext() + + def test_class_conforms_to_IWriteZopeDublinCore(self): + from zope.interface.verify import verifyClass + from zope.dublincore.interfaces import IWriteZopeDublinCore + verifyClass(IWriteZopeDublinCore, self._getTargetClass()) + + def test_instance_conforms_to_IWriteZopeDublinCore(self): + from zope.interface.verify import verifyObject + from zope.dublincore.interfaces import IWriteZopeDublinCore + self._registerAnnotations() + verifyObject(IWriteZopeDublinCore, self._makeOne()) + + def test_ctor_wo_existing_DC_annotations(self): + from zope.dublincore.annotatableadapter import DCkey + self._registerAnnotations() + context = self._makeContext() + adapter = self._makeOne(context) + self.assertEqual(adapter.annotations[DCkey], None) + self.assertEqual(adapter._mapping, {}) + + def test_ctor_w_existing_DC_annotations(self): + DCDATA = {'title': 'TITLE'} + self._registerAnnotations(DCDATA) + context = self._makeContext() + adapter = self._makeOne(context) + self.assertEqual(adapter.annotations, None) + self.assertEqual(adapter._mapping, DCDATA) + + def test__changed_wo_existing_DC_annotations(self): + from zope.dublincore.annotatableadapter import DCkey + annotations = self._registerAnnotations() + context = self._makeContext() + adapter = self._makeOne(context) + adapter._mapping['title'] = 'NEW TITLE' + adapter._changed() + self.assertEqual(annotations[DCkey]['title'], 'NEW TITLE') + + def test__changed_w_existing_DC_annotations(self): + from zope.dublincore.annotatableadapter import DCkey + DCDATA = {'title': 'TITLE'} + annotations = self._registerAnnotations(DCDATA) + context = self._makeContext() + adapter = self._makeOne(context) + adapter._changed() + self.assertEqual(annotations[DCkey]['title'], 'TITLE') #unchanged + +class DirectPropertyTests(unittest.TestCase): + + def _getTargetClass(self): + from zope.dublincore.annotatableadapter import DirectProperty + return DirectProperty + + def _makeOne(self, name, attrname): + return self._getTargetClass()(name, attrname) + + def test___get___via_klass(self): + prop = self._makeOne('title', 'headline') + class Testing(object): + title = prop + self.failUnless(Testing.title is prop) + + def test___get___via_instance(self): + prop = self._makeOne('title', 'headline') + class Context(object): + headline = u'HEADLINE' + class ZDCPartialAnnotatableAdapter(object): + title = prop + def __init__(self, context): + self.__context = context + context = Context() + testing = ZDCPartialAnnotatableAdapter(context) + self.assertEqual(testing.title, u'HEADLINE') + + def test___set___non_unicode_raises(self): + prop = self._makeOne('title', 'headline') + class Context(object): + headline = u'HEADLINE' + class ZDCPartialAnnotatableAdapter(object): + title = prop + def __init__(self, context): + self.__context = context + context = Context() + testing = ZDCPartialAnnotatableAdapter(context) + try: + testing.title = 123 + except TypeError: + pass + else: + self.fail("Didn't raise TypeError") + + def test___set___unchanged_doesnt_mutate(self): + prop = self._makeOne('title', 'headline') + class Context(object): + headline = u'HEADLINE' + def __setattr__(self, name, value): + assert 0 + class ZDCPartialAnnotatableAdapter(object): + title = prop + def __init__(self, context): + self.__context = context + context = Context() + testing = ZDCPartialAnnotatableAdapter(context) + testing.title = u'HEADLINE' # doesn't raise + + def test___set___changed_mutates(self): + prop = self._makeOne('title', 'headline') + class Context(object): + headline = u'HEADLINE1' + class ZDCPartialAnnotatableAdapter(object): + title = prop + def __init__(self, context): + self.__context = context + context = Context() + testing = ZDCPartialAnnotatableAdapter(context) + testing.title = u'HEADLINE2' + self.assertEqual(context.headline, u'HEADLINE2') + +class Test_partialAnnotatableAdapterFactory(unittest.TestCase): + + def _callFUT(self, direct_fields): + from zope.dublincore.annotatableadapter \ + import partialAnnotatableAdapterFactory + return partialAnnotatableAdapterFactory(direct_fields) + + def test_w_empty_list_raises(self): + self.assertRaises(ValueError, self._callFUT, []) + + def test_w_empty_dict_raises(self): + self.assertRaises(ValueError, self._callFUT, {}) + + def test_w_unknown_field_raises(self): + self.assertRaises(ValueError, self._callFUT, ['nonesuch']) + + def test_w_date_fields_raises(self): + self.assertRaises(ValueError, self._callFUT, ['created']) + self.assertRaises(ValueError, self._callFUT, ['modified']) + self.assertRaises(ValueError, self._callFUT, ['effective']) + self.assertRaises(ValueError, self._callFUT, ['expires']) + + def test_w_sequence_fields_raises(self): + self.assertRaises(ValueError, self._callFUT, ['creators']) + self.assertRaises(ValueError, self._callFUT, ['subjects']) + self.assertRaises(ValueError, self._callFUT, ['contributors']) + + def test_w_scalar_prop_samename(self): + from zope.dublincore.annotatableadapter import DirectProperty + klass = self._callFUT(['title']) + prop = klass.title + self.failUnless(isinstance(prop, DirectProperty)) + self.assertEqual(prop.__name__, 'title') + self.assertEqual(prop._DirectProperty__attrname, 'title') # XXX + + def test_w_scalar_prop_mapped(self): + from zope.dublincore.annotatableadapter import DirectProperty + klass = self._callFUT({'title': 'headline'}) + prop = klass.title + self.failUnless(isinstance(prop, DirectProperty)) + self.assertEqual(prop.__name__, 'title') + self.assertEqual(prop._DirectProperty__attrname, 'headline') # XXX + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(ZDCAnnotatableAdapterTests), + unittest.makeSuite(DirectPropertyTests), + unittest.makeSuite(Test_partialAnnotatableAdapterFactory), + )) + diff -Nru zope3-3.4.0/src/zope/dublincore/tests/test_creatorannotator.py zope3-3.5~bzr18/src/zope/dublincore/tests/test_creatorannotator.py --- zope3-3.4.0/src/zope/dublincore/tests/test_creatorannotator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/test_creatorannotator.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,133 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 creator annotation. + +$Id: test_creatorannotator.py 104029 2009-09-15 10:29:47Z nadako $ +""" +import unittest + +class CreatorAnnotatorTests(unittest.TestCase): + + def setUp(self): + from zope.testing.cleanup import cleanUp + cleanUp() + self._makeInterface() + self._registerAdapter() + + def tearDown(self): + from zope.testing.cleanup import cleanUp + from zope.security.management import endInteraction + endInteraction() + cleanUp() + + def _callFUT(self, event): + from zope.dublincore.creatorannotator import CreatorAnnotator + return CreatorAnnotator(event) + + def _makeInterface(self): + from zope.interface import Interface + + class IDummyContent(Interface): + pass + + self._iface = IDummyContent + + def _registerAdapter(self): + from zope.component import provideAdapter + from zope.dublincore.interfaces import IZopeDublinCore + provideAdapter(DummyDCAdapter, (self._iface, ), IZopeDublinCore) + + def _makeContextAndEvent(self): + + from zope.interface import implements + + class DummyDublinCore(object): + implements(self._iface) + creators = () + + class DummyEvent(object): + def __init__(self, object): + self.object = object + + context = DummyDublinCore() + event = DummyEvent(context) + return context, event + + def _setPrincipal(self, id): + from zope.security.management import newInteraction + class DummyPrincipal(object): + title = 'TITLE' + description = 'DESCRIPTION' + def __init__(self, id): + self.id = id + if id is None: + newInteraction(DummyRequest(None)) + else: + newInteraction(DummyRequest(DummyPrincipal(id))) + + def test_w_no_request(self): + context, event = self._makeContextAndEvent() + self._callFUT(event) + self.assertEqual(context.creators, ()) + + def test_w_request_no_existing_creators(self): + context, event = self._makeContextAndEvent() + self._setPrincipal('phred') + self._callFUT(event) + self.assertEqual(context.creators, ('phred',)) + + def test_w_request_w_existing_creator_nomatch(self): + context, event = self._makeContextAndEvent() + context.creators = ('bharney',) + self._setPrincipal('phred') + self._callFUT(event) + self.assertEqual(context.creators, ('bharney', 'phred',)) + + def test_w_request_w_existing_creator_match(self): + context, event = self._makeContextAndEvent() + context.creators = ('bharney', 'phred') + self._setPrincipal('phred') + self._callFUT(event) + self.assertEqual(context.creators, ('bharney', 'phred',)) + + def test_w_request_no_principal(self): + context, event = self._makeContextAndEvent() + context.creators = ('bharney', 'phred') + self._setPrincipal(None) + self._callFUT(event) + self.assertEqual(context.creators, ('bharney', 'phred',)) + +class DummyDCAdapter(object): + + def _getcreator(self): + return self.context.creators + def _setcreator(self, value): + self.context.creators = value + creators = property(_getcreator, _setcreator, None, "Adapted Creators") + + def __init__(self, context): + self.context = context + + +class DummyRequest(object): + + def __init__(self, principal): + self.principal = principal + self.interaction = None + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(CreatorAnnotatorTests), + )) diff -Nru zope3-3.4.0/src/zope/dublincore/tests/test_dcsv.py zope3-3.5~bzr18/src/zope/dublincore/tests/test_dcsv.py --- zope3-3.4.0/src/zope/dublincore/tests/test_dcsv.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/test_dcsv.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,328 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 the Dublib Core Structured Value support functions. + +$Id: test_dcsv.py 66902 2006-04-12 20:16:30Z philikon $ +""" +import unittest + +from zope.testing.doctestunit import DocTestSuite +from zope.dublincore.dcsv import encode, decode + + +# TODO still need tests for errors, and createMapping() + + +def test_decode_empty(): + """ + >>> decode('') + [] + >>> decode(' ') + [] + """ + +def test_decode_simple_value(): + """ + >>> decode('v') + [('', 'v')] + >>> decode(' v ') + [('', 'v')] + >>> decode('v;') + [('', 'v')] + >>> decode(' v ; ') + [('', 'v')] + """ + +def test_decode_simple_escaped_value(): + # Make the docstring a raw string to avoid having escapes + # interpreted twice; each test within the docstring will be parsed + # again! + r""" + >>> decode(r'\v') + [('', 'v')] + >>> decode(r'\;') + [('', ';')] + >>> decode(r'\;;') + [('', ';')] + >>> decode(r'\= ') + [('', '=')] + >>> decode(r'\= ; ') + [('', '=')] + + >>> decode(r'\\\=\; ; ') + [('', '\\=;')] + >>> decode(r'\\\=\;') + [('', '\\=;')] + >>> decode(r'\\\=\; = ; ') + [('\\=;', '')] + >>> decode(r'\;\;\;;') + [('', ';;;')] + """ + +def test_decode_trailing_backslash(): + r""" + >>> decode('\\') + [('', '\\')] + >>> decode('v\\') + [('', 'v\\')] + + These are tricky, but for different reasons: + + >>> decode(r'v\ ') + [('', 'v\\')] + >>> decode(r'v\ ; ') + [('', 'v')] + """ + +def test_decode_simple_list(): + """ + >>> decode('a;b;c') + [('', 'a'), ('', 'b'), ('', 'c')] + >>> decode('a;b;c;') + [('', 'a'), ('', 'b'), ('', 'c')] + """ + +def test_decode_simple_escaped_list(): + r""" + >>> decode(r'\a;\b;\c') + [('', 'a'), ('', 'b'), ('', 'c')] + >>> decode(r' \a ; \b ; \c ; ') + [('', 'a'), ('', 'b'), ('', 'c')] + + >>> decode(r'\;;b;c') + [('', ';'), ('', 'b'), ('', 'c')] + >>> decode(r' \=;b;c;') + [('', '='), ('', 'b'), ('', 'c')] + """ + +def test_decode_empty_values(): + # weird case; hard to know the intent of the specification + """ + >>> decode('=') + [('', '')] + >>> decode(';') + [('', '')] + >>> decode(' ; ') + [('', '')] + >>> decode(';;') + [('', ''), ('', '')] + >>> decode(' ; ; ') + [('', ''), ('', '')] + >>> decode('=;') + [('', '')] + >>> decode(' = ; ') + [('', '')] + >>> decode('=;=;') + [('', ''), ('', '')] + >>> decode(' = ; = ; ') + [('', ''), ('', '')] + >>> decode(' = ; = ; = ') + [('', ''), ('', ''), ('', '')] + """ + +def test_decode_labeled_values(): + """ + >>> decode('a=b') + [('a', 'b')] + >>> decode('a=b;') + [('a', 'b')] + >>> decode('a=b;c=d') + [('a', 'b'), ('c', 'd')] + + Not really sure about this one yet; assuming that the space in 'd ;' + is supposed to be removed until we have information that says + otherwise: + + >>> decode('a =b; c= d ;') + [('a', 'b'), ('c', 'd')] + """ + +def test_decode_mixed_values(): + """ + >>> decode('a;b=c') + [('', 'a'), ('b', 'c')] + >>> decode('a=b;c') + [('a', 'b'), ('', 'c')] + >>> decode('a;b=c; ') + [('', 'a'), ('b', 'c')] + >>> decode('a=b;c ; ') + [('a', 'b'), ('', 'c')] + + >>> decode('a;b;c;d=e;f;g;') + [('', 'a'), ('', 'b'), ('', 'c'), ('d', 'e'), ('', 'f'), ('', 'g')] + >>> decode('a=b;c=d;e;f=g') + [('a', 'b'), ('c', 'd'), ('', 'e'), ('f', 'g')] + """ + +def test_decode_duplicate_labels(): + """ + >>> decode('a=b;a=c; a=d') + [('a', 'b'), ('a', 'c'), ('a', 'd')] + """ + +def test_encode_empty_list(): + """ + >>> encode([]) + '' + """ + +def test_encode_single_item(): + """ + >>> encode(['']) + ';' + >>> encode([('', '')]) + ';' + >>> encode(['a']) + 'a;' + >>> encode([('', 'a')]) + 'a;' + >>> encode([('a','')]) + 'a=;' + >>> encode([('a', 'b')]) + 'a=b;' + + The label from a pair can be any non-true value: + + >>> encode([(None, '')]) + ';' + >>> encode([(None, 'a')]) + 'a;' + >>> encode([(0, 'a')]) + 'a;' + >>> encode([((), 'a')]) + 'a;' + + This may be a mis-feature, but seems harmless since no one in + their right mind would use it intentionally (except maybe with + None). + """ + +def test_encode_single_value_needing_escapes(): + r""" + >>> encode(['=']) + '\\=;' + >>> encode([';']) + '\\;;' + >>> encode(['\\']) + '\\\\;' + >>> encode([r'\\']) + '\\\\\\\\;' + """ + +def test_encode_labeled_value_needing_escapes(): + r""" + Escaping needed in the labels: + + >>> encode([('\\', '')]) + '\\\\=;' + >>> encode([('\\', 'a')]) + '\\\\=a;' + >>> encode([('=', '')]) + '\\==;' + >>> encode([(';', 'a')]) + '\\;=a;' + + Escaping needed in the values: + + >>> encode([('a', '\\')]) + 'a=\\\\;' + >>> encode([('a', '=')]) + 'a=\\=;' + >>> encode([('a', ';')]) + 'a=\\;;' + + Escaping needed in both: + + >>> encode([('\\', '\\')]) + '\\\\=\\\\;' + >>> encode([('=', '=')]) + '\\==\\=;' + >>> encode([(';', ';')]) + '\\;=\\;;' + """ + +def test_encode_simple_list(): + """ + >>> encode(['a', 'b', 'c']) + 'a; b; c;' + >>> encode(['', '', '']) + '; ; ;' + >>> encode(['a b', 'c d']) + 'a b; c d;' + """ + +def test_encode_labeled_values(): + # Single items were tested above; these all demonstrate with more + # than one item. + """ + >>> encode([('a', ''), ('b', '')]) + 'a=; b=;' + >>> encode([('a', 'b'), ('c', 'd')]) + 'a=b; c=d;' + """ + +def test_encode_mixed_items(): + """ + >>> encode(['a', ('b', 'c')]) + 'a; b=c;' + >>> encode([('', 'a'), ('b', 'c')]) + 'a; b=c;' + >>> encode([('b', 'c'), 'a']) + 'b=c; a;' + >>> encode([('b', 'c'), ('', 'a')]) + 'b=c; a;' + """ + +def test_encode_error_non_strings(): + """ + >>> encode([(42, '')]) + Traceback (most recent call last): + ... + TypeError: labels must be strings; found 42 + >>> encode([('', 42)]) + Traceback (most recent call last): + ... + TypeError: values must be strings; found 42 + >>> encode([('label', 42)]) + Traceback (most recent call last): + ... + TypeError: values must be strings; found 42 + """ + +def test_encode_error_outer_whitespace(): + """ + >>> encode([' a']) + Traceback (most recent call last): + ... + ValueError: values may not include leading or trailing spaces: ' a' + >>> encode(['a ']) + Traceback (most recent call last): + ... + ValueError: values may not include leading or trailing spaces: 'a ' + >>> encode([('', 'a ')]) + Traceback (most recent call last): + ... + ValueError: values may not include leading or trailing spaces: 'a ' + >>> encode([('label', 'a ')]) + Traceback (most recent call last): + ... + ValueError: values may not include leading or trailing spaces: 'a ' + """ + + +def test_suite(): + return DocTestSuite() + +if __name__ == '__main__': + unittest.main(defaultTest="test_suite") diff -Nru zope3-3.4.0/src/zope/dublincore/tests/test_partialannotatable.py zope3-3.5~bzr18/src/zope/dublincore/tests/test_partialannotatable.py --- zope3-3.4.0/src/zope/dublincore/tests/test_partialannotatable.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/test_partialannotatable.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,17 @@ +"""Tests of the 'partial' annotatable adapter. + +""" +__docformat__ = "reStructuredText" + +import zope.testing.doctest +import zope.component.testing +from zope.component.testing import tearDown +from zope.annotation.attribute import AttributeAnnotations + +def setUp(test): + zope.component.testing.setUp(test) + zope.component.provideAdapter(AttributeAnnotations) + +def test_suite(): + return zope.testing.doctest.DocFileSuite( + "partial.txt", setUp=setUp, tearDown=tearDown) diff -Nru zope3-3.4.0/src/zope/dublincore/tests/test_property.py zope3-3.5~bzr18/src/zope/dublincore/tests/test_property.py --- zope3-3.4.0/src/zope/dublincore/tests/test_property.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/test_property.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ +############################################################################## +# +# Copyright (c) 2006 Lovely Systems 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 the Dublin Core Property implementation + +$Id: test_property.py 104029 2009-09-15 10:29:47Z nadako $ +""" +__docformat__ = "reStructuredText" + +import doctest +import unittest + +from zope import component + +from zope.testing.doctestunit import DocFileSuite +from zope.testing import cleanup + +from zope.annotation.attribute import AttributeAnnotations +from zope.dublincore import testing + + +def setUp(test): + cleanup.setUp() + component.provideAdapter(AttributeAnnotations) + testing.setUpDublinCore() + +def tearDown(test): + cleanup.tearDown() + +def test_suite(): + return unittest.TestSuite( + ( + DocFileSuite('../property.txt', + setUp=setUp, + tearDown=tearDown, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, + ), + )) diff -Nru zope3-3.4.0/src/zope/dublincore/tests/test_xmlmetadata.py zope3-3.5~bzr18/src/zope/dublincore/tests/test_xmlmetadata.py --- zope3-3.4.0/src/zope/dublincore/tests/test_xmlmetadata.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/test_xmlmetadata.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,292 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 loading of Dublin Core metadata from the XML representation. + +$Id: test_xmlmetadata.py 66902 2006-04-12 20:16:30Z philikon $ +""" +import unittest + +from zope.dublincore import dcterms +from zope.dublincore.xmlmetadata import dumpString, parseString + + +class XMLDublinCoreLoadingTests(unittest.TestCase): + + # Note: We're not using the 'traditional' namespace prefixes in + # the tests since we want to make sure we're doing the right thing + # in the content handler. Also, we should use something we're not + # using in zope.dublincore.dcterms. + _prefix = ("\n" + "\n" + % (dcterms.DC_NS, dcterms.DCTERMS_NS, dcterms.XSI_NS)) + _suffix = "\n" + + def parse(self, text): + return parseString("%s%s%s" % (self._prefix, text, self._suffix)) + + def check1(self, text, name, value, generic=None): + expected = {name: (value,)} + m = self.parse(text) + self.assertEqual(m, expected) + m = self.parse("%s" % text) + self.assertEqual(m, expected) + if generic: + m = self.parse("<%s>%s" % (generic, text, generic)) + self.assertEqual(m, expected) + m = self.parse("<%s>%s" + % (generic, text, generic)) + self.assertEqual(m, expected) + + # tests with acceptable input + + def test_empty(self): + m = parseString("") + self.assertEqual(m, {}) + + # core elements and related refinements + + def test_simple_title(self): + self.check1("Foo", "Title", u"Foo") + + def test_two_titles(self): + m = self.parse("Foo" + "Bar") + self.assertEqual(m, {"Title": (u"Foo", u"Bar")}) + + def test_alternative_title(self): + m = self.parse("Foo" + "Bar") + self.assertEqual(m, {"Title": (u"Foo",), + "Title.Alternative": (u"Bar",)}) + + def test_creator(self): + self.check1("somebody", + "Creator", "somebody") + + def test_subject(self): + self.check1("something", + "Subject", "something") + self.check1("something", + "Subject.LCSH", "something") + self.check1("something", + "Subject.MESH", "something") + self.check1("something", + "Subject.DDC", "something") + self.check1("something", + "Subject.LCC", "something") + self.check1("something", + "Subject.UDC", "something") + + def test_description(self): + self.check1("foo", + "Description", "foo") + self.check1("foo", + "Description.Abstract", "foo", generic="d:description") + self.check1("foo", + "Description.Table Of Contents", "foo", + generic="d:description") + + def test_publisher(self): + self.check1("pub", + "Publisher", "pub") + + def test_contributor(self): + self.check1("somebody", + "Contributor", "somebody") + + def test_date(self): + self.check1("2003-08-20", + "Date", "2003-08-20") + # refinements used by Zope + self.check1("2003-08-20", + "Date.Created", "2003-08-20", generic="d:date") + self.check1("2003-08-20", + "Date.Modified", "2003-08-20", generic="d:date") + # other refinements + self.check1("2003-08-20", + "Date.Accepted", "2003-08-20", generic="d:date") + self.check1("2003-08-20", + "Date.Available", "2003-08-20", generic="d:date") + self.check1("2003-08-20", + "Date.Copyrighted", "2003-08-20", generic="d:date") + self.check1("2003-08-20", + "Date.Issued", "2003-08-20", generic="d:date") + self.check1("2003-08-20", + "Date.Submitted", "2003-08-20", generic="d:date") + self.check1("2003-08-20", + "Date.Valid", "2003-08-20", generic="d:date") + + def test_type(self): + self.check1("some type", + "Type", "some type") + self.check1("Collection", + "Type.DCMIType", "Collection") + + def test_format(self): + self.check1("some format", + "Format", "some format") + self.check1("text/xml", + "Format.IMT", "text/xml") + self.check1("1 hour", + "Format.Extent", "1 hour", generic="d:format") + self.check1("70mm IMAX celluloid", + "Format.Medium", "70mm IMAX celluloid", generic="d:format") + + def test_identifier(self): + self.check1("ident", + "Identifier", "ident") + self.check1("" + " citation " + "", + "Identifier.Bibliographic Citation", "citation", + generic="d:identifier") + + def test_source(self): + self.check1("src", + "Source", "src") + self.check1("http://example.com/", + "Source.URI", "http://example.com/") + + def test_language(self): + self.check1("Klingon", + "Language", "Klingon") + self.check1("abc", + "Language.ISO639-2", "abc") + self.check1("en", + "Language.RFC1766", "en") + self.check1("en-GB-oed", + "Language.RFC3066", "en-GB-oed") + + def test_relation(self): + self.check1("rel", + "Relation", "rel") + self.check1("that", + "Relation.Is Version Of", "that", generic="d:relation") + self.check1("that", + "Relation.Has Version", "that", generic="d:relation") + self.check1("that", + "Relation.Is Replaced By", "that", generic="d:relation") + self.check1("that", + "Relation.Replaces", "that", generic="d:relation") + self.check1("that", + "Relation.Is Required By", "that", generic="d:relation") + self.check1("that", + "Relation.Requires", "that", generic="d:relation") + self.check1("that", + "Relation.Is Part Of", "that", generic="d:relation") + self.check1("that", + "Relation.Has Part", "that", generic="d:relation") + self.check1("that", + "Relation.Is Referenced By", "that", generic="d:relation") + self.check1("that", + "Relation.References", "that", generic="d:relation") + self.check1("that", + "Relation.Is Format Of", "that", generic="d:relation") + self.check1("that", + "Relation.Has Format", "that", generic="d:relation") + self.check1("that", + "Relation.Conforms To", "that", generic="d:relation") + + def test_coverage(self): + self.check1("how much", + "Coverage", "how much") + self.check1("where", + "Coverage.Spatial", "where", generic="d:coverage") + self.check1("when", + "Coverage.Temporal", "when", generic="d:coverage") + self.check1("" + " name=Period Name; start=1812; end=2112; " + "", + "Coverage.Temporal.Period", + "name=Period Name; start=1812; end=2112;", + generic="d:coverage") + self.check1("2003-08-20", + "Coverage.Temporal.W3CDTF", "2003-08-20", + generic="d:coverage") + + def test_rights(self): + self.check1("rights", + "Rights", "rights") + self.check1("rights", + "Rights.Access Rights", "rights", generic="d:rights") + + # non-core elements + + def test_audience(self): + # Audience is the only DCMI element not in the core + self.check1("people", + "Audience", "people") + self.check1("people", + "Audience.Education Level", "people", generic="t:audience") + self.check1("people", + "Audience.Mediator", "people", generic="t:audience") + + def test_nested_refinement(self): + # direct nesting + self.check1(("" + "Foo" + ""), + "Title.Alternative", u"Foo") + # nesting with an intermediate element + self.check1(("" + "Foo" + ""), + "Title.Alternative", u"Foo") + + # tests with errors in the input + + def test_invalid_nested_refinement(self): + self.assertRaises(ValueError, self.parse, + ("" + "Title" + "")) + self.assertRaises(ValueError, self.parse, + ("" + "Title" + "")) + + def test_invalid_type(self): + self.assertRaises(ValueError, self.parse, + "x") + + def test_invalid_dcmitype(self): + self.assertRaises(ValueError, self.parse, + "flub") + +class XMLDublinCoreSerializationTests(unittest.TestCase): + + def roundtrip(self, mapping): + text = dumpString(mapping) + parsed = parseString(text) + self.assertEqual(parsed, mapping) + + def test_serialize_empty(self): + self.roundtrip({}) + + def test_single_entry(self): + self.roundtrip({"Title.Alternative": (u"Foo",)}) + + def test_two_titles(self): + self.roundtrip({"Title": (u"Foo", u"Bar")}) + + +def test_suite(): + suite = unittest.makeSuite(XMLDublinCoreLoadingTests) + suite.addTest(unittest.makeSuite(XMLDublinCoreSerializationTests)) + return suite + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff -Nru zope3-3.4.0/src/zope/dublincore/tests/test_zdcannotatableadapter.py zope3-3.5~bzr18/src/zope/dublincore/tests/test_zdcannotatableadapter.py --- zope3-3.4.0/src/zope/dublincore/tests/test_zdcannotatableadapter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/test_zdcannotatableadapter.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 the Dublin Core annotations adapter. + +$Id: test_zdcannotatableadapter.py 66902 2006-04-12 20:16:30Z philikon $ +""" +import unittest + +from zope.annotation.interfaces import IAnnotations +from zope.component.testing import PlacelessSetup +from zope.interface import implements + +class TestAnnotations(dict): + implements(IAnnotations) + +class DublinCoreAdapterTest(PlacelessSetup, unittest.TestCase): + + def testZDCAnnotatableAdapter(self): + from zope.dublincore.annotatableadapter import ZDCAnnotatableAdapter + annotations = TestAnnotations() + dc = ZDCAnnotatableAdapter(annotations) + + self.failIf(annotations, "There shouldn't be any data yet") + self.assertEqual(dc.title, u'') + self.failIf(annotations, "There shouldn't be any data yet") + dc.title = u"Test title" + self.failUnless(annotations, "There should be data now!") + + dc = ZDCAnnotatableAdapter(annotations) + self.assertEqual(dc.title, u'Test title') + +def test_suite(): + return unittest.makeSuite(DublinCoreAdapterTest) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/dublincore/tests/test_zopedublincore.py zope3-3.5~bzr18/src/zope/dublincore/tests/test_zopedublincore.py --- zope3-3.4.0/src/zope/dublincore/tests/test_zopedublincore.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/tests/test_zopedublincore.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,203 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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's Dublin Core implementation + +$Id: test_zopedublincore.py 66902 2006-04-12 20:16:30Z philikon $ +""" + +from unittest import TestCase, TestSuite, main, makeSuite + +class Test(TestCase): + + def testImplementa(self): + from zope.interface.verify import verifyObject + from zope.dublincore.interfaces import IZopeDublinCore + verifyObject(IZopeDublinCore, self.dc) + + def _Test__new(self): + from zope.dublincore.zopedublincore import ZopeDublinCore + return ZopeDublinCore() + + def setUp(self): + self.dc = self._Test__new() + + def __testGetQualified(self, name, values): + ovalues = getattr(self.dc, 'getQualified'+name)() + + ivalues = list(values) + ivalues.sort() + ovalues = list(ovalues) + ovalues.sort() + self.assertEqual(ovalues, ivalues) + + def __testQualified(self, name, + values = [ + (u'', u'blah blah'), + (u'old', u'bleep bleep'), + (u'old', u'bleep bleep \u1111'), + (u'foo\u1111', u'bleep bleep'), + ] + ): + getattr(self.dc, 'setQualified'+name)(values) + self.__testGetQualified(name, values) + + def testOtherQualified(self): + for name in ('Sources', 'Relations', 'Coverages'): + self.__testQualified(name) + + + def testScalars(self): + for qname, mname, pname in ( + ('Titles', 'Title', 'title'), + ('Descriptions', 'Description', 'description'), + ('Publishers', 'Publisher', 'publisher'), + ('Types', 'Type', 'type'), + ('Formats', 'Format', 'format'), + ('Identifiers', 'Identifier', 'identifier'), + ('Languages', 'Language', 'language'), + ('Rights', 'Rights', 'rights'), + ): + self.__testQualified(qname) + dc = self.dc + self.assertEqual(getattr(dc, pname), u'blah blah') + self.assertEqual(getattr(dc, mname)(), u'blah blah') + + self.assertRaises(Exception, setattr, dc, pname, 'foo') + setattr(dc, pname, u'foo') + self.assertEqual(getattr(dc, pname), u'foo') + self.assertEqual(getattr(dc, mname)(), u'foo') + self.__testGetQualified(qname, + [ + (u'', u'foo'), + (u'old', u'bleep bleep'), + (u'old', u'bleep bleep \u1111'), + (u'foo\u1111', u'bleep bleep'), + ] + ) + + def testSequences(self): + for qname, mname, pname in ( + ('Creators', 'Creator', 'creators'), + ('Subjects', 'Subject', 'subjects'), + ('Contributors', 'Contributors', 'contributors'), + ): + self.__testQualified(qname, [ + (u'', u'foo'), + (u'', u'bar'), + (u'', u'baz'), + (u'', u'baz\u1111'), + (u'old', u'bleep bleep'), + (u'old', u'bleep bleep \u1111'), + (u'foo\u1111', u'bleep bleep'), + ] + ) + dc = self.dc + + v = getattr(dc, pname) + v = list(v) + v.sort() + self.assertEqual(v, [u'bar', u'baz', u'baz\u1111', u'foo']) + + v = getattr(dc, mname)() + v = list(v) + v.sort() + self.assertEqual(v, [u'bar', u'baz', u'baz\u1111', u'foo']) + + + self.assertRaises(Exception, setattr, dc, pname, 'foo') + self.assertRaises(Exception, setattr, dc, pname, ['foo']) + + setattr(dc, pname, [u'high', u'low', u'spam', u'eggs', u'ham', ]) + + v = getattr(dc, pname) + v = list(v) + v.sort() + self.assertEqual(v, [u'eggs', u'ham', u'high', u'low', u'spam']) + + v = getattr(dc, mname)() + v = list(v) + v.sort() + self.assertEqual(v, [u'eggs', u'ham', u'high', u'low', u'spam']) + + self.__testGetQualified(qname, + [ + (u'', u'high'), + (u'', u'low'), + (u'', u'spam'), + (u'', u'eggs'), + (u'', u'ham'), + (u'old', u'bleep bleep'), + (u'old', u'bleep bleep \u1111'), + (u'foo\u1111', u'bleep bleep'), + ] + ) + + + + def testDates(self): + self.__testQualified('Dates', [ + (u'', u'1990-01-01'), + (u'Created', u'1980-10-01T23:11:10-04:00'), + (u'Modified', u'2002-10-01T12:09:22-04:00'), + (u'Effective', u'2002-10-09T00:00:00-04:00'), + (u'Expires', u'2002-10-16T00:00:00-04:00'), + (u'xxx', u'2000-07-04'), + (u'xxx', u'2001-12-31'), + (u'foo \u1111', u'2001-12-31'), + ]) + + from zope.datetime import parseDatetimetz + + dc = self.dc + self.assertEqual(dc.created, + parseDatetimetz('1980-10-01T23:11:10-04:00')) + self.assertEqual(dc.modified, + parseDatetimetz('2002-10-01T12:09:22-04:00')) + self.assertEqual(dc.effective, + parseDatetimetz('2002-10-09T00:00:00-04:00')) + self.assertEqual(dc.expires, + parseDatetimetz('2002-10-16T00:00:00-04:00')) + + self.assertEqual(dc.Date(), u'1990-01-01') + self.assertEqual(dc.CreationDate(), u'1980-10-01T23:11:10-04:00') + self.assertEqual(dc.ModificationDate(), u'2002-10-01T12:09:22-04:00') + self.assertEqual(dc.EffectiveDate(), u'2002-10-09T00:00:00-04:00') + self.assertEqual(dc.ExpirationDate(), u'2002-10-16T00:00:00-04:00') + + + dt = parseDatetimetz('2002-10-03T14:51:55-04:00') + + dc.modified = dt + + self.assertRaises(Exception, setattr, dc, 'modified', 'foo') + + modified = [qv[1] + for qv in dc.getQualifiedDates() + if qv[0] == u'Modified'] + + self.failIf(len(modified) != 1, "should be only one: %r" % modified) + + self.assertEqual(parseDatetimetz(modified[0]), dt) + + modified = dc.ModificationDate() + self.assertEqual(parseDatetimetz(modified), dt) + + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/dublincore/timeannotators.py zope3-3.5~bzr18/src/zope/dublincore/timeannotators.py --- zope3-3.4.0/src/zope/dublincore/timeannotators.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/timeannotators.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,43 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Objects that take care of annotating dublin core meta data times + +$Id: timeannotators.py 66902 2006-04-12 20:16:30Z philikon $ +""" +__docformat__ = 'restructuredtext' + +from datetime import datetime +import pytz +from zope.dublincore.interfaces import IZopeDublinCore +from zope.security.proxy import removeSecurityProxy + + +def ModifiedAnnotator(event): + dc = IZopeDublinCore(event.object, None) + if dc is not None: + # Principals that can modify objects do not necessary have permissions + # to arbitrarily modify DC data, see issue 373 + dc = removeSecurityProxy(dc) + dc.modified = datetime.now(pytz.utc) + + +def CreatedAnnotator(event): + dc = IZopeDublinCore(event.object, None) + if dc is not None: + # Principals that can create objects do not necessary have permissions + # to arbitrarily modify DC data, see issue 373 + dc = removeSecurityProxy(dc) + now = datetime.now(pytz.utc) + dc.created = now + dc.modified = now diff -Nru zope3-3.4.0/src/zope/dublincore/xmlmetadata.py zope3-3.5~bzr18/src/zope/dublincore/xmlmetadata.py --- zope3-3.4.0/src/zope/dublincore/xmlmetadata.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/xmlmetadata.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,247 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Dublin Core XML data parser and writer + +$Id: xmlmetadata.py 66902 2006-04-12 20:16:30Z philikon $ +""" +__docformat__ = 'restructuredtext' + +import xml.sax +import xml.sax.handler + +from cStringIO import StringIO +from xml.sax.saxutils import escape, quoteattr + +from zope.dublincore import dcterms + + +XSI_TYPE = (dcterms.XSI_NS, "type") + +dublin_core_namespaces = dcterms.DC_NS, dcterms.DCTERMS_NS + + +DEFAULT_NAMESPACE_PREFIXES = { + # uri: prefix, + dcterms.DC_NS: "dc", + dcterms.DCTERMS_NS: "dcterms", + dcterms.XSI_NS: "xsi", + } + +class NamespaceTracker(object): + def __init__(self, mapping=None): + self._mapping = {} + self._used = {} + if mapping: + self._mapping.update(mapping) + self._counter = 0 + + def encode(self, (uri, localname)): + if not uri: + return localname + if uri not in self._mapping: + self._counter += 1 + prefix = "ns%d" % self._counter + self._mapping[uri] = prefix + self._used[prefix] = uri + else: + prefix = self._mapping[uri] + if prefix not in self._used: + self._used[prefix] = uri + if prefix: + return "%s:%s" % (prefix, localname) + else: + return localname + + def getPrefixMappings(self): + return self._used.items() + + +def dumpString(mapping): + sio = StringIO() + nsmap = NamespaceTracker(DEFAULT_NAMESPACE_PREFIXES) + items = mapping.items() + items.sort() + prev = None + for name, values in items: + name, type = dcterms.splitEncoding(name) + group = name.split(".", 1)[0] + if prev != group: + sio.write("\n") + prev = group + if name in dcterms.name_to_element: + element, t = dcterms.name_to_element[name] + qname = nsmap.encode(element) + if not type: + type = t + if type: + type = " %s=%s" % (nsmap.encode((dcterms.XSI_NS, "type")), + quoteattr(type)) + for value in values: + sio.write(" <%s%s>\n %s\n \n" + % (qname, type, _encode_string(value), qname)) + else: + raise RuntimeError("could not serialize %r metadata element" + % name) + content = sio.getvalue() + sio = StringIO() + sio.write("\n" + "\n") + sio.write(content) + sio.write("\n") + return sio.getvalue() + +try: + unicode +except NameError: + _encode_string = escape +else: + def _encode_string(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + return escape(s) + + +def parse(source, error_handler=None): + parser, ch = _setup_parser(error_handler) + parser.parse(source) + return ch.mapping + +def parseString(text, error_handler=None): + parser, ch = _setup_parser(error_handler) + parser.feed(text) + parser.close() + return ch.mapping + +def _setup_parser(error_handler): + parser = xml.sax.make_parser() + ch = DublinCoreHandler() + parser.setFeature(xml.sax.handler.feature_namespaces, True) + parser.setContentHandler(ch) + if error_handler is not None: + parser.setErrorHandler(error_handler) + return parser, ch + + +class PrefixManager(object): + # We don't use this other than in the DublinCoreHandler, but it's + # entirely general so we'll separate it out for now. + + """General handler for namespace prefixes. + + This should be used as a mix-in when creating a ContentHandler. + """ + + __prefix_map = None + + def startPrefixMapping(self, prefix, uri): + if self.__prefix_map is None: + self.__prefix_map = {} + pm = self.__prefix_map + pm.setdefault(prefix, []).append(uri) + + def endPrefixMapping(self, prefix): + pm = self.__prefix_map + uris = pm[prefix] + del uris[-1] + if not uris: + del pm[prefix] + + def get_uri(self, prefix): + pm = self.__prefix_map + if pm is None: + return None + if prefix in pm: + return pm[prefix][-1] + else: + return None + + +class DublinCoreHandler(PrefixManager, xml.sax.handler.ContentHandler): + + def startDocument(self): + self.mapping = {} + self.stack = [] + + def get_dc_container(self): + name = None + for (uri, localname), dcelem, validator in self.stack: + if uri in dublin_core_namespaces: + name = uri, localname + if name in dcterms.element_to_name: + # dcelem contains type info, so go back to the mapping + return dcterms.element_to_name[name] + else: + return None + + def startElementNS(self, name, qname, attrs): + self.buffer = u"" + # TODO: need convert element to metadata element name + dcelem = validator = None + if name in dcterms.element_to_name: + dcelem = dcterms.element_to_name[name] + type = attrs.get(XSI_TYPE) + if type: + if not dcelem: + raise ValueError( + "data type specified for unknown metadata element: %s" + % qname) + if ":" in type: + prefix, t = type.split(":", 1) + ns = self.get_uri(prefix) + if ns != dcterms.DCTERMS_NS: + raise ValueError("unknown data type namespace: %s" % t) + type = t + if type not in dcterms.encodings: + raise ValueError("unknown data type: %r" % type) + allowed_in, validator = dcterms.encodings[type] + dcelem_split = dcelem.split(".") + for elem in allowed_in: + elem_split = elem.split(".") + if dcelem_split[:len(elem_split)] == elem_split: + break + else: + raise ValueError("%s values are not allowed for %r" + % (type, dcelem)) + dcelem = "%s.%s" % (dcelem, type) + if dcelem: + cont = self.get_dc_container() + if cont and cont != dcelem: + prefix = cont + "." + if not dcelem.startswith(prefix): + raise ValueError("%s is not a valid refinement for %s" + % (dcelem, cont)) + self.stack.append((name, dcelem, validator)) + + def endElementNS(self, name, qname): + startname, dcelem, validator = self.stack.pop() + assert startname == name + if self.buffer is None: + return + data = self.buffer.strip() + self.buffer = None + if not dcelem: + return + if validator is not None: + validator(data) + if dcelem in self.mapping: + self.mapping[dcelem] += (data,) + else: + self.mapping[dcelem] = (data,) + + def characters(self, data): + if self.buffer is not None: + self.buffer += data diff -Nru zope3-3.4.0/src/zope/dublincore/zopedublincore.py zope3-3.5~bzr18/src/zope/dublincore/zopedublincore.py --- zope3-3.4.0/src/zope/dublincore/zopedublincore.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/dublincore/zopedublincore.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,355 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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's Dublin Core Implementation + +$Id: zopedublincore.py 66902 2006-04-12 20:16:30Z philikon $ +""" +__docformat__ = 'restructuredtext' + +from datetime import datetime + +from zope.interface import implements +from zope.datetime import parseDatetimetz +from zope.dublincore.interfaces import IZopeDublinCore + +class SimpleProperty(object): + + def __init__(self, name): + self.__name__ = name + +class ScalarProperty(SimpleProperty): + + def __get__(self, inst, klass): + if inst is None: + return self + data = inst._mapping.get(self.__name__, ()) + if data: + return data[0] + else: + return u'' + + def __set__(self, inst, value): + if not isinstance(value, unicode): + raise TypeError("Element must be unicode") + dict = inst._mapping + __name__ = self.__name__ + inst._changed() + dict[__name__] = (value, ) + dict.get(__name__, ())[1:] + +def _scalar_get(inst, name): + data = inst._mapping.get(name, ()) + if data: + return data[0] + else: + return u'' + +class DateProperty(ScalarProperty): + + def __get__(self, inst, klass): + if inst is None: + return self + data = inst._mapping.get(self.__name__, ()) + if data: + return parseDatetimetz(data[0]) + else: + return None + + def __set__(self, inst, value): + if not isinstance(value, datetime): + raise TypeError("Element must be %s", datetime) + + value = unicode(value.isoformat('T'), 'ascii') + + super(DateProperty, self).__set__(inst, value) + + +class SequenceProperty(SimpleProperty): + + def __get__(self, inst, klass): + if inst is None: + return self + + return inst._mapping.get(self.__name__, ()) + + def __set__(self, inst, value): + value = tuple(value) + for v in value: + if not isinstance(v, unicode): + raise TypeError("Elements must be unicode") + inst._changed() + inst._mapping[self.__name__] = value + +class ZopeDublinCore(object): + """Zope Dublin Core Mixin + + Subclasses should define either `_changed()` or `_p_changed`. + + Just mix with `Persistence` to get a persistent version. + """ + + implements(IZopeDublinCore) + + def __init__(self, mapping=None): + if mapping is None: + mapping = {} + self._mapping = mapping + + def _changed(self): + self._p_changed = True + + title = ScalarProperty(u'Title') + + def Title(self): + "See `IZopeDublinCore`" + return self.title + + creators = SequenceProperty(u'Creator') + + def Creator(self): + "See `IZopeDublinCore`" + return self.creators + + subjects = SequenceProperty(u'Subject') + + def Subject(self): + "See `IZopeDublinCore`" + return self.subjects + + description = ScalarProperty(u'Description') + + def Description(self): + "See `IZopeDublinCore`" + return self.description + + publisher = ScalarProperty(u'Publisher') + + def Publisher(self): + "See IZopeDublinCore" + return self.publisher + + contributors = SequenceProperty(u'Contributor') + + def Contributors(self): + "See `IZopeDublinCore`" + return self.contributors + + def Date(self): + "See IZopeDublinCore" + return _scalar_get(self, u'Date') + + created = DateProperty(u'Date.Created') + + def CreationDate(self): + "See `IZopeDublinCore`" + return _scalar_get(self, u'Date.Created') + + effective = DateProperty(u'Date.Effective') + + def EffectiveDate(self): + "See `IZopeDublinCore`" + return _scalar_get(self, u'Date.Effective') + + expires = DateProperty(u'Date.Expires') + + def ExpirationDate(self): + "See `IZopeDublinCore`" + return _scalar_get(self, u'Date.Expires') + + modified = DateProperty(u'Date.Modified') + + def ModificationDate(self): + "See `IZopeDublinCore`" + return _scalar_get(self, u'Date.Modified') + + type = ScalarProperty(u'Type') + + def Type(self): + "See `IZopeDublinCore`" + return self.type + + format = ScalarProperty(u'Format') + + def Format(self): + "See `IZopeDublinCore`" + return self.format + + identifier = ScalarProperty(u'Identifier') + + def Identifier(self): + "See `IZopeDublinCore`" + return self.identifier + + language = ScalarProperty(u'Language') + + def Language(self): + "See `IZopeDublinCore`" + return self.language + + rights = ScalarProperty(u'Rights') + + def Rights(self): + "See `IZopeDublinCore`" + return self.rights + + def setQualifiedTitles(self, qualified_titles): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Title', qualified_titles) + + def setQualifiedCreators(self, qualified_creators): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Creator', qualified_creators) + + def setQualifiedSubjects(self, qualified_subjects): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Subject', qualified_subjects) + + def setQualifiedDescriptions(self, qualified_descriptions): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Description', qualified_descriptions) + + def setQualifiedPublishers(self, qualified_publishers): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Publisher', qualified_publishers) + + def setQualifiedContributors(self, qualified_contributors): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Contributor', qualified_contributors) + + def setQualifiedDates(self, qualified_dates): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Date', qualified_dates) + + def setQualifiedTypes(self, qualified_types): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Type', qualified_types) + + def setQualifiedFormats(self, qualified_formats): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Format', qualified_formats) + + def setQualifiedIdentifiers(self, qualified_identifiers): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Identifier', qualified_identifiers) + + def setQualifiedSources(self, qualified_sources): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Source', qualified_sources) + + def setQualifiedLanguages(self, qualified_languages): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Language', qualified_languages) + + def setQualifiedRelations(self, qualified_relations): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Relation', qualified_relations) + + def setQualifiedCoverages(self, qualified_coverages): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Coverage', qualified_coverages) + + def setQualifiedRights(self, qualified_rights): + "See `IWritableDublinCore`" + return _set_qualified(self, u'Rights', qualified_rights) + + def getQualifiedTitles(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Title') + + def getQualifiedCreators(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Creator') + + def getQualifiedSubjects(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Subject') + + def getQualifiedDescriptions(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Description') + + def getQualifiedPublishers(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Publisher') + + def getQualifiedContributors(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Contributor') + + def getQualifiedDates(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Date') + + def getQualifiedTypes(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Type') + + def getQualifiedFormats(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Format') + + def getQualifiedIdentifiers(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Identifier') + + def getQualifiedSources(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Source') + + def getQualifiedLanguages(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Language') + + def getQualifiedRelations(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Relation') + + def getQualifiedCoverages(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Coverage') + + def getQualifiedRights(self): + "See `IStandardDublinCore`" + return _get_qualified(self, u'Rights') + + +def _set_qualified(self, name, qvalue): + data = {} + dict = self._mapping + + for qualification, value in qvalue: + data[qualification] = data.get(qualification, ()) + (value, ) + + self._changed() + for qualification, values in data.iteritems(): + qname = qualification and (name + '.' + qualification) or name + dict[qname] = values + +def _get_qualified(self, name): + result = [] + for aname, avalue in self._mapping.iteritems(): + + if aname == name: + qualification = u'' + for value in avalue: + result.append((qualification, value)) + + elif aname.startswith(name): + qualification = aname[len(name)+1:] + for value in avalue: + result.append((qualification, value)) + + return tuple(result) + + +__doc__ = ZopeDublinCore.__doc__ + __doc__ diff -Nru zope3-3.4.0/src/zope/error/configure.zcml zope3-3.5~bzr18/src/zope/error/configure.zcml --- zope3-3.4.0/src/zope/error/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/error/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/error/error.py zope3-3.5~bzr18/src/zope/error/error.py --- zope3-3.4.0/src/zope/error/error.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/error/error.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,292 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Error Reporting Utility + +This is a port of the Zope 2 error reporting object + +$Id: error.py 70794 2006-10-19 04:29:42Z baijum $ +""" +__docformat__ = 'restructuredtext' + +import time +import logging +import codecs + +from persistent import Persistent +from random import random +from threading import Lock + +from zope.exceptions.exceptionformatter import format_exception +from zope.interface import implements + +from zope.error.interfaces import IErrorReportingUtility +from zope.error.interfaces import ILocalErrorReportingUtility + +import zope.location.interfaces + +#Restrict the rate at which errors are sent to the Event Log +_rate_restrict_pool = {} + +# The number of seconds that must elapse on average between sending two +# exceptions of the same name into the the Event Log. one per minute. +_rate_restrict_period = 60 + +# The number of exceptions to allow in a burst before the above limit +# kicks in. We allow five exceptions, before limiting them to one per +# minute. +_rate_restrict_burst = 5 + +# _temp_logs holds the logs. +_temp_logs = {} # { oid -> [ traceback string ] } + +cleanup_lock = Lock() + +logger = logging.getLogger('SiteError') + +def printedreplace(error): + symbols = (ur"\x%02x" % ord(s) + for s in error.object[error.start:error.end]) + return u"".join(symbols), error.end + +codecs.register_error("zope.error.printedreplace", printedreplace) + +def getPrintable(value): + if not isinstance(value, unicode): + if not isinstance(value, str): + # A call to str(obj) could raise anything at all. + # We'll ignore these errors, and print something + # useful instead, but also log the error. + try: + value = str(value) + except: + logger.exception( + "Error in ErrorReportingUtility while getting a str" + " representation of an object") + return u"" % type(value).__name__ + value = unicode(value, errors="zope.error.printedreplace") + return value + +def getFormattedException(info, as_html=False): + lines = [] + for line in format_exception(as_html=as_html, *info): + line = getPrintable(line) + if not line.endswith("\n"): + if not as_html: + line += "\n" + else: + line += "
\n" + lines.append(line) + return u"".join(lines) + +class ErrorReportingUtility(Persistent): + """Error Reporting Utility""" + implements(IErrorReportingUtility, ILocalErrorReportingUtility, + zope.location.interfaces.IContained) + + __parent__ = __name__ = None + + keep_entries = 20 + copy_to_zlog = 0 + _ignored_exceptions = ('Unauthorized',) + + + def _getLog(self): + """Returns the log for this object. + Careful, the log is shared between threads. + """ + log = _temp_logs.get(self._p_oid, None) + if log is None: + log = [] + _temp_logs[self._p_oid] = log + return log + + def _getUsername(self, request): + username = None + + principal = getattr(request, "principal", None) + if principal is None: + return username + + # UnauthenticatedPrincipal does not have getLogin() + getLogin = getattr(principal, "getLogin", None) + if getLogin is None: + login = "unauthenticated" + else: + try: + login = getLogin() + except: + logger.exception("Error in ErrorReportingUtility while" + " getting login of the principal") + login = u"" + + parts = [] + for part in [ + login, + getattr(principal, "id", + u""), + getattr(principal, "title", + u""), + getattr(principal, "description", + u"") + ]: + part = getPrintable(part) + parts.append(part) + username = u", ".join(parts) + return username + + def _getRequestAsHTML(self, request): + lines = [] + for key, value in request.items(): + lines.append(u"%s: %s
\n" % ( + getPrintable(key), getPrintable(value))) + return u"".join(lines) + + # Exceptions that happen all the time, so we dont need + # to log them. Eventually this should be configured + # through-the-web. + def raising(self, info, request=None): + """Log an exception. + Called by ZopePublication.handleException method. + """ + now = time.time() + try: + strtype = unicode(getattr(info[0], '__name__', info[0])) + if strtype in self._ignored_exceptions: + return + + tb_text = None + tb_html = None + if not isinstance(info[2], basestring): + tb_text = getFormattedException(info) + tb_html = getFormattedException(info, True) + else: + tb_text = getPrintable(info[2]) + + url = None + username = None + req_html = None + if request: + # TODO: Temporary fix, which Steve should undo. URL is + # just too HTTPRequest-specific. + if hasattr(request, 'URL'): + url = request.URL + username = self._getUsername(request) + req_html = self._getRequestAsHTML(request) + + strv = getPrintable(info[1]) + + log = self._getLog() + entry_id = str(now) + str(random()) # Low chance of collision + + log.append({ + 'type': strtype, + 'value': strv, + 'time': time.ctime(now), + 'id': entry_id, + 'tb_text': tb_text, + 'tb_html': tb_html, + 'username': username, + 'url': url, + 'req_html': req_html, + }) + cleanup_lock.acquire() + try: + if len(log) >= self.keep_entries: + del log[:-self.keep_entries] + finally: + cleanup_lock.release() + + if self.copy_to_zlog: + self._do_copy_to_zlog(now, strtype, str(url), info) + finally: + info = None + + def _do_copy_to_zlog(self, now, strtype, url, info): + # info is unused; logging.exception() will call sys.exc_info() + # work around this with an evil hack + when = _rate_restrict_pool.get(strtype,0) + if now > when: + next_when = max(when, + now - _rate_restrict_burst * _rate_restrict_period) + next_when += _rate_restrict_period + _rate_restrict_pool[strtype] = next_when + try: + raise info[0], info[1], info[2] + except: + logger.exception(str(url)) + + def getProperties(self): + return { + 'keep_entries': self.keep_entries, + 'copy_to_zlog': self.copy_to_zlog, + 'ignored_exceptions': self._ignored_exceptions, + } + + def setProperties(self, keep_entries, copy_to_zlog=0, + ignored_exceptions=()): + """Sets the properties of this site error log. + """ + self.keep_entries = int(keep_entries) + self.copy_to_zlog = bool(copy_to_zlog) + self._ignored_exceptions = tuple( + [unicode(e) for e in ignored_exceptions if e] + ) + + def getLogEntries(self): + """Returns the entries in the log, most recent first. + + Makes a copy to prevent changes. + """ + res = [entry.copy() for entry in self._getLog()] + res.reverse() + return res + + def getLogEntryById(self, id): + """Returns the specified log entry. + Makes a copy to prevent changes. Returns None if not found. + """ + for entry in self._getLog(): + if entry['id'] == id: + return entry.copy() + return None + +class RootErrorReportingUtility(ErrorReportingUtility): + rootId = 'root' + + def _getLog(self): + """Returns the log for this object. + + Careful, the log is shared between threads. + """ + log = _temp_logs.get(self.rootId, None) + if log is None: + log = [] + _temp_logs[self.rootId] = log + return log + + +globalErrorReportingUtility = RootErrorReportingUtility() + +def _cleanup_temp_log(): + _temp_logs.clear() + +_clear = _cleanup_temp_log + +# Register our cleanup with Testing.CleanUp to make writing unit tests simpler. +try: + from zope.testing.cleanup import addCleanUp + addCleanUp(_clear) + del addCleanUp +except ImportError: + pass diff -Nru zope3-3.4.0/src/zope/error/__init__.py zope3-3.5~bzr18/src/zope/error/__init__.py --- zope3-3.4.0/src/zope/error/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/error/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# import this diff -Nru zope3-3.4.0/src/zope/error/interfaces.py zope3-3.5~bzr18/src/zope/error/interfaces.py --- zope3-3.4.0/src/zope/error/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/error/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,52 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Error Reporting Utility interfaces + +$Id: interfaces.py 70211 2006-09-17 14:45:07Z flox $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface + +class IErrorReportingUtility(Interface): + """Error Reporting Utility""" + + def raising(info, request=None): + """Logs an exception.""" + + +class ILocalErrorReportingUtility(Interface): + """Local Error Reporting Utility + + This interface contains additional management functions. + """ + + def getProperties(): + """Gets the properties as dictionary. + + keep_entries, copy_to_logfile, ignored_exceptions + """ + + def setProperties(keep_entries, copy_to_zlog=0, ignored_exceptions=(), + RESPONSE=None): + """Sets the properties + + keep_entries, copy_to_logfile, ignored_exceptions + """ + + def getLogEntries(): + """Returns the entries in the log, most recent first.""" + + def getLogEntryById(id): + """Return LogEntry by ID""" diff -Nru zope3-3.4.0/src/zope/error/tests.py zope3-3.5~bzr18/src/zope/error/tests.py --- zope3-3.4.0/src/zope/error/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/error/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,152 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Error Reporting Utility Tests + +$Id: tests.py 41724 2006-02-21 13:27:44Z hdima $ +""" +import sys +import unittest + +from zope.exceptions.exceptionformatter import format_exception +from zope.testing import cleanup + +from zope.error.error import ErrorReportingUtility, getFormattedException + + +class Error(Exception): + + def __init__(self, value): + self.value = value + + def __str__(self): + return self.value + + +def getAnErrorInfo(value=""): + try: + raise Error(value) + except: + return sys.exc_info() + + +class TestRequest(object): + """Mock request that mimics the zope.publisher request.""" + + def __init__(self, environ=None): + self._environ = environ or {} + + def setPrincipal(self, principal): + self.principal = principal + + def items(self): + return [] + + +class ErrorReportingUtilityTests(cleanup.CleanUp, unittest.TestCase): + + def test_checkForEmptyLog(self): + # Test Check Empty Log + errUtility = ErrorReportingUtility() + getProp = errUtility.getLogEntries() + self.failIf(getProp) + + def test_checkProperties(self): + # Test Properties test + errUtility = ErrorReportingUtility() + setProp = { + 'keep_entries':10, + 'copy_to_zlog':1, + 'ignored_exceptions':() + } + errUtility.setProperties(**setProp) + getProp = errUtility.getProperties() + self.assertEqual(setProp, getProp) + + def test_ErrorLog(self): + # Test for Logging Error. Create one error and check whether its + # logged or not. + errUtility = ErrorReportingUtility() + exc_info = getAnErrorInfo() + errUtility.raising(exc_info) + getErrLog = errUtility.getLogEntries() + self.assertEquals(1, len(getErrLog)) + + tb_text = ''.join(format_exception(as_html=0, *exc_info)) + + err_id = getErrLog[0]['id'] + self.assertEquals(tb_text, + errUtility.getLogEntryById(err_id)['tb_text']) + + def test_ErrorLog_unicode(self): + # Emulate a unicode url, it gets encoded to utf-8 before it's passed + # to the request. Also add some unicode field to the request's + # environment and make the principal's title unicode. + request = TestRequest(environ={'PATH_INFO': '/\xd1\x82', + 'SOME_UNICODE': u'\u0441'}) + class PrincipalStub(object): + id = u'\u0441' + title = u'\u0441' + description = u'\u0441' + request.setPrincipal(PrincipalStub()) + + errUtility = ErrorReportingUtility() + exc_info = getAnErrorInfo(u"Error (\u0441)") + errUtility.raising(exc_info, request=request) + getErrLog = errUtility.getLogEntries() + self.assertEquals(1, len(getErrLog)) + + tb_text = ''.join(format_exception(as_html=0, *exc_info)) + + err_id = getErrLog[0]['id'] + self.assertEquals(tb_text, + errUtility.getLogEntryById(err_id)['tb_text']) + + username = getErrLog[0]['username'] + self.assertEquals(username, u'unauthenticated, \u0441, \u0441, \u0441') + + def test_ErrorLog_nonascii(self): + # Emulate a unicode url, it gets encoded to utf-8 before it's passed + # to the request. Also add some unicode field to the request's + # environment and make the principal's title unicode. + request = TestRequest(environ={'PATH_INFO': '/\xd1\x82', + 'SOME_NONASCII': '\xe1'}) + class PrincipalStub(object): + id = '\xe1' + title = '\xe1' + description = '\xe1' + request.setPrincipal(PrincipalStub()) + + errUtility = ErrorReportingUtility() + exc_info = getAnErrorInfo("Error (\xe1)") + errUtility.raising(exc_info, request=request) + getErrLog = errUtility.getLogEntries() + self.assertEquals(1, len(getErrLog)) + + tb_text = getFormattedException(exc_info) + + err_id = getErrLog[0]['id'] + self.assertEquals(tb_text, + errUtility.getLogEntryById(err_id)['tb_text']) + + username = getErrLog[0]['username'] + self.assertEquals(username, r"unauthenticated, \xe1, \xe1, \xe1") + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(ErrorReportingUtilityTests), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/event/__init__.py zope3-3.5~bzr18/src/zope/event/__init__.py --- zope3-3.4.0/src/zope/event/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/event/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""base event system implementation + +$Id: __init__.py 110549 2010-04-06 06:49:02Z tseaver $ +""" + +subscribers = [] + +def notify(event): + for subscriber in subscribers: + subscriber(event) diff -Nru zope3-3.4.0/src/zope/event/README.txt zope3-3.5~bzr18/src/zope/event/README.txt --- zope3-3.4.0/src/zope/event/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/event/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,73 @@ +Events +====== + +This package provides a simple event system on which +application-specific event systems can be built. + +Application code can generate events without being concerned about the +event-processing frameworks that might handle the events. + +Events are objects that represent something happening in a system. +They are used to extend processing by providing processing plug +points. + +The package has a list of subscribers. Application code can manage +subscriptions by manipulating this list. For the examples here, we'll +save the current contents away and empty the list. We'll restore the +contents when we're done with our examples. + + >>> import zope.event + >>> old_subscribers = zope.event.subscribers[:] + >>> del zope.event.subscribers[:] + +The package provides a `notify` function, which is used to +notify subscribers that something has happened: + + >>> class MyEvent: + ... pass + + >>> event = MyEvent() + >>> zope.event.notify(event) + +The notify function is called with a single object, which we call an +event. Any object will do: + + >>> zope.event.notify(None) + >>> zope.event.notify(42) + +An extremely trivial subscription mechanism is provided. Subscribers +are simply callback functions: + + >>> def f(event): + ... print 'got:', event + +that are put into the subscriptions list: + + >>> zope.event.subscribers.append(f) + + >>> zope.event.notify(42) + got: 42 + + >>> def g(event): + ... print 'also got:', event + + >>> zope.event.subscribers.append(g) + + >>> zope.event.notify(42) + got: 42 + also got: 42 + +To unsubscribe, simply remove a subscriber from the list: + + >>> zope.event.subscribers.remove(f) + >>> zope.event.notify(42) + also got: 42 + +Generally, application frameworks will provide more sophisticated +subscription mechanisms that build on this simple mechanism. The +frameworks will install subscribers that then dispatch to other +subscribers based on event types or data. + +We're done, so we'll restore the subscribers: + + >>> zope.event.subscribers[:] = old_subscribers diff -Nru zope3-3.4.0/src/zope/event/tests.py zope3-3.5~bzr18/src/zope/event/tests.py --- zope3-3.4.0/src/zope/event/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/event/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,50 @@ +############################################################################## +# +# 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. +# +############################################################################## +""" Test the event system +""" +import unittest +import doctest + +class Test_notify(unittest.TestCase): + + def setUp(self): + from zope.event import subscribers + self._old_subscribers = subscribers[:] + subscribers[:] = [] + + def tearDown(self): + from zope.event import subscribers + subscribers[:] = self._old_subscribers + + def _callFUT(self, event): + from zope.event import notify + notify(event) + + def test_empty(self): + event = object() + self._callFUT(event) + + def test_not_empty(self): + from zope.event import subscribers + dummy = [] + subscribers.append(dummy.append) + event = object() + self._callFUT(event) + self.assertEqual(dummy, [event]) + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test_notify), + doctest.DocFileSuite('README.txt'), + )) diff -Nru zope3-3.4.0/src/zope/exceptions/DEPENDENCIES.cfg zope3-3.5~bzr18/src/zope/exceptions/DEPENDENCIES.cfg --- zope3-3.4.0/src/zope/exceptions/DEPENDENCIES.cfg 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/exceptions/DEPENDENCIES.cfg 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +zope.interface +zope.testing diff -Nru zope3-3.4.0/src/zope/exceptions/exceptionformatter.py zope3-3.5~bzr18/src/zope/exceptions/exceptionformatter.py --- zope3-3.4.0/src/zope/exceptions/exceptionformatter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/exceptions/exceptionformatter.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,240 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""An exception formatter that shows traceback supplements and traceback info, +optionally in HTML. + +$Id: exceptionformatter.py 110561 2010-04-06 07:25:35Z tseaver $ +""" +import sys +import cgi +import linecache +import traceback + +DEBUG_EXCEPTION_FORMATTER = 1 + +class TextExceptionFormatter(object): + + line_sep = '\n' + show_revisions = 0 + + def __init__(self, limit=None, with_filenames=False): + self.limit = limit + self.with_filenames = with_filenames + + def escape(self, s): + return s + + def getPrefix(self): + return 'Traceback (most recent call last):' + + def getLimit(self): + limit = self.limit + if limit is None: + limit = getattr(sys, 'tracebacklimit', 200) + return limit + + def formatSupplementLine(self, line): + return ' - %s' % line + + def formatSourceURL(self, url): + return [self.formatSupplementLine(url)] + + def formatSupplement(self, supplement, tb): + result = [] + fmtLine = self.formatSupplementLine + + url = getattr(supplement, 'source_url', None) + if url is not None: + result.extend(self.formatSourceURL(url)) + + line = getattr(supplement, 'line', 0) + if line == -1: + line = tb.tb_lineno + col = getattr(supplement, 'column', -1) + if line: + if col is not None and col >= 0: + result.append(fmtLine('Line %s, Column %s' % ( + line, col))) + else: + result.append(fmtLine('Line %s' % line)) + elif col is not None and col >= 0: + result.append(fmtLine('Column %s' % col)) + + expr = getattr(supplement, 'expression', None) + if expr: + result.append(fmtLine('Expression: %s' % expr)) + + warnings = getattr(supplement, 'warnings', None) + if warnings: + for warning in warnings: + result.append(fmtLine('Warning: %s' % warning)) + + getInfo = getattr(supplement, 'getInfo', None) + if getInfo is not None: + try: + extra = getInfo() + if extra: + extra = self.escape(extra) + if self.line_sep != "\n": + extra = extra.replace(" ", " ") + extra = extra.replace("\n", self.line_sep) + result.append(extra) + except: + if DEBUG_EXCEPTION_FORMATTER: + import traceback + traceback.print_exc() + # else just swallow the exception. + return result + + def formatTracebackInfo(self, tbi): + return self.formatSupplementLine('__traceback_info__: %s' % (tbi, )) + + def formatLine(self, tb): + f = tb.tb_frame + lineno = tb.tb_lineno + co = f.f_code + filename = co.co_filename + name = co.co_name + locals = f.f_locals + globals = f.f_globals + + if self.with_filenames: + s = ' File "%s", line %d' % (filename, lineno) + else: + modname = globals.get('__name__', filename) + s = ' Module %s, line %d' % (modname, lineno) + + s = s + ', in %s' % name + + result = [] + result.append(self.escape(s)) + + # Append the source line, if available + line = linecache.getline(filename, lineno) + if line: + result.append(" " + self.escape(line.strip())) + + # Output a traceback supplement, if any. + if '__traceback_supplement__' in locals: + # Use the supplement defined in the function. + tbs = locals['__traceback_supplement__'] + elif '__traceback_supplement__' in globals: + # Use the supplement defined in the module. + # This is used by Scripts (Python). + tbs = globals['__traceback_supplement__'] + else: + tbs = None + if tbs is not None: + factory = tbs[0] + args = tbs[1:] + try: + supp = factory(*args) + result.extend(self.formatSupplement(supp, tb)) + except: + if DEBUG_EXCEPTION_FORMATTER: + traceback.print_exc() + # else just swallow the exception. + + try: + tbi = locals.get('__traceback_info__', None) + if tbi is not None: + result.append(self.formatTracebackInfo(tbi)) + except: + if DEBUG_EXCEPTION_FORMATTER: + traceback.print_exc() + # else just swallow the exception. + + return self.line_sep.join(result) + + def formatExceptionOnly(self, etype, value): + result = ''.join(traceback.format_exception_only(etype, value)) + return result.replace('\n', self.line_sep) + + def formatLastLine(self, exc_line): + return self.escape(exc_line) + + def formatException(self, etype, value, tb): + # The next line provides a way to detect recursion. + __exception_formatter__ = 1 + result = [self.getPrefix() + '\n'] + limit = self.getLimit() + n = 0 + while tb is not None and (limit is None or n < limit): + if tb.tb_frame.f_locals.get('__exception_formatter__'): + # Stop recursion. + result.append('(Recursive formatException() stopped)\n') + break + line = self.formatLine(tb) + result.append(line + '\n') + tb = tb.tb_next + n = n + 1 + exc_line = self.formatExceptionOnly(etype, value) + result.append(self.formatLastLine(exc_line)) + return result + + +class HTMLExceptionFormatter(TextExceptionFormatter): + + line_sep = '
\r\n' + + def escape(self, s): + return cgi.escape(s) + + def getPrefix(self): + return '

Traceback (most recent call last):\r\n

    ' + + def formatSupplementLine(self, line): + return '%s' % self.escape(str(line)) + + def formatTracebackInfo(self, tbi): + s = self.escape(str(tbi)) + s = s.replace('\n', self.line_sep) + return '__traceback_info__: %s' % (s, ) + + def formatLine(self, tb): + line = TextExceptionFormatter.formatLine(self, tb) + return '
  • %s
  • ' % line + + def formatLastLine(self, exc_line): + return '
%s

' % self.escape(exc_line) + + +def format_exception(t, v, tb, limit=None, as_html=False, + with_filenames=False): + """Format a stack trace and the exception information. + + Similar to 'traceback.format_exception', but adds supplemental + information to the traceback and accepts two options, 'as_html' + and 'with_filenames'. + """ + if as_html: + fmt = HTMLExceptionFormatter(limit, with_filenames) + else: + fmt = TextExceptionFormatter(limit, with_filenames) + return fmt.formatException(t, v, tb) + + +def print_exception(t, v, tb, limit=None, file=None, as_html=False, + with_filenames=True): + """Print exception up to 'limit' stack trace entries from 'tb' to 'file'. + + Similar to 'traceback.print_exception', but adds supplemental + information to the traceback and accepts two options, 'as_html' + and 'with_filenames'. + """ + if file is None: + file = sys.stderr + lines = format_exception(t, v, tb, limit, as_html, with_filenames) + for line in lines: + file.write(line) diff -Nru zope3-3.4.0/src/zope/exceptions/__init__.py zope3-3.5~bzr18/src/zope/exceptions/__init__.py --- zope3-3.4.0/src/zope/exceptions/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/exceptions/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""General exceptions that wish they were standard exceptions + +These exceptions are so general purpose that they don't belong in Zope +application-specific packages. + +$Id: __init__.py 110561 2010-04-06 07:25:35Z tseaver $ +""" +from zope.exceptions.interfaces import DuplicationError, IDuplicationError +from zope.exceptions.interfaces import UserError, IUserError + +# avoid dependency on zope.security: +try: + import zope.security +except ImportError, v: + # "ImportError: No module named security" + if not str(v).endswith(' security'): + raise +else: + from zope.security.interfaces import IUnauthorized, Unauthorized + from zope.security.interfaces import IForbidden, IForbiddenAttribute + from zope.security.interfaces import Forbidden, ForbiddenAttribute diff -Nru zope3-3.4.0/src/zope/exceptions/interfaces.py zope3-3.5~bzr18/src/zope/exceptions/interfaces.py --- zope3-3.4.0/src/zope/exceptions/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/exceptions/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,114 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""ITracebackSupplement interface definition. + +$Id: interfaces.py 110561 2010-04-06 07:25:35Z tseaver $ + +When zope.exceptionformatter generates a traceback, it looks for local +variables named __traceback_info__ or __traceback_supplement__. It +includes the information provided by those local variables in the +traceback. + +__traceback_info__ is for arbitrary information. +repr(__traceback_info__) gets dumped to the traceback. + +__traceback_supplement__ is more structured. It should be a tuple. +The first item of the tuple is a callable that produces an object that +implements ITracebackSupplement, and the rest of the tuple contains +arguments to pass to the factory. The traceback formatter makes an +effort to clearly present the information provided by the +ITracebackSupplement. +""" +from zope.interface import Interface, Attribute, implements + +class IDuplicationError(Interface): + pass + +class DuplicationError(Exception): + """A duplicate registration was attempted""" + implements(IDuplicationError) + +class IUserError(Interface): + """User error exceptions + """ + +class UserError(Exception): + """User errors + + These exceptions should generally be displayed to users unless + they are handled. + """ + implements(IUserError) + +class ITracebackSupplement(Interface): + """Provides valuable information to supplement an exception traceback. + + The interface is geared toward providing meaningful feedback when + exceptions occur in user code written in mini-languages like + Zope page templates and restricted Python scripts. + """ + + source_url = Attribute( + 'source_url', + """Optional. Set to URL of the script where the exception occurred. + + Normally this generates a URL in the traceback that the user + can visit to manage the object. Set to None if unknown or + not available. + """ + ) + + line = Attribute( + 'line', + """Optional. Set to the line number (>=1) where the exception + occurred. + + Set to 0 or None if the line number is unknown. + """ + ) + + column = Attribute( + 'column', + """Optional. Set to the column offset (>=0) where the exception + occurred. + + Set to None if the column number is unknown. + """ + ) + + expression = Attribute( + 'expression', + """Optional. Set to the expression that was being evaluated. + + Set to None if not available or not applicable. + """ + ) + + warnings = Attribute( + 'warnings', + """Optional. Set to a sequence of warning messages. + + Set to None if not available, not applicable, or if the exception + itself provides enough information. + """ + ) + + + def getInfo(as_html=0): + """Optional. Returns a string containing any other useful info. + + If as_html is set, the implementation must HTML-quote the result + (normally using cgi.escape()). Returns None to provide no + extra info. + """ diff -Nru zope3-3.4.0/src/zope/exceptions/log.py zope3-3.5~bzr18/src/zope/exceptions/log.py --- zope3-3.4.0/src/zope/exceptions/log.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/exceptions/log.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,35 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Log formatter that enhances tracebacks with extra information. + +$Id: log.py 110561 2010-04-06 07:25:35Z tseaver $ +""" + +import logging +import cStringIO +from zope.exceptions.exceptionformatter import print_exception + +class Formatter(logging.Formatter): + + def formatException(self, ei): + """Format and return the specified exception information as a string. + + Uses zope.exceptions.exceptionformatter to generate the traceback. + """ + sio = cStringIO.StringIO() + print_exception(ei[0], ei[1], ei[2], file=sio, with_filenames=True) + s = sio.getvalue() + if s.endswith("\n"): + s = s[:-1] + return s diff -Nru zope3-3.4.0/src/zope/exceptions/tests/__init__.py zope3-3.5~bzr18/src/zope/exceptions/tests/__init__.py --- zope3-3.4.0/src/zope/exceptions/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/exceptions/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/exceptions/tests/test_exceptionformatter.py zope3-3.5~bzr18/src/zope/exceptions/tests/test_exceptionformatter.py --- zope3-3.4.0/src/zope/exceptions/tests/test_exceptionformatter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/exceptions/tests/test_exceptionformatter.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,167 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""ExceptionFormatter tests. + +$Id: test_exceptionformatter.py 111872 2010-05-02 17:27:45Z regebro $ +""" +import sys +from unittest import TestCase, main, makeSuite + +from zope.exceptions.exceptionformatter import format_exception + +def tb(as_html=0): + t, v, b = sys.exc_info() + try: + return ''.join(format_exception(t, v, b, as_html=as_html)) + finally: + del b + + +class ExceptionForTesting (Exception): + pass + + + +class TestingTracebackSupplement(object): + + source_url = '/somepath' + line = 634 + column = 57 + warnings = ['Repent, for the end is nigh'] + + def __init__(self, expression): + self.expression = expression + + + +class Test(TestCase): + + def testBasicNamesText(self, as_html=0): + try: + raise ExceptionForTesting + except ExceptionForTesting: + s = tb(as_html) + # The traceback should include the name of this function. + self.assert_(s.find('testBasicNamesText') >= 0) + # The traceback should include the name of the exception. + self.assert_(s.find('ExceptionForTesting') >= 0) + else: + self.fail('no exception occurred') + + def testBasicNamesHTML(self): + self.testBasicNamesText(1) + + def testSupplement(self, as_html=0): + try: + __traceback_supplement__ = (TestingTracebackSupplement, + "You're one in a million") + raise ExceptionForTesting + except ExceptionForTesting: + s = tb(as_html) + # The source URL + self.assert_(s.find('/somepath') >= 0, s) + # The line number + self.assert_(s.find('634') >= 0, s) + # The column number + self.assert_(s.find('57') >= 0, s) + # The expression + self.assert_(s.find("You're one in a million") >= 0, s) + # The warning + self.assert_(s.find("Repent, for the end is nigh") >= 0, s) + else: + self.fail('no exception occurred') + + def testSupplementHTML(self): + self.testSupplement(1) + + def testTracebackInfo(self, as_html=0): + try: + __traceback_info__ = "Adam & Eve" + raise ExceptionForTesting + except ExceptionForTesting: + s = tb(as_html) + if as_html: + # Be sure quoting is happening. + self.assert_(s.find('Adam & Eve') >= 0, s) + else: + self.assert_(s.find('Adam & Eve') >= 0, s) + else: + self.fail('no exception occurred') + + def testTracebackInfoHTML(self): + self.testTracebackInfo(1) + + def testTracebackInfoTuple(self): + try: + __traceback_info__ = ("Adam", "Eve") + raise ExceptionForTesting + except ExceptionForTesting: + s = tb() + self.assert_(s.find('Adam') >= 0, s) + self.assert_(s.find('Eve') >= 0, s) + else: + self.fail('no exception occurred') + + def testMultipleLevels(self): + # Makes sure many levels are shown in a traceback. + def f(n): + """Produces a (n + 1)-level traceback.""" + __traceback_info__ = 'level%d' % n + if n > 0: + f(n - 1) + else: + raise ExceptionForTesting + + try: + f(10) + except ExceptionForTesting: + s = tb() + for n in range(11): + self.assert_(s.find('level%d' % n) >= 0, s) + else: + self.fail('no exception occurred') + + def testQuoteLastLine(self): + class C(object): pass + try: raise TypeError(C()) + except: + s = tb(1) + else: + self.fail('no exception occurred') + self.assert_(s.find('<') >= 0, s) + self.assert_(s.find('>') >= 0, s) + + def testMultilineException(self): + try: + exec 'syntax error' + except: + s = tb() + # Traceback (most recent call last): + # Module zope.exceptions.tests.test_exceptionformatter, line ??, in testMultilineException + # exec \'syntax error\' + # File "", line 1 + # syntax error + # ^ + # SyntaxError: unexpected EOF while parsing + self.assertEquals(s.splitlines()[-3:], + [' syntax error', + ' ^', + 'SyntaxError: unexpected EOF while parsing']) + + +def test_suite(): + return makeSuite(Test) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/filerepresentation/__init__.py zope3-3.5~bzr18/src/zope/filerepresentation/__init__.py --- zope3-3.4.0/src/zope/filerepresentation/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/filerepresentation/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Import this. diff -Nru zope3-3.4.0/src/zope/filerepresentation/interfaces.py zope3-3.5~bzr18/src/zope/filerepresentation/interfaces.py --- zope3-3.4.0/src/zope/filerepresentation/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/filerepresentation/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,258 @@ +############################################################################## +# Copyright (c) 2003 Zope Corporation 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. +############################################################################## +"""File-system representation interfaces + +The interfaces defined here are used for file-system and +file-system-like representations of objects, such as file-system +synchronization, FTP, PUT, and WebDAV. + +There are three issues we need to deal with: + + File system representation + + Every object is either a directory or a file. + + Properties + + There are two kinds of proprties: + + - Data properties + + Data properties are handled directly by the object implementation. + + - Meta-data properties + + Meta data properties are handled via annotations. + + Completeness + + We must have a complete lossless data representation for file-system + synchronization. This is achieved through serialization of: + + - All annotations (not just properties), and + + - Extra data. + + Strategies for common access mechanisms: + + FTP + + - For getting directory info (statish) information: + + - Use Zope DublinCore to get modification times + + - Show as readable if we can access a read method. + + - Show as writable if we can access a write method. + + FTP and WebDAV + + - Treat as a directory if there is an adapter to `IReadDirectory`. + Treat as a file otherwise. + + - For creating objects: + + - Directories: + + Look for an `IDirectoryFactory` adapter. + + - Files + + First lookj for a `IFileFactory` adapter with a name that is + the same as the extention (e.g. ".pt"). + + Then look for an unnamed `IFileFactory` adapter. + + + File-system synchronization + + Because this must be lossless, we will use class-based adapters + for this, but we want to make it as easy as possible to use other + adapters as well. + + For reading, there must be a class adapter to `IReadSync`. We will + then apply rules similar to those above. + +$Id: interfaces.py 104778 2009-10-04 16:13:01Z optilude $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface +from zope import schema + +from zope.interface.common.mapping import IEnumerableMapping, IItemMapping, \ + IReadMapping + + +class IReadFile(Interface): + """Provide read access to file data + """ + + def read(): + """Return the file data + """ + + def size(): + """Return the data length in bytes. + """ + +class IWriteFile(Interface): + + def write(data): + """Update the file data + """ + +class ICommonFileOperations(Interface): + """Common file operations used by IRawReadFile and IRawWriteFile + """ + + mimeType = schema.ASCIILine( + title=u"File MIME type", + description=u"Provided if it makes sense for this file data" + + u"May be set prior to writing data to a file that " + + u"is writeable. It is an error to set this on a " + + u"file that is not writable.", + readonly=True, + ) + + encoding = schema.Bool( + title=u"The encoding that this file uses", + description=u"Provided if it makes sense for this file data" + + u"May be set prior to writing data to a file that " + + u"is writeable. It is an error to set this on a " + + u"file that is not writable.", + required=False, + ) + + closed = schema.Bool( + title=u"Is the file closed?", + required=True, + ) + + name = schema.TextLine( + title=u"A representative file name", + description=u"Provided if it makes sense for this file data" + + u"May be set prior to writing data to a file that " + + u"is writeable. It is an error to set this on a " + + u"file that is not writable.", + required=False, + ) + + def seek(offset, whence=None): + """Seek the file. See Python documentation for ``file`` for + details. + """ + + def tell(): + """Return the file's current position. + """ + + def close(): + """Close the file. See Python documentation for ``file`` for + details. + """ + +class IRawReadFile(IReadFile, ICommonFileOperations): + """Specialisation of IReadFile to make it act more like a Python file + object. + """ + + def read(size=None): + """Read at most ``size`` bytes of file data. If ``size`` is None, + return all the file data. + """ + + def readline(size=None): + """Read one entire line from the file. See Python documentation for + ``file`` for details. + """ + + def readlines(sizehint=None): + """Read until EOF using readline() and return a list containing the + lines thus read. See Python documentation for ``file`` for details. + """ + + def __iter__(): + """Return an iterator for the file. + + Note that unlike a Python standard ``file``, this does not necessarily + have to return data line-by-line if doing so is inefficient. + """ + + def next(): + """Iterator protocol. See Python documentation for ``file`` for + details. + """ + +class IRawWriteFile(IWriteFile, ICommonFileOperations): + """Specialisation of IWriteFile to make it act more like a Python file + object. + """ + + def write(data): + """Write a chunk of data to the file. See Python documentation for + ``file`` for details. + """ + + def writelines(sequence): + """Write a sequence of strings to the file. See Python documentation + for ``file`` for details. + """ + + def truncate(size): + """Truncate the file. See Python documentation for ``file`` for + details. + """ + + def flush(): + """Flush the file. See Python documentation for ``file`` for details. + """ + +class IReadDirectory(IEnumerableMapping, IItemMapping, IReadMapping): + """Objects that should be treated as directories for reading + """ + +class IWriteDirectory(Interface): + """Objects that should be treated as directories for writing + """ + + def __setitem__(name, object): + """Add the given `object` to the directory under the given name.""" + + def __delitem__(name): + """Delete the named object from the directory.""" + + +class IDirectoryFactory(Interface): + + def __call__(name): + """Create a directory + + where a directory is an object with adapters to IReadDirectory + and IWriteDirectory. + + """ + +class IFileFactory(Interface): + + def __call__(name, content_type, data): + """Create a file + + where a file is an object with adapters to `IReadFile` + and `IWriteFile`. + + The file `name`, content `type`, and `data` are provided to help + create the object. + """ + +# TODO: we will add additional interfaces for WebDAV and File-system +# synchronization. diff -Nru zope3-3.4.0/src/zope/formlib/boolwidgets.py zope3-3.5~bzr18/src/zope/formlib/boolwidgets.py --- zope3-3.4.0/src/zope/formlib/boolwidgets.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/boolwidgets.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,133 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Browser widgets for items + +$Id: boolwidgets.py 112015 2010-05-05 18:27:23Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +from zope.schema.vocabulary import SimpleVocabulary + +from zope.formlib.widget import SimpleInputWidget, renderElement +from zope.formlib.widget import DisplayWidget +from zope.formlib.i18n import _ +from zope.formlib.itemswidgets import RadioWidget +from zope.formlib.itemswidgets import SelectWidget, DropdownWidget + + +class CheckBoxWidget(SimpleInputWidget): + """A checkbox widget used to display Bool fields. + + For more detailed documentation, including sample code, see + ``tests/test_checkboxwidget.py``. + """ + type = 'checkbox' + default = 0 + extra = '' + + def __init__(self, context, request): + super(CheckBoxWidget, self).__init__(context, request) + self.required = False + + def __call__(self): + """Render the widget to HTML.""" + value = self._getFormValue() + if value == 'on': + kw = {'checked': 'checked'} + else: + kw = {} + return "%s %s" % ( + renderElement(self.tag, + type='hidden', + name=self.name+".used", + id=self.name+".used", + value="" + ), + renderElement(self.tag, + type=self.type, + name=self.name, + id=self.name, + cssClass=self.cssClass, + extra=self.extra, + value="on", + **kw), + ) + + def _toFieldValue(self, input): + """Convert from HTML presentation to Python bool.""" + return input == 'on' + + def _toFormValue(self, value): + """Convert from Python bool to HTML representation.""" + return value and 'on' or '' + + def hasInput(self): + """Check whether the field is represented in the form.""" + return self.name + ".used" in self.request.form or \ + super(CheckBoxWidget, self).hasInput() + + def _getFormInput(self): + """Returns the form input used by `_toFieldValue`. + + Return values: + + ``'on'`` checkbox is checked + ``''`` checkbox is not checked + ``None`` form input was not provided + + """ + if self.request.get(self.name) == 'on': + return 'on' + elif self.name + '.used' in self.request: + return '' + else: + return None + + +def BooleanRadioWidget(field, request, true=_('on'), false=_('off')): + vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) ) + widget = RadioWidget(field, vocabulary, request) + widget.required = False + return widget + + +def BooleanSelectWidget(field, request, true=_('on'), false=_('off')): + vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) ) + widget = SelectWidget(field, vocabulary, request) + widget.size = 2 + widget.required = False + return widget + + +def BooleanDropdownWidget(field, request, true=_('on'), false=_('off')): + vocabulary = SimpleVocabulary.fromItems( ((true, True), (false, False)) ) + widget = DropdownWidget(field, vocabulary, request) + widget.required = False + return widget + + +_msg_true = _("True") +_msg_false = _("False") + +class BooleanDisplayWidget(DisplayWidget): + + def __call__(self): + if self._renderedValueSet(): + value = self._data + else: + value = self.context.default + if value: + return _msg_true + else: + return _msg_false diff -Nru zope3-3.4.0/src/zope/formlib/bugs.txt zope3-3.5~bzr18/src/zope/formlib/bugs.txt --- zope3-3.4.0/src/zope/formlib/bugs.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/bugs.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,65 @@ +Functional tests to verify bugs are gone +======================================== + + +`setupWidgets` and `DISPLAY_UNWRITEABLE` +++++++++++++++++++++++++++++++++++++++++ + +This is to verify that bug #219948 is gone: setupWidgets doesn't check for +write access on the adapter. + +Create a form containg two interfaces: + +>>> import zope.formlib.tests +>>> class MyFormBase(object): +... form_fields = zope.formlib.form.FormFields( +... zope.formlib.tests.IOrder, zope.formlib.tests.IDescriptive, +... render_context=zope.formlib.interfaces.DISPLAY_UNWRITEABLE).omit( +... 'now') +>>> class MyEditForm(MyFormBase, zope.formlib.form.EditForm): +... pass + +Instanciate the context objects and the form: + +>>> import zope.publisher.browser +>>> request = zope.publisher.browser.TestRequest() +>>> order = zope.formlib.tests.Order() +>>> form = MyEditForm(order, request) + +When we render the form, the fields of IDescriptive are read only because we +have no write access (this is configured in ftesting.zcml), the others are +writable[#needsinteraction]_: + +>>> form.setUpWidgets() +>>> form.widgets['title'] + +>>> form.widgets['name'] + + + +Make sure we have the same behaviour for non-edit forms: + +>>> class MyForm(MyFormBase, zope.formlib.form.Form): +... pass +>>> import zope.publisher.browser +>>> request = zope.publisher.browser.TestRequest() +>>> order = zope.formlib.tests.Order() +>>> form = MyForm(order, request) +>>> form.setUpWidgets() +>>> form.widgets['title'] + +>>> form.widgets['name'] + + + + +Clean up: + +>>> zope.security.management.endInteraction() + +.. [#needsinteraction] + + >>> import zope.security.management + >>> import zope.security.testing + >>> request.setPrincipal(zope.security.management.system_user) + >>> zope.security.management.newInteraction(request) diff -Nru zope3-3.4.0/src/zope/formlib/configure.zcml zope3-3.5~bzr18/src/zope/formlib/configure.zcml --- zope3-3.4.0/src/zope/formlib/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,598 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/formlib/errors.py zope3-3.5~bzr18/src/zope/formlib/errors.py --- zope3-3.4.0/src/zope/formlib/errors.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/errors.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,53 @@ +############################################################################## +# +# Copyright (c) 2006 Zope Foundation and Contributors. +# +# 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. +# +############################################################################## +"""Error related things. + +$Id: errors.py 112015 2010-05-05 18:27:23Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +from cgi import escape + +from zope.component import adapts +from zope.interface import implements +from zope.interface import Invalid +from zope.i18n import Message +from zope.i18n import translate + +from zope.formlib.interfaces import IWidgetInputErrorView +from zope.publisher.interfaces.browser import IBrowserRequest + + +class InvalidErrorView(object): + + """Display a validation error as a snippet of text.""" + + implements(IWidgetInputErrorView) + adapts(Invalid, IBrowserRequest) + + def __init__(self, context, request): + self.context = context + self.request = request + + def snippet(self): + """Convert a widget input error to an html snippet + + >>> from zope.interface.exceptions import Invalid + >>> error = Invalid("You made an error!") + >>> InvalidErrorView(error, None).snippet() + u'You made an error!' + """ + msg = self.context.args[0] + if isinstance(msg, Message): + msg = translate(msg, context=self.request) + return u'%s' % escape(msg) diff -Nru zope3-3.4.0/src/zope/formlib/errors.txt zope3-3.5~bzr18/src/zope/formlib/errors.txt --- zope3-3.4.0/src/zope/formlib/errors.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/errors.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,71 @@ +============== +Error handling +============== + +These are a couple of functional tests that were written on-the-go ... In the +future this might become more extensive ... + +Displaying invalidation errors +============================== + +Validation errors, e.g. cause by invariants, are converted into readable text +by adapting them to IWidgetInputErrorView: + + >>> from zope.publisher.browser import TestRequest + >>> from zope.interface.exceptions import Invalid + >>> from zope.component import getMultiAdapter + >>> from zope.formlib.interfaces import IWidgetInputErrorView + >>> error = Invalid("You are wrong!") + >>> message = getMultiAdapter((error, TestRequest()), + ... IWidgetInputErrorView).snippet() + >>> message + u'You are wrong!' + +Interface invariant methods raise zope.interface.Invalid exception. Test if +this exception gets handled by the error_views. + + >>> myError = Invalid('My error message') + >>> import zope.formlib.form + >>> mybase = zope.formlib.form.FormBase(None, TestRequest()) + >>> mybase.errors = (myError,) + >>> save = mybase.error_views() + >>> save.next() + u'My error message' + +Now we need to set up the translation framework: + + >>> from zope import component, interface + >>> from zope.i18n.interfaces import INegotiator + >>> class Negotiator: + ... interface.implements(INegotiator) + ... def getLanguage(*ignored): return 'test' + >>> component.provideUtility(Negotiator()) + >>> from zope.i18n.testmessagecatalog import TestMessageFallbackDomain + >>> component.provideUtility(TestMessageFallbackDomain) + +And yes, we can even handle an i18n message in an Invalid exception: + + >>> from zope.i18nmessageid import MessageFactory + >>> _ = MessageFactory('my.domain') + >>> myError = Invalid(_('My i18n error message')) + >>> mybase = zope.formlib.form.FormBase(None, TestRequest()) + >>> mybase.errors = (myError,) + >>> save = mybase.error_views() + >>> save.next() + u'[[my.domain][My i18n error message]]' + +Displaying widget input errors +============================== + +WidgetInputError exceptions also work with i18n messages: + + >>> from zope.formlib.interfaces import WidgetInputError + >>> myError = WidgetInputError( + ... field_name='summary', + ... widget_title=_(u'Summary'), + ... errors=_(u'Foo')) + >>> mybase = zope.formlib.form.FormBase(None, TestRequest()) + >>> mybase.errors = (myError,) + >>> save = mybase.error_views() + >>> save.next() + u'[[my.domain][Summary]]: [[my.domain][Foo]]' diff -Nru zope3-3.4.0/src/zope/formlib/exception.py zope3-3.5~bzr18/src/zope/formlib/exception.py --- zope3-3.4.0/src/zope/formlib/exception.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/exception.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,60 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Form-related exception views + +$Id: exception.py 112015 2010-05-05 18:27:23Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +from cgi import escape + +from zope.interface import implements +from zope.i18n import translate + +from zope.formlib.interfaces import IWidgetInputError, IWidgetInputErrorView + +class WidgetInputErrorView(object): + """Display an input error as a snippet of text.""" + implements(IWidgetInputErrorView) + + __used_for__ = IWidgetInputError + + def __init__(self, context, request): + self.context, self.request = context, request + + def snippet(self): + """Convert a widget input error to an html snippet + + >>> from zope.formlib.interfaces import WidgetInputError + >>> class TooSmallError(object): + ... def doc(self): + ... return "Foo input < 1" + >>> err = WidgetInputError("foo", "Foo", TooSmallError()) + >>> view = WidgetInputErrorView(err, None) + >>> view.snippet() + u'Foo input < 1' + + The only method that IWidgetInputError promises to implement is + `doc()`. Therefore, other implementations of the interface should also + work. + + >>> from zope.formlib.interfaces import ConversionError + >>> err = ConversionError('Could not convert to float.') + >>> view = WidgetInputErrorView(err, None) + >>> view.snippet() + u'Could not convert to float.' + """ + message = self.context.doc() + translated = translate(message, context=self.request, default=message) + return u'%s' % escape(translated) diff -Nru zope3-3.4.0/src/zope/formlib/form.py zope3-3.5~bzr18/src/zope/formlib/form.py --- zope3-3.4.0/src/zope/formlib/form.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/form.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,968 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Foundation and Contributors. +# +# 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. +# +############################################################################## +"""Forms + +$Id: form.py 112015 2010-05-05 18:27:23Z tseaver $ +""" +import datetime +import re +import sys +import pytz +from cgi import escape + +import zope.browser.interfaces +import zope.event +import zope.i18n +import zope.i18nmessageid +import zope.security +import zope.interface.interfaces +import zope.publisher.browser +import zope.publisher.interfaces.browser +from zope import component, interface, schema +from zope.browserpage import ViewPageTemplateFile +from zope.interface.common import idatetime +from zope.interface.interface import InterfaceClass +from zope.schema.interfaces import IField +from zope.schema.interfaces import ValidationError +from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent +from zope.lifecycleevent import Attributes +from zope.browserpage import namedtemplate + +from zope.formlib.interfaces import IWidgetInputErrorView +from zope.formlib.interfaces import IInputWidget, IDisplayWidget +from zope.formlib.interfaces import WidgetsError, MissingInputError +from zope.formlib.interfaces import InputErrors, WidgetInputError + +from zope.formlib import interfaces +from zope.i18nmessageid import MessageFactory +_ = MessageFactory("zope") + + +interface.moduleProvides(interfaces.IFormAPI) + + +def expandPrefix(prefix): + """Expand prefix string by adding a trailing period if needed. + + expandPrefix(p) should be used instead of p+'.' in most contexts. + """ + if prefix and not prefix.endswith('.'): + return prefix + '.' + return prefix + +class FormField: + + interface.implements(interfaces.IFormField) + + def __init__(self, field, name=None, prefix='', + for_display=None, for_input=None, custom_widget=None, + render_context=False, get_rendered=None, interface=None + ): + self.field = field + if name is None: + name = field.__name__ + assert name + self.__name__ = expandPrefix(prefix) + name + self.prefix = prefix + if interface is None: + interface = field.interface + self.interface = interface + self.for_display = for_display + self.for_input = for_input + self.custom_widget = custom_widget + self.render_context = render_context + self.get_rendered = get_rendered + +Field = FormField + + +def _initkw(keep_readonly=(), omit_readonly=False, **defaults): + return keep_readonly, omit_readonly, defaults + + +class FormFields(object): + + interface.implements(interfaces.IFormFields) + + def __init__(self, *args, **kw): + keep_readonly, omit_readonly, defaults = _initkw(**kw) + + fields = [] + for arg in args: + if isinstance(arg, InterfaceClass): + for name, field in schema.getFieldsInOrder(arg): + fields.append((name, field, arg)) + elif IField.providedBy(arg): + name = arg.__name__ + if not name: + raise ValueError( + "Field has no name") + + fields.append((name, arg, arg.interface)) + elif isinstance(arg, FormFields): + for form_field in arg: + fields.append( + (form_field.__name__, + form_field, + form_field.interface) + ) + elif isinstance(arg, FormField): + fields.append((arg.__name__, arg, arg.interface)) + else: + raise TypeError("Unrecognized argument type", arg) + + + seq = [] + byname = {} + for name, field, iface in fields: + if isinstance(field, FormField): + form_field = field + else: + if field.readonly: + if omit_readonly and (name not in keep_readonly): + continue + form_field = FormField(field, interface=iface, **defaults) + name = form_field.__name__ + + if name in byname: + raise ValueError("Duplicate name", name) + seq.append(form_field) + byname[name] = form_field + + self.__FormFields_seq__ = seq + self.__FormFields_byname__ = byname + + def __len__(self): + return len(self.__FormFields_seq__) + + def __iter__(self): + return iter(self.__FormFields_seq__) + + def __getitem__(self, name): + return self.__FormFields_byname__[name] + + def get(self, name, default=None): + return self.__FormFields_byname__.get(name, default) + + def __add__(self, other): + if not isinstance(other, FormFields): + return NotImplemented + return self.__class__(self, other) + + def select(self, *names): + """Return a modified instance with an ordered subset of fields.""" + return self.__class__(*[self[name] for name in names]) + + def omit(self, *names): + """Return a modified instance omitting given fields.""" + return self.__class__(*[ff for ff in self if ff.__name__ not in names]) + +Fields = FormFields + + +def fields_initkw(keep_all_readonly=False, **other): + return keep_all_readonly, other + +# Backward compat +def fields(*args, **kw): + keep_all_readonly, other = fields_initkw(**kw) + other['omit_readonly'] = not keep_all_readonly + return FormFields(*args, **other) + + +class Widgets(object): + + interface.implements(interfaces.IWidgets) + + def __init__(self, widgets, prefix_length=None, prefix=None): + self.__Widgets_widgets_items__ = widgets + self.__Widgets_widgets_list__ = [w for (i, w) in widgets] + if prefix is None: + # BBB Allow old code using the prefix_length argument. + if prefix_length is None: + raise TypeError( + "One of 'prefix_length' and 'prefix' is required." + ) + self.__Widgets_widgets_dict__ = dict( + [(w.name[prefix_length:], w) for (i, w) in widgets] + ) + else: + prefix = expandPrefix(prefix) + self.__Widgets_widgets_dict__ = dict( + [(_widgetKey(w, prefix), w) for (i, w) in widgets] + ) + + def __iter__(self): + return iter(self.__Widgets_widgets_list__) + + def __getitem__(self, name): + return self.__Widgets_widgets_dict__[name] + + # TODO need test + def get(self, name): + return self.__Widgets_widgets_dict__.get(name) + + def __iter_input_and_widget__(self): + return iter(self.__Widgets_widgets_items__) + + + # TODO need test + def __add__(self, other): + widgets = self.__class__([], 0) + widgets.__Widgets_widgets_items__ = ( + self.__Widgets_widgets_items__ + other.__Widgets_widgets_items__) + widgets.__Widgets_widgets_list__ = ( + self.__Widgets_widgets_list__ + other.__Widgets_widgets_list__) + widgets.__Widgets_widgets_dict__ = self.__Widgets_widgets_dict__.copy() + widgets.__Widgets_widgets_dict__.update(other.__Widgets_widgets_dict__) + return widgets + +def canWrite(context, field): + writer = getattr(field, 'writer', None) + if writer is not None: + return zope.security.canAccess(context, writer.__name__) + return zope.security.canWrite(context, field.__name__) + +def setUpWidgets(form_fields, + form_prefix=None, context=None, request=None, form=None, + data=(), adapters=None, ignore_request=False): + + if request is None: + request = form.request + if context is None and form is not None: + context = form.context + if form_prefix is None: + form_prefix = form.prefix + + widgets = [] + adapter = None + for form_field in form_fields: + field = form_field.field + if form_field.render_context: + if adapters is None: + adapters = {} + + # Adapt context, if necessary + interface = form_field.interface + adapter = adapters.get(interface) + if adapter is None: + if interface is None: + adapter = context + else: + adapter = interface(context) + adapters[interface] = adapter + if interface is not None: + adapters[interface.__name__] = adapter + field = field.bind(adapter) + else: + field = field.bind(context) + + readonly = form_field.for_display + readonly = readonly or (field.readonly and not form_field.for_input) + readonly = readonly or ( + (form_field.render_context & interfaces.DISPLAY_UNWRITEABLE) + and not canWrite(adapter, field) + ) + + if form_field.custom_widget is not None: + widget = form_field.custom_widget(field, request) + else: + if readonly: + widget = component.getMultiAdapter((field, request), + IDisplayWidget) + else: + widget = component.getMultiAdapter((field, request), + IInputWidget) + + prefix = form_prefix + if form_field.prefix: + prefix = expandPrefix(prefix) + form_field.prefix + + widget.setPrefix(prefix) + + if ignore_request or readonly or not widget.hasInput(): + # Get the value to render + if form_field.__name__ in data: + widget.setRenderedValue(data[form_field.__name__]) + elif form_field.get_rendered is not None: + widget.setRenderedValue(form_field.get_rendered(form)) + elif form_field.render_context: + widget.setRenderedValue(field.get(adapter)) + else: + widget.setRenderedValue(field.default) + + widgets.append((not readonly, widget)) + + return Widgets(widgets, prefix=form_prefix) + +def setUpInputWidgets(form_fields, form_prefix, context, request, + form=None, ignore_request=False): + widgets = [] + for form_field in form_fields: + field = form_field.field.bind(context) + widget = _createWidget(form_field, field, request, IInputWidget) + + prefix = form_prefix + if form_field.prefix: + prefix = expandPrefix(prefix) + form_field.prefix + + widget.setPrefix(prefix) + + if ignore_request: + if form_field.get_rendered is not None: + value = form_field.get_rendered(form) + else: + value = field.default + widget.setRenderedValue(value) + + widgets.append((True, widget)) + return Widgets(widgets, prefix=form_prefix) + + +def _createWidget(form_field, field, request, iface): + if form_field.custom_widget is None: + return component.getMultiAdapter((field, request), iface) + else: + return form_field.custom_widget(field, request) + + +def getWidgetsData(widgets, form_prefix, data): + errors = [] + form_prefix = expandPrefix(form_prefix) + + for input, widget in widgets.__iter_input_and_widget__(): + if input and IInputWidget.providedBy(widget): + name = _widgetKey(widget, form_prefix) + + if not widget.hasInput(): + continue + + try: + data[name] = widget.getInputValue() + except ValidationError, error: + # convert field ValidationError to WidgetInputError + error = WidgetInputError(widget.name, widget.label, error) + errors.append(error) + except InputErrors, error: + errors.append(error) + + return errors + +def _widgetKey(widget, form_prefix): + name = widget.name + if name.startswith(form_prefix): + name = name[len(form_prefix):] + else: + raise ValueError("Name does not match prefix", name, form_prefix) + return name + +def setUpEditWidgets(form_fields, form_prefix, context, request, + adapters=None, for_display=False, + ignore_request=False): + if adapters is None: + adapters = {} + + widgets = [] + for form_field in form_fields: + field = form_field.field + # Adapt context, if necessary + interface = form_field.interface + adapter = adapters.get(interface) + if adapter is None: + if interface is None: + adapter = context + else: + adapter = interface(context) + adapters[interface] = adapter + if interface is not None: + adapters[interface.__name__] = adapter + + field = field.bind(adapter) + + readonly = form_field.for_display + readonly = readonly or (field.readonly and not form_field.for_input) + readonly = readonly or ( + (form_field.render_context & interfaces.DISPLAY_UNWRITEABLE) + and not canWrite(adapter, field) + ) + readonly = readonly or for_display + + if readonly: + iface = IDisplayWidget + else: + iface = IInputWidget + widget = _createWidget(form_field, field, request, iface) + + prefix = form_prefix + if form_field.prefix: + prefix = expandPrefix(prefix) + form_field.prefix + + widget.setPrefix(prefix) + + if ignore_request or readonly or not widget.hasInput(): + # Get the value to render + value = field.get(adapter) + widget.setRenderedValue(value) + + widgets.append((not readonly, widget)) + + return Widgets(widgets, prefix=form_prefix) + +def setUpDataWidgets(form_fields, form_prefix, context, request, data=(), + for_display=False, ignore_request=False): + widgets = [] + for form_field in form_fields: + field = form_field.field.bind(context) + readonly = for_display or field.readonly or form_field.for_display + if readonly: + iface = IDisplayWidget + else: + iface = IInputWidget + widget = _createWidget(form_field, field, request, iface) + + prefix = form_prefix + if form_field.prefix: + prefix = expandPrefix(prefix) + form_field.prefix + + widget.setPrefix(prefix) + + if ((form_field.__name__ in data) + and (ignore_request or readonly or not widget.hasInput()) + ): + widget.setRenderedValue(data[form_field.__name__]) + + widgets.append((not readonly, widget)) + + return Widgets(widgets, prefix=form_prefix) + + +class NoInputData(interface.Invalid): + """There was no input data because: + + - It wasn't asked for + + - It wasn't entered by the user + + - It was entered by the user, but the value entered was invalid + + This exception is part of the internal implementation of checkInvariants. + + """ + +class FormData: + + def __init__(self, schema, data): + self._FormData_data___ = data + self._FormData_schema___ = schema + + def __getattr__(self, name): + schema = self._FormData_schema___ + data = self._FormData_data___ + try: + field = schema[name] + except KeyError: + raise AttributeError(name) + else: + value = data.get(name, data) + if value is data: + raise NoInputData(name) + if zope.interface.interfaces.IMethod.providedBy(field): + if not IField.providedBy(field): + raise RuntimeError( + "Data value is not a schema field", name) + v = lambda: value + else: + v = value + setattr(self, name, v) + return v + raise AttributeError(name) + + +def checkInvariants(form_fields, form_data): + + # First, collect the data for the various schemas + schema_data = {} + for form_field in form_fields: + schema = form_field.interface + if schema is None: + continue + + data = schema_data.get(schema) + if data is None: + data = schema_data[schema] = {} + + if form_field.__name__ in form_data: + data[form_field.field.__name__] = form_data[form_field.__name__] + + # Now validate the individual schemas + errors = [] + for schema, data in schema_data.items(): + try: + schema.validateInvariants(FormData(schema, data), errors) + except interface.Invalid: + pass # Just collect the errors + + return [error for error in errors if not isinstance(error, NoInputData)] + +def applyData(context, form_fields, data, adapters=None): + if adapters is None: + adapters = {} + + descriptions = {} + + for form_field in form_fields: + field = form_field.field + # Adapt context, if necessary + interface = form_field.interface + adapter = adapters.get(interface) + if adapter is None: + if interface is None: + adapter = context + else: + adapter = interface(context) + adapters[interface] = adapter + + name = form_field.__name__ + newvalue = data.get(name, form_field) # using form_field as marker + if (newvalue is not form_field) \ + and (field.get(adapter) != newvalue): + descriptions.setdefault(interface, []).append(field.__name__) + field.set(adapter, newvalue) + + return descriptions + +def applyChanges(context, form_fields, data, adapters=None): + return bool(applyData(context, form_fields, data, adapters)) + + +def _callify(meth): + """Return method if it is callable, + otherwise return the form's method of the name""" + if callable(meth): + return meth + elif isinstance(meth, str): + return lambda form, *args: getattr(form, meth)(*args) + + +class Action(object): + + interface.implements(interfaces.IAction) + _identifier = re.compile('[A-Za-z][a-zA-Z0-9_]*$') + + def __init__(self, label, success=None, failure=None, + condition=None, validator=None, prefix='actions', + name=None, data=None): + + self.label = label + self.setPrefix(prefix) + self.setName(name) + self.bindMethods(success_handler=success, + failure_handler=failure, + condition=condition, + validator=validator) + + if data is None: + data = {} + self.data = data + + def bindMethods(self, **methods): + """Bind methods to the action""" + for k, v in methods.items(): + setattr(self, k, _callify(v)) + + def setName(self, name): + """Make sure name is ASCIIfiable. + Use action label if name is None + """ + if name is None: + name = self.label + if self._identifier.match(name): + name = name.lower() + else: + if isinstance(name, unicode): + name = name.encode("utf-8") + name = name.encode('hex') + self.name = name + self.__name__ = self.prefix + name + + def setPrefix(self, prefix): + """Set prefix""" + self.prefix = expandPrefix(prefix) + + def __get__(self, form, class_=None): + if form is None: + return self + result = self.__class__.__new__(self.__class__) + result.__dict__.update(self.__dict__) + result.form = form + result.__name__ = expandPrefix(form.prefix) + result.__name__ + interface.alsoProvides(result, interfaces.IBoundAction) + return result + + def available(self): + condition = self.condition + return (condition is None) or condition(self.form, self) + + def validate(self, data): + if self.validator is not None: + return self.validator(self.form, self, data) + + def success(self, data): + if self.success_handler is not None: + return self.success_handler(self.form, self, data) + + def failure(self, data, errors): + if self.failure_handler is not None: + return self.failure_handler(self.form, self, data, errors) + + def submitted(self): + return (self.__name__ in self.form.request.form) and self.available() + + def update(self): + pass + + render = namedtemplate.NamedTemplate('render') + +@namedtemplate.implementation(interfaces.IAction) +def render_submit_button(self): + if not self.available(): + return '' + label = self.label + if isinstance(label, zope.i18nmessageid.Message): + label = zope.i18n.translate(self.label, context=self.form.request) + return ('' % + (self.__name__, self.__name__, escape(label, quote=True)) + ) + +class action: + def __init__(self, label, actions=None, **options): + caller_locals = sys._getframe(1).f_locals + if actions is None: + actions = caller_locals.get('actions') + if actions is None: + actions = caller_locals['actions'] = Actions() + self.actions = actions + self.label = label + self.options = options + + def __call__(self, success): + action = Action(self.label, success=success, **self.options) + self.actions.append(action) + return action + + +class Actions(object): + + interface.implements(interfaces.IActions) + + def __init__(self, *actions): + self.actions = actions + self.byname = dict([(a.__name__, a) for a in actions]) + + def __iter__(self): + return iter(self.actions) + + def __getitem__(self, name): + try: + return self.byname[name] + except TypeError: + if isinstance(name, slice): + return self.__class__( + *self.actions[name.start:name.stop:name.step] + ) + + def append(self, action): + self.actions += (action, ) + self.byname[action.__name__] = action + + # TODO need test + def __add__(self, other): + return self.__class__(*(self.actions + other.actions)) + + def copy(self): + return self.__class__(*self.actions) + + def __get__(self, inst, class_): + if inst is None: + return self + return self.__class__(*[a.__get__(inst) for a in self.actions]) + +def handleSubmit(actions, data, default_validate=None): + + for action in actions: + if action.submitted(): + errors = action.validate(data) + if errors is None and default_validate is not None: + errors = default_validate(action, data) + return errors, action + + return None, None + +# TODO need test for this +def availableActions(form, actions): + result = [] + for action in actions: + condition = action.condition + if (condition is None) or condition(form, action): + result.append(action) + return result + + +class FormBase(zope.publisher.browser.BrowserPage): + + label = u'' + + prefix = 'form' + + status = '' + + errors = () + + interface.implements(interfaces.IForm) + + def setPrefix(self, prefix): + self.prefix = prefix + + def setUpWidgets(self, ignore_request=False): + self.adapters = {} + self.widgets = setUpWidgets( + self.form_fields, self.prefix, self.context, self.request, + form=self, adapters=self.adapters, ignore_request=ignore_request) + + def validate(self, action, data): + return (getWidgetsData(self.widgets, self.prefix, data) + + checkInvariants(self.form_fields, data)) + + template = namedtemplate.NamedTemplate('default') + + # TODO also need to be able to show disabled actions + def availableActions(self): + return availableActions(self, self.actions) + + def resetForm(self): + self.setUpWidgets(ignore_request=True) + + form_result = None + form_reset = True + + def update(self): + self.setUpWidgets() + self.form_reset = False + + data = {} + errors, action = handleSubmit(self.actions, data, self.validate) + # the following part will make sure that previous error not + # get overriden by new errors. This is usefull for subforms. (ri) + if self.errors is None: + self.errors = errors + else: + if errors is not None: + self.errors += tuple(errors) + + if errors: + self.status = _('There were errors') + result = action.failure(data, errors) + elif errors is not None: + self.form_reset = True + result = action.success(data) + else: + result = None + + self.form_result = result + + def render(self): + # if the form has been updated, it will already have a result + if self.form_result is None: + if self.form_reset: + # we reset, in case data has changed in a way that + # causes the widgets to have different data + self.resetForm() + self.form_reset = False + self.form_result = self.template() + + return self.form_result + + def __call__(self): + self.update() + if self.request.response.getStatus() in [301, 302]: + # Avoid rendering if the action caused a redirect. + result = self.form_result or '' + else: + result = self.render() + return result + + def error_views(self): + for error in self.errors: + if isinstance(error, basestring): + yield error + else: + view = component.getMultiAdapter( + (error, self.request), + IWidgetInputErrorView) + title = getattr(error, 'widget_title', None) # duck typing + if title: + if isinstance(title, zope.i18n.Message): + title = zope.i18n.translate(title, context=self.request) + yield '%s: %s' % (title, view.snippet()) + else: + yield view.snippet() + + +def haveInputWidgets(form, action): + for input, widget in form.widgets.__iter_input_and_widget__(): + if input: + return True + else: + return False + +class EditFormBase(FormBase): + + def setUpWidgets(self, ignore_request=False): + self.adapters = {} + self.widgets = setUpEditWidgets( + self.form_fields, self.prefix, self.context, self.request, + adapters=self.adapters, ignore_request=ignore_request + ) + + @action(_("Apply"), condition=haveInputWidgets) + def handle_edit_action(self, action, data): + descriptions = applyData(self.context, self.form_fields, data, + self.adapters) + if descriptions: + descriptions = [Attributes(interface, *tuple(keys)) + for interface, keys in descriptions.items()] + zope.event.notify(ObjectModifiedEvent(self.context, *descriptions)) + formatter = self.request.locale.dates.getFormatter( + 'dateTime', 'medium') + + try: + time_zone = idatetime.ITZInfo(self.request) + except TypeError: + time_zone = pytz.UTC + + status = _("Updated on ${date_time}", + mapping={'date_time': + formatter.format( + datetime.datetime.now(time_zone) + ) + } + ) + self.status = status + else: + self.status = _('No changes') + +class DisplayFormBase(FormBase): + + def setUpWidgets(self, ignore_request=False): + self.adapters = {} + self.widgets = setUpEditWidgets( + self.form_fields, self.prefix, self.context, self.request, + adapters=self.adapters, for_display=True, + ignore_request=ignore_request + ) + + actions = () + + +class AddFormBase(FormBase): + + interface.implements(interfaces.IAddFormCustomization, + zope.component.interfaces.IFactory) + + component.adapts(zope.browser.interfaces.IAdding, + zope.publisher.interfaces.browser.IBrowserRequest) + + def __init__(self, context, request): + self.__parent__ = context + super(AddFormBase, self).__init__(context, request) + + def setUpWidgets(self, ignore_request=False): + self.widgets = setUpInputWidgets( + self.form_fields, self.prefix, self.context, self.request, + ignore_request=ignore_request, + ) + + @action(_("Add"), condition=haveInputWidgets) + def handle_add(self, action, data): + self.createAndAdd(data) + + # zope.formlib.interfaces.IAddFormCustomization + + def createAndAdd(self, data): + ob = self.create(data) + zope.event.notify(ObjectCreatedEvent(ob)) + return self.add(ob) + + def create(self, data): + raise NotImplementedError( + "concrete classes must implement create() or createAndAdd()") + + _finished_add = False + + def add(self, object): + ob = self.context.add(object) + self._finished_add = True + return ob + + def render(self): + if self._finished_add: + self.request.response.redirect(self.nextURL()) + return "" + return super(AddFormBase, self).render() + + def nextURL(self): + return self.context.nextURL() + + +default_page_template = namedtemplate.NamedTemplateImplementation( + ViewPageTemplateFile('pageform.pt'), interfaces.IPageForm) + +default_subpage_template = namedtemplate.NamedTemplateImplementation( + ViewPageTemplateFile('subpageform.pt'), interfaces.ISubPageForm) + +class PageForm(FormBase): + + interface.implements(interfaces.IPageForm) + +Form = PageForm + +class PageEditForm(EditFormBase): + + interface.implements(interfaces.IPageForm) + +EditForm = PageEditForm + +class PageDisplayForm(DisplayFormBase): + + interface.implements(interfaces.IPageForm) + +DisplayForm = PageDisplayForm + +class PageAddForm(AddFormBase): + + interface.implements(interfaces.IPageForm) + +AddForm = PageAddForm + +class SubPageForm(FormBase): + + interface.implements(interfaces.ISubPageForm) + +class SubPageEditForm(EditFormBase): + + interface.implements(interfaces.ISubPageForm) + +class SubPageDisplayForm(DisplayFormBase): + + interface.implements(interfaces.ISubPageForm) diff -Nru zope3-3.4.0/src/zope/formlib/form.txt zope3-3.5~bzr18/src/zope/formlib/form.txt --- zope3-3.4.0/src/zope/formlib/form.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/form.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1877 @@ +===== +Forms +===== + +Forms are web components that use widgets to display and input data. +Typically a template displays the widgets by accessing an attribute or +method on an underlying class. + +This document describes some tools to assist in form development. In +the examples, we will show "forms" that are generated with simple +print statements to keep the examples simpler. Most forms will use +templates in practice. + +This document starts with low-level APIs. We eventually build up to +higher-level APIs that allow forms to be defined with just a little bit +of meta data. Impatient readers may wish to skip to the later +sections, especially the section on `Helpful base classes`_. :) + +A form class can define ordered collections of "form fields" using +the `Fields` constructor. Form fields are distinct from and build on +schema fields. A schema field specified attribute values. Form +fields specify how a schema field should be used in a form. The +simplest way to define a collection of form fields is by passing a +schema to the `Fields` constructor: + + >>> from zope import interface, schema + >>> class IOrder(interface.Interface): + ... identifier = schema.Int(title=u"Identifier", readonly=True) + ... name = schema.TextLine(title=u"Name") + ... min_size = schema.Float(title=u"Minimum size") + ... max_size = schema.Float(title=u"Maximum size") + ... color = schema.TextLine(title=u"Color", required=False) + ... now = schema.Datetime(title=u"Now", readonly=True) + + >>> from zope.formlib import form + >>> class MyForm: + ... form_fields = form.Fields(IOrder) + +This sets up a set of form fields from the interface, IOrder. + + >>> len(MyForm.form_fields) + 6 + + >>> [w.__name__ for w in MyForm.form_fields] + ['identifier', 'name', 'min_size', 'max_size', 'color', 'now'] + +We can access individual form fields by name: + + >>> MyForm.form_fields['name'].__name__ + 'name' + +We can also select and order subsets using the select method of form fields: + + >>> [w.__name__ for w in MyForm.form_fields.select('name', 'identifier')] + ['name', 'identifier'] + +or by omitting fields: + + >>> [w.__name__ for w in MyForm.form_fields.omit('now', 'identifier')] + ['name', 'min_size', 'max_size', 'color'] + +We can omit read-only fields using the omit_readonly option when +setting up the fields: + + >>> class MyForm: + ... form_fields = form.Fields(IOrder, omit_readonly=True) + >>> [w.__name__ for w in MyForm.form_fields] + ['name', 'min_size', 'max_size', 'color'] + + +Getting HTML +============ + +Having defined form fields, we can use them to generate HTML +forms. Typically, this is done at run time by form class +instances. Let's look at an example that displays some input widgets: + + >>> class MyForm: + ... form_fields = form.Fields(IOrder, omit_readonly=True) + ... + ... def __init__(self, context, request): + ... self.context, self.request = context, request + ... + ... def __call__(self, ignore_request=False): + ... widgets = form.setUpWidgets( + ... self.form_fields, 'form', self.context, self.request, + ... ignore_request=ignore_request) + ... return '\n'.join([w() for w in widgets]) + +Here we used ``form.setUpWidgets`` to create widget instances from our +form-field specifications. The second argument to ``setUpWidgets`` is a +form prefix. All of the widgets on this form are given the same +prefix. This allows multiple forms to be used within a single form +tag, assuming that each form uses a different form prefix. + +Now, we can display the form: + + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + + + + + + +If the request contains any form data, that will be reflected in the +output: + + >>> request.form['form.name'] = u'bob' + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + + + + + + +Sometimes we don't want this behavior: we want to ignore the request values, +particularly after a form has been processed and before it is drawn again. +This can be accomplished with the 'ignore_request' argument in +setUpWidgets. + + >>> print MyForm(None, request)(ignore_request=True) + ... # doctest: +NORMALIZE_WHITESPACE + + + + + + +Reading data +============ + +Of course, we don't just want to display inputs. We want to get the +input data. We can use getWidgetsData for that: + + >>> from pprint import pprint + >>> class MyForm: + ... form_fields = form.Fields(IOrder, omit_readonly=True) + ... + ... def __init__(self, context, request): + ... self.context, self.request = context, request + ... + ... def __call__(self): + ... widgets = form.setUpWidgets( + ... self.form_fields, 'form', self.context, self.request) + ... + ... if 'submit' in self.request: + ... data = {} + ... errors = form.getWidgetsData(widgets, 'form', data) + ... if errors: + ... print 'There were errors:' + ... for error in errors: + ... print error + ... else: + ... data = None + ... + ... for w in widgets: + ... print w() + ... error = w.error() + ... if error: + ... print error + ... + ... return data + +We check for a 'submit' variable in the form and, if we see it, we try +to get the data, and errors. We call `getWidgetsData`, passing: + +- Our widgets + +- The form prefix, and + +- A data dictionary to contain input values found + +The keys in the data dictionary have the form prefix stripped off. + +If there are errors, we print them. When we display the widgets, we +also check for errors and show them if present. Let's add a submit +variable: + + >>> request.form['form.min_size'] = u'' + >>> request.form['form.max_size'] = u'' + >>> request.form['submit'] = u'Submit' + >>> MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + There were errors: + ('min_size', u'Minimum size', RequiredMissing('min_size')) + ('max_size', u'Maximum size', RequiredMissing('max_size')) + + + Required input is missing. + + Required input is missing. + + {'name': u'bob'} + + +Note that we got an error because we omitted the values for min_size +and max size. If we provide an invalid value, we'll get an error too: + + >>> request.form['form.min_size'] = u'bob' + >>> MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + There were errors: + (u'Invalid floating point data', + ) + ('max_size', u'Maximum size', RequiredMissing('max_size')) + + + Invalid floating point data + + Required input is missing. + + {'name': u'bob'} + +If we provide valid data, we'll get the data back: + + >>> request.form['form.min_size'] = u'42' + >>> request.form['form.max_size'] = u'142' + >>> pprint(MyForm(None, request)(), width=1) + ... # doctest: +NORMALIZE_WHITESPACE + + + + + {'max_size': 142.0, + 'min_size': 42.0, + 'name': u'bob'} + +It's up to the form to decide what to do with the information. + +Invariants +========== + +The `getWidgetsData` function checks individual field constraints. +Interfaces can also provide invariants that we may also want to check. +The `checkInvariants` function can be used to do that. + +In our order example, it makes sense to require that the maximum is +greater than or equal to the minimum: + + >>> class IOrder(interface.Interface): + ... identifier = schema.Int(title=u"Identifier", readonly=True) + ... name = schema.TextLine(title=u"Name") + ... min_size = schema.Float(title=u"Minimum size") + ... max_size = schema.Float(title=u"Maximum size") + ... now = schema.Datetime(title=u"Now", readonly=True) + ... + ... @interface.invariant + ... def maxGreaterThanMin(order): + ... if order.max_size < order.min_size: + ... raise interface.Invalid("Maximum is less than Minimum") + +We can update our form to check the invariant using 'checkInvariants': + + >>> class MyForm: + ... form_fields = form.Fields(IOrder, omit_readonly=True) + ... + ... def __init__(self, context, request): + ... self.context, self.request = context, request + ... + ... def __call__(self): + ... widgets = form.setUpWidgets( + ... self.form_fields, 'form', self.context, self.request) + ... + ... if 'submit' in self.request: + ... data = {} + ... errors = form.getWidgetsData(widgets, 'form', data) + ... invariant_errors = form.checkInvariants(self.form_fields, + ... data) + ... if errors: + ... print 'There were field errors:' + ... for error in errors: + ... print error + ... + ... if invariant_errors: + ... print 'There were invariant errors:' + ... for error in invariant_errors: + ... print error + ... else: + ... data = None + ... + ... for w in widgets: + ... print w() + ... error = w.error() + ... if error: + ... print error + ... + ... return data + +If we display the form again, we'll get the same result: + + >>> pprint(MyForm(None, request)(), width=1) + ... # doctest: +NORMALIZE_WHITESPACE + + + + {'max_size': 142.0, + 'min_size': 42.0, + 'name': u'bob'} + +But if we reduce the maximum below the minimum, we'll get an invariant +error: + + >>> request.form['form.min_size'] = u'42' + >>> request.form['form.max_size'] = u'14' + + >>> pprint(MyForm(None, request)(), width=1) + ... # doctest: +NORMALIZE_WHITESPACE + There were invariant errors: + Maximum is less than Minimum + + + + {'max_size': 14.0, + 'min_size': 42.0, + 'name': u'bob'} + +We can have field errors and invariant errors: + + >>> request.form['form.name'] = u'' + + >>> pprint(MyForm(None, request)(), width=1) + ... # doctest: +NORMALIZE_WHITESPACE + There were field errors: + ('name', u'Name', RequiredMissing('name')) + There were invariant errors: + Maximum is less than Minimum + + Required input is missing. + + + {'max_size': 14.0, + 'min_size': 42.0} + +If the inputs for some fields tested by invariants are missing, the +invariants are ignored: + + >>> request.form['form.max_size'] = u'' + + >>> pprint(MyForm(None, request)()) # doctest: +NORMALIZE_WHITESPACE + There were field errors: + ('name', u'Name', RequiredMissing('name')) + ('max_size', u'Maximum size', RequiredMissing('max_size')) + + Required input is missing. + + + Required input is missing. + {'min_size': 42.0} + + +Edit Forms +========== + +A common application of forms is edit forms. Edit forms are special +in 2 ways: + +- We want to get the initial data for widgets from the object being + edited. + +- If there are no errors, we want to apply the changes back to the + object being edited. + +The form package provides some functions to assist with creating edit +forms. When we set up our form_fields, we use the `render_context` +option, which uses data from the context passed to setUpWidgets. +Let's create a content class that provides `IOrder` and a simple form +that uses it: + + >>> import datetime + >>> class Order: + ... interface.implements(IOrder) + ... + ... def __init__(self, identifier): + ... self.identifier = identifier + ... self.name = 'unknown' + ... self.min_size = 0.0 + ... self.max_size = 0.0 + ... + ... now = property(lambda self: datetime.datetime.now()) + + >>> order = Order(1) + + >>> class MyForm: + ... form_fields = form.Fields( + ... IOrder, omit_readonly=True, render_context=True) + ... + ... def __init__(self, context, request): + ... self.context, self.request = context, request + ... + ... def __call__(self, ignore_request=False): + ... widgets = form.setUpWidgets( + ... self.form_fields, 'form', self.context, self.request, + ... ignore_request=ignore_request) + ... + ... return '\n'.join([w() for w in widgets]) + + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + + + + +Note that, in this case, we got the values from the request, because +we used an old request. If we want to redraw the form after processing a +request, it is safest to pass ignore_request = True to setUpWidgets so that +the form is redrawn with the values as found in the object, not on the request. + + >>> print MyForm(order, request)(ignore_request=True) + ... # doctest: +NORMALIZE_WHITESPACE + + + + +If we use a new request, we will of course get the same result: + + >>> request = TestRequest() + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + + + + +If we include read-only fields in an edit form, they will get display widgets: + + >>> class MyForm: + ... form_fields = form.Fields(IOrder, render_context=True) + ... form_fields = form_fields.omit('now') + ... + ... def __init__(self, context, request): + ... self.context, self.request = context, request + ... + ... def __call__(self): + ... widgets = form.setUpWidgets( + ... self.form_fields, 'form', self.context, self.request) + ... + ... return '\n'.join([w() for w in widgets]) + + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + 1 + + + + +When the form is submitted, we need to apply the changes back to the +object. We can use the `applyChanges` function for that: + + >>> class MyForm: + ... form_fields = form.Fields(IOrder, render_context=True) + ... form_fields = form_fields.omit('now') + ... + ... def __init__(self, context, request): + ... self.context, self.request = context, request + ... + ... def __call__(self): + ... widgets = form.setUpWidgets( + ... self.form_fields, 'form', self.context, self.request) + ... + ... if 'submit' in self.request: + ... data = {} + ... errors = form.getWidgetsData(widgets, 'form', data) + ... invariant_errors = form.checkInvariants(self.form_fields, + ... data) + ... if errors: + ... print 'There were field errors:' + ... for error in errors: + ... print error + ... + ... if invariant_errors: + ... print 'There were invariant errors:' + ... for error in invariant_errors: + ... print error + ... + ... if not errors and not invariant_errors: + ... changed = form.applyChanges( + ... self.context, self.form_fields, data) + ... + ... else: + ... data = changed = None + ... + ... for w in widgets: + ... print w() + ... error = w.error() + ... if error: + ... print error + ... + ... if changed: + ... print 'Object updated' + ... else: + ... print 'No changes' + ... + ... return data + +Now, if we submit the form with some data: + + >>> request.form['form.name'] = u'bob' + >>> request.form['form.min_size'] = u'42' + >>> request.form['form.max_size'] = u'142' + >>> request.form['submit'] = u'' + >>> pprint(MyForm(order, request)(), width=1) + ... # doctest: +NORMALIZE_WHITESPACE + 1 + + + + Object updated + {'max_size': 142.0, + 'min_size': 42.0, + 'name': u'bob'} + + >>> order.name + u'bob' + + >>> order.max_size + 142.0 + + >>> order.min_size + 42.0 + +Note, however, that if we submit the same request, we'll see that no +changes were applied: + + >>> pprint(MyForm(order, request)(), width=1) + ... # doctest: +NORMALIZE_WHITESPACE + 1 + + + + No changes + {'max_size': 142.0, + 'min_size': 42.0, + 'name': u'bob'} + +because the new and old values are the same. + +The code we included in `MyForm` above is generic: it applies to any +edit form. + +Actions +======= + +Our commit logic is a little complicated. It would be far more +complicated if there were multiple submit buttons. + +We can use action objects to provide some distribution of application logic. + +An action is an object that represents a handler for a submit button. + +In the most common case, an action accepts a label and zero or more options +provided as keyword parameters: + +condition + A callable or name of a method to call to test whether the action is + applicable. if the value is a method name, then the method will be + passed the action when called, otherwise, the callable will be + passed the form and the action. + +validator + A callable or name of a method to call to validate and collect + inputs. This is called only if the action was submitted and if the + action either has no condition, or the condition evaluates to a true + value. If the validator is provided as a method name, the method + will be called with the action and a dictionary in which to save data. + If the validator is provided as a callable, the callable will be + called with the form, the action, and a dictionary in which to save data. + The validator normally returns a (usually empty) list of widget + input errors. It may also return None to behave as if the action + wasn't submitted. + +success + A handler, called when the the action was submitted and there are no + validation errors. The handler may be provided as either a callable + or a method name. If the handler is provided as a method name, the + method will be called with the action and a dictionary containing the + form data. If the success handler is provided as a callable, the + callable will be called with the form, the action, and a dictionary + containing the data. The handler may return a form result + (e.g. page), or may return None to indicate that the form should + generate it's own output. + +failure + A handler, called when the the action was submitted and there are + validation errors. The handler may be provided as either a callable + or a method name. If the handler is provided as a method name, the + method will be called with the action, a dictionary containing the form + data, and a list of errors. If the failure handler is provided as a + callable, the callable will be called with the form, the action, a + dictionary containing the data, and a list of errors. The handler + may return a form result (e.g. page), or may return None to indicate + that the form should generate it's own output. + +prefix + A form prefix for the action. When generating submit actions, the + prefix should be combined with the action name, separating the two + with a dot. The default prefix is "actions"form. + +name + The action name, without a prefix. If the label is a valid Python + identifier, then the lower-case label will be used, otherwise, a hex encoding + of the label will be used. If for some strange reason the labels in + a set of actions with the same prefix is not unique, a name will + have to be given for some actions to get unique names. + +data + A bag of extra information that can be used by handlers, validators, + or conditions. + +Let's update our edit form to use an action. We are also going to +rearrange our form quite a bit to make things more modular: + +- We've created a separate `validation` method to validate inputs and + compute errors. + +- We've created a `handle_edit_action` method for applying changes. + +- We've created a template method for displaying the form. + Normally, this would be a ZPT template, but we just provide a Python + version here. + +- We've created a call method that is described below + +- We've defined a number of instance attributes for passing + information between the various methods: + + - `status` is a string that, if set, is displayed at the top of the + form. + + - `errors` is the set of errors found when validating. + + - `widgets` is a list of set-up widgets + +Here's the new version: + + >>> class MyForm: + ... form_fields = form.Fields(IOrder, render_context=True) + ... form_fields = form_fields.omit('now') + ... + ... status = errors = None + ... prefix = 'form' + ... + ... actions = form.Actions( + ... form.Action('Edit', success='handle_edit_action'), + ... ) + ... + ... def __init__(self, context, request): + ... self.context, self.request = context, request + ... + ... def validate(self, action, data): + ... return (form.getWidgetsData(self.widgets, self.prefix, data) + + ... form.checkInvariants(self.form_fields, data)) + ... + ... def handle_edit_action(self, action, data): + ... if form.applyChanges(self.context, self.form_fields, data): + ... self.status = 'Object updated' + ... else: + ... self.status = 'No changes' + ... + ... def template(self): + ... if self.status: + ... print self.status + ... + ... result = [] + ... + ... if self.errors: + ... result.append('There were errors:') + ... for error in self.errors: + ... result.append(str(error)) + ... + ... for w in self.widgets: + ... result.append(w()) + ... error = w.error() + ... if error: + ... result.append(str(error)) + ... + ... for action in self.actions: + ... result.append(action.render()) + ... + ... return '\n'.join(result) + ... + ... def __call__(self): + ... self.widgets = form.setUpWidgets( + ... self.form_fields, self.prefix, self.context, self.request) + ... + ... data = {} + ... errors, action = form.handleSubmit( + ... self.actions, data, self.validate) + ... self.errors = errors + ... + ... if errors: + ... result = action.failure(data, errors) + ... elif errors is not None: + ... result = action.success(data) + ... else: + ... result = None + ... + ... if result is None: + ... result = self.template() + ... + ... return result + +Lets walk through the `__call__` method. + +- We set up our widgets as before. + +- We use `form.handleSubmit` to validate our data. We pass the form, + actions, prefix, and `validate` method. For each action, + `form.handleSubmit` checks to see if the action was submitted. If the + action was submitted, it checks to see if it has a validator. If + the action has a validator, the action's validator is called, + otherwise the validator passed is called. The validator result (a + list of widget input errors) and the action are returned. If no + action was submitted, then `None` is returned for the errors and the + action. + +- If a action was submitted and there were no errors, we call the + success method on the action. If the action has a handler defined, + it will be called and the return value is returned, otherwise None + is returned. A return value of None indicates that the form should + generate it's own result. + +- If a action was submitted but there were errors, we call the + action's failure method. If the action has a failure handler + defined, it will be called and the return value is returned, + otherwise None is returned. A return value of None indicates that + the form should generate it's own result. + +- No action was submitted, the result is set to None. + +- If we don't have a result, we generate one with our template. + +Let's try the new version of our form: + + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + 1 + + + + + +In this case, we didn't get any output about changes because the +request form data didn't include a submit action that matched our +action definition. Let's add one and try again: + + >>> request.form['form.actions.edit'] = u'' + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + No changes + 1 + + + + + +This time, we got a status message indicating that there weren't any +changes. + +Let's try changing some data: + + >>> request.form['form.max_size'] = u'10,0' + >>> print MyForm(order, request)() + ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + There were errors: + (u'Invalid floating point data', ) + 1 + + + + Invalid floating point data + + +Oops, we had a typo, let's fix it: + + >>> request.form['form.max_size'] = u'10.0' + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + There were errors: + Maximum is less than Minimum + 1 + + + + + +Oh yeah, we need to reduce the minimum too: :) + + >>> request.form['form.min_size'] = u'1.0' + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + Object updated + 1 + + + + + +Ah, much better. And our order has been updated: + + >>> order.max_size + 10.0 + + >>> order.min_size + 1.0 + +Helpful base classes +==================== + +Our form has a lot of repetitive code. A number of helpful base +classes provide standard form implementation. + +Form +---- + +The `Form` base class provides a number of common attribute definitions. +It provides: + +`__init__` + A constructor + +`validate` + A default validation method + +`__call__` + To render the form + +`template` + A default template. Note that this is a NamedTemplate named "default", + so the template may also be overridden by registering an alternate + default template. + +`prefix` + A string added to all widget and action names. + +`setPrefix` + method for changing the prefix + +`availableActions` + method for getting available actions + +`adapters` + Dictionary of objects implementing each given schema + +Subclasses need to: + +- Provide a form_fields variable containing a list of form fields + +- a actions attribute containing a list of action definitions + +Subclasses may: + +- Provide a label function or message id to produce + a form label. + +- Override the setUpWidgets method to control how widgets are + set up. This is fairly rarely needed. + +- Override the template. The form defines variables: + + status + providing a short summary of the operation performed. + + widgets + A collection of widgets, which can be accessed through iteration + or by name + + errors + A (possibly empty) list of errors + + +Let's update our example to use the base class: + + >>> class MyForm(form.Form): + ... form_fields = form.Fields(IOrder, render_context=True) + ... form_fields = form_fields.omit('now') + ... + ... @form.action("Edit", failure='handle_edit_action_failure') + ... def handle_edit_action(self, action, data): + ... if form.applyChanges(self.context, self.form_fields, data): + ... self.status = 'Object updated' + ... else: + ... self.status = 'No changes' + ... + ... def handle_edit_action_failure(self, action, data, errors): + ... self.status = 'There were %d errors.' % len(errors) + +We inherited most of our behavior from the base class. + +We also used the `action` decorator. The action decorator: + +- creates an `actions` variable if one isn't already created, + +- defines an action with the given label and any other arguments, and + +- appends the action to the `actions` list. + +The `action` decorator accepts the same arguments as the `Action` +class with the exception of the `success` option. + +The creation of the `actions` is a bit magic, but provides +simplification in common cases. + +Now we can try out our form: + + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + No changes + 1 + + + + + + >>> request.form['form.min_size'] = u'20.0' + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + There were 1 errors. + Invalid: Maximum is less than Minimum + 1 + + + + + + >>> request.form['form.max_size'] = u'30.0' + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + Object updated + 1 + + + + + + >>> order.max_size + 30.0 + + >>> order.min_size + 20.0 + +EditForm +-------- + +Our `handle_edit_action` action is common to edit forms. An +`EditForm` base class captures this commonality. It also sets up +widget widgets a bit differently. The `EditForm` base class sets up +widgets as if the form fields had been set up with the `render_context` +option. + + >>> class MyForm(form.EditForm): + ... form_fields = form.Fields(IOrder) + ... form_fields = form_fields.omit('now') + + >>> request.form['form.actions.apply'] = u'' + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + No changes + 1 + + + + + + >>> request.form['form.min_size'] = u'40.0' + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + There were errors + Invalid: Maximum is less than Minimum + 1 + + + + + + >>> request.form['form.max_size'] = u'50.0' + >>> print MyForm(order, request)() + ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + Updated on ... ... ... ...:...:... + 1 + + + + + + >>> order.max_size + 50.0 + + >>> order.min_size + 40.0 + +Note that `EditForm` shows the date and time when content are +modified. + +Multiple Schemas and Adapters +============================= + +Forms can use fields from multiple schemas. This can be done in a +number of ways. For example, multiple schemas can be passed to +`form.Fields`: + + >>> class IDescriptive(interface.Interface): + ... title = schema.TextLine(title=u"Title") + ... description = schema.TextLine(title=u"Description") + + >>> class MyForm(form.EditForm): + ... form_fields = form.Fields(IOrder, IDescriptive) + ... form_fields = form_fields.omit('now') + +In addition, if the the object being edited doesn't provide any of the +schemas, it will be adapted to the schemas it doesn't provide. + +Suppose we have a generic adapter for storing descriptive information +on objects: + + >>> from zope import component + >>> class Descriptive(object): + ... component.adapts(interface.Interface) + ... interface.implements(IDescriptive) + ... def __init__(self, context): + ... self.context = context + ... + ... def title(): + ... def get(self): + ... try: + ... return self.context.__title + ... except AttributeError: + ... return '' + ... def set(self, v): + ... self.context.__title = v + ... return property(get, set) + ... title = title() + ... + ... def description(): + ... def get(self): + ... try: + ... return self.context.__description + ... except AttributeError: + ... return '' + ... def set(self, v): + ... self.context.__description = v + ... return property(get, set) + ... description = description() + + >>> component.provideAdapter(Descriptive) + +Now, we can use a single form to edit both the regular order data and +the descriptive data: + + >>> request = TestRequest() + >>> print MyForm(order, request)() # doctest: +NORMALIZE_WHITESPACE + 1 + + + + + + + + >>> request.form['form.name'] = u'bob' + >>> request.form['form.min_size'] = u'10.0' + >>> request.form['form.max_size'] = u'20.0' + >>> request.form['form.title'] = u'Widgets' + >>> request.form['form.description'] = u'Need more widgets' + >>> request.form['form.actions.apply'] = u'' + >>> myform = MyForm(order, request) + >>> print myform() + ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + Updated on ... ... ... ...:...:... + 1 + + + + + + + + >>> order.min_size + 10.0 + + >>> order.title + Traceback (most recent call last): + ... + AttributeError: Order instance has no attribute 'title' + + >>> Descriptive(order).title + u'Widgets' + +Often, we'd like to get at the adapters used. If `EditForm` is used, +the adapters are available in the adapters attribute, which is a +dictionary that allows adapters to be looked up by by schema or schema +name: + + >>> myform.adapters[IOrder].__class__.__name__ + 'Order' + + >>> myform.adapters['IOrder'].__class__.__name__ + 'Order' + + >>> myform.adapters[IDescriptive].__class__.__name__ + 'Descriptive' + + >>> myform.adapters['IDescriptive'].__class__.__name__ + 'Descriptive' + +If you aren't using `EditForm`, you can get a dictionary populated in +the same way by `setUpWidgets` by passing the dictionary as an +`adapters` keyword argument. + + +Named Widget Access +=================== + +The value returned from `setUpWidgets` supports named-based lookup as well as +iteration: + + >>> myform.widgets['name'].__class__.__name__ + 'TextWidget' + + >>> myform.widgets['name'].name + 'form.name' + + >>> myform.widgets['title'].__class__.__name__ + 'TextWidget' + + >>> myform.widgets['title'].name + 'form.title' + +Form-field manipulations +======================== + +The form-field constructor is very flexible. We've already seen that +we can supply multiple schemas. Here are some other things you can +do. + +Specifying individual fields +---------------------------- + +You can specify individual fields for a form. Here, we'll create a +form that collects just the name from `IOrder` and the title from +`IDescriptive`: + + >>> class MyForm(form.EditForm): + ... form_fields = form.Fields(IOrder['name'], + ... IDescriptive['title']) + ... actions = () + + >>> print MyForm(order, TestRequest())() # doctest: +NORMALIZE_WHITESPACE + + + +You can also use stand-alone fields: + + >>> class MyForm(form.EditForm): + ... form_fields = form.Fields( + ... schema.TextLine(__name__='name', title=u"Who?"), + ... IDescriptive['title'], + ... ) + ... actions = () + + >>> print MyForm(order, TestRequest())() # doctest: +NORMALIZE_WHITESPACE + + + +But make sure the fields have a '__name__', as was done above. + +Concatenating field collections +------------------------------- + +It is sometimes convenient to combine multiple field collections. +Field collections support concatenation. For example, we may want to +combine field definitions: + + >>> class MyExpandedForm(form.Form): + ... form_fields = ( + ... MyForm.form_fields + ... + + ... form.Fields(IDescriptive['description']) + ... ) + ... actions = () + + >>> print MyExpandedForm(order, TestRequest())() + ... # doctest: +NORMALIZE_WHITESPACE + + + + +Using fields for display +------------------------ + +Normally, any writable fields get input widgets. We may want to +indicate that some fields should be used for display only. We can do +this using the `for_display` option when setting up form_fields: + + >>> class MyForm(form.EditForm): + ... form_fields = ( + ... form.Fields(IOrder, for_display=True).select('name') + ... + + ... form.Fields(IOrder).select('min_size', 'max_size') + ... ) + + + >>> print MyForm(order, TestRequest())() # doctest: +NORMALIZE_WHITESPACE + bob + + + + +Note that if all of the fields in an edit form are for display: + + >>> class MyForm(form.EditForm): + ... form_fields = form.Fields(IOrder, for_display=True + ... ).select('name', 'min_size', 'max_size') + + >>> print MyForm(order, TestRequest())() # doctest: +NORMALIZE_WHITESPACE + bob + 10.0 + 20.0 + +we don't get an edit action. This is because the edit action defined +by `EditForm` has a condition to prevent it's use when there are no +input widgets. Check it out for an example of using action conditions. + +Using fields for input +---------------------- + +We may want to indicate that some fields should be used for input even +if the underlying schema field is read-only. We can do this using the +`for_input` option when setting up form_fields: + + >>> class MyForm(form.Form): + ... form_fields = form.Fields(IOrder, for_input=True, + ... render_context=True) + ... form_fields = form_fields.omit('now') + ... + ... actions = () + + + >>> print MyForm(order, TestRequest())() # doctest: +NORMALIZE_WHITESPACE + + + + + +Displaying or editing raw data +============================== + +Sometimes, you want to display or edit data that doesn't come from an +object. One way to do this is to pass the data to setUpWidgets. + +Lets look at an example: + + >>> class MyForm(form.Form): + ... + ... form_fields = form.Fields(IOrder) + ... form_fields = form_fields.omit('now') + ... + ... actions = () + ... + ... def setUpWidgets(self, ignore_request=False): + ... self.widgets = form.setUpWidgets( + ... self.form_fields, self.prefix, self.context, self.request, + ... data=dict(identifier=42, name=u'sally'), + ... ignore_request=ignore_request + ... ) + +In this case, we supplied initial data for the identifier and the +name. Now if we display the form, we'll see our data and defaults for +the fields we didn't supply data for: + + >>> print MyForm(None, TestRequest())() # doctest: +NORMALIZE_WHITESPACE + 42 + + + + +If data are passed in the request, they override initial data for +input fields: + + >>> request = TestRequest() + >>> request.form['form.name'] = u'fred' + >>> request.form['form.identifier'] = u'0' + >>> request.form['form.max_size'] = u'100' + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + 42 + + + + +We'll get display fields if we ask for display fields when setting up +our form fields: + + >>> class MyForm(form.Form): + ... + ... form_fields = form.Fields(IOrder, for_display=True) + ... form_fields = form_fields.omit('now') + ... + ... actions = () + ... + ... def setUpWidgets(self, ignore_request=False): + ... self.widgets = form.setUpWidgets( + ... self.form_fields, self.prefix, self.context, self.request, + ... data=dict(identifier=42, name=u'sally'), + ... ignore_request=ignore_request + ... ) + + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + 42 + sally + + + + +Note that we didn't get data from the request because we are using all +display widgets. + +Passing `ignore_request=True` to the `setUpWidgets` function ignores +the request for all values passed in the data dictionary, in order to +help with redrawing a form after a successful action handler. We'll +fake that quickly by forcing ignore_request to be `True`. + + >>> class MyForm(form.Form): + ... + ... form_fields = form.Fields(IOrder) + ... form_fields = form_fields.omit('now') + ... + ... actions = () + ... + ... def setUpWidgets(self, ignore_request=False): + ... self.widgets = form.setUpWidgets( + ... self.form_fields, self.prefix, self.context, self.request, + ... data=dict(identifier=42, name=u'sally'), + ... ignore_request=True # =ignore_request + ... ) + + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + 42 + + + + + +Specifying Custom Widgets +========================= + +It is possible to use custom widgets for specific fields. This can be +done for a variety of reasons, but the provided mechanism should work +for any of them. + +Custom widgets are specified by providing a widget factory that should +be used instead of the registered field view. The factory will be +called in the same way as any other field view factory, with the bound +field and the request as arguments. + +Let's create a simple custom widget to use in our demonstration:: + + >>> import zope.formlib.widget + + >>> class ISODisplayWidget(zope.formlib.widget.DisplayWidget): + ... + ... def __call__(self): + ... return '2005-05-04' + +To set the custom widget factory for a field, assign to the +`custom_widget` attribute of the form field object:: + + >>> class MyForm(form.Form): + ... actions = () + ... + ... form_fields = form.Fields(IOrder).select("now") + ... + ... # Here we set the custom widget: + ... + ... form_fields["now"].custom_widget = ISODisplayWidget + + >>> print MyForm(None, request)() + 2005-05-04 + +Specifying Fields individually +------------------------------ + +All of the previous examples set up fields as collections. We can +also set up forms individually and pass them to the Fields +constructor. This is especially useful for passing options that +really only apply to a single field. The previous example can be +written more simply as: + + >>> class MyForm(form.Form): + ... actions = () + ... + ... form_fields = form.Fields( + ... form.Field(IOrder['now'], custom_widget=ISODisplayWidget), + ... ) + + >>> print MyForm(None, request)() + 2005-05-04 + +Computing default values +------------------------ + +We saw earlier that we could provide initial widget data by passing a +dictionary to setUpWidgets. We can also supply a function or method +name when we set up form fields. + +We might like to include the `now` field in our forms. We can provide +a function for getting the needed initial value: + + >>> import datetime + + >>> class MyForm(form.Form): + ... actions = () + ... + ... def now(self): + ... return datetime.datetime(2002, 12, 2, 12, 30) + ... + ... form_fields = form.Fields( + ... form.Fields(IOrder).omit('now'), + ... form.Field(IOrder['now'], get_rendered=now), + ... ) + + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + + + + + 2002 12 2 12:30:00 + +Now try the same with the AddFormBase which uses a setUpInputWidget: + + >>> class MyAddForm(form.AddFormBase): + ... actions = () + ... + ... def now(self): + ... return datetime.datetime(2002, 12, 2, 12, 30) + ... + ... form_fields = form.Fields( + ... form.Fields(IOrder).omit('now'), + ... form.Field(IOrder['now'], get_rendered=now), + ... ) + ... + ... def setUpWidgets(self, ignore_request=True): + ... super(MyAddForm, self).setUpWidgets(ignore_request) + + >>> print MyAddForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + + + + + + +Note that a EditForm can't make use of a get_rendered method. The get_rendered +method does only set initial values. + +Note that the function passed must take a form as an argument. The +`setUpWidgets` function takes an optional 'form' argument, which +**must** be passed if any fields use the get_rendered option. The +form base classes always pass the form to `setUpWidgets`. + +Advanced Usage Hints +==================== + +This section documents patterns for advanced usage of the formlib package. + +Multiple button groups +---------------------- + +Multiple button groups can be accomplished many ways, but the way we've found +that reuses the most code is the following: + + >>> class MyForm(form.Form): + ... form_fields = form.Fields(IOrder) + ... primary_actions = form.Actions() + ... secondary_actions = form.Actions() + ... # can use @zope.cachedescriptors.property.Lazy for performance + ... def actions(self): + ... return list(self.primary_actions) + list(self.secondary_actions) + ... @form.action(u'Edit', primary_actions) + ... def handle_edit_action(self, action, data): + ... if form.applyChanges(self.context, self.form_fields, data): + ... self.status = 'Object updated' + ... else: + ... self.status = 'No changes' + ... @form.action(u'Submit for review...', secondary_actions) + ... def handle_review_action(self, action, data): + ... print "do something here" + ... + +The template then can render the button groups separately--something like the +following, for instance: + + + +and + + + +But the form machinery can still find the correct button. # TODO: demo + +Dividing display of widget errors and invariant errors +------------------------------------------------------ + +Even though the form machinery only has a single errors attribute, if designers +wish to render widget errors differently than invariant errors, they can be +separated reasonably easily. The separation takes advantage of the fact that +all widget errors should implement zope.formlib.interfaces.IWidgetInputError, +and invariant errors shouldn't, because they don't come from a widget. +Therefore, a simple division such as the following should suffice. + +# TODO + + +Omitting the form prefix +------------------------ + +For certain use cases (e.g. forms that post data to a different server whose +software you do not control) it is important to be able to generate forms +*without* a prefix. Using an empty string for the prefix omits it entirely. + + >>> form_fields = form.Fields(IOrder).select('name') + >>> request = TestRequest() + >>> widgets = form.setUpWidgets(form_fields, '', None, request) + >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE + + +Of course, getting the widget data still works. + + >>> request.form['name'] = 'foo' + >>> widgets = form.setUpWidgets(form_fields, '', None, request) + >>> data = {} + >>> form.getWidgetsData(widgets, '', data) + [] + >>> data + {'name': u'foo'} + +And the value from the request is also visible in the rendered form. + + >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE + + +The same is true when using the other setup*Widgets helpers. + + >>> widgets = form.setUpInputWidgets(form_fields, '', None, request) + >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE + + + >>> order = Order(42) + >>> widgets = form.setUpEditWidgets(form_fields, '', order, request) + >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE + + + >>> widgets = form.setUpDataWidgets(form_fields, '', None, request) + >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE + + +Form actions have their own prefix in addition to the form prefix. This can be +suppressed for each action by passing the empty string as the 'prefix' +argument. + + >>> class MyForm(form.Form): + ... + ... prefix = '' + ... form_fields = form.Fields() + ... + ... @form.action('Button 1', name='button1') + ... def handle_button1(self, action, data): + ... self.status = 'Button 1 detected' + ... + ... @form.action('Button 2', prefix='', name='button2') + ... def handle_button2(self, action, data): + ... self.status = 'Button 2 detected' + ... + >>> request = TestRequest() + >>> request.form['actions.button1'] = '' + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + Button 1 detected + + + >>> request = TestRequest() + >>> request.form['button2'] = '' + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + Button 2 detected + + + +It is also possible to keep the form prefix and just suppress the 'actions' prefix. + + >>> class MyForm(form.Form): + ... + ... form_fields = form.Fields() + ... + ... @form.action('Button', prefix='', name='button') + ... def handle_button(self, action, data): + ... self.status = 'Button detected' + ... + >>> request = TestRequest() + >>> request.form['form.button'] = '' + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + Button detected + + +Additional Cases +================ + + +Automatic Context Adaptation +---------------------------- + +As you may know already, the formlib will automatically adapt the context to +find a widget and data for a particular field. In an early version of +``zope.formlib``, it simply used ``field.interface`` to get the interface to +adapt to. Unfortunately, this call returns the interface the field has been +defined in and not the interface you got the field from. The following lines +demonstrate the correct behavior: + + >>> import zope.interface + >>> import zope.schema + + >>> class IFoo(zope.interface.Interface): + ... title = zope.schema.TextLine() + + >>> class IFooBar(IFoo): + ... pass + +Here is the unexpected behavior that caused formlib to do the wrong thing: + + >>> IFooBar['title'].interface + + +Note: If this behavior ever changes, the formlib can be simplified again. + + >>> class FooBar(object): + ... zope.interface.implements(IFooBar) + ... title = u'initial' + >>> foobar = FooBar() + + >>> class Blah(object): + ... def __conform__(self, iface): + ... if iface is IFooBar: + ... return foobar + >>> blah = Blah() + +Let's now generate the form fields and instantiate the widgets: + + >>> from zope.formlib import form + + >>> form_fields = form.FormFields(IFooBar) + + >>> request = TestRequest() + >>> widgets = form.setUpEditWidgets(form_fields, 'form', blah, request) + >>> print widgets.get('title')() + + +Here are some more places where the behavior was incorrect: + + >>> widgets = form.setUpWidgets(form_fields, 'form', blah, request) + >>> print widgets.get('title')() + + + >>> form.checkInvariants(form_fields, {'title': 'new'}) + [] + + >>> form.applyChanges(blah, form_fields, {'title': 'new'}) + True + + +Event descriptions +------------------ + +The ObjectModifiedEvent can be annotated with descriptions about the involved +schemas and fields. The formlib provides these annotations with the help of the +applyData function, which returns a list of modification descriptions: + + >>> form.applyData(blah, form_fields, {'title': 'modified'}) + {: ['title']} + +The events are annotated with these descriptions. We need a subscriber to log these +infos: + + >>> def eventLog(event): + ... desc = event.descriptions[0] + ... print 'Modified:', desc.interface.__identifier__, desc.attributes + >>> zope.event.subscribers.append(eventLog) + + + >>> class MyForm(form.EditForm): + ... form_fields = form.FormFields(IFooBar) + + >>> request = TestRequest() + >>> request.form['form.title'] = u'again modified' + >>> request.form['form.actions.apply'] = u'' + >>> MyForm(FooBar(), request)() + Modified: __builtin__.IFooBar ('title',) + ... + +Cleanup: + + >>> zope.event.subscribers.remove(eventLog) + +Actions that cause a redirect +----------------------------- + +When an action causes a redirect, the following `render` phase is omitted as +the result will not be displayed anyway. This is both a performance +improvement and for avoiding application bugs with one-time session +information. + + >>> class MyForm(form.Form): + ... form_fields = form.FormFields(IFooBar) + ... @form.action("Redirect") + ... def redirect(self, action, data): + ... print 'Action: redirect' + ... self.request.response.redirect('foo') + ... @form.action("Stay") + ... def redirect(self, action, data): + ... print 'Action: stay' + ... pass + ... def render(self): + ... print 'render was called' + ... return '' + + >>> request = TestRequest() + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + render was called + >>> request.form['form.actions.redirect'] = u'' + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + Action: redirect + + >>> request = TestRequest() + >>> request.form['form.actions.stay'] = u'' + >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE + Action: stay + render was called diff -Nru zope3-3.4.0/src/zope/formlib/ftesting.zcml zope3-3.5~bzr18/src/zope/formlib/ftesting.zcml --- zope3-3.4.0/src/zope/formlib/ftesting.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/ftesting.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/formlib/i18n.py zope3-3.5~bzr18/src/zope/formlib/i18n.py --- zope3-3.4.0/src/zope/formlib/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/i18n.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,9 @@ +"""\ +I18N support for zope.formlib + +""" +__docformat__ = "reStructuredText" + +import zope.i18nmessageid + +_ = zope.i18nmessageid.MessageFactory("zope") diff -Nru zope3-3.4.0/src/zope/formlib/__init__.py zope3-3.5~bzr18/src/zope/formlib/__init__.py --- zope3-3.4.0/src/zope/formlib/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,15 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Foundation and Contributors. +# +# 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. +# +############################################################################## +""" +$Id: __init__.py 112015 2010-05-05 18:27:23Z tseaver $ +""" diff -Nru zope3-3.4.0/src/zope/formlib/interfaces.py zope3-3.5~bzr18/src/zope/formlib/interfaces.py --- zope3-3.4.0/src/zope/formlib/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1135 @@ +############################################################################## +# +# Copyright (c) 2005 Zope Foundation and Contributors. +# +# 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. +# +############################################################################## +"""Form interfaces + +$Id: interfaces.py 112015 2010-05-05 18:27:23Z tseaver $ +""" +import re +from zope import schema +from zope.publisher.interfaces.browser import IBrowserPage +from zope.schema.interfaces import ValidationError +from zope.publisher.interfaces import IView +from zope.interface import Attribute, Interface, implements, Invalid +from zope.schema import Bool +from zope.exceptions.interfaces import UserError + +class IWidgetInputError(Interface): + """Placeholder for a snippet View""" + + def doc(): + """Returns a string that represents the error message.""" + +class WidgetInputError(UserError): + """One or more user input errors occurred.""" + + implements(IWidgetInputError) + + def __init__(self, field_name, widget_title, errors=None): + """Initialize Error + + `errors` is a ``ValidationError`` or a list of ValidationError objects + """ + UserError.__init__(self, field_name, widget_title, errors) + self.field_name = field_name + self.widget_title = widget_title + self.errors = errors + + def doc(self): + # TODO this duck typing is to get the code working. See + # collector issue 372 + if isinstance(self.errors, basestring): + return self.errors + elif getattr(self.errors, 'doc', None) is not None: + return self.errors.doc() + return '' + + +class MissingInputError(WidgetInputError): + """Required data was not supplied.""" + + +class ConversionError(Exception): + """A conversion error occurred.""" + + implements(IWidgetInputError) + + def __init__(self, error_name, original_exception=None): + Exception.__init__(self, error_name, original_exception) + self.error_name = error_name + self.original_exception = original_exception + + def doc(self): + return self.error_name + +InputErrors = WidgetInputError, ValidationError, ConversionError + + +class ErrorContainer(Exception): + """A base error class for collecting multiple errors.""" + + def append(self, error): + self.args += (error, ) + + def __len__(self): + return len(self.args) + + def __iter__(self): + return iter(self.args) + + def __getitem__(self, i): + return self.args[i] + + def __str__(self): + return "\n".join( + ["%s: %s" % (error.__class__.__name__, error) + for error in self.args] + ) + + __repr__ = __str__ + +class WidgetsError(ErrorContainer): + """A collection of errors from widget processing. + + widgetValues is a map containing the list of values that were obtained + from the widgets, keyed by field name. + """ + + def __init__(self, errors, widgetsData={}): + ErrorContainer.__init__(self, *errors) + self.widgetsData = widgetsData + +class IWidget(IView): + """Generically describes the behavior of a widget. + + Note that this level must be still presentation independent. + """ + + name = Attribute( + """The unique widget name + + This must be unique within a set of widgets.""") + + label = Attribute( + """The widget label. + + Label may be translated for the request. + + The attribute may be implemented as either a read-write or read-only + property, depending on the requirements for a specific implementation. + + """) + + hint = Attribute( + """A hint regarding the use of the widget. + + Hints are traditionally rendered using tooltips in GUIs, but may be + rendered differently depending on the UI implementation. + + Hint may be translated for the request. + + The attribute may be implemented as either a read-write or read-only + property, depending on the requirements for a specific implementation. + + """) + + visible = Attribute( + """A flag indicating whether or not the widget is visible.""") + + def setRenderedValue(value): + """Set the value to be rendered by the widget. + + Calling this method will override any values provided by the user. + + For input widgets (`IInputWidget` implementations), calling + this sets the value that will be rendered even if there is + already user input. + + """ + + def setPrefix(prefix): + """Set the name prefix used for the widget + + The widget name is used to identify the widget's data within + input data. For example, for HTTP forms, the widget name is + used for the form key. + + It is acceptable to *reset* the prefix: set it once to read + values from the request, and again to redraw with a different + prefix but maintained state. + + """ + +class IInputWidget(IWidget): + """A widget for editing a field value.""" + + required = Bool( + title=u"Required", + description=u"""If True, widget should be displayed as requiring input. + + By default, this value is the field's 'required' attribute. This + field can be set to False for widgets that always provide input (e.g. + a checkbox) to avoid unnecessary 'required' UI notations. + """) + + def getInputValue(): + """Return value suitable for the widget's field. + + The widget must return a value that can be legally assigned to + its bound field or otherwise raise ``WidgetInputError``. + + The return value is not affected by `setRenderedValue()`. + """ + + def applyChanges(content): + """Validate the user input data and apply it to the content. + + Return a boolean indicating whether a change was actually applied. + + This raises an error if there is no user input. + """ + + def hasInput(): + """Returns ``True`` if the widget has input. + + Input is used by the widget to calculate an 'input value', which is + a value that can be legally assigned to a field. + + Note that the widget may return ``True``, indicating it has input, but + still be unable to return a value from `getInputValue`. Use + `hasValidInput` to determine whether or not `getInputValue` will return + a valid value. + + A widget that does not have input should generally not be used + to update its bound field. Values set using + `setRenderedValue()` do not count as user input. + + A widget that has been rendered into a form which has been + submitted must report that it has input. If the form + containing the widget has not been submitted, the widget + shall report that it has no input. + + """ + + def hasValidInput(): + """Returns ``True`` is the widget has valid input. + + This method is similar to `hasInput` but it also confirms that the + input provided by the user can be converted to a valid field value + based on the field constraints. + """ + +class IDisplayWidget(IWidget): + """A widget for displaying a field value.""" + + required = Bool( + title=u"Required", + description=u"""If True, widget should be displayed as requiring input. + + Display widgets should never be required. + """) + +class IWidgetFactory(Interface): + """A factory that creates the widget""" + + def __call__(context, request): + """Return a widget""" + +class FormError(Exception): + """There was an error in managing the form + """ + +class IBrowserWidget(IWidget): + """A widget for use in a web browser UI.""" + + def __call__(): + """Render the widget.""" + + def hidden(): + """Render the widget as a hidden field.""" + + def error(): + """Render the validation error for the widget, or return + an empty string if no error""" + + +class ISimpleInputWidget(IBrowserWidget, IInputWidget): + """A widget that uses a single HTML element to collect user input.""" + + tag = schema.TextLine( + title=u'Tag', + description=u'The widget HTML element.') + + type = schema.TextLine( + title=u'Type', + description=u'The element type attribute', + required=False) + + cssClass = schema.TextLine( + title=u'CSS Class', + description=u'The element class attribute.', + required=False) + + extra = schema.TextLine( + title=u'Extra', + description=u'The element extra attribute.', + required=False) + + +class ITextBrowserWidget(ISimpleInputWidget): + + convert_missing_value = schema.Bool( + title=u'Translate Input Value', + description= + u'If True, an empty string is converted to field.missing_value.', + default=True) + +# XXX this seems to be buggy (since at least 2005) +# it returns None, and prefix_re is never defined. +def reConstraint(pat, explanation): + pat = re.compile(pat) + + def constraint(value): + if prefix_re.match(value): + return True + raise Invalid(value, explanation) + +class IWidgetInputErrorView(Interface): + """Display an input error as a snippet of text.""" + + def snippet(): + """Convert a widget input error to an html snippet.""" + + +class ISourceQueryView(Interface): + """View support for querying non-iterable sources + """ + + def render(name): + """Return a rendering of the search form elements + + The query view should use `name` as the prefix for its widgets. + """ + + def results(name): + """Return the results of the query + + The query view should use `name` as the prefix for its widgets. + + The value returned is an iterable. + + None may be returned to indicate that there are no results. + """ + +class ISubPage(Interface): + """A component that computes part of a page + """ + + def update(): + """Update content ot view information based on user input + """ + + def render(): + """Render the sub page, returning a unicode string + """ + + prefix = schema.ASCII( + constraint=reConstraint( + '[a-zA-Z][a-zA-Z0-9_]*([.][a-zA-Z][a-zA-Z0-9_]*)*', + "Must be a sequence of not-separated identifiers"), + description=u"""Page-element prefix + + All named or identified page elements in a subpage should have + names and identifiers that begin with a subpage prefix + followed by a dot. + """, + readonly=True, + ) + + def setPrefix(prefix): + """Update the subpage prefix + """ + + +class IFormAPI(Interface): + """API to facilitate creating forms, provided by zope.formlib.form + """ + + def Field(schema_field, **options): + """Define a form field from a schema field and usage options + + + The following options are supported: + + name + Provide a name to use for the field. + + prefix + The form-field prefix. + + for_display + A flag indicating whether the form-field is to be used + for display. See IFormField. + + for_input + A flag indicating whether the form-field is to be used + for input. See IFormField. + + custom_widget + Factory to use for widget construction. See IFormField. + + render_context + A flag indicating whether the default value to render + should come from the form context. See IFormField. + + get_rendered + A callable or form method name to be used to get a default + rendered value. See IFormField. + + """ + + + def Fields(*arguments, **options): + """Create form-fields collection (IFormFields) + + Creates a form-field collection from a collection of: + + - Schemas + + - Schema fields + + - form fields (IFormField) + + - form-field collections (IFormFields) + + An IFormFields is returned. + + The following options are supported: + + name + Provide a name to use for the field. + + prefix + The form-field prefix for new form-fields created. + When form-field collections are passed, their contents keep + their existing prefixes are retained. + + for_display + A flag indicating whether the form-fiellds are to be used + for display. This value is used for for_display attributes + of all created form fields. This option does not effect + input from form-field collections. + + for_input + A flag indicating whether the form-fiellds are to be used + for input. This value is used for for_input attributes + of all created form fields. This option does not effect + input from form-field collections. + + render_context + A flag indicating whether the default values to render + should come from the form context. See IFormField. + + """ + + def setUpInputWidgets(form_fields, form_prefix, context, request, + ignore_request=False): + """Set up widgets for input + + An IWidgets is returned based on the give form fields. + + All of the resulting widgets will be input widgets, regardless + of whether the form fields are for display or whether the + underlying schema fields are read only. This is so that one + can easily build an input form, such as an add form from an + existing schema. + + The widgets will have prefixes that combine the given form + prefix and any form-field prefixes. + + A context argument is provided to allow field binding. + + If ignore_request passed a true value, then the widgets will + not initialize their values from the request. + """ + + def setUpEditWidgets(form_fields, form_prefix, context, request, + adapters=None, for_display=False, + ignore_request=False): + """Set up widgets for editing or displaying content + + An IWidgets is returned based on the give form fields. + + The resulting widgets will be input widgets unless: + + - the corresponding form field was defined with the + for_display option, + + - the underlying field is read only, or + + - the for_display opetion to setUpEditWidgets was passed a + true value. + + The widgets fields are bound to the context after it is + adapted to the field schema. A mapping object can be passed + to setUpEditWidgets to capture the adapters created. The + adapters are placed in the mapping using both interfaces and + interface names as keys. + + If the ignore_request option is passed a true value, then + widget's rendered data will be set from the context, and user + inputs will be ignored. + """ + + def setUpDataWidgets(form_fields, form_prefix, context, request, data=(), + for_display=False, ignore_request=False): + """Set up widgets for input or display + + An IWidgets is returned based on the give form fields. + + The resulting widgets will be input widgets unless: + + - the corresponding form field was defined with the + for_display option, + + - the underlying field is read only, or + + - the for_display opetion to setUpEditWidgets was passed a + true value. + + A data mapping argument can be passed to provide initial + data. + + If the ignore_request option is passed a true value, then + widget's rendered data will be set from the passed data or + from field defaults, and user inputs will be ignored. + + """ + + def getWidgetsData(widgets, form_prefix, data): + """Get input data and input errors + + A sequence of input errors are returned. Any data available + are added to the data argument, which must be a mapping + argument. The keys in the output mapping are + widget/form-field names without the form prefix. + + """ + + def checkInvariants(form_fields, form_data): + """Check schema invariants for input data + + For each schema that was used to define the form fields and + that had invariants relevent to the fields, the invariants are + checked. Invariants that refer to fields not included in the + form fields are ignored. + + A list of errors is returned. + """ + + def applyChanges(context, form_fields, data, adapters=None): + """Apply form data to an object + + For each form field that has data, the data are applied to the + context argument. The context is adapter to the schema for + each field. If an adapters mapping is passed, it will be used + as a cache. Typically, it would be a mapping object populated + when setUpEditWidgets was called. + + """ + + def Action(label, **options): + """Define a submit action + + options: + + condition + A callable or name of a method to call to test whether + the action is applicable. if the value is a method + name, then the method will be passed the action when + called, otherwise, the callables will be passed the form + and the action. + + validator + A callable or name of a method to call to validate and + collect inputs. This is called only if the action was + submitted and if the action either has no condition, or the + condition evaluates to a true value. If the validator is + provided as a method name, the method will be called the + action and a dictionary in which to save data. If the + validator is provided as a callable, the callable will be + called the form, the action, and a dictionary in which to + save data. The validator normally returns a (usually empty) + list of widget input errors. It may also return None to behave + as if the action wasn't submitted. + + success + A handler, called when the the action was submitted and + there are no validation errors. The handler may be provided + as either a callable or a method name. If the handler is + provided as a method name, the method will be called the + action and a dictionary containing the form data. If the + success handler is provided as a callable, the callable will + be called the form, the action, and a dictionary containing + the data. The handler may return a form result (e.g. page), + or may return None to indicate that the form should generate + it's own output. + + failure + A handler, called when the the action was submitted and + there are validation errors. The handler may be provided as + either a callable or a method name. If the handler is + provided as a method name, the method will be called the + action, a dictionary containing the form data, and a list of + errors. If the failure handler is provided as a callable, + the callable will be called the form, the action, a + dictionary containing the data, and a list of errors. The + handler may return a form result (e.g. page), or may return + None to indicate that the form should generate it's own + output. + + prefix + A form prefix for the action. When generating submit + actions, the prefix should be combined with the action + name, separating the two with a dot. The default prefix + is "actions"form. + + name + The action name, without a prefix. If the label is a + valid Python identifier, then the lowe-case label will + be used, otherwise, a hex encoding of the label will be + used. If for some strange reason the labels in a set of + actions with the same prefix is not unique, a name will + have to be given for some actions to get unique names. + + css_class + The CSS class for the action. The class defaults to + "action"form. + + data + A bag of extra information that can be used by handlers, + validators, or conditions. + + """ + + def action(label, **options): + """Create an action factory + + This function creates a factory for creating an action from a + function, using the function as the action success handler. + The options are the same as for the Action constructor except + that the options don't include the success option. + + The function is designed to be used as a decorator (Python 2.4 + and later), as in: + + @action("Edit") + def handle_edit(self, action, data): + ... + + + """ + + def validate(form, actions, form_prefix, data, default_validate=None): + """Process a submitted action, if any + + Check each of the given actions to see if any were submitted. + + If an action was submitted, then validate the input. The input + is called by calling the action's validator, ir it has one, or + by calling the default_validate passed in. + + If the input is validated successfully, and the action has one + success handler, then the success handler is called. + + If the input was validated and there were errors, then the + action's failure handler will be called, if it has one. + + If an action was submitted, then the function returns the + result of validation and the action. The result of validation + is normally a boolean, but may be None if no validator was + provided. + + If no action was submitted, then None is returned for both the + result of validation and the action. + + """ + + FormBase = Attribute("""Base class for creating forms + + The FormBase class provides reuasable implementation for creating + forms. It implements ISubPage, IBrowserPage, and IFormBaseCustomization. + Subclasses will override or use attributes defined by + IFormBaseCustomization. + """) + +class IFormBaseCustomization(ISubPage, IBrowserPage): + """Attributes provided by the Form base class + + These attributes may be used or overridden. + + Note that the update and render methods are designed to to work + together. If you override one, you probably need to override the + other, unless you use original versions in your override. + + """ + + label = Attribute("A label to display at the top of a form") + + status = Attribute( + """An update status message + + This is normally generated by success or failure handlers. + """) + + errors = Attribute( + """Sequence of errors encountered during validation + """) + + form_result = Attribute( + """Return from action result method + """) + + form_reset = Attribute( + """Boolean indicating whether the form needs to be reset + """) + + form_fields = Attribute( + """The form's form field definitions + + This attribute is used by many of the default methods. + """) + + widgets = Attribute( + """The form's widgets + + - set by setUpWidgets + + - used by validate + """) + + def setUpWidgets(ignore_request=False): + """Set up the form's widgets. + + The default implementation uses the form definitions in the + form_fields attribute and setUpInputWidgets. + + The function should set the widgets attribute. + """ + + def validate(action, data): + """The default form validator + + If an action is submitted and the action doesn't have it's own + validator then this function will be called. + """ + + template = Attribute( + """Template used to display the form + + This can be overridden in 2 ways: + + 1. You can override the attribute in a subclass + + 2. You can register an alternate named template, named + "default" for your form. + + """) + + def resetForm(): + """Reset any cached data because underlying content may have changed + """ + + def error_views(): + """Return views of any errors. + + The errors are returned as an iterable. + """ + + + +class IFormFields(Interface): + """A colection of form fields (IFormField objects) + """ + + def __len__(): + """Get the number of fields + """ + + def __iter__(): + """Iterate over the form fields + """ + + def __getitem__(name): + """Return the form field with the given name + + If the desired firld has a prefix, then the given name should + be the prefix, a dot, and the unprefixed name. Otherwise, the + given name is just the field name. + + Raise a KeyError if a field can't be found for the given name. + """ + + def get(name, default=None): + """Return the form field with the given name + + If the desired firld has a prefix, then the given name should + be the prefix, a dot, and the unprefixed name. Otherwise, the + given name is just the field name. + + Return the default if a field can't be found for the given name. + """ + + def __add__(form_fields): + """Add two form fields collections (IFormFields) + + Return a new IFormFields that is the concatination of the two + IFormFields. + """ + + def select(*names): + """Select fields with given names in order + + Return a new IFormFields that is a selection from the original + IFormFields that has the named fields in the specified order. + """ + + def omit(*names): + """Omit fields with given names + """ + +SKIP_UNAUTHORIZED = 2 +DISPLAY_UNWRITEABLE = 4 + +class IFormField(Interface): + """Definition of a field to be included in a form + + This should not be confused with a schema field. + """ + + __name__ = schema.ASCII( + constraint=reConstraint('[a-zA-Z][a-zA-Z0-9_]*', + "Must be an identifier"), + title = u"Field name", + description=u"""\ + This is the name, without any proefix, used for the field. + It is usually the same as the name of the for field's schem field. + """ + ) + + field = Attribute( + """Schema field that defines the data of the form field + """ + ) + + prefix = schema.ASCII( + constraint=reConstraint('[a-zA-Z][a-zA-Z0-9_]*', + "Must be an identifier"), + title=u"Prefix", + description=u"""\ + Form-field prefix. The form-field prefix is used to + disambiguate fields with the same name (e.g. from different + schema) within a collection of form fields. + """, + default="", + ) + + for_display = schema.Bool( + title=u"Is the form field for display only?", + description=u"""\ + If this attribute has a true value, then a display widget will be + used for the field even if it is writable. + """ + ) + + for_input = schema.Bool( + title=u"Is the form field for input?", + description=u"""\ + If this attribute has a true value, then an input widget will be + used for the field even if it is readonly. + """ + ) + + custom_widget = Attribute( + """Factory to use for widget construction. + + If not set, normal view lookup will be used. + """ + ) + + render_context = schema.Choice( + title=u"Should the rendered value come from the form context?", + description=u"""\ + + If this attribute has a true value, and there is no other + source of rendered data, then use data from the form context + to set the rendered value for the widget. This attribute is + ignored if: + + - There is user input and user input is not being ignored, or + + - Data for the value is passed to setUpWidgets. + + If the value is true, then it is evaluated as a collection of bit + flags with the flags: + + DISPLAY_UNWRITEABLE + If the field isn't writable, then use a display widget + + TODO untested + + + SKIP_UNAUTHORIZED + If the user is not priviledges to perform the requested + operation, then omit a widget. + + TODO unimplemented + + """, + vocabulary=schema.vocabulary.SimpleVocabulary.fromValues(( + False, True, + DISPLAY_UNWRITEABLE, + SKIP_UNAUTHORIZED, + DISPLAY_UNWRITEABLE | SKIP_UNAUTHORIZED, + )), + default=False, + missing_value=False, + ) + + get_rendered = Attribute( + """Object to call to get a rendered value + + This attribute may be set to a callable object or to + a form method name to call to get a value to be rendered in a + widget. + + This attribute is ignored if: + + - There is user input and user input is not being ignored, or + + - Data for the value is passed to setUpWidgets. + + """ + ) + +class IWidgets(Interface): + """A widget collection + + IWidgets provide ordered collections of widgets that also support: + + - Name-based lookup + + - Keeping track of whether a widget is being used for input or + display + + """ + + def __iter__(): + """Return an interator in the widgets, in order + """ + + def __getitem__(name): + """Get the widget with the given name + + Widgets are computed from form fields (IFormField). If the + form field used to create a widget has a prefix, then that + should be reflected in the name passed. + """ + + def __iter_input_and_widget__(): + """Return an iterator of flag/widget pairs + + The flags indicate whether the corresponding widgets are used + for input. This is necessary because there is currently no + way to introspect a widget to determine whether it is being + used for input. + """ + + def __add__(widgets): + """Add two widgets collections + + The result has the widgets in the first collection followed by + the widgets in the second collection. + + Widgets should have different names in the two collections. + The bahavior is undefined if the names overlap. + + """ + +class IForm(Interface): + """Base type for forms + + This exists primarily to provide something for which to register + form-related conponents. + + """ + +class ISubPageForm(IForm, ISubPage): + """A component that displays a part of a page. + + The rendered output must not have a form tag. It is the + responsibility of the surrounding page to supply a form tag. + + """ + +class IPageForm(IForm, IBrowserPage): + """A component that displays a form as a page. + """ + +class IAction(ISubPage): + """Form submit actions + """ + + label = schema.TextLine(title=u"Action label") + + name = schema.TextLine(title=u"Action name") + __name__ = schema.TextLine(title=u"Action name with its prefix") + + data = schema.Dict(title=u"Application data") + + condition = Attribute( + """Action condition + + This is a callable object that will be passed a form and an + action and that returns a boolean to indicate whether the + action is available. + + """) + + validator = Attribute( + """Action validator + + This is a callable object that will be passed a form and an + action and that returns a (possibly empty) list of widget + input errors. + + """) + + def available(): + """Return a boolean indicating whether the action is available + """ + + def submitted(): + """Return a boolean indicating whether the action was submitted + """ + + def validate(data): + """Validate inputs + + If an action was submitted and has a custom validator, then + the validator result is returned. Otherwise, None is returned. + + Validated inputs, if any, are placed into the mapping object + passed as an argument, + """ + + def success(data): + """Handle sucessful submition + + This method is called when the action was submitted and the + submitted data was valid. + """ + + def failure(data, errors): + """Handle unsucessful submition + + This method is called when the action was submitted and the + submitted data was not valid. + """ + + def __get__(form, form_class=None): + """Bind an action to a form + + Note that the other methods defined in this interface are + valid only after the action has been bound to a form. + """ + +class IActions(Interface): + """An action collection + + IActions provide ordered collections of actions that also support + name-based lookup. + + """ + + def __iter__(): + """Return an interator in the actions, in order + """ + + def __getitem__(name): + """Get the action with the given name + + Actions are computed from form fields (IFormField). If the + form field used to create an action has a prefix, then that + should be reflected in the name passed. + """ + + def __add__(actions): + """Add two actions collections + + The result has the actions in the first collection followed by + the actions in the second collection. + + Actions should have different names in the two collections. + The bahavior is undefined if the names overlap. + + """ + +class IBoundAction(IAction): + """An action that has been bound to a form + """ + + form = Attribute("The form to which the action is bound") + + +class IAddFormCustomization(IFormBaseCustomization): + """Form responsible for adding an object. + """ + + def create(data): + """Create and return an object to be added to the context. + + The data argument is a dictionary with values supplied by the + form. + + If any user errors occur, they should be collected into a list + and raised as a `WidgetsError`. + + """ + + def add(object): + """Add an object to the context. Returns the added object. + """ + + def createAndAdd(data): + """Create and return an object that has been added to the context. + + The data argument is a dictionary with values supplied by the + form. + + If any user errors occur, they should be collected into a list + and raised as a `WidgetsError`. + + This is normally expected to simply call the create() and + add() methods. + + """ + + def nextURL(): + """Return the URL to be displayed after the add operation. + + This can be relative to the view's context. + + The default implementation returns `self.context.nextURL()`, + i.e. it delegates to the `IAdding` view. + + """ diff -Nru zope3-3.4.0/src/zope/formlib/itemswidgets.py zope3-3.5~bzr18/src/zope/formlib/itemswidgets.py --- zope3-3.4.0/src/zope/formlib/itemswidgets.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/itemswidgets.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,633 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Browser widgets for items + +$Id: itemswidgets.py 112015 2010-05-05 18:27:23Z tseaver $ +""" +__docformat__ = 'restructuredtext' +from xml.sax.saxutils import escape + +from zope import component +from zope.interface import implements +from zope.i18n import translate +from zope.schema.interfaces import InvalidValue +from zope.schema.interfaces import ITitledTokenizedTerm + +from zope.formlib.widget import SimpleInputWidget, renderElement +from zope.formlib.interfaces import IInputWidget, IDisplayWidget +from zope.formlib.interfaces import ConversionError +from zope.formlib.i18n import _ +from zope.browserpage import ViewPageTemplateFile + + +# For choices, we want to make the widget a view of the field and vocabulary. + +def ChoiceDisplayWidget(field, request): + return component.getMultiAdapter((field, field.vocabulary, request), + IDisplayWidget) + +def ChoiceInputWidget(field, request): + return component.getMultiAdapter((field, field.vocabulary, request), + IInputWidget) + +# for collections, we want to make the widget a view of the field and the +# value_type. If the value_type is None we may fall over. We may +# not be able to do any better than that. + +def CollectionDisplayWidget(field, request): + return component.getMultiAdapter((field, field.value_type, request), + IDisplayWidget) + +def CollectionInputWidget(field, request): + return component.getMultiAdapter((field, field.value_type, request), + IInputWidget) + +# for collections of choices, we want to make the widget a view of the field, +# the value type, and the vocabulary. + +def ChoiceCollectionDisplayWidget(field, value_type, request): + return component.getMultiAdapter((field, value_type.vocabulary, request), + IDisplayWidget) + +def ChoiceCollectionInputWidget(field, value_type, request): + return component.getMultiAdapter((field, value_type.vocabulary, request), + IInputWidget) + +class TranslationHook(object): + """A mixin class that provides the translation capabilities.""" + + def translate(self, msgid): + return translate(msgid, context=self.request, default=msgid) + +class ItemsWidgetBase(TranslationHook, SimpleInputWidget): + """Convenience base class for widgets displaying items/choices.""" + + extra = "" + + def __init__(self, field, vocabulary, request): + """Initialize the widget.""" + # only allow this to happen for a bound field + assert field.context is not None + self.vocabulary = vocabulary + super(ItemsWidgetBase, self).__init__(field, request) + self.empty_marker_name = self.name + "-empty-marker" + + def setPrefix(self, prefix): + """Set the prefixes for the field names of the form.""" + super(ItemsWidgetBase, self).setPrefix(prefix) + # names for other information from the form + self.empty_marker_name = self.name + "-empty-marker" + + def __call__(self): + """Render the widget to HTML.""" + raise NotImplementedError( + "__call__() must be implemented by a subclass; use _getFormValue()" + ) + + def textForValue(self, term): + """Extract a string from the `term`. + + The `term` must be a vocabulary tokenized term. + + This can be overridden to support more complex `term` + objects. The token is returned here since it's the only thing + known to be a string, or str()able. + + """ + titled = ITitledTokenizedTerm(term, None) + if titled is None: + return term.token + return self.translate(titled.title) + + def convertTokensToValues(self, tokens): + """Convert term tokens to the terms themselves. + + Tokens are used in the HTML form to represent terms. This method takes + the form tokens and converts them back to terms. + """ + values = [] + for token in tokens: + try: + term = self.vocabulary.getTermByToken(token) + except LookupError: + raise InvalidValue("token %r not found in vocabulary" % token) + else: + values.append(term.value) + return values + + def _emptyMarker(self): + """Mark the form so that empty selections are also valid.""" + return '' % ( + self.empty_marker_name) + + def hasInput(self): + """Check whether we have any input.""" + return (self.name in self.request.form or + self.empty_marker_name in self.request.form) + + def _toFieldValue(self, input): + """See `SimpleInputWidget`""" + raise NotImplementedError( + "_toFieldValue(input) must be implemented by a subclass\n" + "It may be inherited from the mix-in classes SingleDataHelper\n" + "or MultiDataHelper") + + +class SingleDataHelper(object): + """Mix-in helper class for getting the term from the HTML form. + + This is used when we expect a single input, i.e. the Choice field. + """ + + def _toFieldValue(self, input): + if input: + try: + return self.convertTokensToValues([input])[0] + except (InvalidValue, TypeError), e: + raise ConversionError(_("Invalid value"), e) + else: + return self.context.missing_value + + def hidden(self): + #XXX: _getFormValue() should return a string value that can be + # used in a HTML form, but it doesn't. When + # http://www.zope.org/Collectors/Zope3-dev/584 gets fixed + # this hack should be reverted. + # -- Bjorn Tillenius, 2006-04-12 + value = self._getFormValue() + if value == self._missing: + form_value = '' + else: + form_value = self.vocabulary.getTerm(value).token + return renderElement(u'input', + type='hidden', + name=self.name, + id=self.name, + value=form_value, + cssClass=self.cssClass, + extra=self.extra) + + +class MultiDataHelper(object): + """Mix-in helper class for getting the term from the HTML form. + + This is used when we expect a multiple inputs, i.e. Sequence fields with a + Choice field as value_type. + """ + + def _toFieldValue(self, input): + """See SimpleInputWidget""" + if input is None: + input = [] + elif not isinstance(input, list): + input = [input] + try: + values = self.convertTokensToValues(input) + except InvalidValue, e: + raise ConversionError(_("Invalid value"), e) + + # All AbstractCollection fields have a `_type` attribute specifying + # the type of collection. Use it to generate the correct type, + # otherwise return a list. TODO: this breaks encapsulation. + if hasattr(self.context, '_type'): + _type = self.context._type + if isinstance(_type, tuple): + _type = _type[0] + return _type(values) + else: + return values + + + def _getDefault(self): + # Return the default value for this widget; + # may be overridden by subclasses. + val = self.context.default + if val is None: + val = [] + return val + + +## Display-Widgets for Items-related fields. + +class ItemDisplayWidget(SingleDataHelper, ItemsWidgetBase): + """Simple single-selection display that can be used in many cases.""" + + def __init__(self, *args, **kw): + ItemsWidgetBase.__init__(self, *args, **kw) + self.required = False + + _messageNoValue = _("item-missing-single-value-for-display", "") + + def __call__(self): + """See IBrowserWidget.""" + value = self._getFormValue() + if value is None or value == u'': + return self.translate(self._messageNoValue) + else: + term = self.vocabulary.getTerm(value) + return self.textForValue(term) + + +class ItemsMultiDisplayWidget(MultiDataHelper, ItemsWidgetBase): + """Displays a sequence of items.""" + + def __init__(self, *args, **kw): + ItemsWidgetBase.__init__(self, *args, **kw) + self.required = False + + _messageNoValue = _("vocabulary-missing-multiple-value-for-display", "") + + itemTag = 'li' + tag = 'ol' + + def __call__(self): + """See IBrowserWidget.""" + value = self._getFormValue() + if value: + rendered_items = self.renderItems(value) + return renderElement(self.tag, + id=self.name, + cssClass=self.cssClass, + contents="\n".join(rendered_items), + extra=self.extra) + else: + return self.translate(self._messageNoValue) + + def renderItems(self, value): + """Render items of sequence.""" + items = [] + cssClass = self.cssClass or '' + if cssClass: + cssClass += "-item" + tag = self.itemTag + for item in value: + term = self.vocabulary.getTerm(item) + items.append(renderElement( + tag, + cssClass=cssClass, + contents=escape(self.textForValue(term)))) + return items + +class ListDisplayWidget(ItemsMultiDisplayWidget): + """Display widget for ordered multi-selection fields. + + This can be used for both Sequence, List, and Tuple fields. + """ + tag = 'ol' + +class SetDisplayWidget(ItemsMultiDisplayWidget): + """Display widget for unordered multi-selection fields. + + This can be used for both Set field. + """ + tag = 'ul' + + +## Edit-Widgets for Items-related fields. + +# BBB Set to False to never display an item for the missing value if the field +# is required, which was the behaviour of versions up to and including 3.5.0. +EXPLICIT_EMPTY_SELECTION = True + + +class ItemsEditWidgetBase(SingleDataHelper, ItemsWidgetBase): + """Widget Base for rendering item-related fields. + + These widgets work with Choice fields and Sequence fields that have Choice + as value_type. + """ + implements(IInputWidget) + + size = 5 + tag = 'select' + + _displayItemForMissingValue = True + + # Whether an empty selection should always be made explicit, i.e. even + # if the field is required. + explicit_empty_selection = False + + def __init__(self, field, vocabulary, request): + """Initialize the widget.""" + super(ItemsEditWidgetBase, self).__init__(field, vocabulary, request) + + def setPrefix(self, prefix): + """Set the prefix of the input name. + + Once we set the prefix of input field, we use the name of the input + field and the postfix '-query' for the associated query view. + """ + super(ItemsEditWidgetBase, self).setPrefix(prefix) + + + def __call__(self): + """See IBrowserWidget.""" + value = self._getFormValue() + contents = [] + + contents.append(self._div('value', self.renderValue(value))) + contents.append(self._emptyMarker()) + + return self._div(self.cssClass, "\n".join(contents)) + + + def _div(self, cssClass, contents, **kw): + """Render a simple div tag.""" + if contents: + return renderElement('div', + cssClass=cssClass, + contents="\n%s\n" % contents, + **kw) + return "" + + + def renderItemsWithValues(self, values): + """Render the list of possible values, with those found in + `values` being marked as selected.""" + + cssClass = self.cssClass + + # multiple items with the same value are not allowed from a + # vocabulary, so that need not be considered here + rendered_items = [] + count = 0 + + # Handle case of missing value + missing = self._toFormValue(self.context.missing_value) + + if self._displayItemForMissingValue and ( + not self.context.required or + EXPLICIT_EMPTY_SELECTION and + self.explicit_empty_selection and + missing in values and + self.context.default is None): + + if missing in values: + render = self.renderSelectedItem + else: + render = self.renderItem + + missing_item = render(count, + self.translate(self._messageNoValue), + missing, + self.name, + cssClass) + rendered_items.append(missing_item) + count += 1 + + # Render normal values + for term in self.vocabulary: + item_text = self.textForValue(term) + + if term.value in values: + render = self.renderSelectedItem + else: + render = self.renderItem + + rendered_item = render(count, + item_text, + term.token, + self.name, + cssClass) + + rendered_items.append(rendered_item) + count += 1 + + return rendered_items + + def renderItem(self, index, text, value, name, cssClass): + """Render an item for a particular `value`.""" + return renderElement('option', + contents=escape(text), + value=value, + cssClass=cssClass) + + def renderSelectedItem(self, index, text, value, name, cssClass): + """Render an item for a particular `value` that is selected.""" + return renderElement('option', + contents=escape(text), + value=value, + cssClass=cssClass, + selected='selected') + + +class SelectWidget(ItemsEditWidgetBase): + """Provide a selection list for the item.""" + + _messageNoValue = _("vocabulary-missing-single-value-for-edit", + "(nothing selected)") + + def renderValue(self, value): + rendered_items = self.renderItems(value) + contents = "\n%s\n" %"\n".join(rendered_items) + return renderElement('select', + name=self.name, + id=self.name, + contents=contents, + size=self.size, + extra=self.extra) + + def renderItems(self, value): + return self.renderItemsWithValues([value]) + + +class DropdownWidget(SelectWidget): + """Variation of the SelectWidget that uses a drop-down list.""" + size = 1 + explicit_empty_selection = True + + +class RadioWidget(SelectWidget): + """Radio widget for single item choices. + + This widget can be used when the number of selections is going + to be small. + """ + orientation = "vertical" + + _messageNoValue = _("vocabulary-missing-single-value-for-edit", + "(nothing selected)") + + def renderItem(self, index, text, value, name, cssClass): + """Render an item of the list.""" + return self._renderItem(index, text, value, name, cssClass) + + def renderSelectedItem(self, index, text, value, name, cssClass): + """Render a selected item of the list.""" + return self._renderItem(index, text, value, name, cssClass, + checked=True) + + def _renderItem(self, index, text, value, name, cssClass, checked=False): + kw = {} + if checked: + kw['checked'] = 'checked' + id = '%s.%s' % (name, index) + elem = renderElement(u'input', + value=value, + name=name, + id=id, + cssClass=cssClass, + type='radio', + **kw) + return renderElement(u'label', + contents='%s %s' % (elem, text), + **{'for': id}) + + def renderValue(self, value): + rendered_items = self.renderItems(value) + if self.orientation == 'horizontal': + return "  ".join(rendered_items) + else: + return "
".join(rendered_items) + + +class ItemsMultiEditWidgetBase(MultiDataHelper, ItemsEditWidgetBase): + """Items widget supporting multiple selections.""" + + _messageNoValue = _("vocabulary-missing-multiple-value-for-edit", + "(nothing selected)") + _displayItemForMissingValue = False + + def renderItems(self, value): + if value == self.context.missing_value: + values = [] + else: + values = list(value) + return self.renderItemsWithValues(values) + + def renderValue(self, value): + # All we really add here is the ':list' in the name argument + # and mutliple='multiple' to renderElement(). + rendered_items = self.renderItems(value) + return renderElement(self.tag, + name=self.name + ':list', + id=self.name, + multiple='multiple', + size=self.size, + contents="\n".join(rendered_items), + extra=self.extra) + + def hidden(self): + items = [] + for item in self._getFormValue(): + items.append( + renderElement(u'input', + type='hidden', + name=self.name+':list', + id=self.name, + value=self.vocabulary.getTerm(item).token, + cssClass=self.cssClass, + extra=self.extra)) + return '\n'.join(items) + + +class MultiSelectWidget(ItemsMultiEditWidgetBase): + """Provide a selection list for the list to be selected.""" + + +class MultiSelectSetWidget(MultiSelectWidget): + """Provide a selection list for the set to be selected.""" + + def _toFieldValue(self, input): + value = super(MultiSelectSetWidget, self)._toFieldValue(input) + if isinstance(value, list): + value = set(value) + return value + + +class MultiSelectFrozenSetWidget(MultiSelectWidget): + """Provide a selection list for the set to be selected.""" + + def _toFieldValue(self, input): + value = super(MultiSelectFrozenSetWidget, self)._toFieldValue(input) + if isinstance(value, list): + value = frozenset(value) + return value + +class OrderedMultiSelectWidget(ItemsMultiEditWidgetBase): + """A multi-selection widget with ordering support.""" + + template = ViewPageTemplateFile('orderedSelectionList.pt') + + def choices(self): + """Return a set of tuples (text, value) that are available.""" + # Not all content objects must necessarily support the attributes + if hasattr(self.context.context, self.context.__name__): + available_values = self.context.get(self.context.context) + else: + available_values = [] + return [{'text': self.textForValue(term), 'value': term.token} + for term in self.vocabulary + if term.value not in available_values] + + def selected(self): + """Return a list of tuples (text, value) that are selected.""" + # Get form values + values = self._getFormValue() + # Not all content objects must necessarily support the attributes + if hasattr(self.context.context, self.context.__name__): + # merge in values from content + for value in self.context.get(self.context.context): + if value not in values: + values.append(value) + + terms = [self.vocabulary.getTerm(value) + for value in values] + return [{'text': self.textForValue(term), 'value': term.token} + for term in terms] + + def __call__(self): + return self.template() + + +class MultiCheckBoxWidget(ItemsMultiEditWidgetBase): + """Provide a list of checkboxes that provide the choice for the list.""" + + orientation = "vertical" + + _joinButtonToMessageTemplate = u"%s %s" + + def renderValue(self, value): + rendered_items = self.renderItems(value) + if self.orientation == 'horizontal': + return "  ".join(rendered_items) + else: + return "
".join(rendered_items) + + def renderItem(self, index, text, value, name, cssClass): + """Render an item of the list.""" + return self._renderItem(index, text, value, name, cssClass) + + def renderSelectedItem(self, index, text, value, name, cssClass): + """Render a selected item of the list.""" + return self._renderItem(index, text, value, name, cssClass, + checked=True) + + def _renderItem(self, index, text, value, name, cssClass, checked=False): + kw = {} + if checked: + kw['checked'] = 'checked' + id = '%s.%s' % (name, index) + elem = renderElement('input', + type="checkbox", + cssClass=cssClass, + name=name, + id=id, + value=value, + **kw) + contents = self._joinButtonToMessageTemplate % (elem, escape(text)) + return renderElement(u'label', + contents=contents, + **{'for': id}) + diff -Nru zope3-3.4.0/src/zope/formlib/namedtemplate.py zope3-3.5~bzr18/src/zope/formlib/namedtemplate.py --- zope3-3.4.0/src/zope/formlib/namedtemplate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/namedtemplate.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2005-2009 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. +# +############################################################################## +""" + +$Id: namedtemplate.py 112015 2010-05-05 18:27:23Z tseaver $ +""" + +# BBB +from zope.browserpage.namedtemplate import ( + INamedTemplate, NamedTemplateImplementation, implementation, + NamedTemplate, NamedTemplatePathAdapter) diff -Nru zope3-3.4.0/src/zope/formlib/objectwidget.pt zope3-3.5~bzr18/src/zope/formlib/objectwidget.pt --- zope3-3.4.0/src/zope/formlib/objectwidget.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/objectwidget.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ +
+ The Legend +
+ +
+
diff -Nru zope3-3.4.0/src/zope/formlib/objectwidget.py zope3-3.5~bzr18/src/zope/formlib/objectwidget.py --- zope3-3.4.0/src/zope/formlib/objectwidget.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/objectwidget.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,193 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Browser widgets for text-like data + +$Id: objectwidget.py 112015 2010-05-05 18:27:23Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +from zope import component +from zope.interface import implements +from zope.schema import getFieldNamesInOrder + +from zope.formlib.interfaces import IInputWidget +from zope.formlib.widget import InputWidget +from zope.formlib.widget import BrowserWidget +from zope.formlib.utility import setUpWidgets, applyWidgetsChanges +from zope.browserpage import ViewPageTemplateFile +from zope.formlib.interfaces import IWidgetInputErrorView + + +class ObjectWidgetView: + + template = ViewPageTemplateFile('objectwidget.pt') + + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return self.template() + + +class ObjectWidget(BrowserWidget, InputWidget): + """A widget over an Interface that contains Fields. + + ``factory`` + + factory used to create content that this widget (field) represents + + ``*_widget`` + + Optional CustomWidgets used to generate widgets for the fields in this + widget + """ + + implements(IInputWidget) + + _object = None # the object value (from setRenderedValue & request) + _request_parsed = False + + def __init__(self, context, request, factory, **kw): + super(ObjectWidget, self).__init__(context, request) + + # define view that renders the widget + self.view = ObjectWidgetView(self, request) + + # factory used to create content that this widget (field) + # represents + self.factory = factory + + # handle foo_widget specs being passed in + self.names = getFieldNamesInOrder(self.context.schema) + for k, v in kw.items(): + if k.endswith('_widget'): + setattr(self, k, v) + + # set up my subwidgets + self._setUpEditWidgets() + + def setPrefix(self, prefix): + super(ObjectWidget, self).setPrefix(prefix) + self._setUpEditWidgets() + + def _setUpEditWidgets(self): + # subwidgets need a new name + setUpWidgets(self, self.context.schema, IInputWidget, + prefix=self.name, names=self.names, + context=self.context) + + def __call__(self): + return self.view() + + def legendTitle(self): + return self.context.title or self.context.__name__ + + def getSubWidget(self, name): + return getattr(self, '%s_widget' % name) + + def subwidgets(self): + return [self.getSubWidget(name) for name in self.names] + + def hidden(self): + """Render the object as hidden fields.""" + result = [] + for name in self.names: + result.append(self.getSubwidget(name).hidden()) + return "".join(result) + + def error(self): + if self._error: + errormessages = [] + keys = self._error.keys(); keys.sort() + for key in keys: + errormessages.append(str(key) + ': ') + errormessages.append(component.getMultiAdapter( + (self._error[key], self.request), + IWidgetInputErrorView).snippet()) + errormessages.append(str(key) + ', ') + return ''.join(errormessages[0:-1]) + return "" + + def getInputValue(self): + """Return converted and validated widget data. + + The value for this field will be represented as an `ObjectStorage` + instance which holds the subfield values as attributes. It will + need to be converted by higher-level code into some more useful + object (note that the default EditView calls `applyChanges`, which + does this). + """ + + errors = [] + content = self.factory() + for name in self.names: + try: + setattr(content, name, self.getSubWidget(name).getInputValue()) + except Exception, e: + errors.append(e) + if self._error is None: + self._error = {} + + if name not in self._error: + self._error[name] = e + + if errors: + raise errors[0] + + return content + + + def applyChanges(self, content): + field = self.context + + # create our new object value + value = field.query(content, None) + if value is None: + # TODO: ObjectCreatedEvent here would be nice + value = self.factory() + + # apply sub changes, see if there *are* any changes + # TODO: ObjectModifiedEvent here would be nice + changes = applyWidgetsChanges(self, field.schema, target=value, + names=self.names) + + # if there's changes, then store the new value on the content + if changes: + field.set(content, value) + # TODO: If value implements ILocation, set name to field name and + # parent to content + + return changes + + def hasInput(self): + """Is there input data for the field + + Return ``True`` if there is data and ``False`` otherwise. + """ + for name in self.names: + if self.getSubWidget(name).hasInput(): + return True + return False + + def setRenderedValue(self, value): + """Set the default data for the widget. + + The given value should be used even if the user has entered + data. + """ + # re-call setupwidgets with the content + self._setUpEditWidgets() + for name in self.names: + self.getSubWidget(name).setRenderedValue(getattr(value, name, None)) diff -Nru zope3-3.4.0/src/zope/formlib/objectwidget.txt zope3-3.5~bzr18/src/zope/formlib/objectwidget.txt --- zope3-3.4.0/src/zope/formlib/objectwidget.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/objectwidget.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,152 @@ +============= +Object Widget +============= + +The following example shows a Family with Mother and Father. +First define the interface for a person: + + >>> from zope.interface import Interface, implements + >>> from zope.schema import TextLine + + >>> class IPerson(Interface): + ... """Interface for Persons.""" + ... + ... name = TextLine(title=u'Name', description=u'The first name') + +Let's define the class: + + >>> class Person(object): + ... + ... implements(IPerson) + ... + ... def __init__(self, name=''): + ... self.name = name + +Let's define the interface family: + + >>> from zope.schema import Object + + >>> class IFamily(Interface): + ... """The familiy interface.""" + ... + ... mother = Object(title=u'Mother', + ... required=False, + ... schema=IPerson) + ... + ... father = Object(title=u'Father', + ... required=False, + ... schema=IPerson) + +Let's define the class family with FieldProperty's mother and father +FieldProperty validate the values if they get added: + + >>> from zope.schema.fieldproperty import FieldProperty + + >>> class Family(object): + ... """The familiy interface.""" + ... + ... implements(IFamily) + ... + ... mother = FieldProperty(IFamily['mother']) + ... father = FieldProperty(IFamily['father']) + ... + ... def __init__(self, mother=None, father=None): + ... self.mother = mother + ... self.father = father + +Let's make a instance of Family with None attributes: + + >>> family = Family() + >>> bool(family.mother == None) + True + + >>> bool(family.father == None) + True + +Let's make a instance of Family with None attributes: + + >>> mother = Person(u'Margrith') + >>> father = Person(u'Joe') + >>> family = Family(mother, father) + >>> IPerson.providedBy(family.mother) + True + + >>> IPerson.providedBy(family.father) + True + +Let's define a dummy class which doesn't implements IPerson: + + >>> class Dummy(object): + ... """Dummy class.""" + ... def __init__(self, name=''): + ... self.name = name + +Raise a SchemaNotProvided exception if we add a Dummy instance to a Family +object: + + >>> foo = Dummy('foo') + >>> bar = Dummy('bar') + >>> family = Family(foo, bar) + Traceback (most recent call last): + ... + SchemaNotProvided + +Now let's setup a enviroment for use the widget like in a real application: + + + >>> from zope.publisher.browser import TestRequest + >>> from zope.schema.interfaces import ITextLine + >>> from zope.schema import TextLine + >>> from zope.formlib.widgets import TextWidget + >>> from zope.formlib.widgets import ObjectWidget + >>> from zope.formlib.interfaces import IInputWidget + +Register the TextLine widget used in the IPerson interface for the field 'name'. + + >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer + >>> from zope.component import provideAdapter + >>> provideAdapter(TextWidget, (ITextLine, IDefaultBrowserLayer), + ... IInputWidget) + +Let's define a request and provide input value for the mothers name used +in the family object: + + >>> request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl') + >>> request.form['field.mother.name'] = u'Margrith Ineichen' + +Before we update the object let's check the value name of the mother +instance on the family object: + + >>> family.mother.name + u'Margrith' + +Now let's initialize a ObjectWidget with the right attributes: + + >>> mother_field = IFamily['mother'] + >>> factory = Person + >>> widget = ObjectWidget(mother_field, request, factory) + +Now comes the magic. Apply changes means we force the ObjectWidget to read +the request, extract the value and save it on the content. The ObjectWidget +instance uses a real Person class (factory) for add the value. The value is +temporary stored in this factory class. The ObjectWidget reads the value from +this factory and set it to the attribute 'name' of the instance mother +(The object mother is allready there). If we don't have a instance mother +allready store in the family object, the factory instance will be stored +directly to the family attribute mother. For more information see the method +'applyChanges()' in the interface +zope.formlib.objectwidget.ObjectWidget. + + >>> widget.applyChanges(family) + True + +Test the updated mother's name value on the object family: + + >>> family.mother.name + u'Margrith Ineichen' + + >>> IPerson.providedBy(family.mother) + True + +So, now you know my mothers and fathers name. I hope it's also clear how to +use the Object field and the ObjectWidget. diff -Nru zope3-3.4.0/src/zope/formlib/orderedSelectionList.pt zope3-3.5~bzr18/src/zope/formlib/orderedSelectionList.pt --- zope3-3.4.0/src/zope/formlib/orderedSelectionList.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/orderedSelectionList.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,198 @@ + + + + + + + + + +
+ + + +
+ +
+ + + + + + + +
+ +
diff -Nru zope3-3.4.0/src/zope/formlib/pageform.pt zope3-3.5~bzr18/src/zope/formlib/pageform.pt --- zope3-3.4.0/src/zope/formlib/pageform.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/formlib/pageform.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,169 @@ + + + + + +
+ +
+ +
+ + + +
+ +

Do something

+ + + +
+ +
+ Form status summary +
+ +
    +
  • + Error Type +
  • +
+
+ +
+ +
+
+ +
+ + + + + + + + + + + + + + + + + +
Extra top
+ + + + +
+
+
+ + error +
+
Extra bottom
+
+ +
+
+ + + +
+ +
+ + + +

Do something

+ +
+ +
+ Form status summary +
+ +
    +
  • + Error Type +
  • +
+
+ +
+
+ + + + + + + + + + + + + + + + + + +
Extra top
+ + + + +
+
+
+ + error +
+
Extra bottom
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + Forenede Arabiske Emirater + Afghanistan + Antigua og Barbuda + Anguilla + Albanien + Armenien + Hollandske Antiller + Angola + Antarktis + Argentina + Amerikansk Samoa + Østrig + Australien + Aruba + Aserbajdsjan + Bosnien-Hercegovina + Barbados + Bangladesh + Belgien + Burkina Faso + Bulgarien + Bahrain + Burundi + Benin + Bermuda + Brunei Darussalam + Bolivia + Brasilien + Bahamas + Bhutan + Bouvetø + Botswana + Hviderusland + Belize + Canada + Cocos-øerne (Keelingøerne) + Den Demokratiske Republik Congo + Centralafrikanske Republik + Congo + Schweiz + Elfenbenskysten + Cook-øerne + Chile + Cameroun + Kina + Colombia + Costa Rica + Cuba + Kap Verde + Juleøen + Cypern + Tjekkiet + Tyskland + Djibouti + Danmark + Dominica + Den Dominikanske Republik + Algeriet + Ecuador + Estland + Egypten + Vestsahara + Eritrea + Spanien + Etiopien + Finland + Fiji-øerne + Falklandsøerne + Mikronesiens Forenede Stater + Færøerne + Frankrig + en + Gabon + Storbritannien + Grenada + Georgien + Fransk Guyana + Ghana + Gibraltar + Grønland + Gambia + Guinea + Guadeloupe + Ækvatorialguinea + Grækenland + South Georgia og De Sydlige Sandwichøer + Guatemala + Guam + Guinea-Bissau + Guyana + SAR Hongkong + Heard- og McDonald-øerne + Honduras + Kroatien + Haiti + Ungarn + Indonesien + Irland + Israel + Indien + Det Britiske Territorium i Det Indiske Ocean + Irak + Iran + Island + Italien + Jamaica + Jordan + Japan + Kenya + Kirgisistan + Cambodja + Kiribati + Comorerne + Saint Kitts og Nevis + Nordkorea + Sydkorea + Kuwait + Caymanøerne + Kasakhstan + Laos + Libanon + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Litauen + Luxembourg + Letland + Libyen + Marokko + Monaco + Republikken Moldova + Madagaskar + Marshalløerne + Republikken Makedonien + Mali + Myanmar + Mongoliet + SAR Macao + Nordmarianerne + Martinique + Mauretanien + Montserrat + Malta + Mauritius + Maldiverne + Malawi + Mexico + Malaysia + Mozambique + Namibia + Ny Caledonien + Niger + Norfolk Island + Nigeria + Nicaragua + Holland + Norge + Nepal + Nauru + Niue + New Zealand + Oman + Panama + Peru + Fransk Polynesien + Papua Ny Guinea + Filippinerne + Pakistan + Polen + Saint Pierre og Miquelon + Pitcairn + Puerto Rico + De palæstinensiske områder + Portugal + Palau + Paraguay + Qatar + Reunion + Rumænien + Rusland + Rwanda + Saudi-Arabien + Salomonøerne + Seychellerne + Sudan + Sverige + Singapore + St. Helena + Slovenien + Svalbard og Jan Mayen + Slovakiet + Sierra Leone + San Marino + Senegal + Somalia + Serbien + Surinam + São Tomé og Príncipe + El Salvador + Syrien + Swaziland + Turks- og Caicosøerne + Tchad + Franske Besiddelser i Det Sydlige Indiske Ocean + Togo + Thailand + Tadsjikistan + Tokelau + Timor-Leste + Turkmenistan + Tunesien + Tonga + Tyrkiet + Trinidad og Tobago + Tuvalu + Taiwan + Tanzania + Ukraine + Uganda + De Mindre Amerikanske Oversøiske Øer + USA + Uruguay + Usbekistan + Vatikanstaten + St. Vincent og Grenadinerne + Venezuela + De britiske jomfruøer + De amerikanske jomfruøer + Vietnam + Vanuatu + Wallis og Futunaøerne + Samoa + Yemen + Mayotte + Jugoslavien + Sydafrika + Zambia + Zimbabwe + + + Kalender + Sortering + Valuta + + + Buddhistisk kalender + Kinesisk kalender + Gregoriansk kalender + Jødisk kalender + Islamisk kalender + Verdslig islamisk kalender + Japansk kalender + Direkte sorteringsrækkefølge + Sorteringsrækkefølge i telefonbøger + Pinyin-baseret sorteringsrækkefølge + Stroke-baseret sorteringsrækkefølge + Traditionel sorteringsrækkefølge + + + + [a-z æ å ø á é í ó ú ý] + + + GuMtkHmsSEDFwWahKzUeygAZ + + + + + + jan + feb + mar + apr + maj + jun + jul + aug + sep + okt + nov + dec + + + J + F + M + A + M + J + J + A + S + O + N + D + + + januar + februar + marts + april + maj + juni + juli + august + september + oktober + november + december + + + + + + + søn + man + tir + ons + tor + fre + lør + + + S + M + T + O + T + F + L + + + søndag + mandag + tirsdag + onsdag + torsdag + fredag + lørdag + + + + + + + + + + f.Kr. + e.Kr. + + + + + + + EEEE dd MMMM yyyy + + + + + d. MMM yyyy + + + + + dd-MM-yyyy + + + + + dd-MM-yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Pacific-normaltid + Pacific-sommertid + + + PST + PDT + + Los Angeles + + + + Pacific-normaltid + Pacific-sommertid + + + PST + PDT + + Los Angeles + + + + Mountain-normaltid + Mountain-sommertid + + + MST + MDT + + Denver + + + + Mountain-normaltid + Mountain-sommertid + + + MST + MDT + + Denver + + + + Mountain-normaltid + Mountain-normaltid + + + MST + MST + + Phoenix + + + + Mountain-normaltid + Mountain-normaltid + + + MST + MST + + Phoenix + + + + Central-normaltid + Central-sommertid + + + CST + CDT + + Chicago + + + + Central-normaltid + Central-sommertid + + + CST + CDT + + Chicago + + + + Eastern-normaltid + Eastern-sommertid + + + EST + EDT + + New York + + + + Eastern-normaltid + Eastern-sommertid + + + EST + EDT + + New York + + + + Eastern-normaltid + Eastern-normaltid + + + EST + EST + + Indianapolis + + + + Eastern-normaltid + Eastern-normaltid + + + EST + EST + + Indianapolis + + + + Hawaii-normaltid + Hawaii--normaltid + + + HST + HST + + Honolulu + + + + Hawaii-normaltid + Hawaii-normaltid + + + HST + HST + + Honolulu + + + + Alaska-normaltid + Alaska-sommertid + + + AST + ADT + + Anchorage + + + + Alaska-normaltid + Alaska-sommertid + + + AST + ADT + + Anchorage + + + + Atlantic-normaltid + Atlantic-sommertid + + + AST + ADT + + Halifax + + + + Newfoundland-normaltid + Newfoundland-sommertid + + + CNT + CDT + + St. Johns + + + + Newfoundland-normaltid + Newfoundland-sommertid + + + CNT + CDT + + St. Johns + + + + Mellemeuropæisk normaltid + Mellemeuropæisk sommertid + + + CET + CEST + + Paris + + + + Mellemeuropæisk normaltid + Mellemeuropæisk sommertid + + + CET + CEST + + Paris + + + + Verdenstid + Verdenstid + + + GMT + GMT + + London + + + + Verdenstid + Verdenstid + + + GMT + GMT + + Casablanca + + + + Israelsk normaltid + Israelsk sommertid + + + IST + IDT + + Jerusalem + + + + Japansk normaltid + Japansk normaltid + + + JST + JST + + Tokyo + + + + Japansk normaltid + Japansk normaltid + + + JST + JST + + Tokyo + + + + Østeuropæisk normaltid + Østeuropæisk sommertid + + + EET + EEST + + Bukarest + + + + Kinesisk normaltid + Kinesisk normaltid + + + CTT + CDT + + Shanghai + + + + Kinesisk normaltid + Kinesisk normaltid + + + CTT + CDT + + Shanghai + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + Andorransk diner + ADD + + + Andorransk peseta + ADP + + + Dirham fra de Forenede Arabiske Emirater + AED + + + Affars og Issas franc + AIF + + + Albansk lek (1946-1961) + ALK + + + Albansk lek + lek + + + Albansk lek + ALV + + + Armensk dram + dram + + + Gylden fra De Nederlandske Antiller + NA f. + + + Angolansk kwanza + AOA + + + Angolansk kwanza (1977-1990) + AOK + + + Ny angolansk kwanza (1990-2000) + AON + + + Angolansk kwanza reajustado (1995-1999) + AOR + + + Angolansk escudo + AOS + + + Argentinsk austral + ARA + + + Argentinsk peso moneda nacional + ARM + + + Argentinsk peso (1983-1985) + ARP + + + Argentinsk peso + Arg$ + + + Østrigsk schilling + ATS + + + Australsk dollar + $A + + + Australsk pund + AUP + + + Arubansk gylden + AWG + + + Aserbajdsjansk manat + AZM + + + Bosnien-Hercegovinsk dinar + BAD + + + Bosnien-Hercegovinsk konvertibel mark + KM + + + Ny bosnien-hercegovinsk dinar + BAN + + + Barbadisk dollar + BDS$ + + + Bangladeshisk taka + Tk + + + Belgisk franc (konvertibel) + BEC + + + Belgisk franc + BF + + + Belgisk franc (financial) + BEL + + + Bulgarsk hard lev + lev + + + Bulgarsk socialist lev + BGM + + + Ny Bulgarsk lev + BGN + + + Bulgarsk lev (1879-1952) + BGO + + + Bahrainsk dinar + BD + + + Burundisk franc + Fbu + + + Bermudansk dollar + Ber$ + + + Bermudansk pund + BMP + + + Bruneisk dollar + BND + + + Boliviansk peso + BOP + + + Boliviansk mvdol + BOV + + + Brasiliansk cruzeiro novo (1967-1986) + BRB + + + Brasiliansk cruzado + BRC + + + Brasiliansk cruzeiro (1990-1993) + BRE + + + Brasiliansk real + R$ + + + Brasiliansk cruzado novo + BRN + + + Brasiliansk cruzeiro + BRR + + + Brasiliansk cruzeiro (1942-1967) + BRZ + + + Bahamansk dollar + BSD + + + Bahamansk pund + BSP + + + Bhutansk ngultrum + Nu + + + Bhutansk rupee + BTR + + + Burmesisk kyat + BUK + + + Burmesisk rupee + BUR + + + Botswansk pula + BWP + + + Ny hviderussisk rubel (1994-1999) + BYB + + + Hviderussisk rubel (1992-1994) + BYL + + + Hviderussisk rubel + Rbl + + + Belizisk dollar + BZ$ + + + Britisk Honduras dollar + BZH + + + Canadisk dollar + Can$ + + + Congolesisk franc congolais + CDF + + + Congolesisk franc + CDG + + + Congolesisk Zaire + CDL + + + CFA-franc fra den Centralafrikanske republik + CFF + + + Schweizisk franc + SwF + + + Dollar fra Cookøerne + CKD + + + Chilensk condor + CLC + + + Chilensk escudo + CLE + + + Chilensk unidades de fomento + CLF + + + Chilensk peso + Ch$ + + + Camerounsk CFA-franc + CMF + + + Kinesisk jen min piao yuan + CNP + + + Kinesisk yuan renminbi + Y + + + Colombiansk papirpeso + COB + + + Congolesisk CFA-franc + COF + + + Colombiansk peso + Col$ + + + Costaricansk colon + C + + + Tjekkoslovakisk koruna + CSC + + + Tjekkoslovakisk hard koruna + CSK + + + Cubansk peso + CUP + + + Kapverdisk escudo + CVEsc + + + Curacaosk gylden + CWG + + + Cypriotisk pund + £C + + + Tjekkisk koruna + CZK + + + Østtysk mark + DDM + + + Tysk mark + DEM + + + Tysk sperrmark + DES + + + Djiboutisk franc + DF + + + Dansk krone + kr + + + Dominikansk peso + RD$ + + + Algerisk dinar + DA + + + Ny algerisk franc + DZF + + + Algerisk franc germinal + DZG + + + Ecuadoriansk sucre + ECS + + + Estisk kroon + EEK + + + Egyptisk pund + EGP + + + Eritreisk nakfa + ERN + + + Spansk peseta + ESP + + + Etiopisk birr + Br + + + Etiopisk dollar + ETD + + + Euro + + + + Finsk mark + FIM + + + Finsk mark (1860-1962) + FIN + + + Fijiansk dollar + F$ + + + Fijiansk pund + FJP + + + Pund fra Falklandsøerne + FKP + + + Færøsk krone + FOK + + + Fransk franc + FRF + + + Gabonesisk CFA-franc + GAF + + + Britisk pund + £ + + + Georgisk kupon larit + GEK + + + Georgisk lari + lari + + + Ghanesisk cedi + GHC + + + Gammel ghanesisk cedi + GHO + + + Ghanesisk pund + GHP + + + Ghanesisk revalueret cedi + GHR + + + Gibraltarisk pund + GIP + + + Grønlandsk krone + GLK + + + Gambisk dalasi + GMD + + + Gambisk pund + GMP + + + Guineansk franc + GF + + + Guineansk franc (1960-1972) + GNI + + + Guineansk syli + GNS + + + Guadeloupsk franc + GPF + + + Ækvatorialguineask ekwele guineana + GQE + + + Ækvatorialguineask franco + GQF + + + Ækvatorialguineask peseta guineana + GQP + + + Græsk drachma + GRD + + + Ny græsk drachma + GRN + + + Guatemalansk quetzal + Q + + + Fransk-guyansk franc guiana + GUF + + + Portugisisk guinea escudo + GWE + + + Portugisisk guinea mil reis + GWM + + + Guineansk peso + GWP + + + Guyansk dollar + G$ + + + Honduransk lempira + L + + + Kroatisk dinar + HRD + + + Kroatisk kuna + HRK + + + Haitisk gourde + HTG + + + Ungarsk forint + Ft + + + Nordirsk pund + IBP + + + Indonesisk nica guilder + IDG + + + Indonesisk java rupiah + IDJ + + + Ny indonesisk rupiah + IDN + + + Indonesisk pupiah + Rp + + + Irsk pund + IR£ + + + Israelsk shekel + ILL + + + Israelsk pund + ILP + + + Ny israelsk shekel + ILS + + + Indisk rupee + =0#Rs.|1#Re.|1<Rs. + + + Irakisk dinar + ID + + + Iransk rial + RI + + + Islandsk krona + ISK + + + Italiensk lira + + + + Pund fra Jersey + JEP + + + Jamaicansk dollar + J$ + + + Jamaicansk pund + JMP + + + Jordansk dinar + JD + + + Japansk yen + ¥ + + + Kenyansk shilling + K Sh + + + Kirgisisk som + som + + + Gammel cambodjansk riel + KHO + + + Cambodjansk riel + CR + + + Comorisk franc + CF + + + Nordkoreansk won + KPW + + + Sydkoreansk hwan + KRH + + + Gammel sydkoreansk won + KRO + + + Sydkoreansk won + KRW + + + Kuwaitisk dinar + KD + + + Dollar fra Caymanøerne + KYD + + + Kasakhisk rubel + KZR + + + Kasakhisk tenge + T + + + Laotisk kip + LAK + + + Libanesisk pund + LL + + + Liechtensteinsk franc + LIF + + + Srilankansk rupee + SL Re + + + Ceylonesisk rupee + LNR + + + Liberisk dollar + LRD + + + Lesothisk loti + M + + + Litauisk lita + LTL + + + Litauisk talonas + LTT + + + Luxembourgsk franc + LUF + + + Lettisk lats + LVL + + + Lettisk rubel + LVR + + + Libysk dinar + LD + + + Libysk pund + LYP + + + Marokkansk dirham + MAD + + + Marokkansk franc + MAF + + + Ny monegaskisk franc + MCF + + + Monegaskisk franc germinal + MCG + + + Moldovisk leu cupon + MDC + + + Moldovisk leu + MDL + + + Moldovisk ruble cupon + MDR + + + Madagaskisk ariary + MGA + + + Madagaskisk franc + MGF + + + Dollar fra Marshalløerne + MHD + + + Makedonsk denar + MDen + + + Makedonsk denar (1992-1993) + MKN + + + Malisk franc + MLF + + + Myanmarsk kyat + MMK + + + Mongolsk tugrik + Tug + + + Macaosk pataca + MOP + + + Martiniquisk franc + MQF + + + Mauritansk ouguiya + UM + + + Maltesisk lira + Lm + + + Maltesisk pund + MTP + + + Mauritisk rupee + MUR + + + Maldivisk rupee + MVP + + + Maldivisk rufiyaa + MVR + + + Malawisk kwacha + MK + + + Malawisk pund + MWP + + + Mexicansk peso + MEX$ + + + Mexicansk silver peso (1861-1992) + MXP + + + Malaysisk ringgit + RM + + + Mozambiquisk escudo + MZE + + + Mozambiquisk metical + Mt + + + Namibisk dollar + N$ + + + Nykaledonsk franc germinal + NCF + + + Nigeriansk naira + NGN + + + Nigeriansk pund + NGP + + + CFP-franc fra Ny-Hebriderne + NHF + + + Nicaraguansk cordoba + NIC + + + Nicaraguansk gold cordoba + NIG + + + Nicaraguansk cordoba oro + NIO + + + Hollandsk guilder + NLG + + + Norsk krone + NKr + + + Nepalesisk rupee + Nrs + + + New Zealandsk dollar + $NZ + + + New Zealandsk pund + NZP + + + Omansk rial + RO + + + Omansk rial saidi + OMS + + + Panamansk balboa + PAB + + + Transdniestrisk rubelkupon + PDK + + + Ny transdniestrisk rubel + PDN + + + Transdniestrisk rubel + PDR + + + Peruviansk inti + PEI + + + Peruviansk sol nuevo + PEN + + + Peruviansk sol + PES + + + Papuansk kina + PGK + + + Filippinsk peso + PHP + + + Pakistansk rupee + Pra + + + Polsk zloty + Zl + + + Polsk zloty (1950-1995) + PLZ + + + Palæstinensisk pund + PSP + + + Portugisisk conto + PTC + + + Portugisisk escudo + PTE + + + Paraguaysk guarani + PYG + + + Qatarsk rial + QR + + + Rumænsk leu + leu + + + Ny rumænsk leu + RON + + + Russisk rubel + RUB + + + Russisk rubel (1991-1998) + RUR + + + Rwandisk franc + RWF + + + Saudisk riyal + SRl + + + Saudisk sovereign riyal + SAS + + + Salomonsk dollar + SI$ + + + Seychellisk rupee + SR + + + Sudansk dinar + SDD + + + Sudansk pund + SDP + + + Svensk krona + SKr + + + Singaporeansk dollar + S$ + + + Pund fra Saint Helena + SHP + + + Slovensk tolar bons + SIB + + + Slovensk tolar + SIT + + + Slovakisk koruna + Sk + + + Sierraleonsk leone + SLL + + + Lira fra San Marino + SML + + + Somalisk shilling + So. Sh. + + + Somalilands shilling + SQS + + + Surinamsk guilder + Sf + + + Skotsk pund + SSP + + + Dobra fra Sao Tome og Principe + Db + + + Escudo fra Sao Tome og Principe + STE + + + Ny sovjetisk rubel + SUN + + + Sovjetisk rubel + SUR + + + Salvadoransk colon + SVC + + + Syrisk pund + LS + + + Swazilandsk lilangeni + E + + + Tchadisk CFA-franc + TDF + + + Thailandsk baht + THB + + + Tadsjikisk rubel + TJR + + + Tadsjikisk somoni + TJS + + + Turkmensk manat + TMM + + + Tunesisk dinar + TND + + + Tongask paʻanga + T$ + + + Tongask pund + TOS + + + Escudo fra Timor + TPE + + + Pataca fra Timor + TPP + + + Tyrkisk lira + TL + + + Dollar fra Trinidad og Tobago + TT$ + + + Gammel dollar fra Trinidad og Tobago + TTO + + + Tuvaluansk dollar + TVD + + + Ny taiwansk dollar + NT$ + + + Tanzanisk shilling + T Sh + + + Ukrainsk grynia + UAH + + + Ukrainsk karbovanetz + UAK + + + Ugandisk shilling (1966-1987) + UGS + + + Ugandisk shilling + U Sh + + + Amerikanske dollar + US$ + + + Amerikansk dollar (næste dag) + USN + + + Amerikansk dollar (samme dag) + USS + + + Uruguaysk peso fuerte + UYF + + + Uruguaysk peso (1975-1993) + UYP + + + Uruguaysk peso uruguayo + Ur$ + + + Usbekisk coupon som + UZC + + + Usbekisk sum + UZS + + + Vatikansk lira + VAL + + + Nordvietnamesisk piastre dong viet + VDD + + + Ny nordvietnamesisk dong + VDN + + + Nordvietnamesisk viet minh piastre dong viet + VDP + + + Venezuelansk bolivar + Be + + + Dollar fra De Britiske Jomfruøer + VGD + + + Vietnamesisk dong + VND + + + Ny vietnamesisk dong + VNN + + + Vietnamesisk republikansk dong + VNR + + + Vietnamesisk national dong + VNS + + + Vanuaisk vatu + VT + + + Samoansk pund + WSP + + + Samoansk tala + WST + + + Beninsk CFA-franc + XAF + + + Guld + XAU + + + Øst-karaibisk dollar + EC$ + + + Fransk guldfranc + XFO + + + Fransk UIC-franc + XFU + + + Islamisk dinar + XID + + + CFP-franc + CFPF + + + Yemenitisk dinar + YDD + + + Yemenitisk imadi riyal + YEI + + + Yemenitisk rial + YRl + + + Jugoslavisk hard dinar + YUD + + + Jugoslavisk federation dinar + YUF + + + Jugoslavisk 1994 dinar + YUG + + + Jugoslavisk noviy dinar + YUM + + + Jugoslavisk convertible dinar + YUN + + + Jugoslavisk october dinar + YUO + + + Jugoslavisk reformed dinar + YUR + + + Sydafrikansk rand (financial) + ZAL + + + Sydafrikansk pund + ZAP + + + Sydafrikansk rand + R + + + Zambisk kwacha + ZMK + + + Zambisk pund + ZMP + + + Ny zairisk zaire + ZRN + + + Zairisk naire + ZRZ + + + Zimbabwisk dollar + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/de_AT.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/de_AT.xml --- zope3-3.4.0/src/zope/i18n/locales/data/de_AT.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/de_AT.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + Jän + Feb + Mär + Apr + Mai + Jun + Jul + Aug + Sep + Okt + Nov + Dez + + + Jänner + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + + + + + + + + + + + EEEE, dd. MMMM yyyy + + + + + dd. MMMM yyyy + + + + + dd.MM.yyyy + + + + + dd.MM.yy + + + + + + + + HH:mm' Uhr 'z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/de_BE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/de_BE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/de_BE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/de_BE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + Jan + Feb + Mär + Apr + Mai + Jun + Jul + Aug + Sep + Okt + Nov + Dez + + + + + + + Son + Mon + Die + Mit + Don + Fre + Sam + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + d-MMM-yy + + + + + d/MM/yy + + + + + + + + HH 'h' mm 'min' ss 's' z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + + Franken + FF + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/de_CH.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/de_CH.xml --- zope3-3.4.0/src/zope/i18n/locales/data/de_CH.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/de_CH.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + Bangladesh + Brunei + Botswana + Kapverden + Djibouti + Grossbritannien + Marshall-Inseln + Rwanda + Salomon-Inseln + Sao Tomé und Principe + Zimbabwe + + + + + . + ' + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0.00;¤-#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/de_DE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/de_DE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/de_DE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/de_DE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/de_LI.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/de_LI.xml --- zope3-3.4.0/src/zope/i18n/locales/data/de_LI.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/de_LI.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,26 @@ + + + + + + + + + + + + . + ' + ; + % + 0 + # + + + - + E + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/de_LU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/de_LU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/de_LU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/de_LU.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + + Luxemburgischer Franc + F + #,##0 ¤;-#,##0 ¤ + #,##0 ¤;-#,##0 ¤ + , + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/de.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/de.xml --- zope3-3.4.0/src/zope/i18n/locales/data/de.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/de.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2069 @@ + + + + + + + + + + + Afar + Abchasisch + Avestisch + Afrikaans + Akan + Amharisch + Aragonesisch + Arabisch + Assamesisch + Awarisch + Aymará-Sprache + Aserbaidschanisch + Baschkirisch + Weißrussisch + Bulgarisch + Biharisch + Bislama + Bambara-Sprache + Bengalisch + Tibetisch + Bretonisch + Bosnisch + Katalanisch + Tschetschenisch + Chamorro-Sprache + Cherokee + Korsisch + Cree + Tschechisch + Kirchenslawisch + Tschuwaschisch + Kymrisch + Dänisch + Deutsch + Maledivisch + Bhutanisch + Ewe-Sprache + Griechisch + Englisch + Mittelenglisch + Esperanto + Spanisch + Estnisch + Baskisch + Persisch + Ful + Finnisch + Fidschianisch + Färöisch + Französisch + Friesisch + Irisch + Schottisch-Gälisch + Altäthiopisch + Galizisch + Guarani + Gujarati + Manx + Hausa + Hawaiianisch + Hebräisch + Hindi + Hiri-Motu + Kroatisch + Kreolisch + Ungarisch + Armenisch + Herero-Sprache + Interlingua + Indonesisch + Interlingue + Igbo-Sprache + Inupiak + Isländisch + Italienisch + Inukitut + Japanisch + Javanisch + Georgisch + Kongo + Kikuyu-Sprache + Kwanyama + Kasachisch + Grönländisch + Kambodschanisch + Kannada + Koreanisch + Konkani + Kanuri-Sprache + Kaschmirisch + Kurdisch + Komi-Sprache + Kornisch + Kirgisisch + Latein + Luxemburgisch + Ganda-Sprache + Limburgisch + Lingala + Laotisch + Litauisch + Luba + Lettisch + Madagassisch + Marschallesisch + Maori + Mazedonisch + Malayalam + Mongolisch + Moldauisch + Marathi + Malaiisch + Maltesisch + Birmanisch + Nauruisch + Norwegisch Bokmål + Ndebele-Sprache (Nord) + Nepalesisch + Ndonga + Niederländisch + Norwegisch Nynorsk + Norwegisch + Ndebele-Sprache (Süd) + Navajo-Sprache + Chewa-Sprache + Okzitanisch + Ojibwa-Sprache + Oromo + Orija + Ossetisch + Pandschabisch + Pali + Polnisch + Afghanisch (Paschtu) + Portugiesisch + Quechua + Rätoromanisch + Rundi-Sprache + Rumänisch + Russisch + Rwanda-Sprache + Sanskrit + Sardisch + Sindhi + Nord-Samisch + Sango + Serbo-Kroatisch + Singhalesisch + Sidamo + Slowakisch + Slowenisch + Samoanisch + Shona + Somali + Albanisch + Serbisch + Swazi + Süd-Sotho-Sprache + Sudanesisch + Schwedisch + Suaheli + Syrisch + Tamilisch + Telugu + Tadschikisch + Thai + Tigrinja + Tigre + Turkmenisch + Tagalog + Tswana-Sprache + Tongaisch + Türkisch + Tsonga + Tatarisch + Twi + Tahitisch + Uigurisch + Ukrainisch + Urdu + Usbekisch + Venda-Sprache + Vietnamesisch + Volapük + Wallonisch + Wolof + Xhosa + Jiddisch + Joruba + Zhuang + Zapotekisch + Chinesisch + Zulu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + Vereinigte Arabische Emirate + Afghanistan + Antigua und Barbuda + Anguilla + Albanien + Armenien + Niederländische Antillen + Angola + Antarktis + Argentinien + Amerikanisch-Samoa + Österreich + Australien + Aruba + Aserbaidschan + Bosnien und Herzegowina + Barbados + Bangladesch + Belgien + Burkina Faso + Bulgarien + Bahrain + Burundi + Benin + Bermuda + Brunei Darussalam + Bolivien + Brasilien + Bahamas + Bhutan + Bouvetinsel + Botsuana + Belarus + Belize + Kanada + Kokosinseln (Keeling) + Demokratische Republik Kongo + Zentralafrikanische Republik + Kongo + Schweiz + Côte d’Ivoire + Cookinseln + Chile + Kamerun + China + Kolumbien + Costa Rica + Kuba + Kap Verde + Weihnachtsinsel + Zypern + Tschechische Republik + Deutschland + Dschibuti + Dänemark + Dominica + Dominikanische Republik + Algerien + Ecuador + Estland + Ägypten + Westsahara + Eritrea + Spanien + Äthiopien + Finnland + Fidschi + Falklandinseln + Mikronesien + Färöer + Frankreich + en + Gabun + Vereinigtes Königreich + Grenada + Georgien + Französisch-Guayana + Ghana + Gibraltar + Grönland + Gambia + Guinea + Guadeloupe + Äquatorialguinea + Griechenland + Südgeorgien und die Südlichen Sandwichinseln + Guatemala + Guam + Guinea-Bissau + Guyana + Hong Kong S.A.R., China + Heard und McDonaldinseln + Honduras + Kroatien + Haiti + Ungarn + Indonesien + Irland + Israel + Indien + Britisches Territorium im Indischen Ozean + Irak + Iran + Island + Italien + Jamaika + Jordanien + Japan + Kenia + Kirgisistan + Kambodscha + Kiribati + Komoren + St. Kitts und Nevis + Demokratische Volksrepublik Korea + Republik Korea + Kuwait + Kaimaninseln + Kasachstan + Laos + Libanon + St. Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Litauen + Luxemburg + Lettland + Libyen + Marokko + Monaco + Republik Moldau + Madagaskar + Marshallinseln + Mazedonien + Mali + Myanmar + Mongolei + Macau S.A.R., China + Nördliche Marianen + Martinique + Mauretanien + Montserrat + Malta + Mauritius + Malediven + Malawi + Mexiko + Malaysia + Mosambik + Namibia + Neukaledonien + Niger + Norfolkinsel + Nigeria + Nicaragua + Niederlande + Norwegen + Nepal + Nauru + Niue + Neuseeland + Oman + Panama + Peru + Französisch-Polynesien + Papua-Neuguinea + Philippinen + Pakistan + Polen + St. Pierre und Miquelon + Pitcairn + Puerto Rico + Palästinensische Gebiete + Portugal + Palau + Paraguay + Katar + Réunion + Rumänien + Russische Föderation + Ruanda + Saudi-Arabien + Salomonen + Seychellen + Sudan + Schweden + Singapur + St. Helena + Slowenien + Svalbard und Jan Mayen + Slowakei + Sierra Leone + San Marino + Senegal + Somalia + Serbien + Suriname + São Tomé und Príncipe + El Salvador + Syrien + Swasiland + Turks- und Caicosinseln + Tschad + Französische Süd- und Antarktisgebiete + Togo + Thailand + Tadschikistan + Tokelau + Osttimor + Turkmenistan + Tunesien + Tonga + Türkei + Trinidad und Tobago + Tuvalu + Taiwan + Tansania + Ukraine + Uganda + Amerikanisch-Ozeanien + Vereinigte Staaten + Uruguay + Usbekistan + Vatikanstadt + St. Vincent und die Grenadinen + Venezuela + Britische Jungferninseln + Amerikanische Jungferninseln + Vietnam + Vanuatu + Wallis und Futuna + Samoa + Jemen + Mayotte + Jugoslawien + Südafrika + Sambia + Simbabwe + + + en + Posix + Revidiert + + + Kalender + Sortierung + Währung + + + Buddhistischer Kalender + Chinesischer Kalender + Gregorianischer Kalender + Hebräischer Kalender + Islamischer Kalender + Bürgerlicher islamischer Kalender + Japanischer Kalender + Direkte Sortierregeln + Telefonbuch-Sortierregeln + Pinyin-Sortierregeln + Strichfolge + Traditionelle Sortierregeln + + + + [a-z ä ö ü ß] + + + GjMtkHmsSEDFwWahKzJeugAZ + + + + + + Jan + Feb + Mrz + Apr + Mai + Jun + Jul + Aug + Sep + Okt + Nov + Dez + + + J + F + M + A + M + J + J + A + S + O + N + D + + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + + + + + + So + Mo + Di + Mi + Do + Fr + Sa + + + S + M + D + M + D + F + S + + + Sonntag + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + + + + + + + + vorm. + nachm. + + + v. Chr. + n. Chr. + + + + + + + EEEE, d. MMMM yyyy + + + + + d. MMMM yyyy + + + + + dd.MM.yyyy + + + + + dd.MM.yy + + + + + + + + H:mm' Uhr 'z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Mitteleuropäische Zeit + Mitteleuropäische Sommerzeit + + + MEZ + MESZ + + Berlin + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + Andorranischer Diner + ADD + + + Andorranische Pesete + ADP + + + UAE Dirham + AED + + + Afghani + AFA + + + Afghani + Af + + + Afar und Issa Franc + AIF + + + Albanischer Lek (1946-1961) + ALK + + + Lek + ALL + + + Albanischer Lek Valute + ALV + + + Dram + AMD + + + Niederl. Antillen Gulden + ANG + + + Kwanza + AOA + + + Angolanischer Kwanza (1977-1990) + AOK + + + Neuer Kwanza + AON + + + Kwanza Reajustado + AOR + + + Angolanischer Escudo + AOS + + + Argentinischer Austral + ARA + + + Argentinischer Peso Moneda Nacional + ARM + + + Argentinischer Peso (1983-1985) + ARP + + + Argentinischer Peso + ARS + + + Österreichischer Schilling + öS + + + Australischer Dollar + AUD + + + Australisches Pfund + AUP + + + Aruba Florin + AWG + + + Aserbeidschan Manat + AZM + + + Bosnien und Herzegowina Dinar + BAD + + + Konvertierbare Mark + BAM + + + Bosnien und Herzegowina Neuer Dinar + BAN + + + Barbados-Dollar + BBD + + + Taka + BDT + + + Belgischer Franc (konvertibel) + BEC + + + Belgischer Franc + BEF + + + Lew + BGL + + + Lew + BGN + + + Lew (1879-1952) + BGO + + + Bahrain-Dinar + BHD + + + Burundi-Franc + BIF + + + Bermuda-Dollar + BMD + + + Bermuda-Pfund + BMP + + + Brunei-Dollar + BND + + + Boliviano + BOB + + + Boliviano (1863-1962) + BOL + + + Bolivianischer Peso + BOP + + + Mvdol + BOV + + + Brasilianischer Cruzeiro Novo (1967-1986) + BRB + + + Brasilianischer Cruzado + BRC + + + Brasilianischer Cruzeiro (1990-1993) + BRE + + + Real + BRL + + + Brasilianischer Cruzado Novo + BRN + + + Brasilianischer Cruzeiro + BRR + + + Brasilianischer Cruzeiro (1942-1967) + BRZ + + + Bahama-Dollar + BSD + + + Bahama-Pfund + BSP + + + Ngultrum + BTN + + + Bhutan Rupie + BTR + + + Birmanischer Kyat + BUK + + + Birmanische Rupie + BUR + + + Pula + BWP + + + Belarus Rubel (alt) + BYB + + + Belarus Rubel (1992-1994) + BYL + + + Belarus Rubel (neu) + BYR + + + Belize-Dollar + BZD + + + Kanadischer Dollar + CAD + + + Franc congolais + CDF + + + Republik Kongo Franc + CDG + + + Kongolesische Zaire + CDL + + + Zentralafrikanische Republik CFA Franc + CFF + + + Schweizer Franken + SFr. + + + Chilenischer Condor + CLC + + + Chilenischer Escudo + CLE + + + Unidades de Fomento + CLF + + + Chilenischer Peso + CLP + + + Kamerun CFA Franc + CMF + + + Chinesischer Jen Min Piao Yuan + CNP + + + Renminbi Yuan + CNY + + + Kolumbianischer Papier-Peso + COB + + + Kongo CFA Franc + COF + + + Kolumbianischer Peso + COP + + + Costa Rica Colon + CRC + + + Tschechoslowakische Krone + CSC + + + Kubanischer Peso + CUP + + + Kap Verde Escudo + CVE + + + Zypern Pfund + CYP + + + Tschechische Krone + CZK + + + Deutsche Mark + DM + + + Sperrmark + DES + + + Dschibuti-Franc + DJF + + + Dänische Krone + DKK + + + Dominikanischer Peso + DOP + + + Algerischer Dinar + DZD + + + Algerischer Neuer Franc + DZF + + + Algerischer Franc Germinal + DZG + + + Ecuadorianischer Sucre + ECS + + + Verrechnungseinheit für EC + ECV + + + Estnische Krone + EEK + + + Ägyptisches Pfund + EGP + + + Nakfa + ERN + + + Spanische Pesete + ESP + + + Birr + ETB + + + Äthiopischer Dollar + ETD + + + Euro + + + + Finnische Mark + FIM + + + Finnische Mark (1860-1962) + FIN + + + Fidschi Dollar + FJD + + + Fidschi Pfund + FJP + + + Falkland Pfund + FKP + + + Färöer Inseln Krone + FOK + + + Französischer Franc + FF + + + Französischer Franc Germinal/Franc Poincare + FRG + + + Pfund Sterling + £ + + + Georgischer Kupon Larit + GEK + + + Georgischer Lari + GEL + + + Cedi + GHC + + + Ghana Cedi (alt) + GHO + + + Ghana Pfund + GHP + + + Gibraltar Pfund + GIP + + + Grönland Krone + GLK + + + Dalasi + GMD + + + Gambia Pfund + GMP + + + Guinea Franc + GNF + + + Äquatorialguinea Ekwele Guineana + GQE + + + Äquatorialguinea Franco + GQF + + + Äquatorialguinea Peseta Guineana + GQP + + + Griechische Drachme + GRD + + + Neue Griechische Drachme + GRN + + + Quetzal + GTQ + + + Französisch Guayana Franc Guiana + GUF + + + Portugiesisch Guinea Escudo + GWE + + + Portugiesisch Guinea Mil Reis + GWM + + + Guinea Bissau Peso + GWP + + + Guyana Dollar + GYD + + + Hongkong Dollar + HKD + + + Lempira + HNL + + + Kroatischer Dinar + HRD + + + Kuna + HRK + + + Gourde + HTG + + + Forint + HUF + + + Nordirisches Pfund + IBP + + + Indonesischer Nica Guilder + IDG + + + Indonesische Java Rupiah + IDJ + + + Indonesische Neue Rupiah + IDN + + + Rupiah + IDR + + + Irisches Pfund + IEP + + + Schekel + ILL + + + Israelisches Pfund + ILP + + + Schekel + ILS + + + Indische Rupie + =0#Rs.|1#Re.|1<Rs. + + + Irak Dinar + IQD + + + Rial + IRR + + + Isländische Krone + ISK + + + Italienische Lire + + + + Jersey Pfund Sterling + JEP + + + Jamaika Dollar + JMD + + + Jamaika Pfund + JMP + + + Jordanischer Dinar + JOD + + + Yen + ¥ + + + Kenia Schilling + KES + + + Som + som + + + Riel (alt) + KHO + + + Riel + KHR + + + Komoren Franc + KMF + + + Nordkoreanischer Won (alt) + KPP + + + Nordkoreanischer Won + KPW + + + Südkoreanischer Hwan + KRH + + + Südkoreanischer Won (alt) + KRO + + + Südkoreanischer Won + KRW + + + Kuwait Dinar + KWD + + + Kaiman-Dollar + KYD + + + Kasachstan Rubel + KZR + + + Tenge + KZT + + + Kip + LAK + + + Libanesisches Pfund + LBP + + + Liechtenstein Franken + LIF + + + Sri Lanka Rupie + LKR + + + Ceylon Rupie + LNR + + + Liberianischer Dollar + LRD + + + Loti + LSL + + + Litauischer Litas + LTL + + + Litauischer Talonas + LTT + + + Luxemburgischer Franc + LUF + + + Lettischer Lats + LVL + + + Lettischer Rubel + LVR + + + Libyscher Dinar + LYD + + + Libysches Pfund + LYP + + + Marokkanischer Dirham + MAD + + + Marokkanischer Franc + MAF + + + Monaco Franc Nouveau + MCF + + + Monaco Franc Germinal + MCG + + + Moldau Leu Cupon + MDC + + + Moldau Leu + MDL + + + Moldau Rubel Cupon + MDR + + + Madagaskar Ariary + MGA + + + Madagaskar Franc + MGF + + + Marshall Inseln Dollar + MHD + + + Denar + MKD + + + Kyat + MMK + + + Tugrik + MNT + + + Pataca + MOP + + + Ouguiya + MRO + + + Maltesische Lira + MTL + + + Maltesisches Pfund + MTP + + + Mauritius Rupie + MUR + + + Malediven Rupie + MVP + + + Rufiyaa + MVR + + + Malawi Kwacha + MWK + + + Malawi Pfund + MWP + + + Mexikanischer Peso + MXN + + + Mexikanischer Silber-Peso (1861-1992) + MXP + + + Mexican Unidad de Inversion (UDI) + MXV + + + Malaysischer Ringgit + MYR + + + Metical + MZM + + + Namibia Dollar + NAD + + + Neukaledonien Franc Germinal + NCF + + + Naira + NGN + + + Nigerianisches Pfund + NGP + + + Neue Hebriden CFP Franc + NHF + + + Cordoba + NIC + + + Gold-Cordoba + NIG + + + Gold-Cordoba + NIO + + + Holländischer Gulden + NLG + + + Norwegische Krone + NOK + + + Nepalesische Rupie + NPR + + + Neuseeland Dollar + NZD + + + Neuseeland Pfund + NZP + + + Rial Omani + OMR + + + Balboa + PAB + + + Dnjestr-Republik Rubel Kupon + PDK + + + Dnjestr-Republik Rubel (neu) + PDN + + + Dnjestr-Republik Rubel + PDR + + + Peruanischer Inti + PEI + + + Neuer Sol + PEN + + + Sol + PES + + + Kina + PGK + + + Philippinischer Peso + PHP + + + Pakistanische Rupie + PKR + + + Zloty + PLN + + + Zloty (1950-1995) + PLZ + + + Palästina Pfund + PSP + + + Portugiesischer Conto + PTC + + + Portugiesischer Escudo + PTE + + + Guarani + PYG + + + Katar Riyal + QAR + + + Leu + ROL + + + Neuer Leu + RON + + + Russischer Rubel (neu) + RUB + + + Russischer Rubel (alt) + RUR + + + Ruanda Franc + RWF + + + Saudi Riyal + SAR + + + Salomonen Dollar + SBD + + + Seychellen Rupie + SCR + + + Sudanesischer Dinar + SDD + + + Sudanesisches Pfund + SDP + + + Schwedische Krone + SEK + + + Singapur Dollar + SGD + + + St. Helena Pfund + SHP + + + Tolar Bons + SIB + + + Tolar + SIT + + + Slowakische Krone + SKK + + + Leone + SLL + + + San Marino Lire + SML + + + Somalia Schilling + SOS + + + Somaliland Schilling + SQS + + + Suriname Gulden + SRG + + + Schottisches Pfund + SSP + + + Dobra + STD + + + Sao Tome und Principe Escudo + STE + + + Sowjetischer Neuer Rubel + SUN + + + Sowjetischer Rubel + SUR + + + El Salvador Colon + SVC + + + Syrisches Pfund + SYP + + + Lilangeni + SZL + + + Turks und Caicos Krone + TCC + + + Tschad CFA Franc + TDF + + + Baht + THB + + + Tadschikistan Rubel + TJR + + + Tadschikistan Somoni + TJS + + + Turkmenistan-Manat + TMM + + + Tunesischer Dinar + TND + + + Paʻanga + TOP + + + Tonga Pfund Sterling + TOS + + + Timor Escudo + TPE + + + Timor Pataca + TPP + + + Türkische Lira + TRL + + + Trinidad und Tobago Dollar + TTD + + + Trinidad und Tobago Dollar (alt) + TTO + + + Tuvalu Dollar + TVD + + + Neuer Taiwan Dollar + TWD + + + Tansania Schilling + TZS + + + Hryvnia + UAH + + + Ukrainischer Karbovanetz + UAK + + + Uganda Schilling (1966-1987) + UGS + + + Uganda Schilling + UGX + + + US Dollar + $ + + + US Dollar (Nächster Tag) + USN + + + US Dollar (Gleicher Tag) + USS + + + Uruguayischer Peso + UYU + + + Usbekistan Sum + UZS + + + Vatikanstadt Lire + VAL + + + Nordvietnam Piastre Dong Viet + VDD + + + Nordvietnam Neuer Dong + VDN + + + Nordvietnam Viet Minh Piastre Dong Viet + VDP + + + Bolivar + VEB + + + Britische Jungferninseln Dollar + VGD + + + Dong + VND + + + Vietnamesischer Neuer Dong + VNN + + + Republik Vietnam Dong + VNR + + + Vietnamesischer Dong + VNS + + + Vatu + VUV + + + West-Samoa Pfund + WSP + + + Tala + WST + + + CFA Franc (Äquatorial) + XAF + + + Gold + XAU + + + Europäische Rechnungseinheit + XBA + + + Europäische Rechnungseinheit (XBC) + XBC + + + Europäische Rechnungseinheit (XBD) + XBD + + + Ostkaribischer Dollar + EC$ + + + Sonderziehungsrechte + XDR + + + Französischer Gold-Franc + XFO + + + Französischer UIC-Franc + XFU + + + Islamischer Dinar + XID + + + Französischer Antillen CFA Franc + XNF + + + CFA Franc (West) + XOF + + + CFP Franc + XPF + + + COMECON Transferabler Rubel + XTR + + + Jemen Dinar + YDD + + + Jemen Imadi Riyal + YEI + + + Jemen Rial + YER + + + Jugoslawischer Dinar (1966-1990) + YUD + + + Jugoslawische Föderation Dinar + YUF + + + Jugoslawischer 1994-Dinar + YUG + + + Neuer Dinar + YUM + + + Jugoslawischer Dinar (konvertibel) + YUN + + + Jugoslawischer Oktober-Dinar + YUO + + + Jugoslawischer Reformierter Dinar + YUR + + + Südafrikanisches Pfund + ZAP + + + Rand + ZAR + + + Kwacha + ZMK + + + Sambisches Pfund + ZMP + + + Neuer Zaire + ZRN + + + Zaire + ZRZ + + + Simbabwe Dollar + ZWD + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/dv_MV.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/dv_MV.xml --- zope3-3.4.0/src/zope/i18n/locales/data/dv_MV.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/dv_MV.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-M-yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/dv.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/dv.xml --- zope3-3.4.0/src/zope/i18n/locales/data/dv.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/dv.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + ދިވެހިބަސް + + + ދިވެހި ރާއްޖެ + + + + [[:Thaa:]‌‍‏‎] + + + + . + , + ، + % + ٠ + # + + + - + E + + + + + + + + #,##,##0.###;-#,##,##0.### + + + + + + + #E0 + + + + + + + #,##,##0% + + + + + + + ¤ #,##,##0.00;-¤ #,##,##0.00 + + + + + + MVR + ރ. + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/el_GR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/el_GR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/el_GR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/el_GR.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00¤;-¤#,##0.00 + + + + + + Δρχ + Δρχ + #,##0.00 ¤;-#,##0.00 ¤ + #,##0.00 ¤;-#,##0.00 ¤ + . + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/el.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/el.xml --- zope3-3.4.0/src/zope/i18n/locales/data/el.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/el.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,541 @@ + + + + + + + + + + + Αραβικά + Βουλγαρικά + Καταλανικά + Τσεχικά + Δανικά + Γερμανικά + Ελληνικά + Αγγλικά + Ισπανικά + Εσθονικά + Φινλανδικά + Γαλλικά + Εβραϊκά + Κροατικά + Ουγγρικά + Ιταλικά + Ιαπωνικά + Κορεατικά + Λιθουανικά + Λετονικά + Σλαβομακεδονικά + Ολλανδικά + Νορβηγικά + Πολωνικά + Πορτογαλικά + Ρουμανικά + Ρωσικά + Σλοβακικά + Σλοβενικά + Αλβανικά + Σερβικά + Σουηδικά + Τουρκικά + Κινεζικά + + + Ανδόρα + Ηνωμένα Αραβικά Εμιράτα + Αφγανιστάν + Αντίγκουα και Μπαρμπούντα + Ανγκουίλα + Αλβανία + Αρμενία + Ολλανδικές Αντίλλες + Ανγκόλα + Ανταρκτική + Αργεντινή + Αμερικανική Σαμόα + Αυστρία + Αυστραλία + Αρούμπα + Αζερμπαϊτζάν + Βοσνία - Ερζεγοβίνη + Μπαρμπάντος + Μπανγκλαντές + Βέλγιο + Μπουρκίνα Φάσο + Βουλγαρία + Μπαχρέιν + Μπουρούντι + Μπένιν + Βερμούδες + Μπρουνέι Νταρουσαλάμ + Βολιβία + Βραζιλία + Μπαχάμες + Μπουτάν + Νήσος Μπουβέ + Μποτσουάνα + Λευκορωσία + Μπελίζ + Καναδάς + Νήσοι Κόκος (Κήλινγκ) + Κονγκό, Λαϊκή Δημοκρατία του + Κεντροαφρικανική Δημοκρατία + Κονγκό + Ελβετία + Ακτή Ελεφαντόδοντος + Νήσοι Κουκ + Χιλή + Καμερούν + Κίνα + Κολομβία + Κόστα Ρίκα + Κούβα + Νήσοι Πράσινου Ακρωτηρίου + Νήσος Χριστουγέννων + Κύπρος + Τσεχία + Γερμανία + Τζιμπουτί + Δανία + Ντομίνικα + Δομινικανή Δημοκρατία + Αλγερία + Ισημερινός + Εσθονία + Αίγυπτος + Δυτική Σαχάρα + Ερυθραία + Ισπανία + Αιθιοπία + Φινλανδία + Φίτζι + Νήσοι Φώκλαντ + Μικρονησία, Ομόσπονδες Πολιτείες της + Νήσοι Φερόες + Γαλλία + Γκαμπόν + Ηνωμένο Βασίλειο + Γρενάδα + Γεωργία + Γαλλική Γουιάνα + Γκάνα + Γιβραλτάρ + Γροιλανδία + Γκάμπια + Γουινέα + Γουαδελούπη + Ισημερινή Γουινέα + Ελλάδα + Νότια Γεωργία και Νήσοι Νότιες Σάντουιτς + Γουατεμάλα + Γκουάμ + Γουινέα-Μπισάου + Γουιάνα + Χονγκ Κονγκ, Ειδική Διοικητική Περιφέρεια της Κίνας + Νήσοι Χερντ και Μακντόναλντ + Ονδούρα + Κροατία + Αϊτή + Ουγγαρία + Ινδονησία + Ιρλανδία + Ισραήλ + Ινδία + Βρετανικά Έδάφη Ινδικού Ωκεανού + Ιράκ + Ιράν, Ισλαμική Δημοκρατία του + Ισλανδία + Ιταλία + Τζαμάικα + Ιορδανία + Ιαπωνία + Κένυα + Κιργιζία + Καμπότζη + Κιριμπάτι + Κομόρες + Σαιντ Κιτς και Νέβις + Κορέα, Βόρεια + Κορέα, Νότια + Κουβέιτ + Νήσοι Κέιμαν + Καζακστάν + Λατινική Αμερική + Λίβανος + Αγία Λουκία + Λιχτενστάιν + Σρι Λάνκα + Λιβερία + Λεσότο + Λιθουανία + Λουξεμβούργο + Λετονία + Μαρόκο + Μονακό + Μολδαβία, Δημοκρατία της + Μαδαγασκάρη + Νήσοι Μάρσαλ + ΠΓΔ Μακεδονίας + Μάλι + Μιανμάρ + Μογγολία + Μακάο, Ειδική Διοικητική Περιφέρεια της Κίνας + Νήσοι Βόρειες Μαριάνες + Μαρτινίκα + Μαυριτανία + Μονσεράτ + Μάλτα + Μαυρίκιος + Μαλδίβες + Μαλάουι + Μεξικό + Μαλαισία + Μοζαμβίκη + Ναμίμπια + Νέα Καληδονία + Νίγηρ + Νήσος Νόρφολκ + Νιγηρία + Νικαράγουα + Ολλανδία + Νορβηγία + Νεπάλ + Ναούρου + Νιούε + Νέα Ζηλανδία + Ομάν + Παναμάς + Περού + Γαλλική Πολυνησία + Παπούα - Νέα Γουινέα + Φιλιππίνες + Πακιστάν + Πολωνία + Σαιντ Πιέρ και Μικελόν + Πίτκερν + Πουέρτο Ρίκο + Παλαιστινιακά Εδάφη + Πορτογαλία + Παλάου + Παραγουάη + Κατάρ + Ρεϋνιόν + Ρουμανία + Ρωσία + Ρουάντα + Σαουδική Αραβία + Νήσοι Σολομώντος + Σεϋχέλλες + Σουδάν + Σουηδία + Σιγκαπούρη + Αγία Ελένη + Σλοβενία + Νήσοι Σβάλμπαρ και Γιαν Μαγιέν + Σλοβακία + Σιέρα Λεόνε + Άγιος Μαρίνος + Σενεγάλη + Σομαλία + Σερβία + Σουρινάμ + Σάο Τομέ και Πρίνσιπε + Ελ Σαλβαδόρ + Συρία, Αραβική Δημοκρατία της + Σουαζιλάνδη + Νήσοι Τερκς και Κάικος + Τσαντ + Γαλλικά Νότια Εδάφη + Τόγκο + Ταϊλάνδη + Τατζικιστάν + Τοκελάου + Ανατολικό Τιμόρ + Τουρκμενιστάν + Τυνησία + Τόνγκα + Τουρκία + Τρινιδάδ και Τομπάγκο + Τουβαλού + Ταϊβάν (Δ.Κ.) + Τανζανία + Ουκρανία + Ουγκάντα + Απομακρυσμένες Νησίδες των Ηνωμένων Πολιτειών + Ηνωμένες Πολιτείες + Ουρουγουάη + Ουζμπεκιστάν + Αγία Έδρα (Βατικανό) + Άγιος Βικέντιος και Γρεναδίνες + Βενεζουέλα + Βρετανικές Παρθένοι Νήσοι + Αμερικανικές Παρθένοι Νήσοι + Βιετνάμ + Βανουάτου + Νήσοι Ουαλλίς και Φουτουνά + Σαμόα + Υεμένη + Μαγιότ + Γιουγκοσλαβία + Νότια Αφρική + Ζάμπια + Ζιμπάμπουε + + + + [ά-ώ] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + Ιαν + Φεβ + Μαρ + Απρ + Μαϊ + Ιουν + Ιουλ + Αυγ + Σεπ + Οκτ + Νοε + Δεκ + + + Ι + Φ + Μ + Α + Μ + Ι + Ι + Α + Σ + Ο + Ν + Δ + + + Ιανουαρίου + Φεβρουαρίου + Μαρτίου + Απριλίου + Μαΐου + Ιουνίου + Ιουλίου + Αυγούστου + Σεπτεμβρίου + Οκτωβρίου + Νοεμβρίου + Δεκεμβρίου + + + + + Ιαν + Φεβ + Μαρ + Απρ + Μαϊ + Ιουν + Ιουλ + Αυγ + Σεπ + Οκτ + Νοε + Δεκ + + + Ι + Φ + Μ + Α + Μ + Ι + Ι + Α + Σ + Ο + Ν + Δ + + + Ιανουάριος + Φεβρουάριος + Μάρτιος + Απρίλιος + Μάιος + Ιούνιος + Ιούλιος + Αύγουστος + Σεπτέμβριος + Οκτώβριος + Νοέμβριος + Δεκέμβριος + + + + + + + Κυρ + Δευ + Τρι + Τετ + Πεμ + Παρ + Σαβ + + + Κυριακή + Δευτέρα + Τρίτη + Τετάρτη + Πέμπτη + Παρασκευή + Σάββατο + + + + + + + + ΠΜ + ΜΜ + + + π.Χ. + μ.Χ. + + + + + + + EEEE, dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd MMM yyyy + + + + + dd/MM/yyyy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + ΔΟΛΑΡΙΟ ΑΥΣΤΡΑΛΙΑΣ + AUD + + + ΔΟΛΑΡΙΟ ΚΑΝΑΔΑ + CAD + + + ΦΡΑΓΚΟ ΕΛΒΕΤΙΑΣ + CHF + + + ΛΙΡΑ ΚΥΠΡΟΥ + CYP + + + ΚΟΡΟΝΑ ΔΑΝΙΑΣ + DKK + + + ΕΥΡΩ + + + + ΛΙΡΑ ΑΓΓΛΙΑΣ + £ + + + Δρχ + Δρχ + + + ΓΙΕΝ ΙΑΠΩΝΙΑΣ + ¥ + + + ΚΟΡΟΝΑ ΝΟΡΒΗΓΙΑΣ + NOK + + + ΚΟΡΟΝΑ ΣΟΥΗΔΙΑΣ + SEK + + + ΔΟΛΑΡΙΟ ΗΠΑ + USD + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_AS.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_AS.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_AS.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_AS.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_AU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_AU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_AU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_AU.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + EEEE, d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd/MM/yyyy + + + + + d/MM/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + Australian Dollar + $ + + + US Dollar + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_BE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_BE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_BE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_BE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + EEE d MMM yyyy + + + + + dd MMM yyyy + + + + + dd/MM/yy + + + + + + + + HH' h 'mm' min 'ss' s 'z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + + Belgian Franc + BF + #,##0.00 ¤;-#,##0.00 ¤ + #,##0.00 ¤;-#,##0.00 ¤ + . + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_BW.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_BW.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_BW.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_BW.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + MMM dd,yy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_BZ.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_BZ.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_BZ.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_BZ.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd-MMM-yy + + + + + dd/MM/yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_CA.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_CA.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_CA.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_CA.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + EEEE, MMMM d, yyyy + + + + + MMMM d, yyyy + + + + + d-MMM-yy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + + Canadian Dollar + $ + + + US Dollar + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_GB.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_GB.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_GB.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_GB.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + EEEE, d MMMM yyyy + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + dd/MM/yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Greenwich Mean Time + British Summer Time + + + GMT + BST + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_GU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_GU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_GU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_GU.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_HK.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_HK.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_HK.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_HK.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + EEEE, d MMMM yyyy + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + dd/MM/yyyy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + + Hong Kong Dollar + $ + + + USD + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_IE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_IE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_IE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_IE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,120 @@ + + + + + + + + + + + + + a.m. + p.m. + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + dd/MM/yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Greenwich Mean Time + Irish Summer Time + + + GMT + IST + + Dublin + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + Irish Pound + £ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_IN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MMM-yy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_JM.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_JM.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_JM.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_JM.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_MH.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_MH.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_MH.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_MH.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_MP.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_MP.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_MP.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_MP.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_MT.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_MT.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_MT.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_MT.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + EEEE, d MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd MMM yyyy + + + + + dd/MM/yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + British Pound Sterling + GBP + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_NZ.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_NZ.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_NZ.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_NZ.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + EEEE, d MMMM yyyy + + + + + d MMMM yyyy + + + + + d/MM/yyyy + + + + + d/MM/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + New Zealand Dollar + $ + + + US Dollar + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_PH.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_PH.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_PH.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_PH.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + EEEE, MMMM d, yyyy + + + + + MMMM d, yyyy + + + + + MM d, yy + + + + + M/d/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + + Peso + PHP + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_SG.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_SG.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_SG.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_SG.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + dd MMMM yyyy + + + + + dd MMM yyyy + + + + + dd-MMM-yy + + + + + dd/MM/yy + + + + + + + + a hh:mm:ss + + + + + a hh:mm:ss + + + + + a hh:mm + + + + + a hh:mm + + + + + + + {1} {0} + + + + + + + + + Singapore Standard Time + Singapore Standard Time + + + SST + SST + + Singapore + + + + + + + Singapore Dollar + $ + + + USD + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_TT.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_TT.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_TT.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_TT.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_UM.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_UM.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_UM.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_UM.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_US_POSIX.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_US_POSIX.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_US_POSIX.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_US_POSIX.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,55 @@ + + + + + + + + + + + + + . + , + ; + % + 0 + # + + + - + E + 0/00 + INF + NaN + + + + + ###0.###;-###0.### + + + + + + + 0.000000E+000 + + + + + + + ###0% + + + + + + + ¤ ###0.00;-¤ ###0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_US.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_US.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_US.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_US.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,53 @@ + + + + + + + + + + + + + 279 + 216 + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + + US Dollar + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_VI.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_VI.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_VI.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_VI.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2800 @@ + + + + + + + + + + + Afar + Abkhazian + Achinese + Acoli + Adangme + Adyghe + Avestan + Afrikaans + Afro-Asiatic (Other) + Afrihili + Akan + Akkadian + Aleut + Algonquian Languages + Amharic + Aragonese + English, Old (ca.450-1100) + Apache Languages + Arabic + Aramaic + Araucanian + Arapaho + Artificial (Other) + Arawak + Assamese + Asturian + Athapascan Languages + Australian Languages + Avaric + Awadhi + Aymara + Azerbaijani + Bashkir + Banda + Bamileke Languages + Baluchi + Bambara + Balinese + Basa + Baltic (Other) + Belarusian + Beja + Bemba + Berber + Bulgarian + Bihari + Bhojpuri + Bislama + Bikol + Bini + Siksika + Bambara + Bengali + Bantu + Tibetan + Breton + Braj + Bosnian + Batak + Buriat + Buginese + Blin + Catalan + Caddo + Central American Indian (Other) + Carib + Caucasian (Other) + Chechen + Cebuano + Celtic (Other) + Chamorro + Chibcha + Chagatai + Chuukese + Mari + Chinook Jargon + Choctaw + Chipewyan + Cherokee + Cheyenne + Chamic Languages + Corsican + Coptic + Creoles and Pidgins, English-based (Other) + Creoles and Pidgins, French-based (Other) + Creoles and pidgins, Portuguese-based (Other) + Cree + Crimean Turkish; Crimean Tatar + Creoles and Pidgins (Other) + Czech + Kashubian + Church Slavic + Cushitic (Other) + Chuvash + Welsh + Danish + Dakota + Dargwa + Dayak + German + Delaware + Slave + Dogrib + Dinka + Dogri + Dravidian (Other) + Lower Sorbian + Duala + Dutch, Middle (ca. 1050-1350) + Divehi + Dyula + Dzongkha + Ewe + Efik + Egyptian (Ancient) + Ekajuk + Greek + Elamite + English + English, Middle (1100-1500) + Esperanto + Spanish + Estonian + Basque + Ewondo + Persian + Fang + Fanti + Fulah + Finnish + Finno - Ugrian (Other) + Fijian + Faroese + Fon + French + French, Middle (ca.1400-1600) + French, Old (842-ca.1400) + Friulian + Frisian + Irish + Ga + Gayo + Gbaya + Scottish Gaelic + Germanic (Other) + Geez + Gilbertese + Gallegan + German, Middle High (ca.1050-1500) + Guarani + German, Old High (ca.750-1050) + Gondi + Gorontalo + Gothic + Gerbo + Greek, Ancient (to 1453) + Gujarati + Manx + Gwichʻin + Hausa + Haida + Hawaiian + Hebrew + Hindi + Hiligaynon + Himachali + Hittite + Hmong + Hiri Motu + Croatian + Upper Sorbian + Haitian + Hungarian + Hupa + Armenian + Herero + Interlingua + Iban + Indonesian + Interlingue + Igbo + Sichuan Yi + Ijo + Inupiaq + Iloko + Indic (Other) + Indo-European (Other) + Ingush + Ido + Iranian + Iroquoian languages + Icelandic + Italian + Inuktitut + Japanese + Lojban + Judeo-Persian + Judeo-Arabic + Javanese + Georgian + Kara-Kalpak + Kabyle + Kachin + Kamba + Karen + Kawi + Kabardian + Kongo + Khasi + Khoisan (Other) + Khotanese + Kikuyu + Kuanyama + Kazakh + Kalaallisut + Khmer + Kimbundu + Kannada + Korean + Konkani + Kosraean + Kpelle + Kanuri + Karachay-Balkar + Kru + Kurukh + Kashmiri + Kurdish + Kumyk + Kutenai + Komi + Cornish + Kirghiz + Latin + Ladino + Lahnda + Lamba + Luxembourgish + Lezghian + Ganda + Limburgish + Lingala + Lao + Mongo + Lozi + Lithuanian + Luba-Katanga + Luba-Lulua + Luiseno + Lunda + Luo + Lushai + Latvian + Madurese + Magahi + Maithili + Makasar + Mandingo + Austronesian + Masai + Moksha + Mandar + Mende + Malagasy + Irish, Middle (900-1200) + Marshallese + Maori + Micmac + Minangkabau + Miscellaneous Languages + Macedonian + Mon-Khmer (Other) + Malayalam + Mongolian + Manchu + Manipuri + Manobo Languages + Moldavian + Mohawk + Mossi + Marathi + Malay + Maltese + Multiple Languages + Munda Languages + Creek + Marwari + Burmese + Mayan + Erzya + Nauru + Nahuatl + North American Indian (Other) + Neapolitan + Norwegian Bokmål + Ndebele, North + Low German; Low Saxon + Nepali + Newari + Ndonga + Nias + Niger - Kordofanian (Other) + Niuean + Dutch + Norwegian Nynorsk + Norwegian + Nogai + Norse, Old + Ndebele, South + Sotho, Northern + Nubian Languages + Navajo + Nyanja; Chichewa; Chewa + Nyamwezi + Nyankole + Nyoro + Nzima + Occitan (post 1500); Provençal + Ojibwa + Oromo + Oriya + Ossetic + Osage + Turkish, Ottoman (1500-1928) + Otomian Languages + Punjabi + Papuan (Other) + Pangasinan + Pahlavi + Pampanga + Papiamento + Palauan + Persian Old (ca.600-400 B.C.) + Philippine (Other) + Phoenician + Pali + Polish + Pohnpeian + Prakrit Languages + Provençal, Old (to 1500) + Pashto (Pushto) + Portuguese + Quechua + Rajasthani + Rapanui + Rarotongan + Rhaeto-Romance + Rundi + Romanian + Romance (Other) + Romany + Root + Russian + Kinyarwanda + Sanskrit + Sandawe + Yakut + South American Indian (Other) + Salishan languages + Samaritan Aramaic + Sasak + Santali + Sardinian + Scots + Sindhi + Northern Sami + Selkup + Semitic (Other) + Sango + Irish, Old (to 900) + Sign Languages + Serbo-Croatian + Shan + Sinhalese + Sidamo + Siouan Languages + Sino-Tibetan (Other) + Slovak + Slovenian + Slavic (Other) + Samoan + Southern Sami + Sami languages (Other) + Lule Sami + Inari Sami + Skolt Sami + Shona + Soninke + Somali + Sogdien + Songhai + Albanian + Serbian + Serer + Swati + Nilo-Saharam (Other) + Sotho, Southern + Sundanese + Sukuma + Susu + Sumerian + Swedish + Swahili + Syriac + Tamil + Tai (Other) + Telugu + Timne + Tereno + Tetum + Tajik + Thai + Tigrinya + Tigre + Tiv + Turkmen + Tokelau + Tagalog + Tlingit + Tamashek + Tswana + Tonga (Tonga Islands) + Tonga (Nyasa) + Tok Pisin + Turkish + Tsonga + Tsimshian + Tatar + Tumbuka + Tupi languages + Altaic (Other) + Tuvalu + Twi + Tahitian + Tuvinian + Udmurt + Uighur + Ugaritic + Ukrainian + Umbundu + Undetermined + Urdu + Uzbek + Vai + Venda + Vietnamese + Volapük + Votic + Walloon + Wakashan Languages + Walamo + Waray + Washo + Sorbian Languages + Wolof + Kalmyk + Xhosa + Yao + Yapese + Yiddish + Yoruba + Yupik Languages + Zhuang + Zapotec + Zenaga + Chinese + Zande + Zulu + Zuni + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + United Arab Emirates + Afghanistan + Antigua and Barbuda + Anguilla + Albania + Armenia + Netherlands Antilles + Angola + Antarctica + Argentina + American Samoa + Austria + Australia + Aruba + Azerbaijan + Bosnia and Herzegovina + Barbados + Bangladesh + Belgium + Burkina Faso + Bulgaria + Bahrain + Burundi + Benin + Bermuda + Brunei + Bolivia + Brazil + Bahamas + Bhutan + Bouvet Island + Botswana + Belarus + Belize + Canada + Cocos (Keeling) Islands + Democratic Republic of the Congo + Central African Republic + Congo + Switzerland + Côte d’Ivoire + Cook Islands + Chile + Cameroon + China + Colombia + Costa Rica + Cuba + Cape Verde + Christmas Island + Cyprus + Czech Republic + Germany + Djibouti + Denmark + Dominica + Dominican Republic + Algeria + Ecuador + Estonia + Egypt + Western Sahara + Eritrea + Spain + Ethiopia + Finland + Fiji + Falkland Islands + Micronesia + Faroe Islands + France + Gabon + United Kingdom + Grenada + Georgia + French Guiana + Ghana + Gibraltar + Greenland + Gambia + Guinea + Guadeloupe + Equatorial Guinea + Greece + South Georgia and the South Sandwich Islands + Guatemala + Guam + Guinea-Bissau + Guyana + Hong Kong S.A.R., China + Heard Island and McDonald Islands + Honduras + Croatia + Haiti + Hungary + Indonesia + Ireland + Israel + India + British Indian Ocean Territory + Iraq + Iran + Iceland + Italy + Jamaica + Jordan + Japan + Kenya + Kyrgyzstan + Cambodia + Kiribati + Comoros + Saint Kitts and Nevis + North Korea + South Korea + Kuwait + Cayman Islands + Kazakhstan + Laos + Lebanon + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Lithuania + Luxembourg + Latvia + Libya + Morocco + Monaco + Moldova + Madagascar + Marshall Islands + Macedonia + Mali + Myanmar + Mongolia + Macao S.A.R., China + Northern Mariana Islands + Martinique + Mauritania + Montserrat + Malta + Mauritius + Maldives + Malawi + Mexico + Malaysia + Mozambique + Namibia + New Caledonia + Niger + Norfolk Island + Nigeria + Nicaragua + Netherlands + Norway + Nepal + Nauru + Niue + New Zealand + Oman + Panama + Peru + French Polynesia + Papua New Guinea + Philippines + Pakistan + Poland + Saint Pierre and Miquelon + Pitcairn + Puerto Rico + Palestinian Territory + Portugal + Palau + Paraguay + Qatar + Réunion + Romania + Russia + Rwanda + Saudi Arabia + Solomon Islands + Seychelles + Sudan + Sweden + Singapore + Saint Helena + Slovenia + Svalbard and Jan Mayen + Slovakia + Sierra Leone + San Marino + Senegal + Somalia + Serbia + Suriname + Sao Tome and Principe + El Salvador + Syria + Swaziland + Turks and Caicos Islands + Chad + French Southern Territories + Togo + Thailand + Tajikistan + Tokelau + Timor-Leste + Turkmenistan + Tunisia + Tonga + Turkey + Trinidad and Tobago + Tuvalu + Taiwan + Tanzania + Ukraine + Uganda + United States Minor Outlying Islands + United States + Uruguay + Uzbekistan + Vatican + Saint Vincent and the Grenadines + Venezuela + British Virgin Islands + U.S. Virgin Islands + Vietnam + Vanuatu + Wallis and Futuna + Samoa + Yemen + Mayotte + Yugoslavia + South Africa + Zambia + Zimbabwe + + + Posix + Revised Orthography + + + Calendar + Collation + Currency + + + Buddhist Calendar + Chinese Calendar + Gregorian Calendar + Hebrew Calendar + Islamic Calendar + Islamic-Civil Calendar + Japanese Calendar + Direct Order + Phonebook Order + Pinyin Order + Stroke Order + Traditional + + + + [a-z] + + + + + + + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + + January + February + March + April + May + June + July + August + September + October + November + December + + + + + + + Sun + Mon + Tue + Wed + Thu + Fri + Sat + + + Sunday + Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + + + + + + BC + AD + + + + + + + EEEE, MMMM d, yyyy + + + + + MMMM d, yyyy + + + + + MMM d, yyyy + + + + + M/d/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + Pacific Standard Time + Pacific Daylight Time + + + PST + PDT + + Los Angeles + + + + Pacific Standard Time + Pacific Daylight Time + + + PST + PDT + + Los Angeles + + + + Mountain Standard Time + Mountain Daylight Time + + + MST + MDT + + Denver + + + + Mountain Standard Time + Mountain Daylight Time + + + MST + MDT + + Denver + + + + Mountain Standard Time + Mountain Standard Time + + + MST + MST + + Phoenix + + + + Mountain Standard Time + Mountain Standard Time + + + MST + MST + + Phoenix + + + + Central Standard Time + Central Daylight Time + + + CST + CDT + + Chicago + + + + Central Standard Time + Central Daylight Time + + + CST + CDT + + Chicago + + + + Eastern Standard Time + Eastern Daylight Time + + + EST + EDT + + New York + + + + Eastern Standard Time + Eastern Daylight Time + + + EST + EDT + + New York + + + + Eastern Standard Time + Eastern Standard Time + + + EST + EST + + Indianapolis + + + + Eastern Standard Time + Eastern Standard Time + + + EST + EST + + Indianapolis + + + + Hawaii Standard Time + Hawaii Standard Time + + + HST + HST + + Honolulu + + + + Hawaii Standard Time + Hawaii Standard Time + + + HST + HST + + Honolulu + + + + Alaska Standard Time + Alaska Daylight Time + + + AST + ADT + + Anchorage + + + + Alaska Standard Time + Alaska Daylight Time + + + AST + ADT + + Anchorage + + + + Atlantic Standard Time + Atlantic Daylight Time + + + AST + ADT + + Halifax + + + + Newfoundland Standard Time + Newfoundland Daylight Time + + + CNT + CDT + + St. Johns + + + + Newfoundland Standard Time + Newfoundland Daylight Time + + + CNT + CDT + + St. Johns + + + + Central European Standard Time + Central European Daylight Time + + + CET + CEST + + Paris + + + + Central European Standard Time + Central European Daylight Time + + + CET + CEST + + Paris + + + + Greenwich Mean Time + Greenwich Mean Time + + + GMT + GMT + + London + + + + Greenwich Mean Time + Greenwich Mean Time + + + GMT + GMT + + Casablanca + + + + Israel Standard Time + Israel Daylight Time + + + IST + IDT + + Jerusalem + + + + Japan Standard Time + Japan Standard Time + + + JST + JST + + Tokyo + + + + Japan Standard Time + Japan Standard Time + + + JST + JST + + Tokyo + + + + Eastern European Standard Time + Eastern European Daylight Time + + + EET + EEST + + Bucharest + + + + China Standard Time + China Standard Time + + + CTT + CDT + + Shanghai + + + + China Standard Time + China Standard Time + + + CTT + CDT + + Shanghai + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + Andorran Diner + ADD + + + Andorran Peseta + ADP + + + United Arab Emirates Dirham + AED + + + Afghani (1927-2002) + AFA + + + Afghani + Af + + + Affars and Issas Franc + AIF + + + Albanian Lek (1946-1961) + ALK + + + Albanian Lek + lek + + + Albanian Lek Valute + ALV + + + Albanian Dollar Foreign Exchange Certificates + ALX + + + Armenian Dram + dram + + + Netherlands Antillan Guilder + NA f. + + + Angolan Kwanza + AOA + + + Angolan Kwanza (1977-1990) + AOK + + + Angolan New Kwanza (1990-2000) + AON + + + Angolan Kwanza Reajustado (1995-1999) + AOR + + + Angolan Escudo + AOS + + + Argentine Austral + ARA + + + Argentine Peso Moneda Nacional + ARM + + + Argentine Peso (1983-1985) + ARP + + + Argentine Peso + Arg$ + + + Austrian Schilling + ATS + + + Australian Dollar + $A + + + Australian Pound + AUP + + + Aruban Guilder + AWG + + + Azerbaijanian Manat + AZM + + + Bosnia-Herzegovina Dinar + BAD + + + Bosnia-Herzegovina Convertible Mark + KM + + + Bosnia-Herzegovina New Dinar + BAN + + + Barbados Dollar + BDS$ + + + Bangladesh Taka + Tk + + + Belgian Franc (convertible) + BEC + + + Belgian Franc + BF + + + Belgian Franc (financial) + BEL + + + Bulgarian Hard Lev + lev + + + Bulgarian Socialist Lev + BGM + + + Bulgarian New Lev + BGN + + + Bulgarian Lev (1879-1952) + BGO + + + Bulgarian Lev Foreign Exchange Certificates + BGX + + + Bahraini Dinar + BD + + + Burundi Franc + Fbu + + + Bermudan Dollar + Ber$ + + + Bermudan Pound + BMP + + + Brunei Dollar + BND + + + Boliviano + Bs + + + Boliviano (1863-1962) + BOL + + + Bolivian Peso + BOP + + + Bolivian Mvdol + BOV + + + Brazilian Cruzeiro Novo (1967-1986) + BRB + + + Brazilian Cruzado + BRC + + + Brazilian Cruzeiro (1990-1993) + BRE + + + Brazilian Real + R$ + + + Brazilian Cruzado Novo + BRN + + + Brazilian Cruzeiro + BRR + + + Brazilian Cruzeiro (1942-1967) + BRZ + + + Bahamian Dollar + BSD + + + Bahamian Pound + BSP + + + Bhutan Ngultrum + Nu + + + Bhutan Rupee + BTR + + + Burmese Kyat + BUK + + + Burmese Rupee + BUR + + + Botswanan Pula + BWP + + + Belarussian New Ruble (1994-1999) + BYB + + + Belarussian Ruble (1992-1994) + BYL + + + Belarussian Ruble + Rbl + + + Belize Dollar + BZ$ + + + British Honduras Dollar + BZH + + + Canadian Dollar + Can$ + + + Congolese Franc Congolais + CDF + + + Congolese Republic Franc + CDG + + + Congolese Zaire + CDL + + + Central African Republic CFA Franc + CFF + + + Swiss Franc + SwF + + + Cook Islands Dollar + CKD + + + Chilean Condor + CLC + + + Chilean Escudo + CLE + + + Chilean Unidades de Fomento + CLF + + + Chilean Peso + Ch$ + + + Cameroon CFA Franc + CMF + + + Chinese Jen Min Piao Yuan + CNP + + + Chinese US Dollar Foreign Exchange Certificates + CNX + + + Chinese Yuan Renminbi + Y + + + Colombian Paper Peso + COB + + + Congo CFA Franc + COF + + + Colombian Peso + Col$ + + + Costa Rican Colon + C + + + Czechoslovak Koruna + CSC + + + Czechoslovak Hard Koruna + CSK + + + Cuban Peso + CUP + + + Cuban Foreign Exchange Certificates + CUX + + + Cape Verde Escudo + CVEsc + + + Curacao Guilder + CWG + + + Cyprus Pound + £C + + + Czech Republic Koruna + CZK + + + East German Ostmark + DDM + + + Deutsche Mark + DEM + + + German Sperrmark + DES + + + Djibouti Franc + DF + + + Danish Krone + DKr + + + Dominican Peso + RD$ + + + Algerian Dinar + DA + + + Algerian New Franc + DZF + + + Algerian Franc Germinal + DZG + + + Ecuador Sucre + ECS + + + Ecuador Unidad de Valor Constante (UVC) + ECV + + + Estonian Kroon + EEK + + + Egyptian Pound + EGP + + + Eritrean Nakfa + ERN + + + Spanish Peseta + + + + Ethiopian Birr + Br + + + Ethiopian Dollar + ETD + + + Euro + + + + Finnish Markka + FIM + + + Finnish Markka (1860-1962) + FIN + + + Fiji Dollar + F$ + + + Fiji Pound + FJP + + + Falkland Islands Pound + FKP + + + Faeroe Islands Kronur + FOK + + + French Franc + FRF + + + French Franc Germinal/Franc Poincare + FRG + + + Gabon CFA Franc + GAF + + + British Pound Sterling + £ + + + Georgian Kupon Larit + GEK + + + Georgian Lari + lari + + + Ghana Cedi + GHC + + + Ghana Old Cedi + GHO + + + Ghana Pound + GHP + + + Ghana Revalued Cedi + GHR + + + Gibraltar Pound + GIP + + + Greenland Krone + GLK + + + Gambia Dalasi + GMD + + + Gambia Pound + GMP + + + Guinea Franc + GF + + + Guinea Franc (1960-1972) + GNI + + + Guinea Syli + GNS + + + Guadeloupe Franc + GPF + + + Equatorial Guinea Ekwele Guineana + GQE + + + Equatorial Guinea Franco + GQF + + + Equatorial Guinea Peseta Guineana + GQP + + + Greek Drachma + GRD + + + Greek New Drachma + GRN + + + Guatemala Quetzal + Q + + + French Guyana Franc Guiana + GUF + + + Portuguese Guinea Escudo + GWE + + + Portuguese Guinea Mil Reis + GWM + + + Guinea-Bissau Peso + GWP + + + Guyana Dollar + G$ + + + Hong Kong Dollar + HK$ + + + Hoduras Lempira + L + + + Croatian Dinar + HRD + + + Croatian Kuna + HRK + + + Haitian Gourde + HTG + + + Hungarian Forint + Ft + + + Northern Irish Pound + IBP + + + Indonesian Nica Guilder + IDG + + + Indonesian Java Rupiah + IDJ + + + Indonesian New Rupiah + IDN + + + Indonesian Rupiah + Rp + + + Irish Pound + IR£ + + + Israeli Sheqel + ILL + + + Israeli Pound + ILP + + + Israeli New Sheqel + ILS + + + Isle of Man Pound Sterling + IMP + + + Indian Rupee + =0#Rs.|1#Re.|1<Rs. + + + Iraqi Dinar + ID + + + Iranian Rial + RI + + + Icelandic Krona + ISK + + + Italian Lira + + + + Jersey Pound Sterling + JEP + + + Jamaican Dollar + J$ + + + Jamaican Pound + JMP + + + Jordanian Dinar + JD + + + Japanese Yen + ¥ + + + Kenyan Shilling + K Sh + + + Kyrgystan Som + som + + + Cambodian Old Riel + KHO + + + Cambodian Riel + CR + + + Kiribati Dollar + KID + + + Comoro Franc + CF + + + North Korean People’s Won + KPP + + + North Korean Won + KPW + + + South Korean Hwan + KRH + + + South Korean Old Won + KRO + + + South Korean Won + KRW + + + Kuwaiti Dinar + KD + + + Cayman Islands Dollar + KYD + + + Kazakhstan Ruble + KZR + + + Kazakhstan Tenge + T + + + Laotian Kip + LAK + + + Lebanese Pound + LL + + + Liechtenstein Franc + LIF + + + Sri Lanka Rupee + SL Re + + + Ceylon Rupee + LNR + + + Liberian Dollar + LRD + + + Lesotho Loti + M + + + Lithuanian Lita + LTL + + + Lithuanian Talonas + LTT + + + Luxembourg Franc + LUF + + + Latvian Lats + LVL + + + Latvian Ruble + LVR + + + Libyan British Military Authority Lira + LYB + + + Libyan Dinar + LD + + + Libyan Pound + LYP + + + Moroccan Dirham + MAD + + + Moroccan Franc + MAF + + + Monaco Franc Nouveau + MCF + + + Monaco Franc Germinal + MCG + + + Moldovan Leu Cupon + MDC + + + Moldovan Leu + MDL + + + Moldovan Ruble Cupon + MDR + + + Madagascar Ariary + MGA + + + Madagascar Franc + MGF + + + Marshall Islands Dollar + MHD + + + Macedonian Denar + MDen + + + Macedonian Denar (1992-1993) + MKN + + + Mali Franc + MLF + + + Myanmar Kyat + MMK + + + Myanmar Dollar Foreign Exchange Certificates + MMX + + + Mongolian Tugrik + Tug + + + Macao Pataca + MOP + + + Martinique Franc + MQF + + + Mauritania Ouguiya + UM + + + Maltese Lira + Lm + + + Maltese Pound + MTP + + + Mauritius Rupee + MUR + + + Maldive Islands Rupee + MVP + + + Maldive Islands Rufiyaa + MVR + + + Malawi Kwacha + MK + + + Malawi Pound + MWP + + + Mexican Peso + MEX$ + + + Mexican Silver Peso (1861-1992) + MXP + + + Mexican Unidad de Inversion (UDI) + MXV + + + Malaysian Ringgit + RM + + + Mozambique Escudo + MZE + + + Mozambique Metical + Mt + + + Namibia Dollar + N$ + + + New Caledonia Franc Germinal + NCF + + + Nigerian Naira + NGN + + + Nigerian Pound + NGP + + + New Hebrides CFP Franc + NHF + + + Nicaraguan Cordoba + NIC + + + Nicaraguan Gold Cordoba + NIG + + + Nicaraguan Cordoba Oro + NIO + + + Netherlands Guilder + NLG + + + Norwegian Krone + NKr + + + Nepalese Rupee + Nrs + + + New Zealand Dollar + $NZ + + + New Zealand Pound + NZP + + + Oman Rial + RO + + + Oman Rial Saidi + OMS + + + Panamanian Balboa + PAB + + + Transdniestria Ruble Kupon + PDK + + + Transdniestria New Ruble + PDN + + + Transdniestria Ruble + PDR + + + Peruvian Inti + PEI + + + Peruvian Sol Nuevo + PEN + + + Peruvian Sol + PES + + + Papua New Guinea Kina + PGK + + + Philippine Peso + PHP + + + Pakistan Rupee + Pra + + + Polish Zloty + Zl + + + Polish US Dollar Foreign Exchange Certificates + PLX + + + Polish Zloty (1950-1995) + PLZ + + + Palestine Pound + PSP + + + Portuguese Conto + PTC + + + Portuguese Escudo + PTE + + + Paraguay Guarani + PYG + + + Qatari Rial + QR + + + Reunion Franc + REF + + + Romanian Leu + leu + + + Romanian New Leu + RON + + + Russian Ruble + RUB + + + Russian Ruble (1991-1998) + RUR + + + Rwandan Franc + RWF + + + Saudi Riyal + SRl + + + Saudi Sovereign Riyal + SAS + + + Solomon Islands Dollar + SI$ + + + Seychelles Rupee + SR + + + Sudanese Dinar + SDD + + + Sudanese Pound + SDP + + + Swedish Krona + SKr + + + Singapore Dollar + S$ + + + Saint Helena Pound + SHP + + + Slovenia Tolar Bons + SIB + + + Slovenia Tolar + SIT + + + Slovak Koruna + Sk + + + Sierra Leone Leone + SLL + + + San Marino Lira + SML + + + Somali Shilling + So. Sh. + + + Somaliland Shilling + SQS + + + Suriname Guilder + Sf + + + Scotland Pound + SSP + + + Sao Tome and Principe Dobra + Db + + + Sao Tome and Principe Escudo + STE + + + Soviet New Ruble + SUN + + + Soviet Rouble + SUR + + + El Salvador Colon + SVC + + + Syrian Pound + LS + + + Swaziland Lilangeni + E + + + Turks and Caicos Crown + TCC + + + Chad CFA Franc + TDF + + + Thai Baht + THB + + + Tajikistan Ruble + TJR + + + Tajikistan Somoni + TJS + + + Turkmenistan Manat + TMM + + + Tunisian Dinar + TND + + + Tonga Paʻanga + T$ + + + Tonga Pound Sterling + TOS + + + Timor Escudo + TPE + + + Timor Pataca + TPP + + + Turkish Lira + TL + + + Trinidad and Tobago Dollar + TT$ + + + Trinidad and Tobago Old Dollar + TTO + + + Tuvalu Dollar + TVD + + + Taiwan New Dollar + NT$ + + + Tanzanian Shilling + T Sh + + + Ukrainian Hryvnia + UAH + + + Ukrainian Karbovanetz + UAK + + + Ugandan Shilling (1966-1987) + UGS + + + Ugandan Shilling + U Sh + + + US Dollar + US$ + + + US Dollar (Next day) + USN + + + US Dollar (Same day) + USS + + + Uruguay Peso Fuerte + UYF + + + Uruguay Peso (1975-1993) + UYP + + + Uruguay Peso Uruguayo + Ur$ + + + Uzbekistan Coupon Som + UZC + + + Uzbekistan Sum + UZS + + + Vatican City Lira + VAL + + + North Vietnam Piastre Dong Viet + VDD + + + North Vietnam New Dong + VDN + + + North Vietnam Viet Minh Piastre Dong Viet + VDP + + + Venezuelan Bolivar + Be + + + British Virgin Islands Dollar + VGD + + + Vietnamese Dong + VND + + + Vietnamese New Dong + VNN + + + Vietnamese Republic Dong + VNR + + + Vietnamese National Dong + VNS + + + Vanuatu Vatu + VT + + + Western Samoa Pound + WSP + + + Western Samoa Tala + WST + + + Asian Dinar Unit of Account + XAD + + + CFA Franc BEAC + XAF + + + Asian Monetary Unit + XAM + + + Gold + XAU + + + European Composite Unit + XBA + + + European Monetary Unit + XBB + + + European Unit of Account (XBC) + XBC + + + European Unit of Account (XBD) + XBD + + + East Caribbean Dollar + EC$ + + + CFA Nouveau Franc + XCF + + + Special Drawing Rights + XDR + + + CFA Franc BCEAEC + XEF + + + European Currency Unit + XEU + + + French Gold Franc + XFO + + + French UIC-Franc + XFU + + + Islamic Dinar + XID + + + French Metropolitan Nouveau Franc + XMF + + + French Antilles CFA Franc + XNF + + + CFA Franc BCEAO + XOF + + + CFP Franc + CFPF + + + COMECON Transferable Ruble + XTR + + + Yemeni Dinar + YDD + + + Yemeni Imadi Riyal + YEI + + + Yemeni Rial + YRl + + + Yugoslavian Hard Dinar + YUD + + + Yugoslavian Federation Dinar + YUF + + + Yugoslavian 1994 Dinar + YUG + + + Yugoslavian Noviy Dinar + YUM + + + Yugoslavian Convertible Dinar + YUN + + + Yugoslavian October Dinar + YUO + + + Yugoslavian Reformed Dinar + YUR + + + South African Rand (financial) + ZAL + + + South African Pound + ZAP + + + South African Rand + R + + + Zambian Kwacha + ZMK + + + Zambian Pound + ZMP + + + Zairean New Zaire + ZRN + + + Zairean Zaire + ZRZ + + + Zimbabwe Dollar + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_ZA.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_ZA.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_ZA.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_ZA.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd MMM yyyy + + + + + yyyy/MM/dd + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/en_ZW.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/en_ZW.xml --- zope3-3.4.0/src/zope/i18n/locales/data/en_ZW.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/en_ZW.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + EEEE dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd MMM,yy + + + + + d/M/yyyy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + USD + US$ + + + Zimbabwean Dollar + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/eo.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/eo.xml --- zope3-3.4.0/src/zope/i18n/locales/data/eo.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/eo.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,523 @@ + + + + + + + + + + + afara + abĥaza + afrikansa + amhara + araba + asama + ajmara + azerbajĝana + baŝkira + belorusa + bulgara + bihara + bislamo + bengala + tibeta + bretona + kataluna + korsika + ĉeĥa + kimra + dana + germana + dzonko + greka + angla + esperanto + hispana + estona + eŭska + persa + finna + fiĝia + feroa + franca + frisa + irlanda + gaela + galega + gvarania + guĝarata + haŭsa + hebrea + hinda + kroata + hungara + armena + interlingvao + indonezia + okcidentalo + eskima + islanda + itala + inuita + japana + java + kartvela + kazaĥa + gronlanda + kmera + kanara + korea + kaŝmira + kurda + kirgiza + latino + lingala + laŭa + litova + latva + malagasa + maoria + makedona + malajalama + mongola + marata + malaja + malta + birma + naura + nepala + nederlanda + norvega + okcitana + oroma + orijo + panĝaba + pola + paŝtua + portugala + keĉua + romanĉa + burunda + rumana + rusa + ruanda + sanskrito + sinda + sangoa + serbo-Kroata + sinhala + slovaka + slovena + samoa + ŝona + somala + albana + serba + svazia + sota + sunda + sveda + svahila + tamila + telugua + taĝika + taja + tigraja + turkmena + filipina + cvana + tongaa + turka + conga + tatara + akana + ujgura + ukraina + urduo + uzbeka + vjetnama + volapuko + volofa + ksosa + jida + joruba + ĝuanga + ĉina + zulua + + + Andoro + Unuiĝintaj Arabaj Emirlandos + Afganujo + Antigvo-Barbudo + Angvilo + Albanujo + Armenujo + Nederlandaj Antiloj + Angolo + Antarkto + Argentino + Aŭstrujo + Aŭstralio + Arubo + Azerbajĝano + Bosnio-Hercegovino + Barbado + Bangladeŝo + Belgujo + Burkino + Bulgarujo + Barejno + Burundo + Benino + Bermudoj + Brunejo + Bolivio + Brazilo + Bahamoj + Butano + Bocvano + Belorusujo + Belizo + Kanado + Centr-Afrika Respubliko + Kongolo + Svisujo + Ebur-Bordo + Kukinsuloj + Ĉilio + Kameruno + Ĉinujo + Kolombio + Kostariko + Kubo + Kabo-Verdo + Kipro + Ĉeĥujo + Germanujo + Ĝibutio + Danujo + Dominiko + Domingo + Alĝerio + Ekvadoro + Estonujo + Egipto + Okcidenta Saharo + Eritreo + Hispanujo + Etiopujo + Finnlando + Fiĝoj + Mikronezio + Ferooj + Francujo + Gabono + Unuiĝinta Reĝlando + Grenado + Kartvelujo + Franca Gviano + Ganao + Ĝibraltaro + Gronlando + Gambio + Gvineo + Gvadelupo + Ekvatora Gvineo + Grekujo + Sud-Georgio kaj Sud-Sandviĉinsuloj + Gvatemalo + Gvamo + Gvineo-Bisaŭo + Gujano + Herda kaj Makdonaldaj Insuloj + Honduro + Kroatujo + Haitio + Hungarujo + Indonezio + Irlando + Israelo + Hindujo + Brita Hindoceana Teritorio + Irako + Irano + Islando + Italujo + Jamajko + Jordanio + Japanujo + Kenjo + Kirgizistano + Kamboĝo + Kiribato + Komoroj + Sent-Kristofo kaj Neviso + Nord-Koreo + Sud-Koreo + Kuvajto + Kejmanoj + Kazaĥstano + Laoso + Libano + Sent-Lucio + Liĥtenŝtejno + Sri-Lanko + Liberio + Lesoto + Litovujo + Luksemburgo + Latvujo + Libio + Maroko + Monako + Moldavujo + Madagaskaro + Marŝaloj + Makedonujo + Malio + Mjanmao + Mongolujo + Nord-Marianoj + Martiniko + Maŭritanujo + Malto + Maŭricio + Maldivoj + Malavio + Meksiko + Malajzio + Mozambiko + Namibio + Nov-Kaledonio + Niĝero + Norfolkinsulo + Niĝerio + Nikaragvo + Nederlando + Norvegujo + Nepalo + Nauro + Niuo + Nov-Zelando + Omano + Panamo + Peruo + Franca Polinezio + Papuo-Nov-Gvineo + Filipinoj + Pakistano + Pollando + Sent-Piero kaj Mikelono + Pitkarna Insulo + Puerto-Riko + Portugalujo + Belaŭo + Paragvajo + Kataro + Reunio + Rumanujo + Rusujo + Ruando + Saŭda Arabujo + Salomonoj + Sejŝeloj + Sudano + Svedujo + Singapuro + Sent-Heleno + Slovenujo + Svalbardo kaj Jan-Majen-insulo + Slovakujo + Siera-Leono + San-Marino + Senegalo + Somalujo + Serbujo + Surinamo + Sao-Tomeo kaj Principeo + Salvadoro + Sirio + Svazilando + Ĉado + Togolo + Tajlando + Taĝikujo + Turkmenujo + Tunizio + Tongo + Turkujo + Trinidado kaj Tobago + Tuvalo + Tajvano + Tanzanio + Ukrajno + Ugando + Usonaj malgrandaj insuloj + Usono + Urugvajo + Uzbekujo + Vatikano + Sent-Vincento kaj la Grenadinoj + Venezuelo + Britaj Virgulininsuloj + Usonaj Virgulininsuloj + Vjetnamo + Vanuatuo + Valiso kaj Futuno + Samoo + Jemeno + Majoto + Sud-Afriko + Zambio + Zimbabvo + + + + [a-z ŭ ĉ ĝ ĥ ĵ ŝ] + + + GjMtkHmslTDUSnahKzJdugAZ + + + + + + jan + feb + mar + apr + maj + jun + jul + aŭg + sep + okt + nov + dec + + + januaro + februaro + marto + aprilo + majo + junio + julio + aŭgusto + septembro + oktobro + novembro + decembro + + + + + + + di + lu + ma + me + ĵa + ve + sa + + + dimanĉo + lundo + mardo + merkredo + ĵaŭdo + vendredo + sabato + + + + + + + + atm + ptm + + + aK + pK + + + + + + + EEEE, d'-a de 'MMMM yyyy + + + + + yyyy-MMMM-dd + + + + + yyyy-MMM-dd + + + + + yy-MM-dd + + + + + + + + H'-a horo kaj 'm z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_AR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_AR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_AR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_AR.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + HH'h'''mm z + + + + + H:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + Peso Argentino + $ + + + Dólar Americano + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_BO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_BO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_BO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_BO.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_CL.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_CL.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_CL.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_CL.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd-MM-yyyy + + + + + dd-MM-yy + + + + + + + + HH:mm:ss z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;¤-#,##0.00 + + + + + + Peso Chileno + $ + + + Dólar Americano + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_CO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_CO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_CO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_CO.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + d/MM/yyyy + + + + + d/MM/yy + + + + + + + + HH:mm:ss z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + Peso de Colombia + $ + + + Dólar Americano + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_CR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_CR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_CR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_CR.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_DO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_DO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_DO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_DO.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_EC.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_EC.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_EC.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_EC.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + HH:mm:ss z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;¤-#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_ES.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_ES.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_ES.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_ES.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + HH'H'mm''ss" z + + + + + HH:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + + peseta española + + #,##0 ¤;-#,##0 ¤ + #,##0 ¤;-#,##0 ¤ + . + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_GT.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_GT.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_GT.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_GT.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + d/MM/yyyy + + + + + d/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_HN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_HN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_HN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_HN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + EEEE dd' de 'MMMM' de 'yyyy + + + + + dd' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_MX.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_MX.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_MX.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_MX.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + + MXN + $ + + + Dólar Americano + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_NI.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_NI.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_NI.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_NI.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_PA.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_PA.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_PA.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_PA.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + MM/dd/yyyy + + + + + MM/dd/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_PE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_PE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_PE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_PE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;¤-#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_PR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_PR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_PR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_PR.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + MM/dd/yyyy + + + + + MM/dd/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + + Dólar Americano + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_PY.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_PY.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_PY.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_PY.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0;¤ -#,##0 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_SV.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_SV.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_SV.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_SV.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_US.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_US.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_US.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_US.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,110 @@ + + + + + + + + + + + + + 279 + 216 + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + MMM d, yyyy + + + + + M/d/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_UY.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_UY.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_UY.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_UY.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es_VE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es_VE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es_VE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es_VE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;¤ -#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/es.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/es.xml --- zope3-3.4.0/src/zope/i18n/locales/data/es.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/es.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2553 @@ + + + + + + + + + + + afar + abkhaziano + avéstico + afrikaans + akan + amárico + aragonés + árabe + asamés + avar + aymara + azerí + bashkir + bielorruso + búlgaro + bihari + bislama + bambara + bengalí + tibetano + bretón + bosnio + blin + catalán + checheno + chamorro + cherokee + corso + cree + checo + eslavo eclesiástico + chuvash + galés + danés + alemán + divehi + bhutaní + ewe + griego + inglés + esperanto + español + estonio + vasco + farsi + fula + finlandés + fidji + feroés + francés + frisón + irlandés + gaélico escocés + geez + gallego + guaraní + gujarati + gaélico manés + hausa + hawaiano + hebreo + hindi + hiri motu + croata + haitiano + húngaro + armenio + herero + interlingua + indonesio + interlingue + igbo + sichuan yi + inupiak + ido + islandés + italiano + inuktitut + japonés + javanés + georgiano + kongo + kikuyu + kuanyama + kazajo + groenlandés + jemer + canarés + coreano + konkani + kanuri + cachemiro + kurdo + komi + córnico + kirghiz + latín + luxemburgués + ganda + limburgués + lingala + laosiano + lituano + luba-katanga + letón + malgache + marshalés + maorí + macedonio + malayalam + mongol + moldavo + marathi + malayo + maltés + birmano + nauruano + bokmal noruego + ndebele septentrional + nepalí + ndonga + holandés + nynorsk noruego + noruego + ndebele meridional + navajo + nyanja + occitano (después del 1500) + ojibwa + oromo + oriya + osético + punjabí + pali + polaco + pashto + portugués + quechua + reto-romance + kiroundi + rumano + raíz + ruso + kinyarwanda + sánscrito + sardo + sindhi + sami septentrional + sango + serbo-croata + singalés + sidamo + eslovaco + esloveno + samoano + shona + somalí + albanés + serbio + siswati + sesotho + sundanés + sueco + swahili + siriaco + tamil + telugu + tayiko + tailandés + tigrinya + tigré + turkmeno + tagalo + setchwana + tonga (Islas Tonga) + turco + tsonga + tatar + twi + tahitiano + uigur + ucraniano + urdu + uzbeko + venda + vietnamita + volapuk + valón + uolof + xhosa + yidish + yoruba + zhuang + chino + zulú + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + Emiratos Árabes Unidos + Afganistán + Antigua y Barbuda + Anguila + Albania + Armenia + Antillas Neerlandesas + Angola + Antártida + Argentina + Samoa Americana + Austria + Australia + Aruba + Azerbaiyán + Bosnia-Herzegovina + Barbados + Bangladesh + Bélgica + Burkina Faso + Bulgaria + Bahráin + Burundi + Benín + Bermudas + Brunéi + Bolivia + Brasil + Bahamas + Bután + Isla Bouvet + Botsuana + Bielorrusia + Belice + Canadá + Islas Cocos (Keeling) + República Democrática del Congo + República Centroafricana + Congo + Suiza + Costa de Marfil + Islas Cook + Chile + Camerún + China + Colombia + Costa Rica + Cuba + Cabo Verde + Isla Navidad + Chipre + República Checa + Alemania + Yibuti + Dinamarca + Dominica + República Dominicana + Argelia + Ecuador + Estonia + Egipto + Sáhara Occidental + Eritrea + España + Etiopía + Finlandia + Fiyi + Islas Falkland (Malvinas) + Micronesia + Islas Feroe + Francia + en + Gabón + Reino Unido + Granada + Georgia + Guayana Francesa + Ghana + Gibraltar + Groenlandia + Gambia + Guinea + Guadalupe + Guinea Ecuatorial + Grecia + Islas Georgia del Sur y Sandwich del Sur + Guatemala + Guam + Guinea-Bissau + Guyana + Hong-Kong, Región administrativa especial de China + Islas Heard y McDonald + Honduras + Croacia + Haití + Hungría + Indonesia + Irlanda + Israel + India + Territorios Británico del Océano Índico + Iraq + Irán + Islandia + Italia + Jamaica + Jordania + Japón + Kenia + Kirguizistán + Camboya + Kiribati + Comoras + San Cristóbal y Nieves + Corea del Norte + Corea del Sur + Kuwait + Islas Caimán + Kazajstán + Laos + Líbano + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Lituania + Luxemburgo + Letonia + Libia + Marruecos + Mónaco + Moldova + Madagascar + Islas Marshall + Macedonia + Malí + Myanmar + Mongolia + Macao, Región administrativa especial de China + Islas Marianas del Norte + Martinica + Mauritania + Montserrat + Malta + Mauricio + Maldivas + Malawi + México + Malasia + Mozambique + Namibia + Nueva Caledonia + Níger + Isla Norfolk + Nigeria + Nicaragua + Países Bajos + Noruega + Nepal + Nauru + Isla Niue + Nueva Zelanda + Omán + Panamá + Perú + Polinesia Francesa + Papúa Nueva Guinea + Filipinas + Pakistán + Polonia + San Pedro y Miquelón + Pitcairn + Puerto Rico + Territorios Palestinos + Portugal + Palau + Paraguay + Qatar + Réunion + Rumanía + Rusia + Ruanda + Arabia Saudí + Islas Salomón + Seychelles + Sudán + Suecia + Singapur + Santa Elena + Eslovenia + Svalbard y Jan Mayen + Eslovaquia + Sierra Leona + San Marino + Senegal + Somalia + Serbia + Suriname + Santo Tomé y Príncipe + El Salvador + Siria + Suazilandia + Islas Turcas y Caicos + Chad + Territorios Australes Franceses + Togo + Tailandia + Tayikistán + Islas Tokelau + Timor Oriental + Turkmenistán + Túnez + Tonga + Turquía + Trinidad y Tabago + Tuvalu + Taiwán, República de China + Tanzania + Ucrania + Uganda + Islas menores alejadas de los Estados Unidos + Estados Unidos + Uruguay + Uzbekistán + Ciudad del Vaticano + San Vicente y las Granadinas + Venezuela + Islas Vírgenes Británicas + Islas Vírgenes de los Estados Unidos + Vietnam + Vanuatu + Wallis y Futuna + Samoa + Yemen + Mayotte + Yugoslavia + Sudáfrica + Zambia + Zimbabue + + + Revisado + + + calendario + intercalación + moneda + + + calendario budista + calendario chino + calendario gregoriano + calendario hebreo + calendario islámico + calendario civil islámico + calendario japonés + orden directo + orden de listín telefónico + orden pinyin + orden pincelada + orden tradicional + + + + [a-z ñ á é í ó ú ü] + + + GuMtkHmsSEDFwWahKzUeygAZ + + + + + + ene + feb + mar + abr + may + jun + jul + ago + sep + oct + nov + dic + + + E + F + M + A + M + J + J + A + S + O + N + D + + + enero + febrero + marzo + abril + mayo + junio + julio + agosto + septiembre + octubre + noviembre + diciembre + + + + + + + dom + lun + mar + mié + jue + vie + sáb + + + D + L + M + M + J + V + S + + + domingo + lunes + martes + miércoles + jueves + viernes + sábado + + + + + + + + + + a.C. + d.C. + + + + + + + EEEE d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd-MMM-yy + + + + + d/MM/yy + + + + + + + + HH'H'mm''ss" z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Hora estándar del Pacífico + Hora de verano del Pacífico + + + PST + PDT + + Los Ángeles + + + + Hora estándar del Pacífico + Hora de verano del Pacífico + + + PST + PDT + + Los Ángeles + + + + Hora estándar de Montaña + Hora de verano de Montaña + + + MST + MDT + + Denver + + + + Hora estándar de Montaña + Hora de verano de Montaña + + + MST + MDT + + Denver + + + + Hora estándar de Montaña + Hora estándar de Montaña + + + MST + MST + + Phoenix + + + + Hora estándar de Montaña + Hora estándar de Montaña + + + MST + MST + + Phoenix + + + + Hora estándar central + Hora de verano central + + + CST + CDT + + Chicago + + + + Hora estándar central + Hora de verano central + + + CST + CDT + + Chicago + + + + Hora estándar oriental + Hora de verano oriental + + + EST + EDT + + Nueva York + + + + Hora estándar oriental + Hora de verano oriental + + + EST + EDT + + Nueva York + + + + Hora estándar oriental + Hora estándar oriental + + + EST + EST + + Indianápolis + + + + Hora estándar oriental + Hora estándar oriental + + + EST + EST + + Indianápolis + + + + Hora estándar de Hawai + Hora estándar de Hawai + + + HST + HST + + Honolulu + + + + Hora estándar de Hawai + Hora estándar de Hawai + + + HST + HST + + Honolulu + + + + Hora estándar de Alaska + Hora de verano de Alaska + + + AST + ADT + + Anchorage + + + + Hora estándar de Alaska + Hora de verano de Alaska + + + AST + ADT + + Anchorage + + + + Hora estándar del Atlántico + Hora de verano del Atlántico + + + AST + ADT + + Halifax + + + + Hora estándar de Newfoundland + Hora de verano de Newfoundland + + + CNT + CDT + + St. Johns + + + + Hora estándar de Newfoundland + Hora de verano de Newfoundland + + + CNT + CDT + + St. Johns + + + + Hora estándar de Europa Central + Hora de verano de Europa Central + + + CET + CEST + + París + + + + Hora estándar de Europa Central + Hora de verano de Europa Central + + + CET + CEST + + París + + + + Hora media de Greenwich + Hora media de Greenwich + + + GMT + GMT + + Londres + + + + Hora media de Greenwich + Hora media de Greenwich + + + GMT + GMT + + Casablanca + + + + Hora estándar de Israel + Hora de verano de Israel + + + IST + IDT + + Jerusalén + + + + Hora estándar de Japón + Hora estándar de Japón + + + JST + JST + + Tokio + + + + Hora estándar de Japón + Hora estándar de Japón + + + JST + JST + + Tokio + + + + Hora estándar de Europa del Este + Hora de verano de Europa del Este + + + EET + EEST + + Bucarest + + + + Hora estándar de China + Hora estándar de China + + + CTT + CDT + + Shanghai + + + + Hora estándar de China + Hora estándar de China + + + CTT + CDT + + Shanghai + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + + diner andorrano + ADD + + + peseta andorrana + ADP + + + dirham de los Emiratos Árabes Unidos + AED + + + afgani (1927-2002) + AFA + + + afgani + Af + + + franco de Affars e Issas + AIF + + + lek albanés (1946-1961) + ALK + + + lek albanés + lek + + + lek valute albanés + ALV + + + certificados de cambio albaneses en dólares + ALX + + + dram armenio + dram + + + florín de las Antillas Neerlandesas + NA f. + + + kwanza angoleño + AOA + + + kwanza angoleño (1977-1990) + AOK + + + nuevo kwanza angoleño (1990-2000) + AON + + + kwanza reajustado angoleño (1995-1999) + AOR + + + escudo angoleño + AOS + + + austral argentino + ARA + + + peso moneda nacional argentino + ARM + + + peso argentino (1983-1985) + ARP + + + peso argentino + Arg$ + + + chelín austriaco + ATS + + + dólar australiano + $A + + + libra australiana + AUP + + + florín de Aruba + AWG + + + manat azerí + AZM + + + dinar bosnio + BAD + + + marco bosnio convertible + KM + + + nuevo dinar bosnio + BAN + + + dólar de Barbados + BDS$ + + + taka de Bangladesh + Tk + + + franco belga (convertible) + BEC + + + franco belga + BF + + + franco belga (financiero) + BEL + + + lev fuerte búlgaro + lev + + + lev socialista búlgaro + BGM + + + nuevo lev búlgaro + BGN + + + lev búlgaro (1879-1952) + BGO + + + certificados de cambio búlgaros en leva + BGX + + + dinar bahreiní + BD + + + franco de Burundi + Fbu + + + dólar de Bermudas + Ber$ + + + libra de Bermudas + BMP + + + dólar de Brunéi + BND + + + boliviano + Bs + + + boliviano (1863-1962) + BOL + + + peso boliviano + BOP + + + MVDOL boliviano + BOV + + + nuevo cruceiro brasileño (1967-1986) + BRB + + + cruzado brasileño + BRC + + + cruceiro brasileño (1990-1993) + BRE + + + real brasileño + R$ + + + nuevo cruzado brasileño + BRN + + + cruceiro brasileño + BRR + + + cruceiro brasileño (1942-1967) + BRZ + + + dólar de las Bahamas + BSD + + + libra de las Bahamas + BSP + + + ngultrum butanés + Nu + + + rupia butanesa + BTR + + + kyat birmano + BUK + + + rupia birmana + BUR + + + pula botsuano + BWP + + + nuevo rublo bielorruso (1994-1999) + BYB + + + rublo bielorruso (1992-1994) + BYL + + + rublo bielorruso + Rbl + + + dólar de Belice + BZ$ + + + dólar de Honduras Británica + BZH + + + dólar canadiense + Can$ + + + franco congoleño + CDF + + + franco de la República del Congo + CDG + + + zaire congoleño + CDL + + + franco CFA de la República Centroafricana + CFF + + + franco suizo + SwF + + + dólar de las Islas Cook + CKD + + + cóndor chileno + CLC + + + escudo chileno + CLE + + + unidad de fomento chilena + CLF + + + peso chileno + Ch$ + + + franco CFA de Camerún + CMF + + + jen min piao yuan chino + CNP + + + certificados de cambio chinos en dólares estadounidenses + CNX + + + yuan renminbi chino + Y + + + peso de papel colombiano + COB + + + franco CFA del Congo + COF + + + peso colombiano + Col$ + + + colón costarricense + C + + + corona checoslovaca + CSC + + + corona fuerte checoslovaca + CSK + + + peso cubano + CUP + + + certificados de cambio cubanos + CUX + + + escudo de Cabo Verde + CVEsc + + + florín de Curazao + CWG + + + libra chipriota + £C + + + corona checa + CZK + + + ostmark de Alemania del Este + DDM + + + marco alemán + DEM + + + sperrmark alemán + DES + + + franco de Yibuti + DF + + + corona danesa + DKr + + + peso dominicano + RD$ + + + dinar argelino + DA + + + nuevo franco argelino + DZF + + + franco germinal argelino + DZG + + + sucre ecuatoriano + ECS + + + unidad de valor constante (UVC) ecuatoriana + ECV + + + corona estonia + EEK + + + libra egipcia + EGP + + + nakfa eritreo + ERN + + + peseta española + + + + birr etíope + Br + + + dólar etíope + ETD + + + euro + + + + marco finlandés + FIM + + + marco finlandés (1860-1962) + FIN + + + dólar de las Islas Fiyi + F$ + + + libra de las Islas Fiyi + FJP + + + libra de las Islas Malvinas + FKP + + + corona de las Islas Feroe + FOK + + + franco francés + FRF + + + franco germinal/franco Poincaré francés + FRG + + + franco CFA de Gabón + GAF + + + libra esterlina británica + £ + + + kupon larit georgiano + GEK + + + lari georgiano + lari + + + cedi ghanés + GHC + + + antiguo cedi ghanés + GHO + + + libra ghanesa + GHP + + + cedi revaluado ghanés + GHR + + + libra de Gibraltar + GIP + + + corona de Groenlandia + GLK + + + dalasi gambiano + GMD + + + libra gambiana + GMP + + + franco guineo + GF + + + franco guineo (1960-1972) + GNI + + + syli guineano + GNS + + + franco de Guadalupe + GPF + + + ekuele de Guinea Ecuatorial + GQE + + + franco de Guinea Ecuatorial + GQF + + + peseta guineana de Guinea Ecuatorial + GQP + + + dracma griego + GRD + + + nuevo dracma griego + GRN + + + quetzal guatemalteco + Q + + + franco guayanés de la Guayana Francesa + GUF + + + escudo de Guinea Portuguesa + GWE + + + mil reis de Guinea Portuguesa + GWM + + + peso de Guinea-Bissáu + GWP + + + dólar guyanés + G$ + + + dólar de Hong Kong + HK$ + + + lempira hondureño + L + + + dinar croata + HRD + + + kuna croata + HRK + + + gourde haitiano + HTG + + + forinto húngaro + Ft + + + libra de Irlanda del Norte + IBP + + + florín Nica indonesio + IDG + + + rupia Java indonesia + IDJ + + + nueva rupia indonesia + IDN + + + rupia indonesia + Rp + + + libra irlandesa + IR£ + + + sheqel israelí + ILL + + + libra israelí + ILP + + + nuevo sheqel israelí + ILS + + + libra esterlina de la Isla de Man + IMP + + + rupia india + =0#Rs.|1#Re.|1<Rs. + + + dinar iraquí + ID + + + rial iraní + RI + + + corona islandesa + ISK + + + lira italiana + + + + libra esterlina de Jersey + JEP + + + dólar de Jamaica + J$ + + + libra jamaicana + JMP + + + dinar jordano + JD + + + yen japonés + ¥ + + + chelín keniata + K Sh + + + som kirguís + som + + + antiguo riel camboyano + KHO + + + riel camboyano + CR + + + dólar de Kiribati + KID + + + franco comorense + CF + + + won del pueblo norcoreano + KPP + + + won norcoreano + KPW + + + hwan surcoreano + KRH + + + antiguo won surcoreano + KRO + + + won surcoreano + KRW + + + dinar kuwaití + KD + + + dólar de las Islas Caimán + KYD + + + rublo kazako + KZR + + + tenge kazako + T + + + kip laosiano + LAK + + + libra libanesa + LL + + + franco de Liechtenstein + LIF + + + rupia de Sri Lanka + SL Re + + + rupia cingalesa + LNR + + + dólar liberiano + LRD + + + loti lesothense + M + + + litas lituano + LTL + + + talonas lituano + LTT + + + franco luxemburgués + LUF + + + lats letón + LVL + + + rublo letón + LVR + + + lira libia de la Autoridad Militar Británica + LYB + + + dinar libio + LD + + + libra libia + LYP + + + dirham marroquí + MAD + + + franco marroquí + MAF + + + nuevo franco monegasco + MCF + + + franco germinal monegasco + MCG + + + cupón leu moldavo + MDC + + + leu moldavo + MDL + + + cupón rublo moldavo + MDR + + + ariary malgache + MGA + + + franco malgache + MGF + + + dólar de las Islas Marshall + MHD + + + dinar macedonio + MDen + + + dinar macedonio (1992-1993) + MKN + + + franco malí + MLF + + + kyat de Myanmar + MMK + + + certificados de cambio birmanos en dólares + MMX + + + tugrik mongol + Tug + + + pataca de Macao + MOP + + + franco de Martinica + MQF + + + ouguiya mauritano + UM + + + lira maltesa + Lm + + + libra maltesa + MTP + + + rupia mauriciana + MUR + + + rupia de Maldivas + MVP + + + rufiyaa de Maldivas + MVR + + + kwacha de Malawi + MK + + + libra de Malawi + MWP + + + peso mexicano + MEX$ + + + peso de plata mexicano (1861-1992) + MXP + + + unidad de inversión (UDI) mexicana + MXV + + + ringgit malasio + RM + + + escudo mozambiqueño + MZE + + + metical mozambiqueño + Mt + + + dólar de Namibia + N$ + + + franco germinal de Nueva Caledonia + NCF + + + naira nigeriano + NGN + + + libra nigeriana + NGP + + + franco CFP de las Nuevas Hébridas + NHF + + + córdoba nicaragüense + NIC + + + córdoba oro nicaragüense + NIG + + + córdoba oro nicaragüense + NIO + + + florín neerlandés + NLG + + + corona noruega + NKr + + + rupia nepalesa + Nrs + + + dólar neozelandés + $NZ + + + libra neozelandesa + NZP + + + rial omaní + RO + + + rial saidi omaní + OMS + + + balboa panameño + PAB + + + cupón rublo de Transdniestria + PDK + + + nuevo rublo de Transdniestria + PDN + + + rublo de Transdniestria + PDR + + + inti peruano + PEI + + + nuevo sol peruano + PEN + + + sol peruano + PES + + + kina de Papúa Nueva Guinea + PGK + + + peso filipino + PHP + + + rupia pakistaní + Pra + + + zloty polaco + Zl + + + certificados de cambio polacos en dólares estadounidenses + PLX + + + zloty polaco (1950-1995) + PLZ + + + libra palestina + PSP + + + conto portugués + PTC + + + escudo portugués + PTE + + + guaraní paraguayo + PYG + + + riyal de Qatar + QR + + + franco de Reunión + REF + + + leu rumano + leu + + + nuevo leu rumano + RON + + + rublo ruso + RUB + + + rublo ruso (1991-1998) + RUR + + + franco ruandés + RWF + + + riyal saudí + SRl + + + riyal soberano saudí + SAS + + + dólar de las Islas Salomón + SI$ + + + rupia de Seychelles + SR + + + dinar sudanés + SDD + + + libra sudanesa + SDP + + + corona sueca + SKr + + + dólar singapurense + S$ + + + libra de Santa Elena + SHP + + + tólar bons esloveno + SIB + + + tólar esloveno + SIT + + + corona eslovaca + Sk + + + leone de Sierra Leona + SLL + + + lira de San Marino + SML + + + chelín somalí + So. Sh. + + + chelín de Somalilandia + SQS + + + florín surinamés + Sf + + + libra escocesa + SSP + + + dobra de Santo Tomé y Príncipe + Db + + + escudo de Santo Tomé y Príncipe + STE + + + nuevo rublo soviético + SUN + + + rublo soviético + SUR + + + colón salvadoreño + SVC + + + libra siria + LS + + + lilangeni suazi + E + + + corona de las Islas Turcas y Caicos + TCC + + + franco CFA del Chad + TDF + + + baht tailandés + THB + + + rublo tayiko + TJR + + + somoni tayiko + TJS + + + manat turcomano + TMM + + + dinar tunecino + TND + + + paʻanga tongano + T$ + + + libra esterlina tongana + TOS + + + escudo timorense + TPE + + + pataca timorense + TPP + + + lira turca + TL + + + dólar de Trinidad y Tobago + TT$ + + + antiguo dólar de Trinidad y Tobago + TTO + + + dólar de Tuvalu + TVD + + + nuevo dólar taiwanés + NT$ + + + chelín tanzano + T Sh + + + grivna ucraniana + UAH + + + karbovanet ucraniano + UAK + + + chelín ugandés (1966-1987) + UGS + + + chelín ugandés + U Sh + + + dólar estadounidense + US$ + + + dólar estadounidense (día siguiente) + USN + + + dólar estadounidense (mismo día) + USS + + + peso fuerte uruguayo + UYF + + + peso uruguayo (1975-1993) + UYP + + + peso uruguayo + Ur$ + + + cupón som uzbeko + UZC + + + sum uzbeko + UZS + + + lira de la Ciudad del Vaticano + VAL + + + piastre dong viet de Vietnam del Norte + VDD + + + nuevo dong de Vietnam del Norte + VDN + + + viet minh piastre dong viet de Vietnam del Norte + VDP + + + bolívar venezolano + Be + + + dólar de las Islas Vírgenes Británicas + VGD + + + dong vietnamita + VND + + + nuevo dong vietnamita + VNN + + + dong de la República del vietnamita + VNR + + + dong nacional vietnamita + VNS + + + vatu vanuatuense + VT + + + libra samoana + WSP + + + tala samoano + WST + + + unidad de cuenta asiática en dinares + XAD + + + franco CFA BEAC + XAF + + + unidad monetaria asiática + XAM + + + oro + XAU + + + unidad compuesta europea + XBA + + + unidad monetaria europea + XBB + + + unidad de cuenta europea (XBC) + XBC + + + unidad de cuenta europea (XBD) + XBD + + + dólar del Caribe Oriental + EC$ + + + nuevo franco CFA + XCF + + + derechos especiales de giro + XDR + + + franco CFA BCEAEC + XEF + + + unidad de moneda europea + XEU + + + franco oro francés + XFO + + + franco UIC francés + XFU + + + dinar islámico + XID + + + nuevo franco metropolitano francés + XMF + + + franco CFA de las Antillas Francesas + XNF + + + franco CFA BCEAO + XOF + + + franco CFP + CFPF + + + rublo transferible del COMECON + XTR + + + dinar yemení + YDD + + + riyal Imadi yemení + YEI + + + rial yemení + YRl + + + dinar fuerte yugoslavo + YUD + + + dinar de la Federación Yugoslava + YUF + + + dinar 1994 yugoslavo + YUG + + + super dinar yugoslavo + YUM + + + dinar convertible yugoslavo + YUN + + + dinar de octubre yugoslavo + YUO + + + dinar reformado yugoslavo + YUR + + + rand sudafricano (financiero) + ZAL + + + libra sudafricana + ZAP + + + rand sudafricano + R + + + kwacha zambiano + ZMK + + + libra zambiana + ZMP + + + nuevo zaire zaireño + ZRN + + + zaire zaireño + ZRZ + + + dólar de Zimbabue + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/et_EE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/et_EE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/et_EE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/et_EE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/et.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/et.xml --- zope3-3.4.0/src/zope/i18n/locales/data/et.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/et.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,435 @@ + + + + + + + + + + + Araabia + Bulgaaria + Tiehhi + Taani + Saksa + Kreeka + Inglise + Hispaania + Eesti + Soome + Prantsuse + Heebrea + Horvaadi + Ungari + Itaalia + Jaapani + Korea + Leedu + Läti + Hollandi + Norra + Poola + Portugali + Rumeenia + Vene + Slovaki + Sloveeni + Rootsi + Türgi + Hiina + + + Andorra + Araabia Ühendemiraadid + Afganistan + Antigua ja Barbuda + Anguilla + Albaania + Armeenia + Hollandi Antillid + Angola + Antarktika + Argentina + Ameerika Samoa + Austria + Austraalia + Aruba + AserbaidĪaan + Bosnia ja Hertsegoviina + Barbados + Bangladesh + Belgia + Burkina Faso + Bulgaaria + Bahrein + Burundi + Benin + Bermuda + Brunei + Boliivia + Brasiilia + Bahama saared + Bhutan + Bouvet’ saar + Botswana + Valgevene + Belize + Kanada + Kookossaared + Kongo DV + Kesk-Aafrika Vabariik + Kongo + Ĩveits + Cote d’Ivoire + Cooki saared + Tiiili + Kamerun + Hiina + Colombia + Costa Rica + Kuuba + Cabo Verde + Jõulusaar + Küpros + Tiehhi Vabariik + Saksamaa + Djibouti + Taani + Dominica + Dominikaani Vabariik + AlĪeeria + Ecuador + Eesti + Egiptus + Lääne-Sahara + Eritrea + Hispaania + Etioopia + Soome + FidĪi + Falklandi saared + Mikroneesia Liiduriigid + Fääri saared + Prantsusmaa + en + Gabon + Ühendkuningriik + Grenada + Gruusia + Prantsuse Guajaana + Ghana + Gibraltar + Gröönimaa + Gambia + Guinea + Guadeloupe + Ekvatoriaal-Guinea + Kreeka + Lõuna-Georgia ja Lõuna-Sandwichi saared + Guatemala + Guam + Guinea-Bissau + Guyana + Hongkongi erihalduspiirkond + Heard ja McDonald + Honduras + Horvaatia + Haiti + Ungari + Indoneesia + Iirimaa + Iisrael + India + Briti India ookeani ala + Iraak + Iraan + Island + Itaalia + Jamaica + Jordaania + Jaapan + Kenya + Kõrgõzstan + KambodĪa + Kiribati + Komoorid + Saint Kitts ja Nevis + Põhja-Korea + Lõuna-Korea + Kuveit + Kaimani saared + Kasahstan + Laose DRV + Liibanon + Saint Lucia + Liechtenstein + Sri Lanka + Libeeria + Lesotho + Leedu + Luksemburg + Läti + Liibüa + Maroko + Monaco + Moldova + Madagaskar + Marshalli Saared + Makedoonia Vabariik + Mali + Myanmar + Mongoolia + Aomeni Hiina erihalduspiirkond + Põhja-Mariaanid + Martinique + Mauritaania + Montserrat + Malta + Mauritius + Maldiivid + Malawi + Mehhiko + Malaisia + Mosambiik + Namiibia + Uus-Kaledoonia + Niger + Norfolk + Nigeeria + Nicaragua + Holland + Norra + Nepal + Nauru + Niue + Uus-Meremaa + Omaan + Panama + Peruu + Prantsuse Polüneesia + Paapua Uus-Guinea + Filipiinid + Pakistan + Poola + Saint-Pierre ja Miquelon + Pitcairn + Puerto Rico + Palestiina Omavalitsus + Portugal + Belau + Paraguay + Katar + Réunion + Rumeenia + Venemaa + Rwanda + Saudi Araabia + Saalomoni Saared + Seiiellid + Sudaan + Rootsi + Singapur + Saint Helena + Sloveenia + Svalbard ja Jan Mayen + Slovakkia + Sierra Leone + San Marino + Senegal + Somaalia + Serbia + Suriname + Sao Tomé ja Principe + El Salvador + Süüria + Svaasimaa + Turks ja Caicos + Tiaad + Prantsuse Lõunaalad + Togo + Tai + TadĪikistan + Tokelau + Ida-Timor + Türkmenistan + Tuneesia + Tonga + Türgi + Trinidad ja Tobago + Tuvalu + Taiwan + Tansaania + Ukraina + Uganda + Ühendriikide hajasaared + Ameerika Ühendriigid + Uruguay + Usbekistan + Püha Tool (Vatikan) + Saint Vincent ja Grenadiinid + Venezuela + Briti Neitsisaared + USA Neitsisaared + Vietnam + Vanuatu + Wallis ja Futuna + Samoa + Jeemen + Mayotte + Jugoslaavia + Lõuna-Aafrika Vabariik + Sambia + Zimbabwe + + + + [a-z õ ä ö ü š ž] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + jaan + veebr + märts + apr + mai + juuni + juuli + aug + sept + okt + nov + dets + + + jaanuar + veebruar + märts + aprill + mai + juuni + juuli + august + september + oktoober + november + detsember + + + + + + + P + E + T + K + N + R + L + + + pühapäev + esmaspäev + teisipäev + kolmapäev + neljapäev + reede + laupäev + + + + + + + + + + e.m.a. + m.a.j. + + + + + + + EEEE, d, MMMM yyyy + + + + + d MMMM yyyy + + + + + dd.MM.yyyy + + + + + dd.MM.yy + + + + + + + + H:mm:ss z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + EEK + kr + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/eu_ES.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/eu_ES.xml --- zope3-3.4.0/src/zope/i18n/locales/data/eu_ES.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/eu_ES.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + EEEE, yyyy'eko' MMMM'ren' dd'a' + + + + + EEE, yyyy'eko' MMM'ren' dd'a' + + + + + yy'-'MMM'-'dd + + + + + yy'-'MM'-'dd + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/eu.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/eu.xml --- zope3-3.4.0/src/zope/i18n/locales/data/eu.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/eu.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,213 @@ + + + + + + + + + + + euskara + + + Arabiar Emirrerri Batuak + Afganistan + Antigua eta Barbuda + Antartika + Bosnia-Herzegovina + Belgika + Bolibia + Brasil + Bahamak + Bielorrusia + Kanada + Afrika Erdiko Errepublika + Kongo + Suitza + Boli Kosta + Txile + Kamerun + Txina + Kolonbia + Kuba + Cabo Verde + Zipre + Txekiar errepublika + Alemania + Djibuti + Danimarka + Dominika + Dominikar Errepublika + Aljeria + Ekuador + Egipto + Mendebaldeko Sahara + Espainia + Etiopia + Finlandia + Mikronesia + Frantzia + Ginea + Ekuatore Ginea + Grezia + Ginea-Bissau + Kroazia + Hungaria + Irlanda + Irak + Islandia + Italia + Jamaika + Jordania + Japonia + Kenia + Kirgizistan + Kanbodia + Komoreak + Saint Kitts eta Nevis + Ipar Korea + Hego Korea + Libano + Santa Luzia + Lituania + Luxenburgo + Letonia + Libia + Maroko + Monako + Moldavia + Madagaskar + Marshall uharteak + Mazedonia + Maurizio + Maldivak + Mexiko + Malasia + Mozambike + Nikaragua + Herbehereak + Norvegia + Zeelanda Berria + Papua Ginea Berria + Filipinak + Polonia + Paraguai + Katar + Errumania + Errusia + Ruanda + Salomon uharteak + Seychelleak + Suedia + Singapur + Eslovenia + Eslovakia + Sierra Leona + Serbia + Surinam + Sao Tomé eta Principe + Siria + Swazilandia + Txad + Tailandia + Tadjikistan + Turkia + Trinidad eta Tobago + Ukraina + Ameriketako Estatu Batuak + Uruguai + Vatikano + Saint Vincent eta Grenadinak + Hegoafrika + + + + [a-zñç] + + + + + + + + urt + ots + mar + api + mai + eka + uzt + abu + ira + urr + aza + abe + + + urtarrila + otsaila + martxoa + apirila + maiatza + ekaina + uztaila + abuztua + iraila + urria + azaroa + abendua + + + + + + + ig + al + as + az + og + or + lr + + + igandea + astelehena + asteartea + asteazkena + osteguna + ostirala + larunbata + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + ESP + + ¤ #,##0;-¤ #,##0 + ¤ #,##0;-¤ #,##0 + . + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fa_AF.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fa_AF.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fa_AF.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fa_AF.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,235 @@ + + + + + + + + + + + + هسپانوی + دری + فنلندی + آیرلندی + کروشیایی + اندونیزیایی + آیسلندی + ایتالوی + جاپانی + کوریایی + قرغزی + مغلی + نیپالی + هالندی + نارویژی + پولندی + پرتگالی + سویدنی + تاجکی + + + اندورا + امارات متحدهٔ عربی + انتیگوا و باربودا + البانیا + انگولا + ارجنتاین + آسترالیا + بوسنیا و هرزه‌گوینا + بنگله‌دیش + بلجیم + بلغاریا + برونی + بولیویا + برازیل + بهاماس + روسیهٔ سفید + جمهوری دموکراتیک کانگو + افریقای مرکزی + کانگو + سویس + چلی + کولمبیا + کاستریکا + کیوبا + دنمارک + اکوادور + استونیا + اریتریا + هسپانیه + ایتوپیا + فنلند + میکرونزیا + گرینادا + گینیا + گینیا استوایی + گواتیمالا + گینیا بیسائو + گیانا + هاندوراس + کروشیا + هایتی + اندونیزیا + آیرلند + آیسلند + جاپان + کینیا + قرغزستان + کمپوچیا + کومور + سنت کیتس و نیویس + کوریای شمالی + کوریای جنوبی + سریلانکا + لیسوتو + لتوانیا + لاتویا + لیبیا + مادغاسکر + منگولیا + موریتانیا + مالتا + مکسیکو + مالیزیا + موزمبیق + نیجریا + نیکاراگوا + هالند + ناروی + نیپال + زیلاند جدید + پانامه + پیرو + پاپوا نیو گینیا + پولند + پرتگال + پاراگوای + رومانیا + روآندا + جزایر سلومون + سویدن + سینگاپور + سلونیا + سلواکیا + سیرالیون + سینیگال + سومالیه + سائو تومه و پرینسیپ + السلوادور + تاجکستان + اکراین + ایالات متحدهٔ امریکا + یوروگوای + سنت وینسنت و گرینادین + ونزویلا + ساموآی غربی + یوگوسلاویا + افریقای جنوبی + زیمبابوی + + + + + + + + + + + + جنو + فبر + مار + اپر + مـی + جون + جول + اگس + سپت + اکت + نوم + دسم + + + جنوری + فبروری + مارچ + اپریل + می + جون + جولای + اگست + سپتمبر + اکتوبر + نومبر + دسمبر + + + + + + + + + + + + + وقت افغانستان + وقت افغانستان + + + AFT + AFT + + کابل + + + + + + ٫ + ٬ + ; + ٪ + ۰ + # + + + + ×۱۰^ + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0 ¤;-#,##0 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fa_IR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fa_IR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fa_IR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fa_IR.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + ٫ + ٬ + ; + ٪ + ۰ + # + + + - + ×۱۰^ + + + + + + + + #,##0.###;−#,##0.### + + + + + + + #E0 + + + + + + + %#,##0 + + + + + + + #,##0 ¤;−#,##0 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fa.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fa.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fa.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fa.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,647 @@ + + + + + + + + + + + آفاری + آبخازی + اوستایی + آفریکانس + افریقا و آسیایی (غیره) + امهری + انگلیسی باستان (حدود ۴۵۰-۱۱۰۰ م.) + زبان‌های آپاچیایی + عربی + آرامی + ساخته‌گی (غیره) + آسامی + زبان‌های استرالیایی + آیمارایی + آذربایجانی + باشکیر + بلوچی + بالتیکی (غیره) + بلوروسی + بلغاری + بیهاری + بوجپوری + بیسلاما + بنگالی + تبتی + بوسنیایی + کاتالونیایی + سرخ‌پوستی امریکای مرکزی (غیره) + چچنی + سلتی (غیره) + چامورویی + چروکی + قبطی + چکی + اسلاوی کلیسایی + چوواشی + ویلزی + دانمارکی + داکوتایی + آلمانی + دراویدی (غیره) + هلندی میانه (حدود ۱۰۵۰-۱۳۵۰ م.) + بوتانی + مصری (باستانی) + یونانی + انگلیسی + انگلیسی میانه (۱۱۰۰ -۱۵۰۰ م.) + اسپرانتو + اسپانیایی + استونیایی + باسکی + فارسی + فنلاندی + فیجیایی + فارویی + فرانسوی + فرانسوی میانه (حدود ۱۴۰۰-۱۶۰۰ م.) + فرانسوی قدیم (۸۴۲-حدود ۱۴۰۰ م.) + ایرلندی + گا + ژرمنی (غیره) + آلمانی علیای میانه (حدود ۱۰۵۰-۱۵۰۰) + گوارانی + آلمانی علیای باستان (حدود ۷۵۰-۱۰۵۰) + گوتیک + یونانی کهن (تا ۱۴۵۳ م.) + گجراتی + مانی + هوسیایی + هاوائیایی + عبری + هندی + هیتی + کرواتی + مجاری + ارمنی + هریرویی + میان‌زبان + اندونزیایی + اینوپیک + هندیک (غیره) + هندوارودپایی + ایرانی + ایسلندی + ایتالیایی + اینوکیتوت + ژاپنی + فارسی یهودی + عربی یهودی + جاوه‌ای + گرجی + قزاقی + خمری + کاناده‌ای + کره‌ای + کنکانی + کشمیری + کردی + کومیایی + کرنوالی + قرقیزی + لاتینی + لتسه‌بورگیش + لینگالا + لائوسی + لیتوانیایی + لتونیایی + آسترونیزیایی + مالاگاسیایی + ایرلندی میانه (۹۰۰-۱۲۰۰ م.) + مارشالی + مائوریایی + زبان‌های متفرقه + مقدونی + مالایالامی + مغولی + مولداویایی + موهاکی + مراتی + مالزیایی + مالتی + چندین زبان + برمه‌ای + مایاییک + نائورویی + سرخ‌پوستی امریکای شمالی (غیره) + بوکسمال نروژی + انده‌بله‌ای شمالی + آلمانی سفلی؛ ساکسون سفلب + نپالی + هلندی + نینورسک نروژی + نروژی + انده‌بله‌ای جنوبی + ناواهویی + اوریه‌ای + ترکی (امپراتوری عثمانی) + پنجابی + پاپوایی (غیره) + پهلوی + فارسی قدیم (حدود ۶۰۰-۴۰۰ ق.م.) + فیایپینی (غیره) + فنیقی + پالی + لهستانی + پشتو + پرتغالی + کچوایی + رومانیایی + ریشه + روسی + کینیارواندایی + سنسکریت + سرخ‌پوستی امریکای جنوبی (غیره) + ساردینیایی + اسکاتلندی + سندی + سامی + سانگویی + ایرلندی باستان (تا ۹۰۰ م.) + زبان‌های نشانه‌ای + صرب و کرواتی + سینهالی + چین و تبتی (غیره) + اسلواکی + اسلووینیایی + ساموآیی + شونایی + سومالیایی + آلبانیایی + صربی + سوتویی جنوبی + سوندایی + سومری + سوئدی + سواحلی + سریانی + تامیلی + تلوگویی + تاجیکی + تایلندی + تیگرینیایی + ترکمتی + تاگالوگی + تسوانایی + تونگایی (جزایر تونگا) + ترکی + تسونگایی + تاتاری + توی‌یایی + تاهیتیایی + اویغوری + اوکراینی + نامشخص + اردو + ازبکی + ویتنامی + ولاپوک + ولوفی + خوسایی + یدی + یوروبایی + چینی + زولویی + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + آندورا + امارات متحده‌ی عربی + افغانستان + آنتیگوا و باربودا + آنگیل + آلبانی + ارمنستان + آنتیل هلند + آنگولا + جنوبگان + آرژانتین + ساموای امریکا + اتریش + استرالیا + آروبا + آذربایجان + بوسنی و هرزگوین + باربادوس + بنگلادش + بلژیک + بورکینافاسو + بلغارستان + بحرین + بوروندی + بنین + برمودا + برونئی + بولیوی + برزیل + باهاما + بوتان + جزیره‌ی بووت + بوتسوانا + بیلوروسی + بلیز + کانادا + جزایر کوکوس + جمهوری دموکراتیک کنگو + جمهوری افریقای مرکزی + کونگو + سوئیس + ساحل عاج + جزایر کوک + شیلی + کامرون + چین + کلمبیا + کاستاریکا + کوبا + کیپ ورد + جزیره‌ی کریسمس + قبرس + جمهوری چک + آلمان + جیبوتی + دانمارک + دومینیکا + جمهوری دومینیکن + الجزایر + اکوادر + استونی + مصر + صحرای غربی + اریتره + اسپانیا + اتیوپی + فنلاند + فیجی + جزایر فالکلند + میکرونزی + جزایر فارو + فرانسه + گابون + انگلستان + گرانادا + گرجستان + گویان فرانسه + غنا + گیبرالتار + گروئنلند + گامبیا + گینه + گوادلوپ + گینه‌ی استوایی + یونان + جورجیای جنوبی و جزایر ساندویچ جنوبی + گواتمالا + گوام + گینه‌ی بیسائو + گویان + هنگ‌کنگ + جزیره‌ی هرد و جزایر مک‌دونالد + هندوراس + کرواسی + هاییتی + مجارستان + اندونزی + ایرلند + اسرائیل + هند + مستعمره‌های انگلستان در اقیانوس هند + عراق + ایران + ایسلند + ایتالیا + جامائیکا + اردن + ژاپن + کنیا + قرقیزستان + کامبوج + کیریباتی + کومورو + سنت کیتس و نویس + کره‌ی شمالی + کره‌ی جنوبی + کویت + جزایر کِیمن + قزاقستان + لائوس + لبنان + سنت لوسیا + لیختن‌اشتاین + سری‌لانکا + لیبریا + لسوتو + لیتوانی + لوکزامبورگ + لتونی + لیبی + مراکش + موناکو + مولدووا + ماداگاسکار + جزایر مارشال + مقدونیه + مالی + میانمار + مغولستان + ماکائو + جزایر ماریانای شمالی + مارتینیک + موریتانی + مونت‌سرات + مالت + موریتیوس + مالدیو + مالاوی + مکزیک + مالزی + موزامبیک + نامیبیا + کالدونیای جدید + نیجر + جزیره‌ی نورفولک + نیجریه + نیکاراگوئه + هلند + نروژ + نپال + نائورو + نیوئه + زلاند نو + عمان + پاناما + پرو + پلی‌نزی فرانسه + پاپوا گینه‌ی نو + فیلیپین + پاکستان + لهستان + سنت پیر و میکلون + پیتکایرن + پورتو ریکو + پرتغال + پالائو + پاراگوئه + قطر + ریونیون + رومانی + روسیه + رواندا + عربستان سعودی + جزایر سلیمان + سیشل + سودان + سوئد + سنگاپور + سنت هلن + اسلوونی + اسوالبارد و جان ماین + اسلواکی + سیرالئون + سان مارینو + سنگال + سومالی + صربستان + سورینام + سائو تومه و پرینسیپه + السالوادور + سوریه + سوازیلند + جزایر ترک و کایکوس + چاد + مستعمره‌های جنوبی فرانسه + توگو + تایلند + تاجیکستان + توکلائو + تیمور شرقی + ترکمنستان + تونس + تونگا + ترکیه + ترینیداد و توباگو + تووالو + تایوان + تانزانیا + اوکراین + اوگاندا + جزایر کوچک دورافتاده‌ی ایالات متحده + ایالات متحده‌ی امریکا + اوروگوئه + ازبکستان + واتیکان + سنت وینسنت و گرنادین + ونزوئلا + جزایر ویرجین انگلستان + جزایر ویرجین ایالات متحده + ویتنام + وانواتو + والیس و فیوتونا + ساموا + یمن + مایوت + یوگسلاوی + افریقای جنوبی + زامبیا + زیمبابوه + + + + + + + [ء-ؤئ-غفقل-وً-ْٰٔپچژکگی‌‍‏‎] + + + + + + + + ژان + فور + مار + آور + مـه + ژون + ژوی + اوت + سپت + اکت + نوا + دسا + + + ژانویه + فوریه + مارس + آوریل + مه + ژوئن + ژوئیه + اوت + سپتامبر + اکتبر + نوامبر + دسامبر + + + + + + + ی. + د. + س. + چ. + پ. + ج. + ش. + + + یک‌شنبه + دوشنبه + سه‌شنبه + چهارشنبه + پنج‌شنبه + جمعه + شنبه + + + + ق.ظ. + ب.ظ. + + + ق.م. + م. + + + + + + + EEEE، d MMMM yyyy + + + + + d MMMM yyyy + + + + + yyyy/MM/d + + + + + yyyy/MM/d + + + + + + + + HH:mm:ss (z) + + + + + HH:mm:ss (z) + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + وقت استاندارد تهران + وقت تابستانی تهران + + + IRST + IRDT + + تهران + + + + + + + افغانی + افغانی + + + IRR + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fi_FI.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fi_FI.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fi_FI.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fi_FI.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fi.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fi.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fi.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fi.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2876 @@ + + + + + + + + + + + afar + abhaasi + aceh + adangme + adyghe + avesta + afrikaans + muut afroaasialaiset kielet + afrihili + akan + akkadi + aleutti + algonkin-kielet + amhara + aragonia + muinaisenglanti + apassi + arabia + aramea + araukaani + arapaho + muut tekokielet + arawak + assami + astuuri + athabasca-kielet + australialaiset kielet + avaari + awadhi + aimara + azerbaizani + baškiiri + banda + bamileke + balutši + bambara + bali + basa + muut balttilaiset kielet + valkovenäjä + bedauje + bemba + berberi + bulgaria + bihari + bhodžpuri + bislama + bicol + bini + mustajalka + bambara + bengali + bantu + tiibet + bretoni + bradž + bosnia + batakki + burjaatti + bugi + blin + katalaani + caddo + muut Keski-Amerikan intiaanikielet + karib + muut kaukasialaiset kielet + tšetšeeni + sebuano + muut kelttiläiset kielet + chamorro + chibcha + tšagatai + truk + mari + chinook + choctaw + chipewya + cherokee + cheyenne + tšam + korsika + kopti + pidginenglanti + muut ranskaan perustuvat kreolit ja pidginit + muut portugaliin perustuvat kreolit ja pidginit + cree + krimin turkki; krimin tataari + muut kreolit ja pidginit + tsekki + kashubi + kirkkoslaavi + muut kuusilaiset kielet + tšuvassi + kymri + tanska + sioux + dargva + dajakki + saksa + delaware + slaavi + dogrib + dinka + dogri + muut dravidakielet + ala-sorbi + duala + keskihollanti + malediivi + djula + bhutani + ewe + efik + muinaisegypti + ekajuk + kreikka + elami + englanti + keskienglanti + esperanto + espanja + viro + baski + ewondo + farsi + fang + fanti + fulani + suomi + muut suomalais-ugrilaiset kielet + fidži + fääri + fong + ranska + keskiranska + muinaisranska + friuli + friisi + iiri + ga + gayo + gbaja + gaeli + muut germaaniset kielet + etiopia + kiribati + galicia + keskiyläsaksa + guarani + muinaisyläsaksa + gondi + gorontalo + gootti + grebo + muinaiskreikka + gujarati + manx + gwitşin + hausa + haida + havaiji + heprea + hindi + ilongo + himachali + heetti + hmong + hiri-motu + kroaatti + ylä-sorbi + haiti + unkari + hupa + armenia + herero + interlingua + iban + indonesia + interlingue + igbo + pohjois-ji + inupiak + iloko + muut intialaiset kielet + muut indoeurooppalaiset kielet + inguuši + ido + iran + irokeesi + islanti + italia + eskimo + japani + lojba + juutalaispersia + juutalaisarabia + jaava + georgia + karakalpakki + kabyyli + džingpho + kamba + karen + kavi + kabardi + kongo + khasi + muut khoisankielet + khotani + kikuju + kuanjama + kazakki + grönlanti + khmer + kimbundu + kannada + korea + konkani + kosrae + kpelle + kanuri + karachay-balkar + kru-kielet + kurukh + kašmiri + kurdi + kumukki + kutenai + komi + korni + kirgiisi + latina + juutalaisespanja + lahnda + lamba + luxemburg + lezgi + ganda + limburgi + lingala + lao + mongo + lozi + liettua + katangan luba + luba + luiseno + lunda + lushai + latvia + madura + magahi + maithili + makassar + mandingo + austronesia + maasai + mokša + mandar + mende + malagasi + keski-iiri + marshalli + maori + micmac + minangkabau + sekalaiset kielet + makedonia + muut mon-khmer-kielet + malajalam + mongoli + mantšu + manipuri + manobo-kielet + moldova + mohawk + mosi + marathi + malaiji + malta + monikielinen + mundakielet + muskogi + marwari + burma + maya + ersä + nauru + nahuatl + muut Pohjois-Amerikan intiaanikielet + napolin italia + kirjanorja + pohjoisndebele + alasaksa + nepali + newari + ndonga + nias + muut nigeriläis-kongolaiset kielet + niue + hollanti + uusnorja + norja + nogai + muinaisskandinaavi + eteländebele + pedi + nuubialaiset kielet + navaho + njandža, tšewa + nyamwezi + nyankole + nyoro + nzima + oksitaani, provensaali + ojibwa + oromo + orija + osseetti + osage + osmani + otomangelaiset kielet + punjabi + muut papualaiskielet + pangasinan + pahlavi + pampanga + papiamentu + palau + muinaispersia + muut filippiiniläiskielet + foinikia + pali + puola + pohnpei + prakriitit + muinaisprovensaali + pašto + portugali + ketšua + rajasthani + rapanui + rarotonga + retoromaani + rundi + romania + muut romaaniset kielet + romani + venäjä + ruanda + sanskriitti + sandawe + jakuutti + muut Etelä-Amerikan intiaanikielet + sališ + länsiaramea + sasak + santali + sardi + skotti + sindhi + pohjoissaame + selkuppi + muut seemiläiset kielet + sango + muinaisiiri + viittomakielet + serbokroatia + šan + singaleesi + sidamo + siouxilaiset kielet + muut sinotiibetiläiset kielet + slovakki + sloveeni + muut slaavilaiset kielet + samoa + eteläsaame + muut saamen kielet + luulajan saame + inarinsaame + koltansaame + shona + soninke + somali + sogdi + songhai + albania + serbia + serer + swazi + muut nilosaharalaiset kielet + eteläsotho + sunda + sukuma + susu + sumeri + ruotsi + suahili + syyria + tamili + muut thaikielet + telugu + temne + tereno + tetum + tadžikki + thai + tigrinja + tigre + turkmeeni + tokelau + tagalog + tlingit + tamašek + tswana + Tonga-saarten tonga + tonga + tok-pisin + turkki + tsonga + tsimshian + tataari + tumbuka + tupilaiset kielet + muut altailaiset kielet + tuvalu + twi + tahiti + tuviini + udmurtti + uiguuri + ugarit + ukraina + umbundu + määrittelemätön + urdu + uzbekki + venda + vietnam + volapük + vatja + valloni + wakasilaiset kielet + walamo + waray + washo + sorbi + wolof + kalmukki + kafferi + jao + jap + jiddi + joruba + juppik-kielet + zhuang + zapoteekki + zenaga + kiina + zande + zulu + zuni + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + Arabiemiirikunnat + Afganistan + Antigua ja Barbuda + Anguilla + Albania + Armenia + Alankomaiden Antillit + Angola + Antarktis + Argentiina + Amerikan Samoa + Itävalta + Australia + Aruba + Azerbaidzan + Bosnia ja Hertsegovina + Barbados + Bangladesh + Belgia + Burkina Faso + Bulgaria + Bahrain + Burundi + Benin + Bermuda + Brunei + Bolivia + Brasilia + Bahama + Bhutan + Bouvet’nsaari + Botswana + Valko-Venäjä + Belize + Kanada + Kookossaaret + Kongon demokraattinen tasavalta + Keski-Afrikan tasavalta + Kongo + Sveitsi + Norsunluurannikko + Cookinsaaret + Chile + Kamerun + Kiina + Kolumbia + Costa Rica + Kuuba + Kap Verde + Joulusaari + Kypros + Tsekin tasavalta + Saksa + Djibouti + Tanska + Dominica + Dominikaaninen tasavalta + Algeria + Ecuador + Viro + Egypti + Länsi-Sahara + Eritrea + Espanja + Etiopia + Suomi + Fidzi + Falklandinsaaret + Mikronesia + Färsaaret + Ranska + en + Gabon + Iso-Britannia + Grenada + Georgia + Ranskan Guayana + Ghana + Gibraltar + Grönlanti + Gambia + Guinea + Guadeloupe + Päiväntasaajan Guinea + Kreikka + Etelä-Georgia ja Eteläiset Sandwichsaaret + Guatemala + Guam + Guinea-Bissau + Guyana + Hongkongin erityishallintoalue + Heard ja McDonaldinsaaret + Honduras + Kroatia + Haiti + Unkari + Indonesia + Irlanti + Israel + Intia + Brittiläinen Intian valtameren alue + Irak + Iran + Islanti + Italia + Jamaika + Jordania + Japani + Kenia + Kirgisia + Kambodza + Kiribati + Komorit + Saint Kitts ja Nevis + Pohjois-Korea + Korean tasavalta + Kuwait + Caymansaaret + Kazakstan + Laos + Libanon + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Liettua + Luxemburg + Latvia + Libya + Marokko + Monaco + Moldova + Madagaskar + Marshallinsaaret + Makedonia + Mali + Myanmar + Mongolia + Macaon erityishallintoalue + Pohjois-Mariaanit + Martinique + Mauritania + Montserrat + Malta + Mauritius + Malediivit + Malawi + Meksiko + Malesia + Mosambik + Namibia + Uusi-Kaledonia + Niger + Norfolkinsaari + Nigeria + Nicaragua + Alankomaat + Norja + Nepal + Nauru + Niue + Uusi-Seelanti + Oman + Panama + Peru + Ranskan Polynesia + Papua-Uusi-Guinea + Filippiinit + Pakistan + Puola + Saint-Pierre ja Miquelon + Pitcairn + Puerto Rico + Palestiina + Portugali + Palau + Paraguay + Qatar + Réunion + Romania + Venäjä + Ruanda + Saudi-Arabia + Salomonsaaret + Seychellit + Sudan + Ruotsi + Singapore + Saint Helena + Slovenia + Huippuvuoret ja Jan Mayen + Slovakia + Sierra Leone + San Marino + Senegal + Somalia + Serbia + Suriname + Sao Tome ja Principe + El Salvador + Syyria + Swazimaa + Turks- ja Caicossaaret + Tsad + Ranskan eteläiset alueet + Togo + Thaimaa + Tadzikistan + Tokelau + Timor-Leste + Turkmenistan + Tunisia + Tonga + Turkki + Trinidad ja Tobago + Tuvalu + Taiwan + Tansania + Ukraina + Uganda + Yhdysvaltain pienet erillissaaret + Yhdysvallat + Uruguay + Uzbekistan + Vatikaani + Saint Vincent ja Grenadiinit + Venezuela + Brittiläiset Neitsytsaaret + Yhdysvaltain Neitsytsaaret + Vietnam + Vanuatu + Wallis ja Futuna + Samoa + Jemen + Mayotte + Jugoslavia + Etelä-Afrikka + Sambia + Zimbabwe + + + Kalenteri + Kerääminen + Valuutta + + + Buddhalainen kalenteri + Kiinalainen kalenteri + Gregoriaaninen kalenteri + Juutalainen kalenteri + Islamilainen kalenteri + Islamilainen siviilikalenteri + Japanilainen kalenteri + Suora järjestys + Osoitekirjajärjestys + Pinyin-järjestys + Piirtojärjestys + Perinteinen + + + + [a-z ä ö] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + tammi + helmi + maalis + huhti + touko + kesä + heinä + elo + syys + loka + marras + joulu + + + T + H + M + H + T + K + H + E + S + L + M + J + + + tammikuu + helmikuu + maaliskuu + huhtikuu + toukokuu + kesäkuu + heinäkuu + elokuu + syyskuu + lokakuu + marraskuu + joulukuu + + + + + + + su + ma + ti + ke + to + pe + la + + + S + M + T + K + T + P + L + + + sunnuntai + maanantai + tiistai + keskiviikko + torstai + perjantai + lauantai + + + + + + + + ap. + ip. + + + eaa. + jaa. + + + + + + + d. MMMM'ta 'yyyy + + + + + d. MMMM'ta 'yyyy + + + + + d.M.yyyy + + + + + d.M.yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Tišrìkuu + Hešvánkuu + Kislévkuu + Tevétkuu + Ševatkuu + Adárkuu + Adárkuu II + Nisánkuu + Ijjárkuu + Sivánkuu + Tammúzkuu + Abkuu + Elúlkuu + + + Tišrìkuu + Hešvánkuu + Kislévkuu + Tevétkuu + Ševatkuu + Adárkuu + Adárkuu II + Nisánkuu + Ijjárkuu + Sivánkuu + Tammúzkuu + Abkuu + Elúlkuu + + + + + + + + + Muhárram + Sáfar + Rabíʻ al-áwwal + Rabíʻ al-ákhir + Džumada-l-úla + Džumada-l-ákhira + Radžab + Šaʻbán + Ramadán + Šawwal + Dhu-l-qada + Dhu-l-hiddža + + + Muhárram + Sáfar + Rabíʻ al-áwwal + Rabíʻ al-ákhir + Džumada-l-úla + Džumada-l-ákhira + Radžab + Šaʻbán + Ramadán + Šawwal + Dhu-l-qada + Dhu-l-hiddža + + + + + + + + + Muhárram + Sáfar + Rabíʻ al-áwwal + Rabíʻ al-ákhir + Džumada-l-úla + Džumada-l-ákhira + Radžab + Šaʻbán + Ramadán + Šawwal + Dhu-l-qada + Dhu-l-hiddža + + + Muhárram + Sáfar + Rabíʻ al-áwwal + Rabíʻ al-ákhir + Džumada-l-úla + Džumada-l-ákhira + Radžab + Šaʻbán + Ramadán + Šawwal + Dhu-l-qada + Dhu-l-hiddža + + + + + + + + + Tyynenmeren normaaliaika + Tyynenmeren kesäaika + + + PST + PDT + + Los Angeles + + + + Tyynenmeren normaaliaika + Tyynenmeren kesäaika + + + PST + PDT + + Los Angeles + + + + Kalliovuorten normaaliaika + Kalliovuorten kesäaika + + + MST + MDT + + Denver + + + + Kalliovuorten normaaliaika + Kalliovuorten kesäaika + + + MST + MDT + + Denver + + + + Kalliovuorten normaaliaika + Kalliovuorten normaaliaika + + + MST + MST + + Phoenix + + + + Kalliovuorten normaaliaika + Kalliovuorten normaaliaika + + + MST + MST + + Phoenix + + + + Keskinen normaaliaika + Keskinen kesäaika + + + CST + CDT + + Chicago + + + + Keskinen normaaliaika + Keskinen kesäaika + + + CST + CDT + + Chicago + + + + Itäinen normaaliaika + Itäinen kesäaika + + + EST + EDT + + New York + + + + Itäinen normaaliaika + Itäinen kesäaika + + + EST + EDT + + New York + + + + Itäinen normaaliaika + Itäinen normaaliaika + + + EST + EST + + Indianapolis + + + + Itäinen normaaliaika + Itäinen normaaliaika + + + EST + EST + + Indianapolis + + + + Havaijin normaaliaika + Havaijin kesäaika + + + HST + HST + + Honolulu + + + + Havaijin normaaliaika + Havaijin normaaliaika + + + HST + HST + + Honolulu + + + + Alaskan normaaliaika + Alaskan kesäaika + + + AST + ADT + + Anchorage + + + + Alaskan normaaliaika + Alaskan kesäaika + + + AST + ADT + + Anchorage + + + + Atlantin normaaliaika + Atlantin kesäaika + + + AST + ADT + + Halifax + + + + Newfoundlandin normaaliaika + Newfoundlandin kesäaika + + + CNT + CDT + + St. Johns + + + + Newfoundlandin normaaliaika + Newfoundlandin kesäaika + + + CNT + CDT + + St. Johns + + + + Keski-Euroopan normaaliaika + Keski-Euroopan kesäaika + + + CET + CEST + + Pariisi + + + + Keski-Euroopan normaaliaika + Keski-Euroopan kesäaika + + + CET + CEST + + Pariisi + + + + Greenwichin aika + Greenwichin aika + + + GMT + GMT + + Lontoo + + + + Greenwichin aika + Greenwichin aika + + + GMT + GMT + + Casablanca + + + + Israelin normaaliaika + Israelin kesäaika + + + IST + IDT + + Jerusalem + + + + Japanin normaaliaika + Japanin normaaliaika + + + JST + JST + + Tokio + + + + Japanin normaaliaika + Japanin normaaliaika + + + JST + JST + + Tokio + + + + Itä-Euroopan normaaliaika + Itä-Euroopan kesäaika + + + EET + EEST + + Bukarest + + + + Kiinan normaaliaika + Kiinan normaaliaika + + + CTT + CDT + + Shanghai + + + + Kiinan normaaliaika + Kiinan normaaliaika + + + CTT + CDT + + Shanghai + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + Andorran dinaari + ADD + + + Andorran peseta + ADP + + + Arabiemiirikuntien dirhami + AED + + + Afgaani (1927-2002) + AFA + + + Afgaani + Af + + + Affarsin ja Issasin frangi + AIF + + + Albanian lek (1946-1961) + ALK + + + Albanian lek + lek + + + Albanian lek valute + ALV + + + Albanian dollarin FEC + ALX + + + Armenian dram + dram + + + Alankomaiden Antillien guldeni + NA f. + + + Angolan kwanza + AOA + + + Angolan kwanza (1977-1990) + AOK + + + Angolan uusi kwanza (1990-2000) + AON + + + Angolan kwanza reajustado (1995-1999) + AOR + + + Angolan escudo + AOS + + + Argentiinan austral + ARA + + + Argentiinan peso moneda nacional + ARM + + + Argentiinan peso (1983-1985) + ARP + + + Argentiinan peso + Arg$ + + + Itävallan shillinki + ATS + + + Australian dollari + $A + + + Australian punta + AUP + + + Aruban guldeni + AWG + + + Azerbaidžanin manat + AZM + + + Bosnia-Hertsegovinan dinaari + BAD + + + Bosnia-Hertsegovinan vaihdettava markka + KM + + + Bosnia-Hertsegovinan uusi dinaari + BAN + + + Barbadosin dollari + BDS$ + + + Bangladeshin taka + Tk + + + Belgian frangi (vaihdettava) + BEC + + + Belgian frangi + BF + + + Belgian frangi (rahoitus) + BEL + + + Bulgarian kova leva + lev + + + Bulgarian sosialistinen lev + BGM + + + Bulgarian uusi lev + BGN + + + Bulgarian lev (1879-1952) + BGO + + + Bulgarian lev FEC + BGX + + + Bahrainin dinaari + BD + + + Burundin frangi + Fbu + + + Bermudan dollari + Ber$ + + + Bermudan punta + BMP + + + Brunein dollari + BND + + + Boliviano + Bs + + + Boliviano (1863-1962) + BOL + + + Bolivian peso + BOP + + + Bolivian mvdol + BOV + + + Brasilian uusi cruzeiro (1967-1986) + BRB + + + Brasilian cruzado + BRC + + + Brasilian cruzeiro (1990-1993) + BRE + + + Brasilian real + R$ + + + Brasilian uusi cruzado + BRN + + + Brasilian cruzeiro + BRR + + + Brazilian cruzeiro (1942-1967) + BRZ + + + Bahaman dollari + BSD + + + Bahaman punta + BSP + + + Bhutanin ngultrum + Nu + + + Bhutanin rupia + BTR + + + Burman kyat + BUK + + + Burman rupia + BUR + + + Botswanan pula + BWP + + + Valko-Venäjän uusi rupla (1994-1999) + BYB + + + Valko-Venäjän rupla (1992-1994) + BYL + + + Valko-Venäjän rupla + Rbl + + + Belizen dollari + BZ$ + + + Brittiläisen Hondurasin dollari + BZH + + + Kanadan dollari + Can$ + + + Kongon kongolainen frangi + CDF + + + Kongon tasavallan frangi + CDG + + + Kongon zaire + CDL + + + Keski-Afrikan tasavallan CFA-frangi + CFF + + + Sveitsin frangi + SwF + + + Cookinsaarten dollari + CKD + + + Chilen condor + CLC + + + Chilen escudo + CLE + + + Chilen unidades de fomento + CLF + + + Chilen peso + Ch$ + + + Kamerunin CFA-frangi + CMF + + + Kiinan jen min piao yuan + CNP + + + Kiinan US-dollarin FEC + CNX + + + Kiinan yuan renminbi + Y + + + Kolumbian paperipeso + COB + + + Kongon CFA-frangi + COF + + + Kolumbian peso + Col$ + + + Costa Rican colon + C + + + Tšekkoslovakian koruna + CSC + + + Tšekkoslovakian kova koruna + CSK + + + Kuuban peso + CUP + + + Kuuban FEC + CUX + + + Kap Verden escudo + CVEsc + + + Curacaon guldeni + CWG + + + Kyproksen punta + £C + + + Tšekin koruna + CZK + + + Itä-Saksan ostmark + DDM + + + Saksan markka + DEM + + + Saksan sperrmark + DES + + + Djiboutin frangi + DF + + + Tanskan kruunu + DKr + + + Dominikaanisen tasavallan peso + RD$ + + + Algerian dinaari + DA + + + Algerian uusi frangi + DZF + + + Algerian franc germinal + DZG + + + Ecuadorin sucre + ECS + + + Ecuadorin UVC + ECV + + + Viron kruunu + EEK + + + Egyptin punta + EGP + + + Eritrean nakfa + ERN + + + Espanjan peseta + ESP + + + Etiopian birr + Br + + + Etiopian dollari + ETD + + + euro + + + + Suomen markka + mk + + + Suomen markka (1860-1962) + FIN + + + Fidžin dollari + F$ + + + Fidžin punta + FJP + + + Falklandinsaarten punta + FKP + + + Färsaarten kruunu + FOK + + + Ranskan frangi + FRF + + + Ranskan franc germinal/franc poincare + FRG + + + Gabonin CFA-frangi + GAF + + + Iso-Britannian sterling-punta + £ + + + Georgian kuponkilari + GEK + + + Georgian lari + lari + + + Ghanan cedi + GHC + + + Ghanan vanha cedi + GHO + + + Ghanan punta + GHP + + + Ghanan revalvoitu cedi + GHR + + + Gibraltarin punta + GIP + + + Grönlannin kruunu + GLK + + + Gambian dalasi + GMD + + + Gambian punta + GMP + + + Guinean frangi + GF + + + Guinean frangi (1960-1972) + GNI + + + Guinean syli + GNS + + + Guadeloupen frangi + GPF + + + Päiväntasaajan Guinean ekwele guineana + GQE + + + Päiväntasaajan Guinean frangi + GQF + + + Päiväntasaajan Guinean peseta guineana + GQP + + + Kreikan drakhma + GRD + + + Kreikan uusi drakhma + GRN + + + Guatemalan quetzal + Q + + + Ranskan Guyanan franc guiana + GUF + + + Portugalin Guinean escudo + GWE + + + Portugalin Guinean mil reis + GWM + + + Guinea-Bissaun peso + GWP + + + Guyanan dollari + G$ + + + Hong Kongin dollari + HK$ + + + Hondurasin lempira + L + + + Kroatian dinaari + HRD + + + Kroatian kuna + HRK + + + Haitin gourde + HTG + + + Unkarin forintti + Ft + + + Pohjois-Irlannin punta + IBP + + + Indonesian nica guldeni + IDG + + + Indonesian java rupia + IDJ + + + Indonesian uusi rupia + IDN + + + Indonesian rupia + Rp + + + Irlannin punta + IR£ + + + Israelin sekeli + ILL + + + Israelin punta + ILP + + + Israelin uusi sekeli + ILS + + + Mansaaren sterling-punta + IMP + + + Intian rupia + =0#Rs.|1#Re.|1<Rs. + + + Irakin dinaari + ID + + + Iranin rial + RI + + + Islannin kruunu + ISK + + + Italian liira + + + + Jerseyn sterling-punta + JEP + + + Jamaikan dollari + J$ + + + Jamaikan punta + JMP + + + Jordanian dinaari + JD + + + Japanin jeni + ¥ + + + Kenian shillinki + K Sh + + + Kirgistanin som + som + + + Kambodžan vanha riel + KHO + + + Kambodžan riel + CR + + + Kiribatin dollari + KID + + + Komorien frangi + CF + + + Pohjois-Korean kansan won + KPP + + + Pohjois-Korean won + KPW + + + Etelä-Korean hwan + KRH + + + Etelä-Korean vanha won + KRO + + + Etelä-Korean won + KRW + + + Kuwaitin dinaari + KD + + + Caymansaarten dollari + KYD + + + Kazakhstanin rupla + KZR + + + Kazakhstanin tenge + T + + + Laosin kip + LAK + + + Libanonin punta + LL + + + Liechtensteinin frangi + LIF + + + Sri Lankan rupia + SL Re + + + Ceylonin rupia + LNR + + + Liberian dollari + LRD + + + Lesothon loti + M + + + Liettuan liti + LTL + + + Liettuan talonas + LTT + + + Luxemburgin frangi + LUF + + + Latvian lati + LVL + + + Latvian rupla + LVR + + + Libyan sotilasvallan liira + LYB + + + Libyan dinaari + LD + + + Libyan punta + LYP + + + Marokon dirhami + MAD + + + Marokon frangi + MAF + + + Monacon uusi frangi + MCF + + + Monacon franc germinal + MCG + + + Moldovan kuponkileu + MDC + + + Moldovan leu + MDL + + + Moldovan kuponkirupla + MDR + + + Madagaskarin ariary + MGA + + + Madagaskarin frangi + MGF + + + Marshallinsaarten dollari + MHD + + + Makedonian dinaari + MDen + + + Makedonian dinaari (1992-1993) + MKN + + + Malin frangi + MLF + + + Myanmarin kyat + MMK + + + Myanmarin dollarin FEC + MMX + + + Mongolian tugrik + Tug + + + Macaon pataca + MOP + + + Martiniquen frangi + MQF + + + Mauritanian ouguiya + UM + + + Maltan liira + Lm + + + Maltan punta + MTP + + + Mauritiuksen rupia + MUR + + + Malediivien rupia + MVP + + + Malediivien rufiyaa + MVR + + + Malawin kwacha + MK + + + Malawin punta + MWP + + + Meksikon peso + MEX$ + + + Meksikon hopeapeso (1861-1992) + MXP + + + Meksikon UDI + MXV + + + Malesian ringgit + RM + + + Mosambikin escudo + MZE + + + Mosambikin metical + Mt + + + Namibian dollari + N$ + + + Uuden-Kaledonian franc germinal + NCF + + + Nigerian naira + NGN + + + Nigerian punta + NGP + + + Uusien-Hebridien CFP-frangi + NHF + + + Nicaraguan cordoba + NIC + + + Nicaraguan kultacordoba + NIG + + + Nicaraguan cordoba oro + NIO + + + Alankomaiden guldeni + NLG + + + Norjan kruunu + NKr + + + Nepalin rupia + Nrs + + + Uuden-Seelannin dollari + $NZ + + + Uuden-Seelannin punta + NZP + + + Omanin rial + RO + + + Omanin rial saidi + OMS + + + Panaman balboa + PAB + + + Transdniestrian kuponkirupla + PDK + + + Transdniestrian uusi rupla + PDN + + + Transdniestrian rupla + PDR + + + Perun inti + PEI + + + Perun uusi sol + PEN + + + Perun sol + PES + + + Papua-Uuden-Guinean kina + PGK + + + Filippiinien peso + PHP + + + Pakistanin rupia + Pra + + + Puolan zloty + Zl + + + Puolan US-dollarin FEC + PLX + + + Puolan zloty (1950-1995) + PLZ + + + Palestiinan punta + PSP + + + Portugalin conto + PTC + + + Portugalin escudo + PTE + + + Paraguayn guarani + PYG + + + Qatarin rial + QR + + + Reunionin frangi + REF + + + Romanian lei + leu + + + Romanian uusi lei + RON + + + Venäjän rupla + RUB + + + Venäjän rupla (1991-1998) + RUR + + + Ruandan frangi + RWF + + + Saudi-Arabian rial + SRl + + + Saudi-Arabian itsenäinen rial + SAS + + + Salomonsaarten dollari + SI$ + + + Seychellien rupia + SR + + + Sudanin dinaari + SDD + + + Sudanin punta + SDP + + + Ruotsin kruunu + SKr + + + Singaporen dollari + S$ + + + Saint Helenan punta + SHP + + + Slovenian tolar bons + SIB + + + Slovenian tolar + SIT + + + Slovakin koruna + Sk + + + Sierra Leonen leone + SLL + + + San Marinon liira + SML + + + Somalin shillinki + So. Sh. + + + Somalimaan shillinki + SQS + + + Surinamin guldeni + Sf + + + Skotlannin punta + SSP + + + São Tomén ja Principén dobra + Db + + + São Tomén ja Principén escudo + STE + + + Neuvostoliiton uusi rupla + SUN + + + Neuvostoliiton rupla + SUR + + + El Salvadorin colon + SVC + + + Syyrian punta + LS + + + Swazimaan lilangeni + E + + + Turks- ja Caicossaarten crown + TCC + + + Tšadin CFA-frangi + TDF + + + Thaimaan baht + THB + + + Tadžikistanin rupla + TJR + + + Tadžikistanin somoni + TJS + + + Turkmenistanin manat + TMM + + + Tunisian dinaari + TND + + + Tongan paʻanga + T$ + + + Tongan sterling-punta + TOS + + + Timorin escudo + TPE + + + Timorin pataca + TPP + + + Turkin liira + TL + + + Trinidadin ja Tobagon dollari + TT$ + + + Trinidadin ja Tobagon vanha dollari + TTO + + + Tuvalun dollari + TVD + + + Taiwanin uusi dollari + NT$ + + + Tansanian shillinki + T Sh + + + Ukrainan hryvnia + UAH + + + Ukrainan karbovanetz + UAK + + + Ugandan shillinki (1966-1987) + UGS + + + Ugandan shillinki + U Sh + + + Yhdysvaltain dollari + US$ + + + Yhdysvaltain dollari (Seuraava päivä) + USN + + + Yhdysvaltain dollari (Sama päivä) + USS + + + Uruguayn peso fuerte + UYF + + + Uruguayn peso (1975-1993) + UYP + + + Uruguayn peso uruguayo + Ur$ + + + Uzbekistanin kuponkisom + UZC + + + Uzbekistanin som + UZS + + + Vatikaanin kaupungin liira + VAL + + + Pohjois-Vietnamin piastre dong viet + VDD + + + Pohjois-Vietnamin uusi dong + VDN + + + Pohjois-Vietnamin viet minh piastre dong viet + VDP + + + Venezuelan bolivar + Be + + + Brittiläisten Neitsytsaarten dollari + VGD + + + Vietnamin dong + VND + + + Vietnamin uusi dong + VNN + + + Vietnamin tasavallan dong + VNR + + + Vietnamin kansallinen dong + VNS + + + Vanuatun vatu + VT + + + Länsi-Samoan punta + WSP + + + Länsi-Samoan tala + WST + + + Aasian dinaarin UA + XAD + + + CFA-frangi BEAC + XAF + + + Aasian rahayksikkö (AMU) + XAM + + + Kulta + XAU + + + EURCO + XBA + + + Euroopan rahayksikkö (EMU) + XBB + + + EUA (XBC) + XBC + + + EUA (XBD) + XBD + + + Itä-Karibian dollari + EC$ + + + CFA uusi frangi + XCF + + + Erityiset nosto-oikeudet + XDR + + + CFA-frangi BCEAEC + XEF + + + Euroopan valuuttayksikkö + XEU + + + Ranskan kulta frangi + XFO + + + Ranskan UIC-frangi + XFU + + + Islamin dinaari + XID + + + Ranskan emämaan uusi frangi + XMF + + + Ranskan Antillien CFA-frangi + XNF + + + CFA-frangi BCEAO + XOF + + + CFP-frangi + CFPF + + + COMECONin siirrettävä rupla + XTR + + + Jemenin dinaari + YDD + + + Jemenin imadi rial + YEI + + + Jemenin rial + YRl + + + Jugoslavian kova dinaari + YUD + + + Jugoslavian liittovaltion dinaari + YUF + + + Jugoslavian 1994 dinaari + YUG + + + Jugoslavian uusi dinaari + YUM + + + Jugoslavian vaihdettava dinaari + YUN + + + Jugoslavian lokakuun dinaari + YUO + + + Jugoslavian uudistettu dinaari + YUR + + + Etelä-Afrikan randi (rahoitus) + ZAL + + + Etelä-Afrikan punta + ZAP + + + Etelä-Afrikan randi + R + + + Zambian kwacha + ZMK + + + Zambian punta + ZMP + + + Zairen uusi zaire + ZRN + + + Zairen zaire + ZRZ + + + Zimbabwen dollari + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fo_FO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fo_FO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fo_FO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fo_FO.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;¤ -#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fo.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fo.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fo.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fo.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,276 @@ + + + + + + + + + + + føroyskt + + + Sameindu Emirríkini + Afganistan + Antigua og Barbuda + Antarktis + Eysturríki + Avstralia + Aserbajdsjan + Bosnia-Hersegovina + Bangladesj + Belgia + Brasilia + Butan + Botsvana + Hvítarussland + Belis + Kanada + Miðafrikalýðveldið + Kongo + Sveis + Fílabeinsstrondin + Kili + Kamerun + Kina + Kolombia + Kosta Rika + Kuba + Grønhøvdaoyggjarnar + Kýpros + Kekkia + Týskland + Danmørk + Dominika + Domingo lýðveldið + Ekvador + Estland + Egyptaland + Spania + Etiopia + Finnland + Mikronesia + Føroyar + Frakland + Ekvator Guinea + Grikkaland + Guinea Bissau + Gujana + Kroatia + Ungarn + Írland + Ísrael + Irak + Ísland + Italia + Jameika + Kenja + Kirgisia + Kambodja + Komorooyggjarnar + Saint Kitts og Nevis + Norður-Korea + Suður-Korea + Kuvait + Kasakstan + Libanon + Saint Lusia + Liktenstein + Lesoto + Litava + Luksemborg + Lettland + Marokko + Monako + Madagaskar + Marshalloyggjarnar + Makedónia + Móritania + Móritius + Maldivuoyggjarnar + Malavi + Meksiko + Maleisia + Mosambik + Nikaragua + Niðurlond + Noreg + Ný Sæland + Perú + Papua Nýguinea + Filipsoyggjar + Pólland + Paraguei + Katar + Rumenia + Russland + Ruanda + Saudi-Arábia + Sálomonoyggjarnar + Seyskelloyggjarnar + Svøríki + Singapor + Surinam + Sao Tome og Prinsipi + Svasiland + Kjad + Tadsjikistan + Tunesia + Turkaland + Trinidad og Tobago + Teivan + Tansania + Ukreina + Sambandsríki Amerika + Uruguei + Usbekistan + Vatikan + Saint Vinsent og Grenadinoyggjar + Venesuela + Sámoa + Jemen + Suðurafrika + Sambia + Simbabvi + + + + [a-záæíðóøúý] + + + + + + + + jan + feb + mar + apr + mai + jun + jul + aug + sep + okt + nov + des + + + januar + februar + mars + apríl + mai + juni + juli + august + september + oktober + november + desember + + + + + + + sun + mán + týs + mik + hós + frí + ley + + + sunnudagur + mánadagur + týsdagur + mikudagur + hósdagur + fríggjadagur + leygardagur + + + + + + + + EEEE dd MMMM yyyy + + + + + d. MMM yyyy + + + + + dd-MM-yyyy + + + + + dd-MM-yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + DKK + kr + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fr_BE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_BE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fr_BE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_BE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MMM-yy + + + + + d/MM/yy + + + + + + + + H' h 'mm' min 'ss' s 'z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fr_CA.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_CA.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fr_CA.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_CA.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + yy-MM-dd + + + + + yy-MM-dd + + + + + + + + HH' h 'mm' min 'ss' s 'z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;(#,##0.00¤) + + + + + + dollar canadien + $ + + + dollar des États-Unis + $ US + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fr_CH.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_CH.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fr_CH.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_CH.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + EEEE, d MMMM yyyy + + + + + d MMMM yyyy + + + + + d MMM yy + + + + + dd.MM.yy + + + + + + + + HH.mm:ss' h' z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + . + ' + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0.00;¤-#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fr_FR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_FR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fr_FR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_FR.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fr_LU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_LU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fr_LU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_LU.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + franc français + FRF + + + franc luxembourgeois + F + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fr_MC.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_MC.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fr_MC.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fr_MC.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/fr.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/fr.xml --- zope3-3.4.0/src/zope/i18n/locales/data/fr.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/fr.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2651 @@ + + + + + + + + + + + afar + abkhaze + avestique + afrikaans + akan + amharique + aragonais + arabe + assamais + avar + aymara + azéri + bachkir + biélorusse + bulgare + bihari + bichlamar + bambara + bengali + tibétain + breton + bosniaque + blin + catalan + tchétchène + chamorro + cherokee + corse + cree + tchèque + slavon d’église + tchouvache + gallois + danois + allemand + maldivien + dzongkha + éwé + grec + anglais + espéranto + espagnol + estonien + basque + persan + peul + finnois + fidjien + féroïen + français + frison + irlandais + gaélique écossais + guèze + galicien + guarani + goudjrati + manx + haoussa + hawaïen + hébreu + hindi + hiri motu + croate + haïtien + hongrois + arménien + héréro + interlingua + indonésien + interlingue + igbo + yi de Sichuan + inupiaq + ido + islandais + italien + inuktitut + japonais + javanais + géorgien + kongo + kikuyu + kuanyama + kazakh + groenlandais + khmer + kannada + coréen + konkani + kanouri + kashmiri + kurde + komi + cornique + kirghize + latin + luxembourgeois + ganda + limbourgeois + lingala + lao + lituanien + luba-katanga + letton + malgache + marshall + maori + macédonien + malayalam + mongol + moldave + marathe + malais + maltais + birman + nauruan + bokmål norvégien + ndébélé du Nord + népalais + ndonga + néerlandais + nynorsk norvégien + norvégien + ndébélé du Sud + navaho + nyanja + occitan (après 1500) + ojibwa + galla + oriya + ossète + pendjabi + pali + polonais + pachto + portugais + quechua + rhéto-roman + roundi + roumain + racine + russe + rwanda + sanskrit + sarde + sindhi + sami du Nord + sango + serbo-croate + singhalais + sidamo + slovaque + slovène + samoan + shona + somali + albanais + serbe + swati + sotho du Sud + soundanais + suédois + swahili + syriaque + tamoul + télougou + tadjik + thaï + tigrigna + tigré + turkmène + tagalog + setswana + tongan (Îles Tonga) + turc + tsonga + tatar + twi + tahitien + ouïgour + ukrainien + ourdou + ouzbek + venda + vietnamien + volapük + wallon + wolof + xhosa + yiddish + yoruba + zhuang + chinois + zoulou + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorre + Émirats arabes unis + Afghanistan + Antigua-et-Barbuda + Anguilla + Albanie + Arménie + Antilles néerlandaises + Angola + Antarctique + Argentine + Samoa américaines + Autriche + Australie + Aruba + Azerbaïdjan + Bosnie-Herzégovine + Barbade + Bangladesh + Belgique + Burkina Faso + Bulgarie + Bahreïn + Burundi + Benin + Bermudes + Brunei + Bolivie + Brésil + Bahamas + Bhoutan + Île Bouvet + Botswana + Bélarus + Belize + Canada + Îles Cocos + République démocratique du Congo + République centrafricaine + Congo + Suisse + Côte d’Ivoire + Îles Cook + Chili + Cameroun + Chine + Colombie + Costa Rica + Cuba + Cap Vert + Île Christmas + Chypre + République tchèque + Allemagne + Djibouti + Danemark + Dominique + République dominicaine + Algérie + Équateur + Estonie + Égypte + Sahara occidental + Érythrée + Espagne + Éthiopie + Finlande + Fidji + Îles Falkland (Malvinas) + Micronésie + Îles Féroé + France + en + Gabon + Royaume-Uni + Grenade + Géorgie + Guyane française + Ghana + Gibraltar + Groenland + Gambie + Guinée + Guadeloupe + Guinée équatoriale + Grèce + Géorgie du Sud, Îles Sandwich du Sud + Guatemala + Guam + Guinée-Bissau + Guyana + Hong-Kong R.A.S. + Îles Heard et MacDonald + Honduras + Croatie + Haïti + Hongrie + Indonésie + Irlande + Israël + Inde + Territoire britannique de l’océan indien + Iraq + Iran + Islande + Italie + Jamaïque + Jordanie + Japon + Kenya + Kirghizistan + Cambodge + Kiribati + Comores + Saint Kitts et Nevis + Corée du Nord + Corée du Sud + Koweït + Îles Caïmanes + Kazakhstan + Laos + Liban + Sainte-Lucie + Liechtenstein + Sri Lanka + Libéria + Lesotho + Lithuanie + Luxembourg + Lettonie + Libye + Maroc + Monaco + Moldova + Madagascar + Îles Marshall + Macédoine + Mali + Myanmar + Mongolie + Macao R.A.S. de Chine + Îles Mariannes du Nord + Martinique + Mauritanie + Montserrat + Malte + Maurice + Maldives + Malawi + Mexique + Malaisie + Mozambique + Namibie + Nouvelle-Calédonie + Niger + Île Norfolk + Nigéria + Nicaragua + Pays-Bas + Norvège + Népal + Nauru + Niué + Nouvelle-Zélande + Oman + Panama + Pérou + Polynésie française + Papouasie-Nouvelle-Guinée + Philippines + Pakistan + Pologne + Saint-Pierre-et-Miquelon + Pitcairn + Porto Rico + Territoire palestinien + Portugal + Palaos + Paraguay + Qatar + Réunion + Roumanie + Russie + Rwanda + Arabie saoudite + Îles Salomon + Seychelles + Soudan + Suède + Singapour + Sainte-Hélène + Slovénie + Svalbard et Île Jan Mayen + Slovaquie + Sierra Leone + Saint-Marin + Sénégal + Somalie + Serbie + Suriname + Sao Tomé-et-Principe + El Salvador + Syrie + Swaziland + Îles Turks et Caïques + Tchad + Terres australes françaises + Togo + Thaïlande + Tadjikistan + Tokelau + Timor-Leste + Turkmenistan + Tunisie + Tonga + Turquie + Trinité-et-Tobago + Tuvalu + Taïwan + Tanzanie + Ukraine + Ouganda + Îles Mineures Éloignées des États-Unis + États-Unis + Uruguay + Ouzbékistan + Saint-Siège (Etat de la Cité du Vatican) + Saint-Vincent-et-les Grenadines + Vénézuela + Îles Vierges Britanniques + Îles Vierges des États-Unis + Viet Nam + Vanuatu + Wallis et Futuna + Samoa + Yémen + Mayotte + Yougoslavie + Afrique du Sud + Zambie + Zimbabwe + + + Révisé + + + Calendrier + Ordonnancement + Devise + + + Calendrier bouddhiste + Calendrier chinois + Calendrier grégorien + Calendrier hébraïque + Calendrier musulman + Calendrier civil musulman + Calendrier japonais + Ordre direct + Ordre de l’annuaire + Ordre pinyin + Ordre des traits + Ordre traditionnel + + + + [a-zéèùçàâêîôûæœëïÿü] + + + GaMjkHmsSEDFwWxhKzAeugXZ + + + + + + janv. + févr. + mars + avr. + mai + juin + juil. + août + sept. + oct. + nov. + déc. + + + J + F + M + A + M + J + J + A + S + O + N + D + + + janvier + février + mars + avril + mai + juin + juillet + août + septembre + octobre + novembre + décembre + + + + + + + dim. + lun. + mar. + mer. + jeu. + ven. + sam. + + + D + L + M + M + J + V + S + + + dimanche + lundi + mardi + mercredi + jeudi + vendredi + samedi + + + + + + av. J.-C. + ap. J.-C. + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + d MMM yy + + + + + dd/MM/yy + + + + + + + + HH' h 'mm z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Tisseri + Hesvan + Kislev + Tébeth + Schébat + Adar + Adar II + Nissan + Iyar + Sivan + Tamouz + Ab + Elloul + + + Tisseri + Hesvan + Kislev + Tébeth + Schébat + Adar + Adar II + Nissan + Iyar + Sivan + Tamouz + Ab + Elloul + + + + + + + + + Mouharram + Safar + Rabiʻ-oul-Aououal + Rabiʻ-out-Tani + Djoumada-l-Oula + Djoumada-t-Tania + Radjab + Chaʻban + Ramadan + Chaououal + Dou-l-Qaʻda + Dou-l-Hidjja + + + Mouharram + Safar + Rabiʻ-oul-Aououal + Rabiʻ-out-Tani + Djoumada-l-Oula + Djoumada-t-Tania + Radjab + Chaʻban + Ramadan + Chaououal + Dou-l-Qaʻda + Dou-l-Hidjja + + + + + + + + + Mouharram + Safar + Rabiʻ-oul-Aououal + Rabiʻ-out-Tani + Djoumada-l-Oula + Djoumada-t-Tania + Radjab + Chaʻban + Ramadan + Chaououal + Dou-l-Qaʻda + Dou-l-Hidjja + + + Mouharram + Safar + Rabiʻ-oul-Aououal + Rabiʻ-out-Tani + Djoumada-l-Oula + Djoumada-t-Tania + Radjab + Chaʻban + Ramadan + Chaououal + Dou-l-Qaʻda + Dou-l-Hidjja + + + + + + + + + Heure Normale du Pacifique + Heure Avancée du Pacifique + + + PST + PDT + + Los Angeles + + + + Heure Normale du Pacifique + Heure Avancée du Pacifique + + + PST + PDT + + Los Angeles + + + + Heure Normale des Rocheuses + Heure Avancée des Rocheuses + + + MST + MDT + + Denver + + + + Heure Normale des Rocheuses + Heure Avancée des Rocheuses + + + MST + MDT + + Denver + + + + Heure Normale des Rocheuses + Heure Normale des Rocheuses + + + MST + MST + + Phoenix + + + + Heure Normale des Rocheuses + Heure Normale des Rocheuses + + + MST + MST + + Phoenix + + + + Heure Normale du Centre + Heure Avancée du Centre + + + CST + CDT + + Chicago + + + + Heure Normale du Centre + Heure Avancée du Centre + + + CST + CDT + + Chicago + + + + Heure Normale de l’Est + Heure Avancée de l’Est + + + EST + EDT + + New York + + + + Heure Normale de l’Est + Heure Avancée de l’Est + + + EST + EDT + + New York + + + + Heure Normale de l’Est + Heure Normale de l’Est + + + EST + EST + + Indianapolis + + + + Heure Normale de l’Est + Heure Normale de l’Est + + + EST + EST + + Indianapolis + + + + Heure Normale de Hawaï + Heure Normale de Hawaï + + + HST + HST + + Honolulu + + + + Heure Normale de Hawaï + Heure Normale de Hawaï + + + HST + HST + + Honolulu + + + + Heure Normale de l’Alaska + Heure Avancée de l’Alaska + + + AST + ADT + + Anchorage + + + + Heure Normale de l’Alaska + Heure Avancée de l’Alaska + + + AST + ADT + + Anchorage + + + + Heure Normale de l’Atlantique + Heure Avancée de l’Atlantique + + + AST + ADT + + Halifax + + + + Heure Normale de Terre-Neuve + Heure Avancée de Terre-Neuve + + + CNT + CDT + + St. Johns + + + + Heure Normale de Terre-Neuve + Heure Avancée de Terre-Neuve + + + CNT + CDT + + St. Johns + + + + Heure Normale de l’Europe Centrale + Heure Avancée de l’Europe Centrale + + + CET + CEST + + Paris + + + + Heure Normale de l’Europe Centrale + Heure Avancée de l’Europe Centrale + + + CET + CEST + + Paris + + + + Greenwich Mean Time + Greenwich Mean Time + + + GMT + GMT + + London + + + + Temps Moyen de Greenwich + Temps Moyen de Greenwich + + + GMT + GMT + + Casablanca + + + + Heure Normale d’Israël + Heure Avancée d’Israël + + + IST + IDT + + Jerusalem + + + + Heure Normale du Japon + Heure Normale du Japon + + + JST + JST + + Tokyo + + + + Heure Normale du Japon + Heure Normale du Japon + + + JST + JST + + Tokyo + + + + Heure Normale de l’Europe de l’Est + Heure Avancée de l’Europe de l’Est + + + EET + EEST + + Bucharest + + + + Heure Normale de Chine + Heure Normale de Chine + + + CTT + CDT + + Shanghai + + + + Heure Normale de Chine + Heure Normale de Chine + + + CTT + CDT + + Shanghai + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + + diner andorran + ADD + + + peseta andorrane + ADP + + + dirham des Émirats arabes unis + AED + + + afghani + AFA + + + afghani + Af + + + franc Affars et Issas + AIF + + + lek albanais (1946-1961) + ALK + + + lek albanais + lek + + + Albanian Lek Valute + ALV + + + dollar albanais (certificat de devises étrangères) + ALX + + + dram arménien + dram + + + florin des Antilles néerl. + ANG + + + kwanza angolais + AOA + + + kwanza angolais (1977-1990) + AOK + + + nouveau kwanza angolais (1990-2000) + AON + + + kwanza angolais réajusté (1995-1999) + AOR + + + escudo angolais + AOS + + + austral + ARA + + + Argentine Peso Moneda Nacional + ARM + + + peso argentin (1983-1985) + ARP + + + peso argentin + Arg$ + + + schilling autrichien + ATS + + + dollar australien + AUD + + + livre australienne + AUP + + + florin d’Aruba + AWG + + + Azerbaijanian Manat + AZM + + + dinar de Bosnie-Herzegovine + BAD + + + mark convertible de Bosnie-Herzegovine + KM + + + nouveau dinar de Bosnie-Herzegovine + BAN + + + dollar de Barbade + BBD + + + taka + BDT + + + franc belge (convertible) + BEC + + + franc belge + FB + + + franc belge (financier) + BEL + + + lev + BGL + + + lev de Bulgarie socialiste + BGM + + + nouveau lef + BGN + + + lev (1879-1952) + BGO + + + lev (certificat de devises étrangères) + BGX + + + dinar de Bahrein + BHD + + + franc du Burundi + BIF + + + dollar des Bermudes + BMD + + + livre des Bermudes + BMP + + + dollar de Brunei + BND + + + boliviano + Bs + + + boliviano (1863-1962) + BOL + + + peso bolivien + BOP + + + Bolivian Mvdol + BOV + + + nouveau cruzeiro (1967-1986) + BRB + + + cruzeiro + BRC + + + cruzeiro (1990-1993) + BRE + + + réal + R$ + + + nouveau cruzado + BRN + + + cruzeiro + BRR + + + cruzeiro (1942-1967) + BRZ + + + dollar des Bahamas + BSD + + + livre des Bahamas + BSP + + + ngultrum + Nu + + + roupie de Bhoutan + BTR + + + kyat + BUK + + + roupie de Birmanie + BUR + + + pula + BWP + + + nouveau rouble biélorusse (1994-1999) + BYB + + + rouble biélorusse (1992-1994) + BYL + + + rouble biélorusse + Rbl + + + dollar de Bélize + BZD + + + dollar du Honduras britannique + BZH + + + dollar canadien + CAD + + + franc congolais + CDF + + + franc de la République Congolaise + CDG + + + Congolese Zaire + CDL + + + franc CFA de la République du Centre Afrique + CFF + + + franc suisse + sFr. + + + dollar des îles Cook + CKD + + + condor chilien + CLC + + + escudo chilien + CLE + + + Chilean Unidades de Fomento + CLF + + + peso chilien + CLP + + + franc CFA camerounais + CMF + + + Chinese Jen Min Piao Yuan + CNP + + + dollar US chinois (certificat de devises étrangères) + CNX + + + Yuan Ren-min-bi + CNY + + + Colombian Paper Peso + COB + + + franc CFA congolais + COF + + + peso colombien + COP + + + colon + CRC + + + couronne tchèque + CSC + + + couronne tchèque + CSK + + + peso cubain + CUP + + + certificat de devises étrangères de Cuba + CUX + + + escudo du Cap-Vert + CVE + + + florin de Curacao + CWG + + + livre cypriote + CYP + + + couronne tchèque + CZK + + + mark est-allemand + DDM + + + deutsche mark + DEM + + + sperrmark allemand + DES + + + franc de Djibouti + DF + + + couronne danoise + DKK + + + peso dominicain + DOP + + + dinar algérien + DZD + + + nouveau franc algérien + DZF + + + franc germinal algérien + DZG + + + sucre + ECS + + + unité de valeur constante équatoriale (UVC) + ECV + + + couronne estonienne + EEK + + + livre égyptienne + EGP + + + Eritrean Nakfa + ERN + + + peseta espagnole + ESP + + + birr + ETB + + + dollar éthiopien + ETD + + + euro + + + + mark finlandais + FIM + + + mark finlandais (1860-1962) + FIN + + + dollar de Fidji + FJD + + + livre de Fiji + FJP + + + livre des Falkland (Malvinas) + FKP + + + Faeroe Islands Kronur + FOK + + + franc français + F + + + franc germinal/franc Poincaré + FRG + + + franc CFA gabonnais + GAF + + + livre sterling + £ + + + Georgian Kupon Larit + GEK + + + lari + lari + + + cédi + GHC + + + ancien cedi + GHO + + + livre ghanéenne + GHP + + + cedi revalorisé + GHR + + + livre de Gibraltar + GIP + + + couronne du Groenland + GLK + + + dalasie + GMD + + + livre de Gambie + GMP + + + franc guinéen + GF + + + franc guinéen (1960-1972) + GNI + + + syli + GNS + + + franc guadeloupéen + GPF + + + ekwélé + GQE + + + franco de Guinée Equatoriale + GQF + + + peseta de Guinée Equatoriale + GQP + + + drachme + GRD + + + nouveau drachme + GRN + + + quetzal + GTQ + + + franc guyanais + GUF + + + Escudo de Guinée Portugaise + GWE + + + Portuguese Guinea Mil Reis + GWM + + + peso de Guinée-Bissau + GWP + + + dollar de Guyane + G$ + + + dollar de Hong Kong + HKD + + + lempira + HNL + + + dinar croate + HRD + + + kuna + HRK + + + gourde + HTG + + + forint + HUF + + + livre d’Irlande du Nord + IBP + + + florin de Nica + IDG + + + roupie de Java + IDJ + + + nouvelle roupie indonésienne + IDN + + + rupiah + IDR + + + livre irlandaise + IEP + + + shékel + ILL + + + livre israélienne + ILP + + + shékel + ILS + + + livre sterling de l’Ile de Man + IMP + + + roupie indienne + =0#Rs.|1#Re.|1<Rs. + + + dinar irakien + IQD + + + rial iranien + IRR + + + couronne islandaise + ISK + + + lire italienne + + + + livre sterling de Jersey + JEP + + + dollar jamaïcain + JMD + + + livre jamaïcaine + JMP + + + dinar jordanien + JOD + + + yen + ¥ + + + shilling du Kenya + KES + + + som du Kyrgystan + som + + + vieux riel + KHO + + + riel + KHR + + + dollar de Kiribati + KID + + + franc des Comores + KMF + + + won du peuple nord-coréen + KPP + + + won nord-coréen + KPW + + + hwan + KRH + + + vieux won + KRO + + + won sud-coréen + KRW + + + dinar koweitien + KWD + + + dollar des îles Caïmans + KYD + + + rouble du Kazakhstan + KZR + + + tenge du Kazakhstan + T + + + kip + LAK + + + livre libanaise + LBP + + + franc du Liechtenstein + LIF + + + roupie de Sri Lanka + LKR + + + roupie de Ceylan + LNR + + + dollar libérien + LRD + + + Lesotho Loti + M + + + Lita de Lithuanian + LTL + + + Talonas de Lithuanie + LTT + + + franc luxembourgeois + LUF + + + lats letton + LVL + + + rouble letton + LVR + + + lire de l’autorié militaire britannique de Libye + LYB + + + dinar Iibyen + LD + + + livre libyenne + LYP + + + dirham marocain + MAD + + + franc marocain + MAF + + + nouveau franc marocain + MCF + + + franc Germinal monégasque + MCG + + + Moldovan Leu Cupon + MDC + + + leu moldave + MDL + + + rouble moldave + MDR + + + ariary malgache + MGA + + + franc malgache + MGF + + + dollar des îles Marshall + MHD + + + dinar macédonien + MDen + + + dinar macédonien (1992-1993) + MKN + + + franc malien + MLF + + + Myanmar Kyat + MMK + + + dollar de Myanmar (certificat de devises étrangères) + MMX + + + tugrik + MNT + + + pataca + MOP + + + franc martiniquais + MQF + + + ouguija + MRO + + + lire maltaise + Lm + + + livre maltaise + MTP + + + roupie de l’île Maurice + MUR + + + roupie des Maldives + MVP + + + roupie des Maldives + MVR + + + kwacha + MWK + + + livre de Malawi + MWP + + + peso d’argent mexicain (1861-1992) + MXP + + + unité de conversion mexicaine (UDI) + MXV + + + ringgit + MYR + + + escudo du Mozambique + MZE + + + métical + MZM + + + dollar de Namibie + N$ + + + franc Germinal de Nouvelle Calédonie + NCF + + + naira + NGN + + + livre nigériane + NGP + + + franc CFP des Nouvelles Hébrides + NHF + + + cordoba + NIC + + + cordoba d’or + NIG + + + cordoba d’or + NIO + + + florin néerlandais + NLG + + + couronne norvégienne + NOK + + + roupie du Népal + NPR + + + dollar néo-zélandais + NZD + + + livre néo-zélandaise + NZP + + + rial omani + OMR + + + Oman Rial Saidi + OMS + + + balboa + PAB + + + Transdniestria Ruble Kupon + PDK + + + nouveau rouble moldave + PDN + + + rouble moldave + PDR + + + Inti péruvien + PEI + + + nouveau sol péruvien + PEN + + + sol péruvien + PES + + + kina + PGK + + + peso philippin + PHP + + + roupie du Pakistan + PKR + + + dollar US polonais (certificat de devises étrangères) + PLX + + + zloty (1950-1995) + PLZ + + + livre palestinienne + PSP + + + conto portugais + PTC + + + escudo portugais + PTE + + + guarani + PYG + + + rial du Qatar + QAR + + + franc de la Réunion + REF + + + leu + ROL + + + nouveau leu + RON + + + rouble de Russie (1991-1998) + RUR + + + franc du Rwanda + RWF + + + riyal séoudien + SAR + + + riyal saoudien + SAS + + + dollar de Salomon + SBD + + + roupie des Seychelles + SCR + + + dinar soudanais + SDD + + + livre soudanaise + SDP + + + couronne suédoise + SEK + + + dollar de Singapour + SGD + + + livre de Sainte-Hélène + SHP + + + bons de tolar slovène + SIB + + + tolar slovène + SIT + + + couronne slovaque + SKK + + + léone + SLL + + + lire de Saint-Marin + SML + + + shilling de Somalie + SOS + + + shilling de Somalie + SQS + + + florin du Surinam + SRG + + + livre écossaise + SSP + + + dobra + STD + + + escudo de Sao Tomé et Principe + STE + + + nouveau rouble soviétique + SUN + + + rouble de C.E.I. + SUR + + + colon + SVC + + + livre syrienne + SYP + + + lilangeni + SZL + + + couronne des îles Turks et Caïques + TCC + + + franc CFA du Tchad + TDF + + + baht + THB + + + rouble du Tadjikistan + TJR + + + somoni du Tadjikistan + TJS + + + Turkmenistan Manat + TMM + + + dinar tunisien + TND + + + paʻanga + TOP + + + livre sterling du Tonga + TOS + + + escudo de Timor + TPE + + + pataca de Timor + TPP + + + livre turque + TL + + + dollar de la Trinité + TTD + + + vieux dollar de la Trinité + TTO + + + dollar du Tuvalu + TVD + + + dollar taïwanais + TWD + + + shilling de Tanzanie + TZS + + + hryvnia + UAH + + + karbovanetz + UAK + + + shilling ougandais (1966-1987) + UGS + + + shilling ougandais + U Sh + + + dollar des États-Unis + $ + + + dollar des Etats-Unis (jour suivant) + USN + + + dollar des Etats-Unis (jour même) + USS + + + peso fort uruguayen + UYF + + + peso uruguayen (1975-1993) + UYP + + + peso uruguayen + Ur$ + + + Uzbekistan Coupon Som + UZC + + + sum + UZS + + + lire du Vatican + VAL + + + North Vietnam Piastre Dong Viet + VDD + + + nouveau dong nord-vietnamien + VDN + + + North Vietnam Viet Minh Piastre Dong Viet + VDP + + + bolivar + VEB + + + dollar des îles Vierges britanniques + VGD + + + dong + VND + + + nouveau dong vietnamien + VNN + + + dong de la République Vietnamienne + VNR + + + dong national du Vietnam + VNS + + + vatu + VUV + + + livre du Samoa + WSP + + + tala + WST + + + dinar asiatique (unité de compte) + XAD + + + franc CFA (BEAC) + XAF + + + unité monétaire asiatique + XAM + + + Or + XAU + + + unité composite européenne + XBA + + + unité monétaire européenne + XBB + + + unité de compte européenne (XBC) + XBC + + + unité de compte européenne (XBD) + XBD + + + dollar des Caraïbes + XCD + + + nouveau franc CFA + XCF + + + franc CFA (BCEAEC) + XEF + + + unité de compte européenne (ECU) + XEU + + + franc or + XFO + + + franc UIC + XFU + + + dinar musulman + XID + + + nouveau franc métropolitain + XMF + + + franc CFA antillais + XNF + + + franc CFA (BCEAO) + XOF + + + franc CFP + XPF + + + rouble transférable du COMECON + XTR + + + dinar du Yémen + YDD + + + riyal du Yémen + YEI + + + riyal du Yémen + YER + + + nouveau dinar yougoslave + YUD + + + dinar de la Fédération Yougoslave + YUF + + + dinar yougoslave 1994 + YUG + + + dinar yougoslave Noviy + YUM + + + dinar yougoslave convertible + YUN + + + dinar yougoslave d’Octobre + YUO + + + dinar yougoslave réformé + YUR + + + rand sud-africain (financier) + ZAL + + + livre sud-africaine + ZAP + + + rand + ZAR + + + kwacha + ZMK + + + livre zambienne + ZMP + + + nouveau zaïre + ZRN + + + zaïre + ZRZ + + + dollar du Zimbabwe + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ga_IE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ga_IE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ga_IE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ga_IE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + dd/MM/yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + Punt Éireannach + £ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ga.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ga.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ga.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ga.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2001 @@ + + + + + + + + + + + Afar + Abcáisis + Aivéistis + Afracáinis + Araibis + Asaimis + Asarbaiseáinis + Baiscíris + Bealarúisis + Bulgáiris + Beangálais + Tibéadais + Briotáinis + Boisnis + Catalóinis + Sisinis + Corsaicis + Craíais + Seicis + Slavais na hEaglaise + Suvaisis + Breatnais + Danmhairgis + Gearmáinis + Gréigis + Béarla + Esperanto + Spáinnis + Eastóinis + Bascais + Peirsis + Fionnlainnis + Fidsis + Faróis + Fraincis + Freaslainnais + Gaeilge + Gaeilge na hAlban + Gúisearáitis + Mannainis + Haváíais + Eabhrais + Hiondúis + Cróitis + Ungáiris + Airméinis + Interlingua + Indinéisis + Interlingue + Inupiaq + Ido + Íoslainnais + Iodáilis + Ionúitis + Seapáinis + Iávais + Seoirsis + Casachais + Cannadais + Cóiréis + Caismíris + Cornais + Cirgeasais + Laidin + Leitseabuirgis + Laosais + Liotuáinis + Laitvis + Malagásais + Maorais + Macadóinis + Mailéalaimis + Mongóilis + Moldáivis + Maraitis + Maltais + Burmais + Nárúis + Ioruais Bokmål + Neipealais + Ollainnais + Ioruais Nynorsk + Ioruais + Navachóis + Ocatáinis (tar éis 1500); Provençal + Óiséitis + Puinseaibis + Polainnis + Paisteo + Portaingéilis + Ceatsuais + Romáinis + Rúisis + Sanscrait + Sairdínis + Sindis + Sáimis Thuaidh + Seirbea-Chróitis + Slóvacais + Slóvéinis + Samóis + Somálais + Albáinis + Seirbis + Sualainnis + Svahaílis + Tamailis + Téalainnis + Tagálaigis + Tuircis + Tatarais + Taihítis + Úcráinis + Urdais + Úisbéicis + Vítneamais + Vallúnais + Giúdais + Sínis + Súlúis + + + Andóra + Aontas na nÉimíríochtaí Arabacha + An Afganastáin + Antigua agus Barbuda + Anguilla + An Albáin + An Airméin + Antillí na hÍsiltíre + Angóla + An Antartaice + An Airgintín + Samó Meiriceánach + An Ostair + An Astráil + Aruba + An Asarbaiseáin + An Bhoisnia-Heirseagaivéin + Barbadós + An Bhanglaidéis + An Bheilg + Buircíne Fasó + An Bhulgáir + Bairéin + An Bhurúin + Beinin + Beirmiúda + Brúiné + An Bholaiv + An Bhrasaíl + Na Bahámaí + An Bhútáin + Oileáin Bouvet + An Bhotsuáin + An Bhealarúis + An Bheilís + Ceanada + Oileáin Cocos (Keeling) + Poblacht Dhaonlathach an Chongó + Poblacht na hAfraice Láir + An Congó + An Eilvéis + An Cósta Eabhair + Oileáin Cook + An tSile + Camarún + An tSín + An Cholóim + Cósta Ríce + Cúba + Rinn Verde + Oileán na Nollag + An Chipir + Poblacht na Seice + An Ghearmáin + Djibouti + An Danmhairg + Doiminice + An Phoblacht Dhoiminiceach + An Ailgéir + Eacuadór + An Eastóin + An Éigipt + An Sahára Thiar + Eritrea + An Spáinn + An Aetóip + An Fhionlainn + Fidsí + Oileáin Fháclainne + An Mhicrinéis + Oileáin Fharó + An Fhrainc + An Ghabúin + An Ríocht Aontaithe + Grenada + An tSeoirsia + An Ghuáin Fhrancach + Gána + Giobráltar + An Ghraonlainn + An Ghaimbia + An Ghuine + Guadalúip + An Ghuine Mheánchriosach + An Ghréig + An tSeoirsia Theas agus Oileáin Sandwich Theas + Guatamala + Guam + An Ghuine-Bhissau + An Ghuáin + Hong Cong + Oileán Heard agus Oileáin McDonald + Hondúras + An Chróit + Háití + An Ungáir + An Indinéis + Éire + Iosrael + An India + Críocha Briotanacha an Aigéin Indiagh + An Iaráic + An Iaráin + An Íoslainn + An Iodáil + Iamáice + An Iordáin + An tSeapáin + An Chéinia + An Chirgeastáin + An Chambóid + Cireabaití + Oileáin Chomóra + Saint Kitts agus Nevis + An Chóiré Thuaidh + An Chóiré Theas + Cuáit + Oileáin Cayman + An Chasacstáin + Laos + An Liobáin + Saint Lucia + Lichtinstéin + Srí Lanca + An Libéir + Leosóta + An Liotuáin + Lucsamburg + An Laitvia + An Libia + Maracó + Monacó + An Mholdóiv + Madagascar + Oileáin Marshall + An Mhacadóin + Mailí + Maenmar + An Mhongóil + Macao + Oileáin Mariana Thuaidh + Martinique + An Mharatáin + Montsarat + Málta + Oileán Mhuirís + Mhaildiví + An Mhaláiv + Meicsiceo + An Mhalaeisia + Mósaimbíc + An Namaib + An Nua-Chaladóin + An Nígir + Oileán Norfolk + An Nigéir + Nicearagua + An Ísiltír + An Iorua + Neipeal + Nárú + Niue + An Nua-Shéalainn + Oman + Panama + Peiriú + An Pholainéis Fhrancach + Nua-Ghuine Phapua + Na hOileáin Fhilipíneacha + An Phacastáin + An Pholainn + Saint Pierre agus Miquelon + Pitcairn + Portó Ríce + Na Críocha Pailistíneacha + An Phortaingéil + Palau + Paragua + Catar + Réunion + An Rómáin + Cónaidhm na Rúise + Ruanda + An Araib Shádach + Oileáin Solomon + Na Séiséil + An tSúdáin + An tSualainn + Singeapór + San Héilin + An tSlóvéin + Svalbard agus Jan Mayen + An tSlóvaic + Siarra Leon + San Mairíne + An tSeineagáil + An tSomáil + An tSeirbia + Suranam + Sao Tome agus Principe + An tSalvadóir + An tSiria + An tSuasalainn + Oileáin Turks agus Caicos + Sead + Críocha Francacha Theas + Tóga + An Téalainn + An Táidsíceastáin + Tócalá + Tíomór-Leste + An Tuircméanastáin + An Túinéis + Tonga + An Tuirc + Oileáin na Tríonóide agus Tobága + Tuvalú + An Téaváin + An Tansáin + An Úcráin + Uganda + Mion-Oileáin Imeallacha S.A.M. + Stáit Aontaithe Mheiriceá + Urugua + Úisbéiceastáin + An Chathaoir Naofa (Stát Chathair na Vatacáine) + Saint Vincent agus na Grenadines + Veiniséala + Oileáin Bhriotanacha na Maighdean + Oileáin na Maighdean S.A.M. + Vítneam + Vanuatú + Oileáin Vailís agus Futúna + Samó + Éimin + Mayotte + An Iúgslaiv + An Afraic Theas + An tSaimbia + An tSiombáib + + + + [a-z á é í ó ú] + + + RbMLkUnsSElFtTauKcBeyrAC + + + + + + Ean + Feabh + Márta + Aib + Beal + Meith + Iúil + Lún + MFómh + DFómh + Samh + Noll + + + Eanáir + Feabhra + Márta + Aibreán + Bealtaine + Meitheamh + Iúil + Lúnasa + Meán Fómhair + Deireadh Fómhair + Samhain + Nollaig + + + + + + + Domh + Luan + Máirt + Céad + Déar + Aoine + Sath + + + Dé Domhnaigh + Dé Luain + Dé Máirt + Dé Céadaoin + Déardaoin + Dé hAoine + Dé Sathairn + + + + a.m. + p.m. + + + RC + AD + + + + + + + + Meán-Am Greenwich + Am Samhraidh na hÉireann + + + MAG + ASÉ + + Baile Átha Cliath + + + + Meán-Am Greenwich + Am Samhraidh na Breataine + + + MAG + ASB + + Londain + + + + Meán-Am Greenwich + Am Samhraidh na Breataine + + + MAG + ASB + + Béal Feirste + + + + Meán-Am Greenwich + Meán-Am Greenwich + + + MAG + MAG + + Londain + + + + + + + Dínear Andóra + ADD + + + Peseta Andóra + ADP + + + Dirham Aontas na nÉimíríochtaí Arabacha + AED + + + Afgainí (1927-2002) + AFA + + + Afgainí + Af + + + Franc Affars agus Issas + AIF + + + Lek Albánach (1946-1961) + ALK + + + Lek Albánach + lek + + + Lek Valute Albánach + ALV + + + Teastais Airgeadraí Dollar na hAlbáine + ALX + + + Dram Airméanach + dram + + + Guilder na nAntillí Ísiltíreach + AÍ f. + + + Kwanza Angólach + AOA + + + Kwanza Angólach (1977-1990) + AOK + + + Kwanza Nua Angólach (1990-2000) + AON + + + Kwanza Reajustado Angólach (1995-1999) + AOR + + + Escudo Angólach + AOS + + + Austral Airgintíneach + ARA + + + Peso Moneda Nacional Airgintíneach + ARM + + + Peso na Airgintíne (1983-1985) + ARP + + + Peso na Airgintíne + Arg$ + + + Scilling Ostarach + ATS + + + Dollar Astrálach + A$ + + + Punt Astrálach + AUP + + + Guilder Aruba + AWG + + + Manat Asarbaiseánach + AZM + + + Dínear Bhoisnia-Heirseagaivéin + BAD + + + Marc Inathraithe Bhoisnia-Heirseagaivéin + KM + + + Dínear Nua Bhoisnia-Heirseagaivéin + BAN + + + Dollar Bharbadóis + BDS$ + + + Taka Bhanglaidéiseach + Tk + + + Franc Beilgeach (inathraithe) + BEC + + + Franc Beilgeach + BF + + + Franc Beilgeach (airgeadúil) + BEL + + + Lev Bulgárach Crua + lev + + + Lev Bulgárach Sóisialaíoch + BGM + + + Lev Nua Bulgárach + BGN + + + Lev Bulgárach (1879-1952) + BGO + + + Teastais Airgeadraí Lev Bulgárach + BGX + + + Dínear na Bairéine + BD + + + Franc na Burúine + Fbu + + + Dollar Bheirmiúda + Ber$ + + + Punt Bheirmiúda + BMP + + + Dollar Bhrúiné + BND + + + Boliviano + Bs + + + Boliviano (1863-1962) + BOL + + + Peso na Bolaive + BOP + + + Mvdol Bolavach + BOV + + + Cruzeiro Novo Brasaíleach (1967-1986) + BRB + + + Cruzado Brasaíleach + BRC + + + Cruzeiro Brasaíleach (1990-1993) + BRE + + + Real Brasaíleach + R$ + + + Cruzado Novo Brasaíleach + BRN + + + Cruzeiro Brasaíleach + BRR + + + Cruzeiro Brasaíleach (1942-1967) + BRZ + + + Dollar na mBahámaí + BSD + + + Punt na mBahámaí + BSP + + + Ngultrum Bútánach + Nu + + + Rúipí na Bútáine + BTR + + + Kyat Burmach + BUK + + + Rúipí Bhurma + BUR + + + Pula Botsuánach + BWP + + + Rúbal Nua Béalarúiseach (1994-1999) + BYB + + + Rúbal Béalarúiseach (1992-1994) + BYL + + + Rúbal Béalarúiseach + Rbl + + + Dollar na Beilíse + BZ$ + + + Dollar Hondúrais Bhriotanaigh + BZH + + + Dollar Ceanada + Can$ + + + Franc Congolais an Chongó + CDF + + + Franc Phoblacht an Chongó + CDG + + + Zaire an Chongó + CDL + + + CFA Franc Phoblacht na hAfraice Láir + CFF + + + Franc na hEilvéise + CHF + + + Dollar Oileáin Cook + CKD + + + Condor na Sile + CLC + + + Escudo na Sile + CLE + + + Unidades de Fomento na Sile + CLF + + + Peso na Sile + Ch$ + + + CFA Franc Chamarúin + CMF + + + Jen Min Piao Yuan Síneach + CNP + + + Teastais Airgeadraí Dollar SAM Síneach + CNX + + + Yuan Renminbi Síneach + Y + + + Peso Páipéir na Colóime + COB + + + CFA Franc Chongó + COF + + + Peso na Colóime + Col$ + + + Colon Chósta Ríce + C + + + Koruna na Seicslóvaice + CSC + + + Koruna Crua na Seicslóvaice + CSK + + + Peso Cúba + CUP + + + Teastais Airgeadraí Chúba + CUX + + + Escudo na Rinne Verde + CVEsc + + + Guilder Curacao + CWG + + + Punt na Cipire + £C + + + Koruna Phoblacht na Seice + CZK + + + Ostmark na hOirGhearmáine + DDM + + + Deutsche Mark + DEM + + + Sperrmark Gearmánach + DES + + + Franc Djibouti + DF + + + Krone Danmhargach + DKr + + + Peso Doimineacach + RD$ + + + Dínear na hAilgéire + DA + + + Franc Nua Ailgérach + DZF + + + Franc Germinal Ailgérach + DZG + + + Sucre Eacuadóir + ECS + + + Unidad de Valor Constante (UVC) Eacuadóir + ECV + + + Kroon na hEastóine + EEK + + + Punt na hÉigipte + EGP + + + Peseta Spáinneach + ESP + + + Birr na hAetóipe + Br + + + Dollar na hAetóipe + ETD + + + Euro + + + + Markka Fionnlannach + FIM + + + Markka Fionnlannach (1860-1962) + FIN + + + Dollar Fhidsí + F$ + + + Punt Fhidsí + FJP + + + Punt Oileáin Fháclainne + FKP + + + Kronur Oileáin Fharó + FOK + + + Franc Francach + FRF + + + Franc Germinal Francach/Franc Poincare + FRG + + + CFA Franc na Gabúine + GAF + + + Punt Steirling + £ + + + Kupon Larit na Grúise + GEK + + + Lari na Grúise + lari + + + Cedi Ghána + GHC + + + Sean-Cedi Ghána + GHO + + + Punt Ghána + GHP + + + Cedi Athluachtha Ghána + GHR + + + Punt Ghiobráltair + GIP + + + Krone na Graonlainne + GLK + + + Dalasi Gaimbia + GMD + + + Punt Gaimbia + GMP + + + Franc Guine + GF + + + Franc Guine (1960-1972) + GNI + + + Syli Guine + GNS + + + Franc Guadeloupe + GPF + + + Ekwele Guineana na Guine Meánchriosaí + GQE + + + Franco na Guine Meánchriosaí + GQF + + + Peseta Guineana na Guine Meánchriosaí + GQP + + + Drachma Gréagach + GRD + + + Drachma Nua Gréagach + GRN + + + Quetzal Guatamala + Q + + + Franc Guiana na Guáine Francaí + GUF + + + Escudo na Guine Portaingéalaí + GWE + + + Mil Reis na Guine Portaingéalaí + GWM + + + Peso Guine-Bhissau + GWP + + + Dollar na Guáine + G$ + + + Dollar Hong Cong + HK$ + + + Lempira Hondúrais + L + + + Dínear na Cróite + HRD + + + Kuna Crótach + HRK + + + Gourde Háití + HTG + + + Forint Ungárach + Ft + + + Punt Thuaisceart Éireann + IBP + + + Nica Guilder Indinéiseach + IDG + + + Java Rupiah Indinéiseach + IDJ + + + Rupiah Nua Indinéiseach + IDN + + + Rupiah Indinéiseach + Rp + + + Punt Éireannach + IR£ + + + Sheqel Iosraelach + ILL + + + Punt Iosraelach + ILP + + + Sheqel Nua Iosraelach + ILS + + + Punt Steirling Oileán Mhanann + IMP + + + Rúipí India + =0#Rs.|1#Re.|1<Rs. + + + Dínear Irácach + ID + + + Rial Iaránach + RI + + + Krona Íoslannach + ISK + + + Lira Iodálach + + + + Punt Steirling Gheirsí + JEP + + + Dollar Iamácach + J$ + + + Punt Iamácach + JMP + + + Dínear Iordánach + JD + + + Yen Seapánach + ¥ + + + Scilling Céiniach + K Sh + + + Som na Cirgeastáine + som + + + Sean-Riel na Cambóide + KHO + + + Riel na Cambóide + CR + + + Dollar Chireabaití + KID + + + Franc Chomóra + CF + + + Won Na nDaoine na Cóiré Thuaidh + KPP + + + Won na Cóiré Thuaidh + KPW + + + Hwan na Cóiré Theas + KRH + + + Sean-Won na Cóiré Theas + KRO + + + Won na Cóiré Theas + KRW + + + Dínear Cuátach + KD + + + Dollar Oileáin Cayman + KYD + + + Rúbal Casacstánach + KZR + + + Tenge Casacstánach + T + + + Kip Laosach + LAK + + + Punt na Liobáine + LL + + + Franc Lichtinstéin + LIF + + + Rúipí Srí Lanca + SL Re + + + Rúipí na Siolióne + LNR + + + Dollar na Libéire + LRD + + + Loti Leosóta + M + + + Lita Liotuánach + LTL + + + Talonas Liotuánach + LTT + + + Franc Lucsamburg + LUF + + + Lats Laitviach + LVL + + + Rúbal Laitviach + LVR + + + Lira Údarás Míleata Briotanach Libia + LYB + + + Dínear Libia + LD + + + Punt Libia + LYP + + + Dirham Mharacó + MAD + + + Franc Mharacó + MAF + + + Franc Nouveau Mhonacó + MCF + + + Franc Germinal Mhonacó + MCG + + + Leu Cúpóin Moldóvach + MDC + + + Leu Moldóvach + MDL + + + Rúbal Cúpóin Moldóvach + MDR + + + Ariary Madagascar + MGA + + + Franc Madagascar + MGF + + + Dollar Oileáin Marshall + MHD + + + Denar na Macadóine + MDen + + + Denar na Macadóine (1992-1993) + MKN + + + Franc Mhailí + MLF + + + Kyat Mhaenmar + MMK + + + Teastais Airgeadra Dollar Mhaenmar + MMX + + + Tugrik Mongólach + Tug + + + Pataca Macao + MOP + + + Franc Martinique + MQF + + + Ouguiya na Maratáine + UM + + + Lira Maltach + Lm + + + Punt Maltach + MTP + + + Rúipí Oileán Mhuirís + MUR + + + Maldive Islands Rúipí + MVP + + + Maldive Islands Rufiyaa + MVR + + + Kwacha na Maláive + MK + + + Punt na Maláive + MWP + + + Peso Meicsiceo + MEX$ + + + Peso Airgid Meicsiceo (1861-1992) + MXP + + + Unidad de Inversion (UDI) Meicsiceo + MXV + + + Ringgit Malaeisia + RM + + + Escudo Mósaimbíce + MZE + + + Metical Mósaimbíce + Mt + + + Dollar na Namaibe + N$ + + + Franc Germinal na Nua-Chaladóine + NCF + + + Naira Nígéarach + NGN + + + Punt Nígéarach + NGP + + + CFP Franc Nua-Inse Ghall + NHF + + + Cordoba Nicearagua + NIC + + + Cordoba Ór Nicearagua + NIG + + + Cordoba Oro Nicearagua + NIO + + + Guilder Ísiltíreach + NLG + + + Krone Ioruach + NKr + + + Rúipí Neipeáil + Nrs + + + Dollar na Nua-Shéalainne + $NZ + + + Punt na Nua-Shéalainne + NZP + + + Rial Omain + RO + + + Rial Saidi Omain + OMS + + + Balboa Panamach + PAB + + + Rúbal Cupóin Transdniestria + PDK + + + Rúbal Nua Transdniestria + PDN + + + Transdniestria Rúbal + PDR + + + Inti Pheiriú + PEI + + + Sol Nuevo Pheiriú + PEN + + + Sol Pheiriú + PES + + + Kina Nua-Ghuine Phapua + PGK + + + Peso Filipíneach + PHP + + + Rúipí na Pacastáine + Pra + + + Zloty Polannach + Zl + + + Teastais Airgeadra Dollar SAM Polannach + PLX + + + Zloty Polannach (1950-1995) + PLZ + + + Punt Pailistíneach + PSP + + + Conto Portaingéalach + PTC + + + Escudo Portaingélach + PTE + + + Guarani Pharagua + PYG + + + Rial Catarach + QR + + + Franc Réunion + REF + + + Leu Rómánach + leu + + + Leu Nua Rómánach + RON + + + Rúbal Rúiseach + RUB + + + Rúbal Rúiseach (1991-1998) + RUR + + + Franc Ruanda + RWF + + + Riyal Sádach + SRl + + + Dollar Oileáin Solomon + SI$ + + + Rúipí na Séiséil + SR + + + Dínear na Súdáine + SDD + + + Punt na Súdáine + SDP + + + Krona Sualannach + SKr + + + Dollar Singeapóir + S$ + + + Punt San Héilin + SHP + + + Tolar Bons Slóvéanach + SIB + + + Tolar Slóvénach + SIT + + + Koruna na Slóvaice + Sk + + + Leone Shiarra Leon + SLL + + + Lira San Marino + SML + + + Scilling na Sómáile + So. Sh. + + + Guilder Shuranaim + Sf + + + Punt Albanach + SSP + + + Dobra Sao Tome agus Principe + Db + + + Escudo Sao Tome agus Principe + STE + + + Rúbal Nua Sóvéadach + SUN + + + Rúbal Sóvéadach + SUR + + + Colon na Salvadóire + SVC + + + Punt Siria + LS + + + Lilangeni na Suasalainne + E + + + CFA Franc Sead + TDF + + + Baht na Téalainne + THB + + + Rúbal na Táidsíceastáine + TJR + + + Somoni na Táidsíceastáine + TJS + + + Manat na An Tuircméanastáine + TMM + + + Dínear na Túinéise + TND + + + Paʻanga Tonga + T$ + + + Punt Steirling Tonga + TOS + + + Escudo Tíomóir + TPE + + + Pataca Tíomóir + TPP + + + Lira Turcach + TL + + + Dollar Oileáin na Tríonóide agus Tobága + TT$ + + + Sean-Dollar Oileáin na Tríonóide agus Tobága + TTO + + + Dollar Tuvalu + TVD + + + Dollar Nua na Téaváine + NT$ + + + Scilling na Tansáine + T Sh + + + Hryvnia Úcránach + UAH + + + Karbovanetz Úcránach + UAK + + + Scilling Uganda (1966-1987) + UGS + + + Scilling Uganda + U Sh + + + Dollar S.A.M. + $ + + + Dollar S.A.M. (an chéad lá eile) + USN + + + Dollar S.A.M. (an la céanna) + USS + + + Peso Fuerte Uragua + UYF + + + Peso Uragua (1975-1993) + UYP + + + Peso Uruguayo Uragua + Ur$ + + + Som Cúpóin na hÚisbéiceastáine + UZC + + + Sum na hÚisbéiceastáine + UZS + + + Lira na Vatacáine + VAL + + + Piastre Dong Viet Vítneam Thuaidh + VDD + + + Dong Nua Vítneam Thuaidh + VDN + + + Viet Minh Piastre Dong Viet Vítneam Thuaidh + VDP + + + Bolivar Veiniséala + Be + + + Dollar Oileáin Bhriotanacha na Maighdean + VGD + + + Dong Vítneamach + VND + + + Dong Nua Vítneamach + VNN + + + Dong Phoblacht Vítneaim + VNR + + + Dong Náisiúnta Vítneamach + VNS + + + Vatu Vanuatú + VT + + + Punt Samó Thiar + WSP + + + Tala Samó Thiar + WST + + + Dínear Áiseach Unit of Account + XAD + + + CFA Franc BEAC + XAF + + + Aonad Airgeadaíochta na hÁise + XAM + + + Ór + XAU + + + Aonad Ilchodach Eorpach + XBA + + + Aonad Airgeadaíochta Eorpach + XBB + + + Aonad Cuntais Eorpach (XBC) + XBC + + + Aonad Cuntais Eorpach (XBD) + XBD + + + Dollar Oirthear na Cairibe + EC$ + + + CFA Nouveau Franc + XCF + + + Cearta Speisialta Tarraingthe + XDR + + + CFA Franc BCEAEC + XEF + + + Aonad Airgeadra Eorpach + XEU + + + Franc Ór Francach + XFO + + + UIC-Franc Francach + XFU + + + Dínear Ioslamach + XID + + + Nouveau Franc Ceannchathartha Francach + XMF + + + CFA Franc na nAntillí Francach + XNF + + + CFA Franc BCEAO + XOF + + + CFP Franc + CFPF + + + Rúbal Inaistrithe COMECON + XTR + + + Dínear Éimin + YDD + + + Imadi Riyal Éimin + YEI + + + Rial Éimin + YRl + + + Dínear Crua Iúgslavach + YUD + + + Dínear Chónaidhm na hIúgslaive + YUF + + + Dínear 1994 Iúgslavach + YUG + + + Noviy Dinar Iúgslavach + YUM + + + Dínear Inathraithe Iúgslavach + YUN + + + Dínear Dheireadh Fómhar Iúgslavach + YUO + + + Dínear Leasaithe Iúgslavach + YUR + + + Rand na hAfraice Theas (airgeadúil) + ZAL + + + Punt na hAfraice Theas + ZAP + + + Rand na hAfraice Theas + R + + + Kwacha Saimbiach + ZMK + + + Punt Saimbiach + ZMP + + + Zaire Nua Sáíreach + ZRN + + + Zaire Sáíreach + ZRZ + + + Dollar Siombábach + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gez_ER.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gez_ER.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gez_ER.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gez_ER.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + EEEE፥ dd MMMM መዓልት yyyy G + + + + + dd MMMM yyyy + + + + + dd-MMM-yyyy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #ወ##0.###;-#ወ##0.### + + + + + + + #E0 + + + + + + + #ወ##0% + + + + + + + ¤#ወ##0.00;-¤#ወ##0.00 + + + + + + ERN + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gez_ET.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gez_ET.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gez_ET.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gez_ET.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + EEEE፥ dd MMMM መዓልት yyyy G + + + + + dd MMMM yyyy + + + + + dd-MMM-yyyy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #ወ##0.###;-#ወ##0.### + + + + + + + #E0 + + + + + + + #ወ##0% + + + + + + + ¤#ወ##0.00;-¤#ወ##0.00 + + + + + + ETB + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gez.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gez.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gez.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gez.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,374 @@ + + + + + + + + + + + am + አፋርኛ + አብሐዚኛ + አፍሪቃንስኛ + አምሐረኛ + ዐርቢኛ + አሳሜዛዊ + አያማርኛ + አዜርባይጃንኛ + ባስኪርኛ + ቤላራሻኛ + ቡልጋሪኛ + ቢሃሪ + ቢስላምኛ + በንጋሊኛ + ትበትንኛ + ብሬቶንኛ + ብሊን + ካታላንኛ + ኮርሲካኛ + ቼክኛ + ወልሽ + ዴኒሽ + ጀርመን + ድዞንግኻኛ + ግሪክኛ + እንግሊዝኛ + ኤስፐራንቶ + ስፓኒሽ + ኤስቶኒአን + ባስክኛ + ፐርሲያኛ + ፊኒሽ + ፊጂኛ + ፋሮኛ + ፈረንሳይኛ + ፍሪስኛ + አይሪሽ + እስኮትስ፡ጌልክኛ + ግዕዝኛ + ጋለጋኛ + ጓራኒኛ + ጉጃርቲኛ + ሃውሳኛ + ዕብራስጥ + ሐንድኛ + ክሮሽያንኛ + ሀንጋሪኛ + አርመናዊ + ኢንቴርሊንጓ + እንዶኒሲኛ + እንተርሊንግወ + እኑፒያቅኛ + አይስላንድኛ + ጣሊያንኛ + እኑክቲቱትኛ + ጃፓንኛ + ጃቫንኛ + ጊዮርጊያን + ካዛክኛ + ካላሊሱትኛ + ክመርኛ + ካናዳኛ + ኮሪያኛ + ካሽሚርኛ + ኩርድሽኛ + ኪርጊዝኛ + ላቲንኛ + ሊንጋላኛ + ላውስኛ + ሊቱአኒያን + ላትቪያን + ማላጋስኛ + ማዮሪኛ + ማከዶኒኛ + ማላያላምኛ + ሞንጎላዊኛ + ሞልዳቫዊና + ማራዚኛ + ማላይኛ + ማልቲስኛ + ቡርማኛ + ናኡሩ + ኔፓሊኛ + ደች + ኖርዌጂያን + ኦኪታንኛ + ኦሮምኛ + ኦሪያኛ + ፓንጃቢኛ + ፖሊሽ + ፑሽቶኛ + ፖርቱጋሊኛ + ኵቿኛ + ሮማንስ + ሩንዲኛ + ሮማኒያን + ራሽኛ + ኪንያርዋንድኛ + ሳንስክሪትኛ + ሲንድሂኛ + ሳንጎኛ + ስንሃልኛ + ሲዳምኛ + ስሎቫክኛ + ስሎቪኛ + ሳሞአኛ + ሾናኛ + ሱማልኛ + ልቤኒኛ + ሰርቢኛ + ስዋቲኛ + ሶዞኛ + ሱዳንኛ + ስዊድንኛ + ስዋሂሊኛ + ታሚልኛ + ተሉጉኛ + ታጂኪኛ + ታይኛ + ትግርኛ + ትግረ + ቱርክመንኛ + ታጋሎገኛ + ጽዋናዊኛ + ቶንጋ + ቱርክኛ + ጾንጋኛ + ታታርኛ + ትዊኛ + ኡዊግሁርኛ + ዩክረኒኛ + ኡርዱኛ + ኡዝበክኛ + ቪትናምኛ + ቮላፑክኛ + ዎሎፍኛ + ዞሳኛ + ይዲሻዊኛ + ዮሩባዊኛ + ዡዋንግኛ + ቻይንኛ + ዙሉኛ + + + አንዶራ + የተባበሩት፡አረብ፡ኤምሬትስ + አልባኒያ + አርሜኒያ + ኔዘርላንድስ፡አንቲልስ + አርጀንቲና + ኦስትሪያ + አውስትሬሊያ + አዘርባጃን + ቦስኒያ፡እና፡ሄርዞጎቪኒያ + ባርቤዶስ + ቤልጄም + ቡልጌሪያ + ባህሬን + ቤርሙዳ + ቦሊቪያ + ብራዚል + ቡህታን + ቤላሩስ + ቤሊዘ + የመካከለኛው፡አፍሪካ፡ሪፐብሊክ + ስዊዘርላንድ + ቺሊ + ካሜሩን + ቻይና + ኮሎምቢያ + ኬፕ፡ቬርዴ + ሳይፕረስ + ቼክ፡ሪፑብሊክ + ጀርመን + ዴንማርክ + ዶሚኒካ + ዶሚኒክ፡ሪፑብሊክ + አልጄሪያ + ኢኳዶር + ኤስቶኒያ + ግብጽ + ምዕራባዊ፡ሳህራ + ኤርትራ + ስፔን + ኢትዮጵያ + ፊንላንድ + ፊጂ + ሚክሮኔዢያ + እንግሊዝ + ጆርጂያ + የፈረንሳይ፡ጉዊአና + ጋምቢያ + ጊኒ + ኢኳቶሪያል፡ጊኒ + ግሪክ + ቢሳዎ + ጉያና + ሆንግ፡ኮንግ + ክሮኤሽያ + ሀይቲ + ሀንጋሪ + ኢንዶኔዢያ + አየርላንድ + እስራኤል + ህንድ + ኢራቅ + አይስላንድ + ጣሊያን + ጃማይካ + ጆርዳን + ጃፓን + ካምቦዲያ + ኮሞሮስ + ደቡብ፡ኮሪያ + ሰሜን፡ኮሪያ + ክዌት + ሊባኖስ + ሊቱዌኒያ + ላትቪያ + ሊቢያ + ሞሮኮ + ሞልዶቫ + ማከዶኒያ + ሞንጎሊያ + ማካዎ + ሞሪቴኒያ + ማልታ + ማሩሸስ + ሜክሲኮ + ማሌዢያ + ናሚቢያ + ኒው፡ካሌዶኒያ + ናይጄሪያ + ኔዘርላንድ + ኖርዌ + ኔፓል + ኒው፡ዚላንድ + ፔሩ + የፈረንሳይ፡ፖሊኔዢያ + ፓፑዋ፡ኒው፡ጊኒ + ፖላንድ + ፖርታ፡ሪኮ + ሮሜኒያ + ራሺያ + ሳውድአረቢያ + ሱዳን + ስዊድን + ሲንጋፖር + ስሎቬኒያ + ስሎቫኪያ + ሴኔጋል + ሱማሌ + ሰርቢያ + ሲሪያ + ቻድ + የፈረንሳይ፡ደቡባዊ፡ግዛቶች + ታይላንድ + ታጃኪስታን + ምስራቅ፡ቲሞር + ቱኒዚያ + ቱርክ + ትሪኒዳድ፡እና፡ቶባጎ + ታንዛኒያ + ዩጋንዳ + አሜሪካ + ዩዝበኪስታን + ቬንዙዌላ + የእንግሊዝ፡ድንግል፡ደሴቶች + የአሜሪካ፡ቨርጂን፡ደሴቶች + የመን + ዩጎዝላቪያ + ደቡብ፡አፍሪካ + ዛምቢያ + + + + [:Ethi:] + + + + + + + + ጠሐረ + ከተተ + መገበ + አኀዘ + ግንባ + ሠንየ + ሐመለ + ነሐሰ + ከረመ + ጠቀመ + ኀደረ + ኀሠሠ + + + ጠሐረ + ከተተ + መገበ + አኀዘ + ግንባት + ሠንየ + ሐመለ + ነሐሰ + ከረመ + ጠቀመ + ኀደረ + ኀሠሠ + + + + + + + እኁድ + ሰኑይ + ሠሉስ + ራብዕ + ሐሙስ + ዓርበ + ቀዳሚ + + + እኁድ + ሰኑይ + ሠሉስ + ራብዕ + ሐሙስ + ዓርበ + ቀዳሚት + + + + + + + + ጽባሕ + ምሴት + + + ዓ/ዓ + ዓ/ም + + + + + + + + + ERN + ERN + + + ETB + ETB + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gl_ES.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gl_ES.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gl_ES.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gl_ES.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + EEEE dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + MMM dd,yy + + + + + dd/MM/yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gl.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gl.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gl.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gl.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + galego + + + España + + + + [a-záéíóúüñ] + + + + + + + + Xan + Feb + Mar + Abr + Mai + Xuñ + Xul + Ago + Set + Out + Nov + Dec + + + Xaneiro + Febreiro + Marzo + Abril + Maio + Xuño + Xullo + Agosto + Setembro + Outubro + Novembro + Decembro + + + + + + + Dom + Lun + Mar + Mér + Xov + Ven + Sáb + + + Domingo + Luns + Martes + Mércores + Xoves + Venres + Sábado + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + ESP + + ¤ #,##0;-¤ #,##0 + ¤ #,##0;-¤ #,##0 + . + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gu_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gu_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gu_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gu_IN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-MM-yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gu.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gu.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gu.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gu.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,113 @@ + + + + + + + + + + + ગુજરાતી + + + ચીન + જમિની + મિસર + જ્યોર્જીયા + ભારત + નેપાળ + કરાંચી + તુર્ક્મનિસ્તાન + તુર્કસ્તાન + સંયુકત રાજ્ય અમેરિકા + + + + [[:Gujr:]‌‍] + + + + + + + + જાન્યુ + ફેબ્રુ + માર્ચ + એપ્રિલ + મે + જૂન + જુલાઈ + ઑગસ્ટ + સપ્ટે + ઑક્ટો + નવે + ડિસે + + + જાન્યુઆરી + ફેબ્રુઆરી + માર્ચ + એપ્રિલ + મે + જૂન + જુલાઈ + ઑગસ્ટ + સપ્ટેમ્બર + ઑક્ટ્બર + નવેમ્બર + ડિસેમ્બર + + + + + + + રવિ + સોમ + મંગળ + બુધ + ગુરુ + શુક્ર + શનિ + + + રવિવાર + સોમવાર + મંગળવાર + બુધવાર + ગુરુવાર + શુક્રવાર + શનિવાર + + + + પૂર્વ મધ્યાહ્ન + ઉત્તર મધ્યાહ્ન + + + + + + . + , + ; + % + + # + + + - + E + + + + + + + INR + રુ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gv_GB.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gv_GB.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gv_GB.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gv_GB.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + EEEE dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + MMM dd,yy + + + + + dd/MM/yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/gv.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/gv.xml --- zope3-3.4.0/src/zope/i18n/locales/data/gv.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/gv.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,88 @@ + + + + + + + + + + + Gaelg + + + Rywvaneth Unys + + + + [a-zç] + + + + + + + + J-guer + T-arree + Mayrnt + Avrril + Boaldyn + M-souree + J-souree + Luanistyn + M-fouyir + J-fouyir + M.Houney + M.Nollick + + + Jerrey-geuree + Toshiaght-arree + Mayrnt + Averil + Boaldyn + Mean-souree + Jerrey-souree + Luanistyn + Mean-fouyir + Jerrey-fouyir + Mee Houney + Mee ny Nollick + + + + + + + Jed + Jel + Jem + Jerc + Jerd + Jeh + Jes + + + Jedoonee + Jelhein + Jemayrt + Jercean + Jerdein + Jeheiney + Jesarn + + + + a.m. + p.m. + + + RC + AD + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/haw_US.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/haw_US.xml --- zope3-3.4.0/src/zope/i18n/locales/data/haw_US.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/haw_US.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,106 @@ + + + + + + + + + + + + + 279 + 216 + + + + + + + + + + EEEE, d MMMM yyyy + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + d/M/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/haw.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/haw.xml --- zope3-3.4.0/src/zope/i18n/locales/data/haw.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/haw.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + ʻōlelo Hawaiʻi + + + Nūhōlani + Kanakā + Kina + Kelemānia + Kenemaka + Kepania + Palani + Aupuni Mōʻī Hui Pū ʻIa + Helene + ʻIlelani + ʻIseraʻela + ʻĪnia + ʻĪkālia + Iāpana + Mekiko + Hōlani + Aotearoa + ʻĀina Pilipino + Lūkia + ʻAmelika Hui Pū ʻIa + + + + [āēīōūaeiouhklmnpwʻ] + + + + + + + + Ian. + Pep. + Mal. + ʻAp. + Mei + Iun. + Iul. + ʻAu. + Kep. + ʻOk. + Now. + Kek. + + + Ianuali + Pepeluali + Malaki + ʻApelila + Mei + Iune + Iulai + ʻAukake + Kepakemapa + ʻOkakopa + Nowemapa + Kekemapa + + + + + + + LP + P1 + P2 + P3 + P4 + P5 + P6 + + + Lāpule + Poʻakahi + Poʻalua + Poʻakolu + Poʻahā + Poʻalima + Poʻaono + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/he_IL.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/he_IL.xml --- zope3-3.4.0/src/zope/i18n/locales/data/he_IL.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/he_IL.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/he.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/he.xml --- zope3-3.4.0/src/zope/i18n/locales/data/he.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/he.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,537 @@ + + + + + + + + + + + ערבית + בולגרית + צ׳כית + דנית + גרמנית + יוונית + אנגלית + ספרדית + אסטונית + פינית + צרפתית + עברית + קרואטית + הונגרית + איטלקית + יפנית + קוריאנית + ליטאית + לטבית + הולנדית + נורווגית + פולנית + פורטוגזית + רומנית + רוסית + סלובקית + סלובנית + שוודית + טורקית + סינית + + + אנדורה + איחוד האמירויות הערביות + אפגניסטן + אנטיגואה וברבודה + אנגילה + אלבניה + ארמניה + האינטילים ההולנדיים + אנגולה + אנטארקטיקה + ארגנטינה + סמואה האמריקנית + אוסטריה + אוסטרליה + ארובה + אזרבייג׳ן + בוסניה הרצגובינה + ברבדוס + בנגלדש + בלגיה + בורקינה פאסו + בולגריה + בחריין + בורונדי + בנין + ברמודה + ברוניי דארסלאם + בוליביה + ברזיל + בהאמה + בוטאן + האי בובה + בוטסוואנה + בלרוס + בליז + קנדה + איי קוקוס (קילינג) + קונגו, הרפובליקה הדמוקרטית של + הרפובליקה של מרכז אפריקה + קונגו + שווייץ + חוף השנהב + איי קוק + צ׳ילה + קמרון + סין + קולומביה + קוסטה ריקה + קובה + קייפ ורדה + איי כריסטמס + קפריסין + הרפובליקה הצ׳כית + גרמניה + ג׳יבוטי + דנמרק + דומיניקה + הרפובליקה הדומיניקנית + אלג׳יריה + אקוואדור + אסטוניה + מצרים + סהרה המערבית + אריתריאה + ספרד + אתיופיה + פינלנד + פיג׳י + איי פוקלנד + מאוריציוס, המדינות המאוגדות של + איי פארו + צרפת + גבון + בריטניה + גרנדה + גרוזיה + גיאנה הצרפתית + גאנה + גיברלטר + גרינלנד + גמביה + גיניאה + גוואדלופ + גיניאה המשוונית + יוון + האי ג׳ורג׳יה הדרומית ואיי סנדוויץ׳ הדרומיים + גווטמאלה + גואם + גיניאה-ביסאו + גיאנה + הונג קונג S.A.R. של סין + איי הרד ואיי מקדונלנד + הונדורס + קרואטיה + האיטי + הונגריה + אינדונזיה + אירלנד + ישראל + הודו + הטריטוריה הבריטית באוקינוס ההודי + עירק + איראן, הרפובליקה האיסלמית + איסלנד + איטליה + ג׳מייקה + ירדן + יפן + קניה + קירגיזסטן + קמבודיה + קיריבאטי + קומורוס + סנט קיטס וניבס + קוריאה, צפון + קוריאה, דרום + כווית + איי קיימאן + קזחסטן + לאוס, הרפובליקה הדמקורטית העממית + לבנון + סנט לושיה + ליכטנשטיין + סרי לנקה + ליבריה + לסוטו + ליטא + לוקסמבורג + לטביה + לוב + מרוקו + מונקו + מולדובה, הרפובליקה + מדגסקר + איי מרשל + מקדוניה, הרפובליקה של + מאלי + מינמאר + מונגוליה + מקאו S.A.R. של סין + איי מריאנה הצפוניים + מרטיניק + מאוריטניה + מונטסראט + מלטה + מאוריציוס + מלדיבאס + מלאווי + מכסיקו + מלזיה + מוזמביק + נמיביה + קלדוניה החדשה + ניז׳ר + איי נורפק + ניגריה + ניקראגווה + הולנד + נורווגיה + נפאל + נאורו + ניווה + ניו זילנד + עומן + פנמה + פרו + פולינזיה הצרפתית + פפואה גיניאה החדשה + פיליפינים + פקיסטן + פולין + סנט פייר ומיקלון + פיטקיירן + פורטו ריקו + הרשות הפלשתינית + פורטוגל + פלאו + פראגוואי + קטר + ראוניון + רומניה + חבר המדינות הרוסיות + רואנדה + ערב הסעודית + איי שלמה + איי סיישל + סודן + שוודיה + סינגפור + סיינט הלנה + סלובניה + סוולבארד וז׳אן מאיין + סלובקיה + סיירה לאונה + סן מרינו + סנגל + סומליה + סורינאם + סן תומה ופרינסיפה + אל סלבאדור + הרפובליקה הערבית הסורית + סווזילנד + איי טורקס וקאיקוס + צ׳אד + טריטוריות דרומיות של צרפת + טוגו + תאילנד + טג׳יקיסטן + טוקלאו + מזרח טימור + טורקמניסטן + טוניסיה + טונגה + טורקיה + טרינידד וטובגו + טובאלו + טיוואן + טנזניה + אוקראינה + אוגנדה + איים קטנים שלחוף ארצות הברית + ארצות הברית + אורוגוואי + אוזבקיסטן + הוותיקן + סנט וינסנט והגרנדינים + ונצואלה + איי הבתולה הבריטיים + איי הבתולה האמריקניים + וייטנאם + ואנואטו + ואליס ופוטונה + סמואה + תימן + מיוטה + יוגוסלביה + דרום אפריקה + זמביה + זימבבווה + + + + + + + [[:Hebr:]‏‎] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + ינו + פבר + מרץ + אפר + מאי + יונ + יול + אוג + ספט + אוק + נוב + דצמ + + + ינואר + פברואר + מרץ + אפריל + מאי + יוני + יולי + אוגוסט + ספטמבר + אוקטובר + נובמבר + דצמבר + + + + + + + א + ב + ג + ד + ה + ו + ש + + + יום ראשון + יום שני + יום שלישי + יום רביעי + יום חמישי + יום שישי + שבת + + + + + + לפנה״ס + לסה״נ + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {0} {1} + + + + + + + + + תשרי + חשון + כסלו + טבת + שבט + אדר ראשון + אדר שני + ניסן + אייר + סיון + תמוז + אב + אלול + + + תשרי + חשון + כסלו + טבת + שבט + אדר ראשון + אדר שני + ניסן + אייר + סיון + תמוז + אב + אלול + + + + + + לבה"ע + + + + + + + + מוחרם + ספר + רביע אל-אוואל + רביע אל-תני + ג׳ומדה אל-אוואל + ג׳ומדה אל-תני + רג׳אב + שעבאן + ראמדן + שוואל + זו אל-QI'DAH + זו אל-חיג׳ה + + + מוחרם + ספר + רביע אל-אוואל + רביע אל-תני + ג׳ומדה אל-אוואל + ג׳ומדה אל-תני + רג׳אב + שעבאן + ראמדן + שוואל + זו אל-QI'DAH + זו אל-חיג׳ה + + + + + + שנת היג׳רה + + + + + + + + מוחרם + ספר + רביע אל-אוואל + רביע אל-תני + ג׳ומדה אל-אוואל + ג׳ומדה אל-תני + רג׳אב + שעבאן + ראמדן + שוואל + זו אל-QI'DAH + זו אל-חיג׳ה + + + מוחרם + ספר + רביע אל-אוואל + רביע אל-תני + ג׳ומדה אל-אוואל + ג׳ומדה אל-תני + רג׳אב + שעבאן + ראמדן + שוואל + זו אל-QI'DAH + זו אל-חיג׳ה + + + + + + שנת היג׳רה + + + + + + + + + ש"ח + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hi_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hi_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hi_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hi_IN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-M-yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hi.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hi.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hi.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hi.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,430 @@ + + + + + + + + + + + अफ़ार + अब्खाज़ियन् + अफ्रीकी + अम्हारिक् + अरबी + असामी + आयमारा + अज़रबैंजानी + बशख़िर + बैलोरूशियन् + बल्गेरियन् + बिहारी + बिस्लामा + बँगाली + तिब्बती + ब्रेटन + कातालान + कोर्सीकन + चेक + वेल्श + डैनीश + ज़र्मन + भुटानी + ग्रीक + अंग्रेजी + एस्पेरान्तो + स्पेनिश + ऐस्तोनियन् + बास्क् + पर्शियन् + फिनिश + फ़ीजी + फिरोज़ी + फ्रेंच + फ्रीज़न् + आईरिश + स्काट्स् गायेलिक् + गैलिशियन् + गुआरानी + गुज़राती + होउसा + हिब्रीऊ + हिंदी + क्रोएशन् + हंगेरी + अरमेनियन् + ईन्टरलिंगुआ + इन्डोनेशियन् + ईन्टरलिंगुइ + इनुपियाक् + आईस्लैंडिक् + ईटालियन् + इनूकीटूत् + जापानी + जावानीस + जॉर्जीयन् + कज़ाख + ग्रीनलैंडिक + कैम्बोडियन् + कन्नड़ + कोरीयन् + कोंकणी + काश्मिरी + कुरदीश + किरघिज़ + लैटीन + लिंगाला + लाओथीयन् + लिथुनियन् + लाटवियन् (लेट्टीश) + मालागासी + मेओरी + मैसेडोनियन् + मलयालम + मोंगोलियन + मोलडावियन् + मराठी + मलय + मालटिस् + बर्लिस + नायरू + नेपाली + डच् + नार्वेजीयन् + ओसीटान + ओरोमो (अफ़ान) + उड़िया + पंजाबी + पॉलिश + पॉशतो (पुशतो) + पुर्तुगी + क्वेशुआ + राजेस्थानी + रहेय्टो-रोमान्स + किरून्दी + रूमानीयन् + शिखर + रुसी + किन्यारवाण्डा + संस्कृत + सिन्धी + साँग्रो + सेर्बो-क्रोएशन् + शिंघालीस् + स्लोवाक् + स्लोवेनियन् + सामोन + सोणा + सोमाली + अल्बेनियन् + सर्बियन् + सीस्वाटि + सेसोथो + सुन्दानीस + स्विडिश + स्वाहिली + तमिल + तेलेगु + ताजिक् + थाई + तिग्रीन्या + तुक्रमेन + तागालोग + सेत्स्वाना + टोंगा + तुक्रीश + सोंगा + टाटर + ट्वी + उईघुर + यूक्रेनियन् + ऊर्दु + उज़बेक् + वियेतनामी + वोलापुक + वोलोफ + षोसा + येहुदी + योरूबा + ज़ुआंग + चीनी + ज़ुलू + + + अन्डोरा + संयुक्त अरब अमीरात + अफगानिस्तान + एन्टिगुवा और बारबूडा + अल्बानिया + आर्मेनिया + अंगोला + अर्जेन्टीना + ऑस्ट्रिया + ऑस्ट्रेलिया + अजरबैजान + बोसनिया हर्जिगोविना + बारबाडोस + बंगलादेश + बेल्जियम + बर्किना फासो + बुल्गारिया + बहरैन + बुरुंडी + बेनिन + ब्रूनइ + बोलीविया + ब्राजील + बहामा + भूटान + बोत्स्वाना + बेलारूस + बेलिज + कनाडा + किंशासा + सेंट्रल अफ्रीकन रिपब्लिक + कांगो + स्विस + आईवरी कोस्ट + चिली + कामेरान + चीन + कोलम्बिया + कोस्टारीका + क्यूबा + कैप वर्त + साइप्रस + चेक गणराज्य + जर्मनी + जिबूती + डेनमार्क + डोमिनिका + डोमिनिकन गणराज्य + अल्जीरिया + इक्वाडोर + एस्टोनिया + मिस्र + पश्चिमी सहारा + इरिट्रिया + स्पेन + इथियोपिया + फिनलैंड + फिजी + फ्रांस + ग्रीस + गोतेदाला + गीनी-बिसाउ + गुयाना + हाण्डूरस + क्रोशिया + हाइती + हंगरी + इंडोनेशिया + आयरलैंड + इसराइल + भारत + इराक + ईरान + आइसलैंड + इटली + जमाइका + जोर्डन + जापान + केन्या + किर्गिज + कम्बोडिया + कोमोरस + सेंट किट्स और नेविस + उत्तर कोरिया + दक्षिण कोरिया + कुवैत + कजाखस्तान + लाओस + लेबनान + सेंट लूसिया + लिकटेंस्टीन + श्रीलंका + लाइबेरिया + लेसोथो + लिथुआनिया + लक्समबर्ग + लात्विया + लीबिया + मोरक्को + मोनाको + मोल्डाविया + मदागास्कर + मैसेडोनिया + माली + म्यानमार + मंगोलिया + मॉरिटानिया + माल्टा + मौरिस + मालदीव + मलावी + मेक्सिको + मलेशिया + मोजाम्बिक + पनामा + पेरू + पापुआ न्यू गिनी + फिलीपिंस + पाकिस्तान + पोलैंड + पुर्तगाल + पारागुए + कतर + रोमानिया + रूस + रूआण्डा + सऊदी अरब + सूडान + स्वीडन + सिंगापुर + स्लोवेनिया + स्लोवाकिया + सियरालेओन + सैन मेरीनो + सेनेगल + सोमालिया + सुरिनाम + साउ-तोम-प्रिंसिप + अल साल्वाडोर + सीरिया + सुआजीलैंड + चाड + टोगो + थाइलैंड + ताजिकिस्तान + तुर्कमेनिस्तान + तुनिशिया + टोंगा + तुर्की + ट्रिनिडाड और टोबैगो + तुवालु + ताइवान + तंजानिया + यूक्रेन + युगांडा + संयुक्त राज्य अमरिका + युरूगुए + उजबेकिस्तान + वैटिकन + वेनेजुएला + ब्रिटिश वर्जीन ऌईलैंडस् + ईउ, एस वर्जीन आईलैंडस् + वियतनाम + वानुअतु + यमन + दक्षिण अफ्रीका + जाम्बिया + जिम्बाब्वे + + + + [[:Deva:]‌‍] + + + + + + + + जनवरी + फरवरी + मार्च + अप्रैल + मई + जून + जुलाई + अगस्त + सितम्बर + अक्तूबर + नवम्बर + दिसम्बर + + + जनवरी + फरवरी + मार्च + अप्रैल + मई + जून + जुलाई + अगस्त + सितम्बर + अक्तूबर + नवम्बर + दिसम्बर + + + + + + + रवि + सोम + मंगल + बुध + गुरु + शुक्र + शनि + + + रविवार + सोमवार + मंगलवार + बुधवार + गुरुवार + शुक्रवार + शनिवार + + + + पूर्वाह्न + अपराह्न + + + ईसापूर्व + सन + + + + + + + + भारतीय समय + भारतीय समय + + + IST + IST + + + + + + + . + , + ; + % + + # + + + - + E + + + + + + + INR + रु + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hr_HR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hr_HR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hr_HR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hr_HR.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hr.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hr.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hr.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hr.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,487 @@ + + + + + + + + + + + arapski + bugarski + češki + danski + njemački + grčki + engleski + španjolski + estonijski + finski + francuski + hebrejski + hrvatski + mađarski + talijanski + japanski + korejski + litvanski + latvijski + nizozemski + norveški + poljski + portugalski + rumunjski + ruski + slovački + slovenski + švedski + turski + kineski + + + Andora + Ujedinjeni Arapski Emirati + Afganistan + Antigua i Barbuda + Anguila + Albanija + Armenija + Nizozemski Antili + Angola + Antarktik + Argentina + Američka Samoa + Austrija + Australija + Aruba + Azerbajdžan + Bosna i Hercegovina + Barbados + Bangladeš + Belgija + Burkina Faso + Bugarska + Bahrein + Burundi + Benin + Bermuda + Brunei Darussalam + Bolivija + Brazil + Bahami + Butan + Otok Bouvet + Bocvana + Bjelorusija + Belize + Kanada + Kokos (Keeling) Otoci + Kongo, Demokratska Republika + Srednjoafrička Republika + Kongo + Švicarska + Obala Bjelokosti + Kukovi Otoci + Čile + Kamerun + Kina + Kolumbija + Kostarika + Kuba + Zeleni Rt + Božićni Otoci + Cipar + Češka Republika + Njemačka + Džibuti + Danska + Dominika + Dominikanska Republika + Alžir + Ekvador + Estonija + Egipat + Zapadna Sahara + Eritreja + Španjolska + Etiopija + Finska + Fidži + Falklandski Otoci + Mikronezija, Federalne Države + Farski Otoci + Francuska + en + Gabon + Ujedinjena Kraljevina + Grenada + Gruzija + Francuska Gvajana + Gana + Gibraltar + Greenland + Gambija + Gvineja + Guadeloupe + Ekvatorska Gvineja + Grčka + Južna Gruzija i Južni Sendvič Otoci + Gvatemala + Guam + Gvineja Bisau + Gvajana + Hong Kong S.A.R. Kine + Heard Otok i McDonald Otoci + Honduras + Hrvatska + Haiti + Mađarska + Indonezija + Irska + Izrael + Indija + Britanski Teritorij Indijskog Oceana + Irak + Iran, Islamska Republika + Island + Italija + Jamajka + Jordan + Japan + Kenija + Kirgistan + Kambodža + Kiribati + Komori + Sveti Kristofor i Nevis + Koreja, Sjeverna + Koreja, Južna + Kuvajt + Kajmanski Otoci + Kazakstan + Laoska Narodna Demokratska Republika + Libanon + Sveta Lucija + Lihtenštajn + Šri Lanka + Liberija + Lesoto + Litva + Luksemburg + Latvija + Libijska Arapska Džamahirija + Maroko + Monako + Moldavija, Republika + Madagaskar + Maršalovi Otoci + Makedonija, Republika + Mali + Mijanma + Mongolija + Makao S.A.R. Kine + Sjeverni Marianski Otoci + Martinik + Mauritanija + Montserat + Malta + Mauricijus + Maldivi + Malavi + Meksiko + Malezija + Mozambik + Namibija + Nova Kaledonija + Niger + Norfolški Otoci + Nigerija + Nikaragva + Nizozemska + Norveška + Nepal + Nauru + Niue + Novi Zeland + Oman + Panama + Peru + Francuska Polinezija + Papua Nova Gvineja + Filipini + Pakistan + Poljska + Sveti Petar i Miguel + Pitcairn + Portoriko + Palestinska Teritoija + Portugal + Palau + Paragvaj + Katar + Reunion + Rumunjska + Ruska Federacija + Ruanda + Saudijska Arabija + Salamunovi Otoci + Sejšeli + Sudan + Švedska + Singapur + Sveta Helena + Slovenija + Svalbard i Jan Mayen + Slovačka + Sijera Leone + San Marino + Senegal + Somalija + Serbia + Surinam + Sveti Toma i Prinsipe + El Salvador + Sirija + Svazi + Turkski i Kaikos Otoci + Čad + Francuske Južne Teritorije + Togo + Tajland + Tadžikistan + Tokelau + Istočni Timor + Turkmenistan + Tunis + Tonga + Turska + Trinidad i Tobago + Tuvalu + Tajvan, Kineska Pokrajina + Tanzanija + Ukrajina + Uganda + Sjedinjene Države Manji Vanjski Otoci + Sjedinjene Države + Urugvaj + Uzbekistan + Sveta Stolica (Država Vatikanskog Grada) + Sveti Vincent i Grenadini + Venezuela + Britanski Djevičanski Otoci + U.S. Djevičanski Otoci + Vijetnam + Vanuatu + Wallis i Futuna + Samoa + Jemen + Majote + Jugoslavija + Južna Afrika + Zambija + Zimbabve + + + + [a-p r-v z đ ć č ž š {lj} {nj} {dž}] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + sij + vel + ožu + tra + svi + lip + srp + kol + ruj + lis + stu + pro + + + s + v + o + t + s + l + s + k + r + l + s + p + + + siječnja + veljače + ožujka + travnja + svibnja + lipnja + srpnja + kolovoza + rujna + listopada + studenoga + prosinca + + + + + sij + vel + ožu + tra + svi + lip + srp + kol + ruj + lis + stu + pro + + + s + v + o + t + s + l + s + k + r + l + s + p + + + siječanj + veljača + ožujak + travanj + svibanj + lipanj + srpanj + kolovoz + rujan + listopad + studeni + prosinac + + + + + + + ned + pon + uto + sri + čet + pet + sub + + + nedjelja + ponedjeljak + utorak + srijeda + četvrtak + petak + subota + + + + + + + + + + + + yyyy. MMMM dd + + + + + yyyy. MMMM dd + + + + + yyyy.MM.dd + + + + + yyyy.MM.dd + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + HRK + Kn + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hu_HU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hu_HU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hu_HU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hu_HU.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hu.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hu.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hu.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hu.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,672 @@ + + + + + + + + + + + afar + abház + afrikai + amhara + arab + asszámi + ajmara + azerbajdzsáni + baskír + belorusz + bolgár + bihari + bislama + bengáli + tibeti + breton + katalán + korzikai + cseh + walesi + dán + német + butáni + görög + angol + eszperantó + spanyol + észt + baszk + perzsa + finn + fidzsi + feröeri + francia + fríz + ír + skót (gael) + galíciai + guarani + gudzsaráti + hausza + héber + hindi + horvát + magyar + örmény + interlingua + indonéz + interlingue + inupiak + izlandi + olasz + inuktitut + japán + jávai + grúz + kazah + grönlandi + kambodzsai + kannada + koreai + kasmíri + kurd + kirgiz + latin + lingala + laoszi + litván + lett + madagaszkári + maori + macedón + malajalam + mongol + moldvai + marati + maláj + máltai + burmai + naurui + nepáli + holland + norvég + okszitán + oromói + orija + pandzsábi + lengyel + pastu (afgán) + portugál + kecsua + rétoromán + kirundi + román + orosz + kiruanda + szanszkrit + szindi + sango + szerb-horvát + szingaléz + szlovák + szlovén + szamoai + sona + szomáli + albán + szerb + sziszuati + szeszotó + szundanéz + svéd + szuahéli + tamil + telugu + tadzsik + thai + tigrinya + türkmén + tagalog + szecsuáni + tonga + török + conga + tatár + tui + ujgur + ukrán + urdu + üzbég + vietnámi + volapük + volof + hosza + zsidó + joruba + zsuang + kínai + zulu + + + Andorra + Egyesült Arab Emirátus + Afganisztán + Antigua és Barbuda + Anguilla + Albánia + Örményország + Holland Antillák + Angola + Antarktisz + Argentína + Amerikai Szamoa + Ausztria + Ausztrália + Aruba + Azerbajdzsán + Bosznia-Hercegovina + Barbados + Banglades + Belgium + Burkina Faso + Bulgária + Bahrain + Burundi + Benin + Bermuda + Brunei Darussalam + Bolívia + Brazília + Bahamák + Bhután + Bouvet-sziget + Botswana + Fehéroroszország + Beliz + Kanada + Kókusz (Keeling)-szigetek + Kongó, Demokratikus köztársaság + Közép-afrikai Köztársaság + Kongó + Svájc + Elefántcsontpart + Cook-szigetek + Chile + Kamerun + Kína + Kolumbia + Costa Rica + Kuba + Zöld-foki Köztársaság + Karácsony-szigetek + Ciprus + Cseh Köztársaság + Németország + Dzsibuti + Dánia + Dominika + Dominikai Köztársaság + Algéria + Ecuador + Észtország + Egyiptom + Nyugat Szahara + Eritrea + Spanyolország + Etiópia + Finnország + Fidzsi + Falkland-szigetek + Mikronézia, Szövetségi Államok + Feröer-szigetek + Franciaország + en + Gabon + Egyesült Királyság + Grenada + Grúzia + Francia Guyana + Ghana + Gibraltár + Grönland + Gambia + Guinea + Guadeloupe + Egyenlítďi Guinea + Görögország + Dél-Georgia és Dél-Sandwich Szigetek + Guatemala + Guam + Guinea-Bissau + Guyana + Hong Kong S.A.R., China + Heard és McDonald Szigetek + Honduras + Horvátország + Haiti + Magyarország + Indonézia + Írország + Izrael + India + Brit Indiai Oceán + Irak + Irán + Izland + Olaszország + Jamaica + Jordánia + Japán + Kenya + Kirgizisztán + Kambodzsa + Kiribati + Comore-szigetek + Saint Kitts és Nevis + Korea, Észak + Korea, Dél + Kuwait + Kajmán-szigetek + Kazahsztán + Laoszi Népi Demokratikus Köztársaság + Libanon + Saint Lucia + Liechtenstein + Sri Lanka + Libéria + Lesotho + Litvánia + Luxemburg + Lettország + Líbiai Arab Jamahiriya + Marokkó + Monaco + Moldáv Köztársaság + Madagaszkár + Marshall-szigetek + Macedónia, Köztársaság + Mali + Mianmar + Mongólia + Macao S.A.R., China + Északi Mariana-szigetek + Martinique (francia) + Mauritánia + Montserrat + Málta + Mauritius + Maldív-szigetek + Malawi + Mexikó + Malajzia + Mozambik + Namíbia + Új Kaledónia (francia) + Niger + Norfolk-sziget + Nigéria + Nicaragua + Hollandia + Norvégia + Nepál + Nauru + Niue + Új-Zéland + Omán + Panama + Peru + Polinézia (francia) + Pápua Új-Guinea + Fülöp-szigetek + Pakisztán + Lengyelország + Saint Pierre és Miquelon + Pitcairn-sziget + Puerto Rico + Palesztín Terület + Portugália + Palau + Paraguay + Katar + Reunion (francia) + Románia + Orosz Köztársaság + Ruanda + Szaud-Arábia + Salamon-szigetek + Seychelles + Szudán + Svédország + Szingapúr + Saint Helena + Szlovénia + Svalbard és Jan Mayen + Szlovákia + Sierra Leone + San Marino + Szenegál + Szomália + Serbia + Suriname + Saint Tome és Principe + El Salvador + Szíriai Arab Köztársaság + Szváziföld + Török és Caicos Szigetek + Csád + Francia Déli Területek + Togo + Thaiföld + Tadzsikisztán + Tokelau + Kelet-Timor + Türkmenisztán + Tunézia + Tonga + Törökország + Trinidad és Tobago + Tuvalu + Taiwan + Tanzánia + Ukrajna + Uganda + United States Minor Outlying Islands + Egyesült Államok + Uruguay + Üzbegisztán + Vatikán + Saint Vincent és Grenadines + Venezuela + Brit Virgin-szigetek + U.S. Virgin-szigetek + Vietnám + Vanuatu + Wallis és Futuna Szigetek + Szamoa + Jemen + Mayotte + Jugoszlávia + Dél-Afrika + Zambia + Zimbabwe + + + + [a-z á é í ó ú ö ü ő ű {cs} {dz} {dzs} {gy} {ly} {ny} {sz} {ty} {zs} {ccs} {ddz} {ddzs} {ggy} {lly} {nny} {ssz} {tty} {zzs}] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + BK + + + + + + + + jan. + febr. + márc. + ápr. + máj. + jún. + júl. + aug. + szept. + okt. + nov. + dec. + + + január + február + március + április + május + június + július + augusztus + szeptember + október + november + december + + + + + + + V + H + K + Sze + Cs + P + Szo + + + vasárnap + hétfő + kedd + szerda + csütörtök + péntek + szombat + + + + + + + + DE + DU + + + i.e. + i.u. + + + + + + + yyyy. MMMM d. + + + + + yyyy. MMMM d. + + + + + yyyy.MM.dd. + + + + + yyyy.MM.dd. + + + + + + + + H:mm:ss z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + Tisri + Hesván + Kiszlév + Tévész + Svát + Ádár risón + Ádár séni + Niszán + Ijár + Sziván + Tamuz + Áv + Elul + + + Tisri + Hesván + Kiszlév + Tévész + Svát + Ádár risón + Ádár séni + Niszán + Ijár + Sziván + Tamuz + Áv + Elul + + + + + + + + + + + + + + Moharrem + Safar + Rébi el avvel + Rébi el accher + Dsemádi el avvel + Dsemádi el accher + Redseb + Sabán + Ramadán + Sevvál + Dsül kade + Dsül hedse + + + Moharrem + Safar + Rébi el avvel + Rébi el accher + Dsemádi el avvel + Dsemádi el accher + Redseb + Sabán + Ramadán + Sevvál + Dsül kade + Dsül hedse + + + + + + MF + + + + + + + + Moharrem + Safar + Rébi el avvel + Rébi el accher + Dsemádi el avvel + Dsemádi el accher + Redseb + Sabán + Ramadán + Sevvál + Dsül kade + Dsül hedse + + + Moharrem + Safar + Rébi el avvel + Rébi el accher + Dsemádi el avvel + Dsemádi el accher + Redseb + Sabán + Ramadán + Sevvál + Dsül kade + Dsül hedse + + + + + + MF + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + HUF + Ft + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hy_AM_REVISED.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hy_AM_REVISED.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hy_AM_REVISED.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hy_AM_REVISED.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + Հնվ + Փտվ + Մրտ + Ապր + Մյս + Հնս + Հլս + Օգս + Սեպ + Հոկ + Նոյ + Դեկ + + + Հունվար + Փետրվար + Մարտ + Ապրիլ + Մայիս + Հունիս + Հուլիս + Օգոստոս + Սեպտեմբեր + Հոկտեմբեր + Նոյեմբեր + Դեկտեմբեր + + + + Առ․ + Կե․ + + + Մ․Թ․Ա․ + Մ․Թ․ + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hy_AM.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hy_AM.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hy_AM.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hy_AM.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + Ք․Ա․ + Ք․Ե․ + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/hy.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/hy.xml --- zope3-3.4.0/src/zope/i18n/locales/data/hy.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/hy.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,380 @@ + + + + + + + + + + + Հայերէն + + + Անդորա + Միացյալ Արաբական Էմիրաթներ + Աֆղանստան + Անտիգուա-Բարբուդա + Ալբանիա + Հայաստանի Հանրապետութիւն + Անգոլա + Արգենտինա + Ավստրիա + Ավստրալիա + Ադրբեջան + Բոսնիա-Հերցեգովինա + Բարբադոս + Բանգլադեշ + Բելգիա + Բուրկինա Ֆասո + Բուլղարիա + Բահրեյն + Բուրունդի + Բենին + Բրունեյ + Բոլիվիա + Բրազիլիա + Բահամներ + Բուտան + Բոտսվանա + Բելոռուս + Բելիզ + Կանադա + Կենտրոնական Աֆրիկյան Հանրապետություն + Կոնգո + Շվեյցարիա + Փղոսկրի Ափ + Չիլի + Կամերուն + Չինաստան + Կոլումբիա + Կոստա-Ռիկա + Կուբա + Կաբո-Վերդե + Կիպրոս + Չեխիայի Հանրապետություն + Գերմանիա + Ջիբուտի + Դանիա + Դոմինիկա + Դոմինիկյան Հանրապետություն + Ալժիր + Էկվադոր + Էստոնիա + Եգիպտոս + Արեվմտյան Սահարա + Էրիտրեա + Իսպանիա + Եթովպիա + Ֆինլանդիա + Ֆիջի + Միկրոնեզիա + Ֆրանսիա + Գաբոն + Մեծ Բրիտանիա + Գրենադա + Վրաստան + Գանա + Գամբիա + Գվինեա + Հասարակածային Գվինեա + Հունաստան + Գվատեմալա + Գվինեա-Բիսաու + Գայանա + Հոնդուրաս + Հորվաթիա + Հաիթի + Հունգարիա + Ինդոնեզիա + Իռլանդիա + Իսրայել + Հնդկաստան + Իրաք + Իրան + Իսլանդիա + Իտալիա + Ջամայկա + Հորդանան + Ճապոնիա + Քենիա + Կիրգիզստան + Կամբոջա + Կիրիբատի + Կոմորոս + Սենտ Կիտս-Նեվիս + Հյուսիսային Կորեա + Հարավային Կորեա + Քուվեյթ + Ղազախստան + Լաոս + Լիբանան + Սանտա Լուչիա + Լիխտենշտեյն + Շրի Լանկա + Լիբերիա + Լեսոտո + Լիտվա + Լյուքսեմբուրգ + Լատվիա + Լիբիա + Մարոկո + Մոնակո + Մոլդովա + Մադագասկար + Մարշալյան կղզիներ + Մակեդոնիա + Մալի + Մյանմա + Մոնղոլիա + Մավրիտանիա + Մալթա + Մավրիտոս + Մալդիվներ + Մալավի + Մեքսիկա + Մալայզիա + Մոզամբիկ + Նամիբիա + Նիգեր + Նիգերիա + Նիկարագուա + Նիդերլանդեր + Նորվեգիա + Նեպալ + Նաուրու + Նոր Զելանդիա + Օման + Պանամա + Պերու + Պապուա Նոր Գվինեա + Ֆիլիպիններ + Պակիստան + Լեհաստան + Պորտուգալիա + Պալաու + Պարագվայ + Կատար + Ռումինիա + Ռուսաստան + Ռուանդա + Սաուդիան Արաբիա + Սոլոմոնյան կղզիներ + Սեյշելներ + Սուդան + Շվեդիա + Սինգապուր + Սլովենիա + Սլովակիա + Սյերա-Լեոնե + Սան Մարինո + Սենեգալ + Սոմալի + Սերբիա + Սուրինամ + Սան-Թոմե-Փրինսիպի + Սալվադոր + Սիրիա + Սվազիլենդ + Չադ + Տոգո + Թաիլանդ + Տաճիկստան + Թուրքմենստան + Թունիս + Տոնգա + Թուրքիա + Տրինիդադ-Տոբագո + Տուվալու + Թայվան + Տանզանիա + Ուկրաինա + Ուգանդա + Ամէրիկայի Միացյալ Նահանգնէր + Ուրուգվայ + Ուզբեկստան + Վատիկան + Սենտ Վիսենտ-Գրենադիններ + Վենեսուելա + Վիետնամ + Վանուատու + Սամոա + Եմեն + Հարավային Աֆրիկա + Զամբիա + Զիմբաբվե + + + + [:Armn:] + + + + + + + + Յնր + Փտր + Մրտ + Ապր + Մյս + Յնս + Յլս + Օգս + Սեպ + Հոկ + Նոյ + Դեկ + + + Յունուար + Փետրուար + Մարտ + Ապրիլ + Մայիս + Յունիս + Յուլիս + Օգոստոս + Սեպտեմբեր + Հոկտեմբեր + Նոյեմբեր + Դեկտեմբեր + + + + + + + Կիր + Երկ + Երք + Չոր + Հնգ + Ուր + Շաբ + + + Կիրակի + Երկուշաբթի + Երեքշաբթի + Չորեքշաբթի + Հինգշաբթի + Ուրբաթ + Շաբաթ + + + + + + + + Առ․ + Եր․ + + + Յ․Տ․ + Ն․Ք․ + + + + + + + EEEE, MMMM d, yyyy + + + + + MMMM dd, yyyy + + + + + MMM d, yyyy + + + + + MM/dd/yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #0.###;-#0.### + + + + + + + #E0 + + + + + + + #0% + + + + + + + #0.00 ¤;-#0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/id_ID.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/id_ID.xml --- zope3-3.4.0/src/zope/i18n/locales/data/id_ID.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/id_ID.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + EEEE dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd MMM yy + + + + + dd/MM/yy + + + + + + + + H:mm:ss + + + + + H:mm:ss + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/id.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/id.xml --- zope3-3.4.0/src/zope/i18n/locales/data/id.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/id.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,590 @@ + + + + + + + + + + + Afar + Abkhaz + Aceh + Avestan + Afrikaans + Afro-Asiatik (Lainnya) + Akan + Akkadien + Bahasa Algonquia + Amharik + Inggris Kuno (kl.450-1100) + Bahasa-bahasa Apache + Arab + Aram + Araucan + Buatan (Lainnya) + Assam + Astur + Bahasa-bahasa Athapaska + Bahasa-bahasa Australia + Avarik + Aymara + Azerbaijan + Bashkir + Bahasa-bahasa Bamileke + Balin + Baltik (Lainnya) + Belarusia + Bulgaria + Bihari + Bislama + Bambara + Bengal + Tibet + Breton + Bosnia + Bugis + Catalan + India Amerika Tengah (Lainnnya) + Karib + Kaukasia (Lainnya) + Chechen + Celtic (Lainnya) + Chamorro + Chuuke + Korsika + Koptik + Cree + Ceko + Chuvash + Welsh + Denmark + Jerman + Divehi + Dzongkha + Ewe + Mesir Kuno + Yunani + Inggris + Inggris, Abad Pertengahan (1100-1500) + Esperanto + Spanyol + Estonian + Basque + Persia + Fulah + Finlandia + Fiji + Faro + Perancis + Perancis, Abad Pertengahan (kl.1400-1600) + Perancis Kuno (842-kl.1400) + Friuli + Frisi + Irlandia + Gaelik Skotlandia + Jermanik (Lainnya) + Gilbert + Gallegan + Jerman, Abad Pertengahan (kl.1050-1500) + Guarani + Jerman Kuno (kl.750-1050) + Gothik + Yunani Kuno (sd 1453) + Gujarati + Manx + Hausa + Hawaii + Ibrani + Hindi + Hiri Motu + Kroasia + Hungaria + Armenia + Herero + Interlingua + Bahasa Indonesia + Interlingue + Igbo + Sichuan Yi + Inupiaq + Ido + Icelandic + Italian + Japanese + Judeo-Persia + Judeo-Arab + Jawa + Georgian + Kongo + Kikuyu + Kuanyama + Kazakh + Kalaallisut + Khmer + Kannada + Korea + Konkani + Kosrae + Kanuri + Kashmir + Kurdi + Komi + Cornish + Kirghiz + Latin + Luxembourg + Lezghia + Ganda + Limburg + Lingala + Lao + Lithuania + Luba-Katanga + Latvian + Madura + Makassar + Austronesia + Malagasi + Irlandia Abad Pertengahan (900-1200) + Marshall + Maori + Bahasa Lain-lain + Macedonian + Mon-Khmer (Lainnya) + Malayalam + Mongolian + Moldavian + Marathi + Malay + Maltese + Burma + Nauru + Norwegian Bokmål + Nepal + Ndonga + Belanda + Norwegian Nynorsk + Norwegian + Navajo + Nyanja; Chichewa; Chewa + Ojibwa + Oromo + Oriya + Ossetic + Punjabi + Papuan (Lainnya) + Persia Kuno (kl.600-400 SM.) + Filipina (Lainnya) + Pali + Polish + Pashto (Pushto) + Portugis + Quechua + Rhaeto-Romance + Rundi + Romanian + Russian + Kinyarwanda + Sanskrit + Sardinian + Sindhi + Northern Sami + Sango + Serbo-Croatian + Sinhalese + Slovak + Slovenian + Samoan + Shona + Somali + Albanian + Serbian + Swati + Sundan + Sumeria + Swedia + Swahili + Syria + Tamil + Telugu + Tajik + Thai + Tigrinya + Turkmen + Tagalog + Tswana + Turkish + Tsonga + Tatar + Twi + Tahitian + Uighur + Ukrainian + Urdu + Uzbek + Venda + Vietnamese + Volapük + Walloon + Wolof + Xhosa + Yiddish + Yoruba + Zhuang + Cina + Zulu + + + Andora + Uni Emirat Arab + Afghanistan + Antigua dan Barbuda + Anguilla + Albania + Armenia + Antilles Belanda + Angola + Antarktika + Argentina + Samoa Amerika + Austria + Australia + Aruba + Azerbaijan + Bosnia dan Herzegovina + Barbados + Bangladesh + Belgia + Burkina Faso + Bulgaria + Bahrain + Burundi + Benin + Bermuda + Brunei + Bolivia + Brazil + Bahamas + Bhutan + Kepulauan Bouvet + Botswana + Belarusia + Belize + Kanada + Kepulauan Cocos + Republik Demokratik Kongo + Republik Afrika Tengah + Kongo + Swiss + Pantai Gading + Kepulauan Cook + Chili + Kamerun + Cina + Kolombia + Kosta Rika + Kuba + Tanjung Verde + Pulau Christmas + Siprus + Republik Ceko + Jerman + Jibouti + Denmark + Dominika + Republik Dominika + Algeria + Ekuador + Estonia + Mesir + Sahara Barat + Eritrea + Spanyol + Ethiopia + Finlandia + Fiji + Kepulauan Falkland + Mikronesia + Kepulauan Faroe + Perancis + Gabon + Inggris Raya + Grenada + Georgia + Guyana Perancis + Ghana + Gibraltar + Greenland + Gambia + Guinea + Guadeloupe + Guinea Khatulistiwa + Yunani + Georgia Selatan dan Kepulauan Sandwich Selatan + Guatemala + Guam + Guinea-Bissau + Guyana + Hong Kong S.A.R., Cina + Pulau Heard dan Kepulauan McDonald + Honduras + Kroasia + Haiti + Hungaria + Indonesia + Irlandia + Israel + India + Iraq + Iran + Islandia + Itali + Jamaika + Yordania + Jepang + Kenya + Kyrgyzstan + Kamboja + Kiribati + Komoros + Saint Kitts dan Nevis + Korea Utara + Korea Selatan + Kuwait + Kepulauan Kayman + Kazakhstan + Laos + Lebanon + Santa Lusia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Lithuania + Luxembourg + Latvia + Libya + Maroko + Monaco + Moldova + Madagaskar + Kepulauan Marshall + Macedonia + Mali + Myanmar + Mongolia + Makao S.A.R. Cina + Kepualuan Mariana Utara + Martinique + Mauritania + Montserrat + Malta + Mauritius + Maldives + Malawi + Mexico + Malaysia + Mozambique + Namibia + Kaledonia Baru + Niger + Kepulauan Norfolk + Nigeria + Nicaragua + Netherlands + Norwegia + Nepal + Nauru + Niue + Selandia Baru + Oman + Panama + Peru + Polynesia Perancis + Papua Nugini + Filipina + Pakistan + Polandia + Saint Pierre dan Miquelon + Pitcairn + Puerto Riko + Otoritas Palestina + Portugis + Palau + Paraguay + Qatar + Réunion + Romania + Rusia + Rwanda + Arab Saudi + Kepulauan Solomon + Seychelles + Sudan + Sweden + Singapura + Saint Helena + Slovenia + Svalbard dan Jan Mayen + Slovakia + Sierra Leone + San Marino + Senegal + Somalia + Serbia + Suriname + Sao Tome dan Principe + El Salvador + Syria + Swaziland + Chad + Togo + Thailand + Tajikistan + Tokelau + Turkmenistan + Tunisia + Tonga + Turkey + Trinidad dan Tobago + Tuvalu + Taiwan + Tanzania + Ukraina + Uganda + Amerika Serikat + Uruguay + Uzbekistan + Vatikan + Saint Vincent dan Grenadines + Venezuela + Kepulauan British Virgin + Kepulauan U.S. Virgin + Vietnam + Vanuatu + Wallis dan Futuna + Samoa + Yaman + Mayotte + Yugoslavia + Afrika Selatan + Zambia + Zimbabwe + + + + [a-z] + + + + + + + + Jan + Feb + Mar + Apr + Mei + Jun + Jul + Agu + Sep + Okt + Nov + Des + + + Januari + Februari + Maret + April + Mei + Juni + Juli + Agustus + September + Oktober + November + Desember + + + + + + + Min + Sen + Sel + Rab + Kam + Jum + Sab + + + Minggu + Senin + Selasa + Rabu + Kamis + Jumat + Sabtu + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + IDR + Rp + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/is_IS.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/is_IS.xml --- zope3-3.4.0/src/zope/i18n/locales/data/is_IS.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/is_IS.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/is.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/is.xml --- zope3-3.4.0/src/zope/i18n/locales/data/is.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/is.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,304 @@ + + + + + + + + + + + íslenska + + + Sameinte arabísku fyrstadæmin + Afganistan + Antigua og Barbuda + Albanía + Armenía + Angóla + Argentína + Austurríki + Ástralía + Bosnía-Hersegovína + Belgía + Búlgaría + Búrúndí + Bólivía + Brasilía + Bahama eyjar + Bhútan + Hvíta Rússland + Kanada + Mið-Afríku lyðveldið + Kongó + Sviss + Fílabeinsströndin + Kamerún + Kína + Kólumbía + Kostaríka + Kúba + Grænhöfðaeyjar + Kýpur + Tékkland + Þýskaland + Danmörk + Dóminíska lýðveldið + Alsír + Ekvador + Eistland + Egyptaland + Vestur-Sahara + Erítrea + Spánn + Eþíópía + Finnland + Mikrónesía + Frakkland + Stóra Bretland + Georgía + Gana + Gínea + Miðbaugs Gínea + Grikkland + Gvatemala + Gínea-Bissau + Hondúras + Króatía + Haítí + Ungverjaland + Indónesía + Írland + Ísrael + Indland + Írak + Íran + Ísland + Ítalía + Jamaíka + Jórdanía + Kenýa + Kirgisistan + Kambódía + Kiribatí + Comoro eyjar + Saint Kitts og Nevis + Norður-Kórea + Suður-Kórea + Kúveit + Kasakstan + Líbanon + Saint Lúsia + Líbería + Lesótó + Litháen + Lúxemborg + Lettland + Lýbía + Marokkó + Mónacó + Moldovía + Madagaskar + Marshalleyjar + Makedónía + Mongólía + Máritanía + Máritíus + Maldíveyjar + Mexíkó + Malasía + Mósambík + Namibía + Níger + Nígería + Níkaragúa + Niðurlönd + Noregur + Nýja-Sjáland + Óman + Perú + Papúa Nýja Gínea + Filipseyjar + Pólland + Portúgal + Palaueyjar + Paragvæ + Katar + Rúmenía + Rússland + Rúanda + Sádi Arabía + Salómons eyjar + Seychelle eyjar + Súdan + Svíþjoð + Slóvenía + Slóvakía + Sómalía + Serbía + Súrinam + Sao Tome og Prinsípe + Sýrland + Svasiland + Tæland + Tadjikistan + Túrkmenistan + Túnis + Tyrkland + Trinidad og Tobago + Túvalú + Tævan + Tansanía + Úkraína + Úganda + Bandaríki Norður-Ameríku + Úrúgvæ + Úsbekistan + Vatíkanið + Saint Vinsent og Grenadíneyjar + Venesúela + Samóa + Jemen + Suður-Afríka + + + + [a-záéíóúýöæðþ] + + + + + + + + jan + feb + mar + apr + maí + jún + júl + ágú + sep + okt + nóv + des + + + janúar + febrúar + mars + apríl + maí + júní + júlí + ágúst + september + október + nóvember + desember + + + + + + + sun + mán + þri + mið + fim + fös + lau + + + sunnudagur + mánudagur + þriðjudagur + miðvikudagur + fimmtudagur + föstudagur + laugardagur + + + + + + + + EEEE, d. MMMM yyyy + + + + + d. MMMM yyyy + + + + + d.M.yyyy + + + + + d.M.yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + ISK + kr. + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/it_CH.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/it_CH.xml --- zope3-3.4.0/src/zope/i18n/locales/data/it_CH.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/it_CH.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + EEEE, d MMMM yyyy + + + + + d MMMM yyyy + + + + + d-MMM-yy + + + + + dd.MM.yy + + + + + + + + HH.mm:ss' h' z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + . + ' + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0.00;¤-#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/it_IT.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/it_IT.xml --- zope3-3.4.0/src/zope/i18n/locales/data/it_IT.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/it_IT.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd/MMM/yy + + + + + dd/MM/yy + + + + + + + + HH:mm:ss z + + + + + H:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + Lira Italiana + + ¤ #,##0;-¤ #,##0 + ¤ #,##0;-¤ #,##0 + . + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/it.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/it.xml --- zope3-3.4.0/src/zope/i18n/locales/data/it.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/it.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2808 @@ + + + + + + + + + + + afar + abkhazian + accinese + acioli + adangme + adyghe + avestan + afrikaans + afro-asiatica (altra lingua) + afrihili + akan + accado + aleuto + lingue algonchine + amarico + aragonese + inglese, antico (ca.450-1100) + lingue apache + arabo + aramaico + araucano + arapaho + artificiale (altra lingua) + aruaco + assamese + asturiano + lingue athabaska + lingue australiane + avaro + awadhi + aymara + azerbaigiano + baschiro + banda + lingue bamileke + beluci + bambara + balinese + basa + baltica (altra lingua) + bielorusso + begia + wemba + berbero + bulgaro + bihari + bhojpuri + bislama + bicol + bini + siksika + bambara + bengalese + bantu + tibetano + bretone + braj + bosniaco + batak + buriat + bugi + blin + catalano + caddo + indiana dell’America Centrale (altra lingua) + caribico + caucasica (altra lingua) + ceceno + cebuano + celtica altra lingua + chamorro + chibcha + ciagataico + chuukese + mari + gergo chinook + choctaw + chipewyan + cherokee + cheyenne + lingue chamic + corso + copto + creolo e pidgins, basato sull’inglese (altra lingua) + creolo e pidgins, basato sul francese (altra lingua) + creolo e pidgins, basato sul portoghese (altra lingua) + cree + turco crimeo; tatar crimeo + creola e Pidgins (altra lingua) + ceco + kashubian + slavo della Chiesa + cuscitica (altra lingua) + chuvash + gallese + danese + dakota + dargwa + dayak + tedesco + delaware + slave + dogrib + dinca + dogri + dravidica (altra lingua) + basso sorabo + duala + olandese, medio (ca. 1050-1350) + divehi + diula + dzongkha + ewe + efik + egiziano (antico) + ekajuka + greco + elamitico + inglese + inglese, medio (1100-1500) + esperanto + spagnolo + estone + basco + ewondo + persiano + fanti + fulah + finlandese + ugrofinnica (altra lingua) + figiano + faroese + francese + francese, medio (ca.1400-1600) + francese, antico (842-ca.1400) + friulano + frisone + irlandese + ga + gayo + gbaya + gaelico scozzese + germanica (altra lingua) + geez + gilbertese + galiziano + tedesco, medio alto (ca.1050-1500) + guarana + tedesco, antico alto (ca.750-1050) + gondi + gorontalo + gotico + gerbo + greco, antico (fino al 1453) + gujarati + manx + gwichʻin + haussa + haida + hawaiano + ebraico + hindi + hiligayna + himachali + hittite + hmong + hiri motu + croato + alto sorabo + haitian + ungherese + hupa + armeno + herero + interlingua + iban + indonesiano + interlingue + igbo + sichuan yi + inupiak + ilocano + indiana (altra lingua) + indoeuropea (altra lingua) + ingush + ido + iraniana + lingue irochesi + islandese + italiano + inuktitut + giapponese + lojban + giudeo persiano + giudeo arabo + giavanese + georgiano + kara-kalpak + kabyle + kachin + kamba + karen + kawi + kabardia + kongo + khasi + khoisan (altra lingua) + khotanese + kikuyu + kuanyama + kazako + kalaallisut + khmer + kimbundu + kannada + coreano + konkani + Kosraean + kpelle + kanuri + karachay-Balkar + kru + kurukh + kashmiri + curdo + kumyk + kutenai + komi + cornico + kirghiso + latino + ladino + lahnda + lamba + lussemburghese + lezghian + ganda + limburgish + lingala + lao + lolo (bantu) + lozi + lituano + luba-katanga + luba-lulua + luiseno + lunda + luo (Kenia e Tanzania) + lushai + lettone + madurese + magahi + maithili + makasar + mandingo + austronesiano + masai + moksha + mandar + mende + malagasy + irlandese medio (900-1200) + marshallese + maori + micmac + menangkabau + lingue diverse + macedone + mon-khmer (altra lingua) + malayalam + mongolo + manchu + manipuri + manobo + moldavo + mohawk + mossi + marathi + malay + maltese + multilingua + munda (altra lingua) + creek + marwari + burmese + lingue maya + erzya + nauru + nahuatl + indiano del Nord America (altra lingua) + napoletano + norvegese bokmål + ndebele del nord + basso tedesco; basso sassone + nepali + newari + ndonga + niger - cordofan (altra lingua) + niue + olandese + norvegese nynorsk + norvegese + nogai + norse antico + ndebele del sud + sotho del nord + nubiano + navajo + nyanja; chichewa; chewa + nyamwezi + nyankole + nyoro + nzima + occitano (post 1500); provenzale + ojibwa + oromo + oriya + ossetico + osage + turco ottomano (1500-1928) + lingue otomi + punjabi + papuano-australiano (altra lingua) + pangasinan + pahlavi + pampanga + papiamento + palau + antico persiano (600-400 A.C.) + filippino (altra lingua) + fenicio + pali + polacco + ponape + pracrito + provenzale, antico (fino al 1500) + pashto + portoghese + quechua + rajasthani + rapanui + rarotonga + lingua rhaeto-romance + rundi + romeno + lingua romanza (altra lingua) + romani + russo + kinyarwanda + sanscrito + sandawe + yakut + indiano del Sud America (altra lingua) + lingue salish + aramaico samaritano + sasak + santali + sardo + scozzese + sindhi + sami del nord + selkup + semitico (altra lingua) + sango + irlandese, antico (fino al ’900) + lingue sign + serbo-croato + shan + singalese + sidamo + lingue sioux + sino-tibetano (altra lingua) + slovacco + sloveno + slavo (altra lingua) + samoano + sami del sud + lingue sami (altra lingua) + sami lule + sami inari + sami skolt + shona + soninke + somalo + sogdiano + songhai + albanese + serbo + serer + swati + nilo-sahariana (altra lingua) + sotho del sud + sundanese + sukuma + susu + sumero + svedese + swahili + siriaco + tamil + tailandese (altra lingua) + telugu + temne + tereno + tetum + tagicco + thai + tigrinya + tigre + turcomanno + tokelau + tagalog + tlingit + tamashek + tswana + tonga (Isole Tonga) + tonga (nyasa) + tok pisin + turco + tsonga + tsimshian + tatarico + tumbuka + lingue tupi + turco-tatarica (altra lingua) + tuvalu + ci + taitiano + tuvinian + udmurt + uigurico + ugaritico + ucraino + mbundu + lingua imprecisata + urdu + usbeco + venda + vietnamita + volapük + voto + walloon + lingue wakash + walamo + waray + washo + sorabo + volof + kalmyk + xosa + yao (bantu) + Yapese + yiddish + yoruba + lingue yupik + zhuang + zapotec + zenaga + cinese + zande + zulu + zuni + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + Emirati Arabi Uniti + Afghanistan + Antigua e Barbuda + Anguilla + Albania + Armenia + Antille Olandesi + Angola + Antartide + Argentina + Samoa Americane + Austria + Australia + Aruba + Azerbaigian + Bosnia Erzegovina + Barbados + Bangladesh + Belgio + Burkina Faso + Bulgaria + Bahrein + Burundi + Benin + Bermuda + Brunei + Bolivia + Brasile + Bahamas + Bhutan + Isola Bouvet + Botswana + Bielorussia + Belize + Canada + Isole Cocos (Keeling) + Repubblica Democratica del Congo + Repubblica Centrafricana + Congo + Svizzera + Costa d’Avorio + Isole Cook + Cile + Camerun + Cina + Colombia + Costa Rica + Cuba + Capo Verde + Isola Christmas + Cipro + Repubblica Ceca + Germania + Gibuti + Danimarca + Dominica + Repubblica Dominicana + Algeria + Ecuador + Estonia + Egitto + Sahara Occidentale + Eritrea + Spagna + Etiopia + Finlandia + Fiji + Isole Falkland + Micronesia + Isole Faroe + Francia + en + Gabon + Regno Unito + Grenada + Georgia + Guiana Francese + Ghana + Gibilterra + Groenlandia + Gambia + Guinea + Guadalupa + Guinea Equatoriale + Grecia + Georgia del Sud e Isole Sandwich del Sud + Guatemala + Guam + Guinea-Bissau + Guyana + Regione Amministrativa Speciale di Hong Kong, Cina + Isole Heard ed Isole McDonald + Honduras + Croazia + Haiti + Ungheria + Indonesia + Irlanda + Israele + India + Territorio Britannico dell’Oceano Indiano + Iraq + Iran + Islanda + Italia + Giamaica + Giordania + Giappone + Kenya + Kirghizistan + Cambogia + Kiribati + Comore + Saint Kitts e Nevis + Corea del Nord + Corea del Sud + Kuwait + Isole Cayman + Kazakistan + Laos + Libano + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Lituania + Lussemburgo + Lettonia + Libia + Marocco + Monaco + Moldavia + Madagascar + Isole Marshall + Macedonia, Repubblica + Mali + Myanmar + Mongolia + Regione Amministrativa Speciale di Macao, Cina + Isole Marianne Settentrionali + Martinica + Mauritania + Montserrat + Malta + Mauritius + Maldive + Malawi + Messico + Malesia + Mozambico + Namibia + Nuova Caledonia + Niger + Isola Norfolk + Nigeria + Nicaragua + Paesi Bassi + Norvegia + Nepal + Nauru + Niue + Nuova Zelanda + Oman + Panama + Perù + Polinesia Francese + Papua Nuova Guinea + Filippine + Pakistan + Polonia + Saint Pierre e Miquelon + Pitcairn + Portorico + Palestina + Portogallo + Palau + Paraguay + Qatar + Réunion + Romania + Federazione Russa + Ruanda + Arabia Saudita + Isole Solomon + Seychelles + Sudan + Svezia + Singapore + Sant’Elena + Slovenia + Svalbard e Jan Mayen + Slovacchia + Sierra Leone + San Marino + Senegal + Somalia + Serbia + Suriname + São Tomé e Príncipe + El Salvador + Siria + Swaziland + Isole Turks e Caicos + Ciad + Territori australi francesi + Togo + Tailandia + Tagikistan + Tokelau + Timor Est + Turkmenistan + Tunisia + Tonga + Turchia + Trinidad e Tobago + Tuvalu + Taiwan (provincia della Cina) + Tanzania + Ucraina + Uganda + Isole Minori lontane dagli Stati Uniti + Stati Uniti + Uruguay + Uzbekistan + Vaticano + Saint Vincent e Grenadines + Venezuela + Isole Vergini Britanniche + Isole Vergini Americane + Vietnam + Vanuatu + Wallis e Futuna + Samoa + Yemen + Mayotte + Yugoslavia + Sud Africa + Zambia + Zimbabwe + + + Modificato + + + Calendario + Collazione + Valuta + + + Calendario Buddista + Calendario Cinese + Calendario Gregoriano + Calendario Ebraico + Calendario Islamico + Calendario Civile Islamico + Calendario Giapponese + Ordine Diretto + Ordine Elenco Telefonico + Ordine Pinyin + Ordine Segni + Ordine Tradizionale + + + + [a-zéóàèìòíúùï] + + + + + + + + gen + feb + mar + apr + mag + giu + lug + ago + set + ott + nov + dic + + + G + F + M + A + M + G + L + A + S + O + N + D + + + gennaio + febbraio + marzo + aprile + maggio + giugno + luglio + agosto + settembre + ottobre + novembre + dicembre + + + + + + + dom + lun + mar + mer + gio + ven + sab + + + D + L + M + M + G + V + S + + + domenica + lunedì + martedì + mercoledì + giovedì + venerdì + sabato + + + + + + + + m. + p. + + + aC + dC + + + + + + + EEEE d MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd/MMM/yy + + + + + dd/MM/yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Ora Standard Pacifico + Ora Legale Pacifico + + + PST + PDT + + Los Angeles + + + + Ora Standard Pacifico + Ora Legale Pacifico + + + PST + PDT + + Los Angeles + + + + Ora Standard Mountain + Ora Legale Mountain + + + MST + MDT + + Denver + + + + Ora Standard Mountain + Ora Legale Mountain + + + MST + MDT + + Denver + + + + Ora Standard Mountain + Ora Standard Mountain + + + MST + MST + + Phoenix + + + + Ora Standard Mountain + Ora Standard Mountain + + + MST + MST + + Phoenix + + + + Ora Standard Centrale + Ora Legale Centrale + + + CST + CDT + + Chicago + + + + Ora Standard Centrale + Ora Legale Centrale + + + CST + CDT + + Chicago + + + + Ora Standard Orientale + Ora Legale Orientale + + + EST + EDT + + New York + + + + Ora Standard Orientale + Ora Legale Orientale + + + EST + EDT + + New York + + + + Ora Standard Orientale + Ora Standard Orientale + + + EST + EST + + Indianapolis + + + + Ora Standard Orientale + Ora Standard Orientale + + + EST + EST + + Indianapolis + + + + Ora Standard Hawaii + Ora Standard Hawaii + + + HST + HST + + Honolulu + + + + Ora Standard Hawaii + Ora Standard Hawaii + + + HST + HST + + Honolulu + + + + Ora Standard Alaska + Ora Legale Alaska + + + AST + ADT + + Anchorage + + + + Ora Standard Alaska + Ora Legale Alaska + + + AST + ADT + + Anchorage + + + + Ora Standard Atlantico + Ora Legale Atlantico + + + AST + ADT + + Halifax + + + + Ora Standard Newfoundland + Ora Legale Newfoundland + + + CNT + CDT + + St. Johns + + + + Ora Standard Newfoundland + Ora Legale Newfoundland + + + CNT + CDT + + St. Johns + + + + Ora Standard Centrale Europeo + Ora Legale Centrale Europeo + + + CET + CEST + + Parigi + + + + Ora Standard Centrale Europeo + Ora Legale Centrale Europeo + + + CET + CEST + + Parigi + + + + Ora Meridiano Greenwich + Ora Meridiano Greenwich + + + GMT + GMT + + Londra + + + + Ora Meridiano Greenwich + Ora Meridiano Greenwich + + + GMT + GMT + + Casablanca + + + + Ora Standard Israele + Ora Legale Israele + + + IST + IDT + + Gerusalemme + + + + Ora Standard Giappone + Ora Standard Giappone + + + JST + JST + + Tokyo + + + + Ora Standard Giappone + Ora Standard Giappone + + + JST + JST + + Tokyo + + + + Ora Standard Europa Orientale + Ora Legale Europa Orientale + + + EET + EEST + + Bucarest + + + + Ora Standard Cina + Ora Standard Cina + + + CTT + CDT + + Shanghai + + + + Ora Standard Cina + Ora Standard Cina + + + CTT + CDT + + Shanghai + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + Diner Andorrano + ADD + + + Peseta Andorrana + ADP + + + Dirham degli Emirati Arabi Uniti + AED + + + Afgani (1927-2002) + AFA + + + Afgani + Af + + + Franco di Affars e Issas + AIF + + + Lek Albanese (1946-1961) + ALK + + + Lek Albanese + lek + + + Lek Valute Albanese + ALV + + + Certificati di cambio esteri albanesi in dollari + ALX + + + Dram Armeno + dram + + + Fiorino delle Antille Olandesi + NA f. + + + Kwanza Angolano + AOA + + + Kwanza Angolano (1977-1990) + AOK + + + Nuovo Kwanza Angolano (1990-2000) + AON + + + Kwanza Reajustado Angolano (1995-1999) + AOR + + + Escudo Angolano + AOS + + + Austral Argentino + ARA + + + Peso Moneda Nacional argentino + ARM + + + Peso Argentino (vecchio Cod.) + ARP + + + Peso Argentino + ARS + + + Scellino Austriaco + ATS + + + Dollaro Australiano + AUD + + + Sterlina Australiana + AUP + + + Fiorino di Aruba + AWG + + + Manat Azero + AZM + + + Dinar Bosnia-Herzegovina + BAD + + + Marco Conv. Bosnia-Erzegovina + KM + + + Nuovo Dinar Bosnia-Herzegovina + BAN + + + Dollaro di Barbados + BDS$ + + + Taka Bangladese + Tk + + + Franco Belga (convertibile) + BEC + + + Franco Belga + BEF + + + Franco Belga (finanziario) + BEL + + + Lev Bulgaro + BGL + + + Lev Bulgaro Socialista + BGM + + + Nuovo Lev Bulgaro + lev + + + Lev Bulgaro (1879-1952) + BGO + + + Certificati di cambio esteri in Lev bulgari + BGX + + + Dinaro del Bahraini + BD + + + Franco del Burundi + Fbu + + + Dollaro delle Bermuda + Ber$ + + + Sterlina delle Bermuda + BMP + + + Dollaro del Brunei + BND + + + Boliviano + BOB + + + Boliviano (1863-1962) + BOL + + + Peso Boliviano + BOP + + + Mvdol Boliviano + BOV + + + Cruzeiro Novo Brasiliano (1967-1986) + BRB + + + Cruzado Brasiliano + BRC + + + Cruzeiro Brasiliano (1990-1993) + BRE + + + Real Brasiliano + BRL + + + Cruzado Novo Brasiliano + BRN + + + Cruzeiro Brasiliano + BRR + + + Cruzeiro Brasiliano (1942-1967) + BRZ + + + Dollaro delle Bahamas + BSD + + + Libra delle Bahamas + BSP + + + Ngultrum Butanese + Nu + + + Rupia Butanese + BTR + + + Kyat Birmano + BUK + + + Rupia Birmana + BUR + + + Pula del Botswana + BWP + + + Nuovo Rublo Bielorussia (1994-1999) + BYB + + + Rublo Bielorussia (1992-1994) + BYL + + + Rublo Bielorussia + Rbl + + + Dollaro Belize + BZ$ + + + Dollaro Honduras Britannico + BZH + + + Dollaro Canadese + CAD + + + Franco Congolese + CDF + + + Franco della Repubblica Congolese + CDG + + + Zaire Congolese + CDL + + + Franco CFA della Repubblica dell’Africa Centrale + CFF + + + Franco Svizzero + SFr. + + + Dollaro delle Isole Cook + CKD + + + Condor Cileno + CLC + + + Escudo Cileno + CLE + + + Unidades de Fomento Chilene + CLF + + + Peso Cileno + CLP + + + Franco CFA del Camerun + CMF + + + Jen Min Piao Yuan Cinese + CNP + + + Certificati di cambio esteri cinesi in dollari statunitensi + CNX + + + Renmimbi Cinese + CNY + + + Peso in banconote colombiano + COB + + + Franco CFA del Congo + COF + + + Peso Colombiano + Col$ + + + Colón Costaricano + C + + + Corona Cecoslovacca + CSC + + + Corona forte cecoslovacca + CSK + + + Peso Cubano + CUP + + + Certificati di cambio esteri cubani + CUX + + + Escudo del Capo Verde + CVEsc + + + Guilder del Curacao + CWG + + + Sterlina Cipriota + CYP + + + Corona Ceca + CZK + + + Ostmark della Germania Orientale + DDM + + + Marco Tedesco + DEM + + + Sperrmark Tedesco + DES + + + Franco Gibutiano + DF + + + Corona Danese + DKK + + + Peso Dominicano + RD$ + + + Dinaro Algerino + DA + + + Nuovo franco algerino + DZF + + + Franco germinale algerino + DZG + + + Sucre dell’Ecuador + ECS + + + Unidad de Valor Constante (UVC) dell’Ecuador + ECV + + + Corona dell’Estonia + EEK + + + Sterlina Egiziana + EGP + + + Nakfa Eritreo + ERN + + + Peseta Spagnola + ESP + + + Birr Etiopico + Br + + + Dollaro Etiopico + ETD + + + Euro + + + + Markka Finlandese + FIM + + + Markka Finlandese (1860-1962) + FIN + + + Dollaro delle Figi + F$ + + + Sterlina delle Figi + FJP + + + Sterlina delle Falkland + FKP + + + Kronur delle Isole Faeroe + FOK + + + Franco Francese + FRF + + + Franco germinale/franco Poincare francese + FRG + + + Franco CFA Gabon + GAF + + + Sterlina Inglese + £ + + + Kupon Larit Georgiano + GEK + + + Lari Georgiano + lari + + + Cedi del Ghana + GHC + + + Vecchi cedi del Ghana + GHO + + + Sterlina del Ghana + GHP + + + Cedi rivalutato del Ghana + GHR + + + Sterlina di Gibilterra + GIP + + + Corona della Groenlandia + GLK + + + Dalasi del Gambia + GMD + + + Sterlina del Gambia + GMP + + + Franco della Guinea + GF + + + Franco della Guinea (1960-1972) + GNI + + + Syli della Guinea + GNS + + + Franco della Guadalupa + GPF + + + Ekwele della Guinea Equatoriale + GQE + + + Franco della Guinea Equatoriale + GQF + + + Peseta Guineana della Guinea Equatoriale + GQP + + + Dracma Greca + GRD + + + Nuova dracma greca + GRN + + + Quetzal Guatemalteco + Q + + + Franco Guiana della Guyana francese + GUF + + + Escudo della Guinea portoghese + GWE + + + Mil Reis della Guinea portoghese + GWM + + + Peso della Guinea-Bissau + GWP + + + Dollaro della Guyana + G$ + + + Dollaro di Hong Kong + HKD + + + Lempira Hoduregno + L + + + Dinaro Croato + HRD + + + Kuna Croata + HRK + + + Gourde Haitiano + HTG + + + Fiorino Ungherese + HUF + + + Sterlina dell’Irlanda del Nord + IBP + + + Fiorino Nica indonesiano + IDG + + + Rupia indonesiana di Java + IDJ + + + Nuova rupia indonesiana + IDN + + + Rupia Indiana + Rp + + + Lira Irlandese + IR£ + + + Sheqel Israeliano + ILL + + + Sterlina Israeliana + ILP + + + Nuovo sheqel israeliano + ILS + + + Lira Sterlina dell’Isola di Man + IMP + + + Rupia Indiana + =0#Rs.|1#Re.|1<Rs. + + + Dinaro Iracheno + ID + + + Rial Iraniano + RI + + + Corona Islandese + ISK + + + Lira Italiana + + + + Lira Sterlina di Jersey + JEP + + + Dollaro Giamaicano + J$ + + + Sterlina Giamaicana + JMP + + + Dinaro Giordano + JOD + + + Yen Giapponese + ¥ + + + Scellino Keniota + K Sh + + + Som Kirghiso + som + + + Vecchio riel cambogiano + KHO + + + Riel Cambogiano + CR + + + Dollaro di Kiribati + KID + + + Franco Comoriano + CF + + + Won della Repubblica popolare democratica nordcoreana + KPP + + + Won Nordcoreano + KPW + + + Hwan sudcoreano + KRH + + + Vecchi won sudcoreano + KRO + + + Won Sudcoreano + KRW + + + Dinaro Kuwaitiano + KD + + + Dollaro delle Isole Cayman + KYD + + + Rublo Kazaco + KZR + + + Tenge Kazaco + T + + + Kip Laotiano + LAK + + + Sterlina Libanese + LL + + + Franco del Liechtenstein + LIF + + + Rupia di Sri Lanka + SL Re + + + Rupia di Ceylon + LNR + + + Dollaro Liberiano + LRD + + + Loti del Lesotho + M + + + Lita Lituana + LTL + + + Talonas Lituani + LTT + + + Franco del Lussemburgo + LUF + + + Lat Lettone + LVL + + + Rublo Lettone + LVR + + + Lira libica della British Military Authority + LYB + + + Dinaro Libico + LD + + + Sterlina Libica + LYP + + + Dirham Marocchino + MAD + + + Franco Marocchino + MAF + + + Franc Nouveau di Monaco + MCF + + + Franco germinale di Monaco + MCG + + + Coupon Leu della Moldavia + MDC + + + Leu Moldavo + MDL + + + Coupon Rublo della Moldavia + MDR + + + Ariary Malgascio + MGA + + + Franco Malgascio + MGF + + + Dollaro delle Isole Marshall + MHD + + + Dinaro Macedone + MDen + + + Dinaro Macedone (1992-1993) + MKN + + + Franco di Mali + MLF + + + Kyat di Myanmar + MMK + + + Certificati di cambio esteri in dollari Myanmar + MMX + + + Tugrik Mongolo + Tug + + + Pataca di Macao + MOP + + + Franco della Martinica + MQF + + + Ouguiya della Mauritania + UM + + + Lira Maltese + Lm + + + Sterlina Maltese + MTP + + + Rupia Mauriziana + MUR + + + Rupia delle Maldive + MVP + + + Rufiyaa delle Maldive + MVR + + + Kwacha Malawiano + MK + + + Sterlina Malawiana + MWP + + + Peso Messicano + MEX$ + + + Peso messicano d’argento (1861-1992) + MXP + + + Unidad de Inversion (UDI) Messicana + MXV + + + Ringgit della Malesia + RM + + + Escudo del Mozambico + MZE + + + Metical del Mozambico + Mt + + + Dollaro Namibiano + N$ + + + Franco germinale della Nuova Caledonia + NCF + + + Naira Nigeriana + NGN + + + Sterlina Nigeriana + NGP + + + Franco CFP delle Nuove Ebridi + NHF + + + Cordoba Nicaraguense + NIC + + + Córdoba d’oro nicaraguense + NIG + + + Córdoba oro nicaraguense + NIO + + + Fiorino Olandese + NLG + + + Corona Norvegese + NOK + + + Rupia Nepalese + Nrs + + + Dollaro Neozelandese + $NZ + + + Sterlina Neozelandese + NZP + + + Rial Omanita + RO + + + Rial Saidi dell’Oman + OMS + + + Balboa di Panama + PAB + + + Coupon in rubli della Transdniestria + PDK + + + Nuovo rublo della Transdniestria + PDN + + + Rublo della Transdniestria + PDR + + + Inti Peruviano + PEI + + + Sol Nuevo Peruviano + PEN + + + Sol Peruviano + PES + + + Kina della Papua Nuova Guinea + PGK + + + Peso delle Filippine + PHP + + + Rupia del Pakistan + Pra + + + Zloty Polacco + Zl + + + Certificati di cambio esteri polacchi in dollari statunitensi + PLX + + + Zloty Polacco (1950-1995) + PLZ + + + Sterlina della Palestina + PSP + + + Conto Portoghese + PTC + + + Escudo Portoghese + PTE + + + Guarani del Paraguay + PYG + + + Rial del Qatar + QR + + + Franco di Reunion + REF + + + Leu della Romania + ROL + + + Nuovo Leu della Romania + RON + + + Rublo Russo + RUB + + + Rublo della CSI + RUR + + + Franco Ruandese + RWF + + + Ryal Saudita + SAR + + + Riyal sovrano saudita + SAS + + + Dollaro delle Isole Solomon + SI$ + + + Rupia delle Seychelles + SR + + + Dinaro Sudanese + SDD + + + Sterlina Sudanese + SDP + + + Corona Svedese + SEK + + + Dollaro di Singapore + SGD + + + Sterlina di Sant’Elena + SHP + + + Tallero Bons Sloveno + SIB + + + Tallero Sloveno + SIT + + + Corona Slovacca + Sk + + + Leone della Sierra Leone + SLL + + + Lira di San Marino + SML + + + Scellino Somalo + So. Sh. + + + Scellino del Somaliland + SQS + + + Fiorino del Suriname + Sf + + + Sterlina Scozzese + SSP + + + Dobra di São Tomé e Principe + Db + + + Escudo di São Tomé e Principe + STE + + + Nuovo rublo sovietico + SUN + + + Rublo Sovietico + SUR + + + Colón Salvadoregno + SVC + + + Sterlina Siriana + LS + + + Lilangeni dello Swaziland + E + + + Corona di Turks e Caicos + TCC + + + Franco CFA del Chad + TDF + + + Baht Tailandese + THB + + + Rublo del Tajikistan + TJR + + + Somoni del Tajikistan + TJS + + + Manat Turkmeno + TMM + + + Dinaro Tunisino + TND + + + Paʻanga di Tonga + T$ + + + Lira sterlina di Tonga + TOS + + + Escudo di Timor + TPE + + + Pataca di Timor + TPP + + + Lira Turca + TRL + + + Dollaro di Trinidad e Tobago + TT$ + + + Vecchio dollaro di Trinidad e Tobago + TTO + + + Dollaro di Tuvalu + TVD + + + Nuovo dollaro taiwanese + NT$ + + + Scellino della Tanzania + T Sh + + + Hrivna Ucraina + UAH + + + Karbovanetz Ucraino + UAK + + + Scellino Ugandese (1966-1987) + UGS + + + Scellino Ugandese + U Sh + + + Dollaro Statunitense + $ + + + Dollaro Statunitense (Next day) + USN + + + Dollaro Statunitense (Same day) + USS + + + Peso Fuerte dell’Uruguaiano + UYF + + + Peso Uruguaiano (1975-1993) + UYP + + + Peso Uruguayo uruguaiano + Ur$ + + + Som Coupon dell’Uzbekistan + UZC + + + Sum dell’Uzbekistan + UZS + + + Lira della Città del Vaticano + VAL + + + Piastre Dong Viet nordvietnamita + VDD + + + Nuovo Dong nordvietnamita + VDN + + + Viet Minh Piastre Dong Viet nordvietnamita + VDP + + + Bolivar Venezuelano + Be + + + Dollaro delle Isole Vergini britanniche + VGD + + + Dong Vietnamita + VND + + + Nuovo dong vietnamita + VNN + + + Dong della Repubblica Vietnamita + VNR + + + Dong Nazionale Vietnamita + VNS + + + Vatu di Vanuatu + VT + + + Sterlina della Samoa Occidentale + WSP + + + Tala della Samoa Occidentale + WST + + + Unità di conto in dinari asiatica + XAD + + + Franco CFA BEAC + XAF + + + Unità Monetaria Asiatica + XAM + + + Oro + XAU + + + Unità composita europea + XBA + + + Unità monetaria europea + XBB + + + Unità di acconto europea (XBC) + XBC + + + Unità di acconto europea (XBD) + XBD + + + Dollaro dei Caraibi Orientali + EC$ + + + Nuovo Franco CFA + XCF + + + Diritti Speciali di Incasso + XDR + + + Franco CFA BCEAEC + XEF + + + Unità Monetaria Europea + XEU + + + Franco Oro Francese + XFO + + + Franco UIC Francese + XFU + + + Dinaro Islamico + XID + + + Nouveau Franc francese metropolitano + XMF + + + Franco CFA delle Antille Francesi + XNF + + + Franco CFA BCEAO + XOF + + + Franco CFP + CFPF + + + Rublo Trasferibile COMECON + XTR + + + Dinaro dello Yemen + YDD + + + Riyal Imadi dello Yemen + YEI + + + Rial dello Yemen + YRl + + + Dinaro Forte Yugoslavo + YUD + + + Dinaro della Federazione Yugoslava + YUF + + + Dinaro 1994 Yugoslavo + YUG + + + Dinaro Noviy Yugoslavo + YUM + + + Dinaro Convertibile Yugoslavo + YUN + + + Dinaro di Ottobre Yugoslavo + YUO + + + Dinaro Riformato Yugoslavo + YUR + + + Rand Sudafricano (finanziario) + ZAL + + + Sterlina Sudafricana + ZAP + + + Rand Sudafricano + ZAR + + + Kwacha dello Zambia + ZMK + + + Sterlina dello Zambia + ZMP + + + Nuovo Zaire dello Zaire + ZRN + + + Zaire dello Zaire + ZRZ + + + Dollaro dello Zimbabwe + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/iu.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/iu.xml --- zope3-3.4.0/src/zope/i18n/locales/data/iu.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/iu.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + ᐃᓄᒃᑎᑐᑦ ᑎᑎᕋᐅᓯᖅ + + + + [ᐃ-ᐆᐊᐋᐱ-ᐴᐸᐹᑉᑎ-ᑑᑕᑖᑦᑭ-ᑰᑲᑳᒃᒋ-ᒎᒐ-ᒑᒡᒥ-ᒨᒪᒫᒻᓂ-ᓅᓇᓈᓐᓕ-ᓘᓚᓛᓪᓯ-ᓲᓴᓵᔅᔨ-ᔫᔭᔮᔾᕆ-ᕉᕋ-ᕌᕐᕕ-ᕚᕝᕿ-ᖃᖅᖏᖑ-ᖖᖠ-ᖦᙱ-ᙶ] + + + + + + + + ᔭᓐᓄᐊᓕ + ᕕᕝᕗᐊᓕ + ᒫᑦᓯ + ᐊᐃᑉᐳᓗ + ᒪᐃ + ᔫᓂ + ᔪᓚᐃ + ᐊᐅᒡᒍᓯ + ᓰᑦᑏᕝᕙ + ᐆᑦᑑᕝᕙ + ᓅᕙᐃᕝᕙ + ᑏᓰᕝᕙ + + + ᔭᓐᓄᐊᓕ + ᕕᕝᕗᐊᓕ + ᒫᑦᓯ + ᐊᐃᑉᐳᓗ + ᒪᐃ + ᔫᓂ + ᔪᓚᐃ + ᐊᐅᒡᒍᓯ + ᓰᑦᑏᕝᕙ + ᐆᑦᑑᕝᕙ + ᓅᕙᐃᕝᕙ + ᑏᓰᕝᕙ + + + + + + + ᓈᑦᓰᖑᔭ + ᓇᒡᒐᔾᔭᐅ + ᓇᒡᒐᔾᔭᐅᓕᖅᑭ + ᐱᖓᑦᓯᖅ + ᓯᑕᒻᒥᖅ + ᑕᓪᓕᕐᒥᖅ + ᓈᑦᓰᖑᔭᓕᖅᕿ + + + ᓈᑦᓰᖑᔭ + ᓇᒡᒐᔾᔭᐅ + ᓇᒡᒐᔾᔭᐅᓕᖅᑭ + ᐱᖓᑦᓯᖅ + ᓯᑕᒻᒥᖅ + ᑕᓪᓕᕐᒥᖅ + ᓈᑦᓰᖑᔭᓕᖅᕿ + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/iw_IL.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/iw_IL.xml --- zope3-3.4.0/src/zope/i18n/locales/data/iw_IL.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/iw_IL.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/iw.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/iw.xml --- zope3-3.4.0/src/zope/i18n/locales/data/iw.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/iw.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,534 @@ + + + + + + + + + + + ערבית + בולגרית + צ׳כית + דנית + גרמנית + יוונית + אנגלית + ספרדית + אסטונית + פינית + צרפתית + עברית + קרואטית + הונגרית + איטלקית + יפנית + קוריאנית + ליטאית + לטבית + הולנדית + נורווגית + פולנית + פורטוגזית + רומנית + רוסית + סלובקית + סלובנית + שוודית + טורקית + סינית + + + אנדורה + איחוד האמירויות הערביות + אפגניסטן + אנטיגואה וברבודה + אנגילה + אלבניה + ארמניה + האינטילים ההולנדיים + אנגולה + אנטארקטיקה + ארגנטינה + סמואה האמריקנית + אוסטריה + אוסטרליה + ארובה + אזרבייג׳ן + בוסניה הרצגובינה + ברבדוס + בנגלדש + בלגיה + בורקינה פאסו + בולגריה + בחריין + בורונדי + בנין + ברמודה + ברוניי דארסלאם + בוליביה + ברזיל + בהאמה + בוטאן + האי בובה + בוטסוואנה + בלרוס + בליז + קנדה + איי קוקוס (קילינג) + קונגו, הרפובליקה הדמוקרטית של + הרפובליקה של מרכז אפריקה + קונגו + שווייץ + חוף השנהב + איי קוק + צ׳ילה + קמרון + סין + קולומביה + קוסטה ריקה + קובה + קייפ ורדה + איי כריסטמס + קפריסין + הרפובליקה הצ׳כית + גרמניה + ג׳יבוטי + דנמרק + דומיניקה + הרפובליקה הדומיניקנית + אלג׳יריה + אקוואדור + אסטוניה + מצרים + סהרה המערבית + אריתריאה + ספרד + אתיופיה + פינלנד + פיג׳י + איי פוקלנד + מאוריציוס, המדינות המאוגדות של + איי פארו + צרפת + גבון + בריטניה + גרנדה + גרוזיה + גיאנה הצרפתית + גאנה + גיברלטר + גרינלנד + גמביה + גיניאה + גוואדלופ + גיניאה המשוונית + יוון + האי ג׳ורג׳יה הדרומית ואיי סנדוויץ׳ הדרומיים + גווטמאלה + גואם + גיניאה-ביסאו + גיאנה + הונג קונג S.A.R. של סין + איי הרד ואיי מקדונלנד + הונדורס + קרואטיה + האיטי + הונגריה + אינדונזיה + אירלנד + ישראל + הודו + הטריטוריה הבריטית באוקינוס ההודי + עירק + איראן, הרפובליקה האיסלמית + איסלנד + איטליה + ג׳מייקה + ירדן + יפן + קניה + קירגיזסטן + קמבודיה + קיריבאטי + קומורוס + סנט קיטס וניבס + קוריאה, צפון + קוריאה, דרום + כווית + איי קיימאן + קזחסטן + לאוס, הרפובליקה הדמקורטית העממית + לבנון + סנט לושיה + ליכטנשטיין + סרי לנקה + ליבריה + לסוטו + ליטא + לוקסמבורג + לטביה + לוב + מרוקו + מונקו + מולדובה, הרפובליקה + מדגסקר + איי מרשל + מקדוניה, הרפובליקה של + מאלי + מינמאר + מונגוליה + מקאו S.A.R. של סין + איי מריאנה הצפוניים + מרטיניק + מאוריטניה + מונטסראט + מלטה + מאוריציוס + מלדיבאס + מלאווי + מכסיקו + מלזיה + מוזמביק + נמיביה + קלדוניה החדשה + ניז׳ר + איי נורפק + ניגריה + ניקראגווה + הולנד + נורווגיה + נפאל + נאורו + ניווה + ניו זילנד + עומן + פנמה + פרו + פולינזיה הצרפתית + פפואה גיניאה החדשה + פיליפינים + פקיסטן + פולין + סנט פייר ומיקלון + פיטקיירן + פורטו ריקו + הרשות הפלשתינית + פורטוגל + פלאו + פראגוואי + קטר + ראוניון + רומניה + חבר המדינות הרוסיות + רואנדה + ערב הסעודית + איי שלמה + איי סיישל + סודן + שוודיה + סינגפור + סיינט הלנה + סלובניה + סוולבארד וז׳אן מאיין + סלובקיה + סיירה לאונה + סן מרינו + סנגל + סומליה + סורינאם + סן תומה ופרינסיפה + אל סלבאדור + הרפובליקה הערבית הסורית + סווזילנד + איי טורקס וקאיקוס + צ׳אד + טריטוריות דרומיות של צרפת + טוגו + תאילנד + טג׳יקיסטן + טוקלאו + מזרח טימור + טורקמניסטן + טוניסיה + טונגה + טורקיה + טרינידד וטובגו + טובאלו + טיוואן + טנזניה + אוקראינה + אוגנדה + איים קטנים שלחוף ארצות הברית + ארצות הברית + אורוגוואי + אוזבקיסטן + הוותיקן + סנט וינסנט והגרנדינים + ונצואלה + איי הבתולה הבריטיים + איי הבתולה האמריקניים + וייטנאם + ואנואטו + ואליס ופוטונה + סמואה + תימן + מיוטה + יוגוסלביה + דרום אפריקה + זמביה + זימבבווה + + + + [[:Hebr:]‏‎] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + ינו + פבר + מרץ + אפר + מאי + יונ + יול + אוג + ספט + אוק + נוב + דצמ + + + ינואר + פברואר + מרץ + אפריל + מאי + יוני + יולי + אוגוסט + ספטמבר + אוקטובר + נובמבר + דצמבר + + + + + + + א + ב + ג + ד + ה + ו + ש + + + יום ראשון + יום שני + יום שלישי + יום רביעי + יום חמישי + יום שישי + שבת + + + + + + לפנה״ס + לסה״נ + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {0} {1} + + + + + + + + + תשרי + חשון + כסלו + טבת + שבט + אדר ראשון + אדר שני + ניסן + אייר + סיון + תמוז + אב + אלול + + + תשרי + חשון + כסלו + טבת + שבט + אדר ראשון + אדר שני + ניסן + אייר + סיון + תמוז + אב + אלול + + + + + + לבה"ע + + + + + + + + מוחרם + ספר + רביע אל-אוואל + רביע אל-תני + ג׳ומדה אל-אוואל + ג׳ומדה אל-תני + רג׳אב + שעבאן + ראמדן + שוואל + זו אל-QI'DAH + זו אל-חיג׳ה + + + מוחרם + ספר + רביע אל-אוואל + רביע אל-תני + ג׳ומדה אל-אוואל + ג׳ומדה אל-תני + רג׳אב + שעבאן + ראמדן + שוואל + זו אל-QI'DAH + זו אל-חיג׳ה + + + + + + שנת היג׳רה + + + + + + + + מוחרם + ספר + רביע אל-אוואל + רביע אל-תני + ג׳ומדה אל-אוואל + ג׳ומדה אל-תני + רג׳אב + שעבאן + ראמדן + שוואל + זו אל-QI'DAH + זו אל-חיג׳ה + + + מוחרם + ספר + רביע אל-אוואל + רביע אל-תני + ג׳ומדה אל-אוואל + ג׳ומדה אל-תני + רג׳אב + שעבאן + ראמדן + שוואל + זו אל-QI'DAH + זו אל-חיג׳ה + + + + + + שנת היג׳רה + + + + + + + + + ש"ח + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ja_JP.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ja_JP.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ja_JP.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ja_JP.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0;-¤#,##0 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ja.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ja.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ja.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ja.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2775 @@ + + + + + + + + + + + アファール語 + アブハズ語 + アヴェスタ語 + アフリカーンス語 + アカン語 + アムハラ語 + アラゴン語 + アラビア語 + アッサム語 + アヴァル語 + アイマラ語 + アゼルバイジャン語 + バシキール語 + ベラルーシ語 + ブルガリア語 + ビハ―ル語 + ビスラマ語 + バンバラ語 + ベンガル語 + チベット語 + ブルトン語 + ボスニア語 + ビリン語 + カタロニア語 + チェチェン語 + チャモロ語 + チェロキー語 + コルシカ語 + クリー語 + チェコ語 + 教会スラブ語 + チュヴァシュ語 + ウェールズ語 + デンマーク語 + ドイツ語 + ディヴェヒ語 + ブータン語 + エウェ語 + ギリシャ語 + 英語 + エスペラント語 + スペイン語 + エストニア語 + バスク語 + ペルシア語 + フラニ語 + フィンランド語 + フィジー語 + フェロー語 + フランス語 + フリジア語 + アイルランド語 + スコットランド・ゲール語 + ゲエズ語 + ガリシア語 + グワラニ語 + グジャラート語 + マン島語 + ハウサ語 + ハワイ語 + ヘブライ語 + ヒンディー語 + ヒリモトゥ語 + クロアチア語 + ハイチ語 + ハンガリー語 + アルメニア語 + ヘレロ語 + 国際語 + インドネシア語 + 国際語 + イボ語 + 四川イ語 + イヌピアック語 + イド語 + アイスランド語 + イタリア語 + イヌクウティトット語 + 日本語 + ジャワ語 + グルジア語 + コンゴ語 + キクユ語 + クアニャマ語 + カザフ語 + グリーンランド語 + カンボジア語 + カンナダ語 + 韓国語 + コンカニ語 + カヌリ語 + カシミール語 + クルド語 + コミ語 + コーンウォール語 + キルギス語 + ラテン語 + ルクセンブルク語 + ガンダ語 + リンブルフ語 + リンガラ語 + ラオ語 + リトアニア語 + ルバ・カタンガ語 + ラトビア語 + マラガシー語 + マーシャル語 + マオリ語 + マケドニア語 + マラヤ―ラム語 + モンゴル語 + モルダビア語 + マラーティー語 + マライ語 + マルタ語 + ビルマ語 + ナウル語 + ノルウェー語 (ボクモール) + 北ンデベレ語 + ネパール語 + ンドンガ語 + オランダ語 + ノルウェー語 (ニューノルスク) + ノルウェー語 + 南ンデベレ語 + ナバホ語 + ニャンジャ語、チチェワ語、チェワ語 + プロヴァンス語 + オブジワ語 + ガラ語 + オリヤー語 + オセト語 + パンジャブ語 + パーリ語 + ポーランド語 + パシュトー語 + ポルトガル語 + ケチュア語 + レト=ロマン語 + ルンジ語 + ルーマニア語 + ロシア語 + ルワンダ語 + サンスクリット語 + サルデーニャ語 + シンド語 + 北サーミ語 + サンゴ語 + セルボ=クロアチア語 + シンハラ語 + シダモ語 + スロバキア語 + スロベニア語 + サモア語 + ショナ語 + ソマリ語 + アルバニア語 + セルビア語 + シスワティ語 + 南セソト語 + スンダ語 + スウェーデン語 + スワヒリ語 + シリア語 + タミール語 + テルグ語 + タジク語 + タイ語 + ティグリニア語 + ティグレ語 + トルクメン語 + タガログ語 + ツワナ語 + トンガ語 + トルコ語 + ツォンガ語 + タタール語 + トゥイ語 + タヒチ語 + ウイグル語 + ウクライナ語 + ウルドゥー語 + ウズベク語 + ベンダ語 + ベトナム語 + ボラピュク語 + ワロン語 + ウォロフ語 + コサ語 + イディッシュ語 + ヨルバ語 + チワン語 + 中国語 + ズールー語 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + アンドラ + アラブ首長国連邦 + アフガニスタン + アンティグア・バーブーダ + アンギラ + アルバニア + アルメニア + オランダ領アンティル諸島 + アンゴラ + 南極大陸 + アルゼンチン + 米領サモア + オーストリア + オーストラリア + アルバ島 + アゼルバイジャン + ボスニア・ヘルツェゴビナ + バルバドス + バングラデシュ + ベルギー + ブルキナファソ + ブルガリア + バーレーン + ブルンジ + ベニン + バーミューダ + ブルネイ + ボリビア + ブラジル + バハマ + ブータン + ブーベ島 + ボツワナ + ベラルーシ + ベリーズ + カナダ + ココス (キーリング) 諸島 + コンゴ民主共和国 + 中央アフリカ共和国 + コンゴ + スイス + コートジボアール + クック諸島 + チリ + カメルーン + 中国 + コロンビア + コスタリカ + キューバ + カーボベルデ + クリスマス島 + キプロス + チェコ + ドイツ + ジブチ + デンマーク + ドミニカ国 + ドミニカ共和国 + アルジェリア + エクアドル + エストニア + エジプト + 西サハラ + エリトリア + スペイン + エチオピア + フィンランド + フィジー + フォークランド諸島 + ミクロネシア + フェロー諸島 + フランス + ガボン + イギリス + グレナダ + グルジア + 仏領ギアナ + ガーナ + ジブラルタル + グリーンランド + ガンビア + ギニア + グアドループ + 赤道ギニア + ギリシア + 南ジョージア島・南サンドイッチ諸島 + グアテマラ + グアム島 + ギニアビサウ + ガイアナ + 中華人民共和国香港特別行政区 + ハード・アンド・マクドナルド・アイランズ + ホンジュラス + クロアチア + ハイチ + ハンガリー + インドネシア + アイルランド + イスラエル + インド + 英領インド洋植民地 + イラク + イラン + アイスランド + イタリア + ジャマイカ + ヨルダン + 日本 + ケニア + キルギスタン + カンボジア + キリバス + コモロ + セントクリストファー・ネイビス + 朝鮮民主主義人民共和国 + 大韓民国 + クウェート + ケイマン諸島 + カザフスタン + ラオス + レバノン + セントルシア + リヒテンシュタイン + スリランカ + リベリア + レソト + リトアニア + ルクセンブルグ + ラトビア + リビア + モロッコ + モナコ + モルドバ + マダガスカル + マーシャル諸島共和国 + マケドニア + マリ + ミャンマー + モンゴル + 中華人民共和国マカオ特別行政区 + 北マリアナ諸島 + マルティニーク島 + モーリタニア + モントセラト島 + マルタ + モーリシャス + モルジブ + マラウィ + メキシコ + マレーシア + モザンビーク + ナミビア + ニューカレドニア + ニジェール + ノーフォーク島 + ナイジェリア + ニカラグア + オランダ + ノルウェー + ネパール + ナウル + ニウエ島 + ニュージーランド + オマーン + パナマ + ペルー + 仏領ポリネシア + パプアニューギニア + フィリピン + パキスタン + ポーランド + サンピエール・エ・ミクロン島 + ピトケアン島 + プエルトリコ + パレスチナ + ポルトガル + パラオ + パラグアイ + カタール + レユニオン島 + ルーマニア + ロシア + ルワンダ + サウジアラビア + ソロモン諸島 + セイシェル + スーダン + スウェーデン + シンガポール + セントヘレナ島 + スロベニア + スバールバル諸島・ヤンマイエン島 + スロバキア + シエラレオネ + サンマリノ + セネガル + ソマリア + セルビア + スリナム + サントメ・プリンシペ + エルサルバドル + シリア + スワジランド + タークス諸島・カイコス諸島 + チャド + 仏南方領 + トーゴ + タイ + タジキスタン + トケラウ諸島 + 東ティモール + トルクメニスタン + チュニジア + トンガ + トルコ + トリニダード・トバゴ + ツバル + 台湾 + タンザニア + ウクライナ + ウガンダ + 米領太平洋諸島 + アメリカ合衆国 + ウルグアイ + ウズベキスタン + ローマ教皇庁 (バチカン市国) + セントビンセント・グレナディーン諸島 + ベネズエラ + 英領バージン諸島 + 米領バージン諸島 + ベトナム + バヌアツ + ウォリス・フトーナ + 西サモア + イエメン + マヨット島 + ユーゴスラビア + 南アフリカ + ザンビア + ジンバブエ + + + 改訂版 + + + + 照合順番 + 通貨 + + + 仏暦 + 中国暦 + グレゴリオ暦 + ヘブライ暦 + イスラム暦 + 太陽イスラム暦 + 和暦 + 直接著錄 + 電話帳方式 + 拼音順 + 画数順 + 旧式 + + + + [一-丁七万-下不-与且世丘-丙両並中丸-丹主久乏乗乙九乱乳乾了予-争事-二互五-井亜亡交享-亭人仁今-介仏仕-他付-仙代-以仮仰仲件任企伏-休会伝伯伴伸伺似但位-佐体何余作佳併使例侍供依価侮-侯侵便係-促俊俗保信修俳俵俸倉個倍倒候借倣-値倫倹偉偏停健側-偶偽傍傑傘-備催債傷傾働像僕僚僧儀億儒償優元-兆先-光克免児党入全八-六共兵具-典兼内-円冊再冒冗写冠冬冷准凍凝凡処凶凸-出刀刃分-刈刊刑列初判-別利到制-券刺-刻則削前剖剛剣-剤副-剰割創劇力功-加劣助-努励労効劾勅勇勉動勘-務勝募勢勤勧勲勺匁包化-北匠匹-医匿十千升-午半卑-協南-単博占印-危即-卵卸厄厘厚原厳去参又及-収叔取-受叙口-句叫-召可-台史-右号-司各合-吉同-向君吟否含吸-吹呈-告周味呼-命和咲哀-品員哲唆-唇唐唯唱商問啓善喚喜-喝喪-喫営嗣嘆嘱器噴嚇囚-四回因団困囲-図固国圏園土圧-在地坂均坊坑坪垂型垣埋城域執培-基堀堂堅堕堤堪報場塀-塁塊塑塔塗塚塩塾境墓増墜墨墳墾壁壇壊壌士壮声-売変夏夕-外多夜夢大天-夫央失奇奉奏契奔奥奨奪奮女-奴好如-妄妊妙妥妨妹妻姉始姓-委姫姻姿威娘娠娯婆婚婦婿媒嫁嫌嫡嬢子孔字-存孝季-孤学孫宅宇-安完宗-定宜-宝実客-室宮宰害-家容宿寂寄密富寒寛寝察寡寧審寮寸寺対-寿封-専射将尉-尋導-小少尚就尺尼-局居屈届-屋展属層-履屯山岐岩岬岳岸峠-峡峰島崇崎崩川-州巡巣工-巨差己巻市-布帆希帝帥師席帯-帰帳常帽幅幕幣干-年幸-幹幻-幾庁広床序底店府度-座庫庭庶-庸廃廉-廊延-廷建弁弊式-弐弓-引弟弦-弧弱張強弾当形彩彫彰-影役彼往-征径-待律-後徐徒-従得御復-循微徳-徴徹心必忌-忍志-忙応忠快念怒怖思怠急性怪恋恐恒恥恨-恩恭息恵悔悟-悠患悦悩-悪悲悼情惑惜惨惰想愁愉意愚-愛感慈態-慌慎慕慢-慣慨慮慰慶憂憎憤憩憲憶憾懇懐懲懸成-戒戦戯戸戻房-所扇扉手才打払扱扶批承-技抄把抑投抗-折抜択披抱抵抹押-抽担拍拐拒-拓拘-拙招拝拠-拡括拷拾持指挑挙挟振挿捕捜捨据掃授掌排掘掛採-探接控-推措掲描-提揚-換握揮援揺損搬-搭携搾摂摘摩撃撤撮撲擁操擦擬支改攻放-政故敏救敗教敢-散敬数整-敵敷文斉斎斗料斜斤-斥断新方施旅旋族旗既日旧-早旬昆-昇明易-昔星-映春昨昭是昼時晩普-景晴晶暁暇暑暖-暗暦暫暮暴曇曜曲更書-曹替-最月-有服朕朗望朝期木未-札朱朴机朽杉材-村束条来杯東松-板析林枚果-枝枠枢枯架柄某染-柔柱柳査栄栓校株核-根格-栽桃案桑桜桟梅械棄棋棒棚棟森棺植検業極楼-楽概構様槽標模権-横樹橋機欄欠-次欧欲欺款歌歓止-正武歩歯歳-歴死殉-残殖殴-段殺-殻殿母-毎毒比毛氏民気水氷-永汁-求汗汚江-池決汽沈沖没-沢河沸-油治-沼沿況泉-泊泌法泡-泣泥注泰泳洋洗洞津洪活派流浄-浅浜浦浪浮浴海-浸消涙涯液涼淑淡深混添清渇-渉渋渓減渡渦温測港湖湯湾-満源準溝溶滅滋滑滝-滞滴漁-漂漆漏演漠漢漫-漬漸潔潜潟潤潮澄激-濁濃濫濯瀬火灯-灰災炉-炊炎炭点-為烈無焦然焼煙照煩煮熟熱燃燥爆爵-父片-版牛牧物牲特犠犬犯状狂狩独-狭猛猟猫献猶猿獄獣獲玄率玉王珍珠班現球理琴環璽瓶甘甚生産用田-申男町-画界畑畔留畜-畝略番異畳疎疑疫疲疾病症痘痛痢痴療癒癖発-登白-百的皆-皇皮皿盆益盗盛盟監-盤目盲直相盾省看-県真-眠眺眼着睡督瞬矛矢知短矯石砂研-砕砲破硝硫-硬碁碑確磁磨礁礎示礼社祈-祉祖祝-神祥票祭禁禅禍福秀-私秋科-秒秘租秩称移程税稚種稲稼稿-穀穂積穏穫穴究空突窃窒-窓窮-窯立竜章童端競竹笑笛符第筆等筋筒答策箇算管箱節範築篤簡簿籍米粉粋粒粗-粘粛粧精糖糧糸系糾紀約-紅紋納純紙-紛素-索紫累-細紳紹-紺終組経結絞絡給統絵-絶絹継-続維綱-網綿緊総緑-緒線締編-緩緯練縁縄縛縦縫縮績繁繊織-繕繭繰缶罪置罰署罷羅羊美群義羽翁翌習翻-翼老考者耐耕耗耳聖聞聴職肉肌肖肝肢肥肩-肪肯育肺胃胆背胎胞胴胸能脂脅脈脚脱脳脹腐腕腰腸-腹膚膜膨臓臣臨自臭至-致興舌舎舗舞-舟航般舶船艇艦良色芋芝花芳芸芽苗若-苦英茂茎茶草荒荘荷菊菌菓菜華落葉著葬蒸蓄蔵薄薦薪-薬藩藻虐虚虜虞虫蚊蚕蛇蛍蛮融血衆行術街衛衝衡衣表衰衷袋被裁-裂装裏裕補裸製複褐褒襟襲西要覆-覇見規視覚覧親観角解触言訂計討訓託-記訟訪設許訳-訴診証詐詔-評詞詠試詩詰-詳誇誉誌-認誓誕誘語誠誤説-読課調談請論諭-諮諸諾謀-謁謄謙講謝謡謹識譜警議譲護谷豆豊豚象豪貝-貞負-貢貧-販貫-責貯貴買-貸費貿-賀賃-賄資賊賓賛-賜賞賠賢賦質購贈赤赦走赴起超越趣足距跡路跳践踊踏躍身車軌-軍軒軟転軸軽較載輝輩-輪輸轄辛辞辱-農辺込迅迎近返迫迭述迷追退-送逃逆透-逐逓-途通逝速-造連逮週-進逸遂遅遇遊-運遍-過道-違遠遣適遭-遮遵遷-選遺避還邦邪邸郊郎郡部郭郵郷都酌-配酒酔酢酪酬酵酷-酸醜醸釈里-量金針釣鈍鈴鉄鉛鉢鉱銀銃銅銑銘銭鋭鋳鋼錘錠錬錯録鍛鎖鎮鏡鐘鑑長門閉開閑間関-閣閥閲闘防阻附降限陛院-陥陪陰陳陵-陶陸険陽隅-隆隊階-随隔際-障隠隣隷隻雄-雇雌雑離-難雨雪雰雲零-雷電需震霊霜霧露青静非面革靴音韻響頂項-順預-頒領頭頻-頼題-額顔-顕願類顧風飛食飢飯飲飼-飾養餓館首香馬駄-駆駐騎騒-験騰驚骨髄高髪鬼魂魅魔魚鮮鯨鳥鳴鶏麗麦麻黄黒黙鼓鼻齢] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + + + + + + + + + + + + + + + + + + + + + + + + 日曜日 + 月曜日 + 火曜日 + 水曜日 + 木曜日 + 金曜日 + 土曜日 + + + + 午前 + 午後 + + + 紀元前 + 西暦 + + + + + + + yyyy'年'M'月'd'日'EEEE + + + + + yyyy'年'M'月'd'日' + + + + + yyyy/MM/dd + + + + + yy/MM/dd + + + + + + + + H'時'mm'分'ss'秒'z + + + + + H:mm:ss:z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + 大化 + 白雉 + 白鳯 + 朱鳥 + 大宝 + 慶雲 + 和銅 + 霊亀 + 養老 + 神亀 + 天平 + 天平感宝 + 天平勝宝 + 天平宝字 + 天平神護 + 神護景雲 + 宝亀 + 天応 + 延暦 + 大同 + 弘仁 + 天長 + 承和 + 嘉祥 + 仁寿 + 斉衡 + 天安 + 貞観 + 元慶 + 仁和 + 寛平 + 昌泰 + 延喜 + 延長 + 承平 + 天慶 + 天暦 + 天徳 + 応和 + 康保 + 安和 + 天禄 + 天延 + 貞元 + 天元 + 永観 + 寛和 + 永延 + 永祚 + 正暦 + 長徳 + 長保 + 寛弘 + 長和 + 寛仁 + 治安 + 万寿 + 長元 + 長暦 + 長久 + 寛徳 + 永承 + 天喜 + 康平 + 治暦 + 延久 + 承保 + 承暦 + 永保 + 応徳 + 寛治 + 嘉保 + 永長 + 承徳 + 康和 + 長治 + 嘉承 + 天仁 + 天永 + 永久 + 元永 + 保安 + 天治 + 大治 + 天承 + 長承 + 保延 + 永治 + 康治 + 天養 + 久安 + 仁平 + 久寿 + 保元 + 平治 + 永暦 + 応保 + 長寛 + 永万 + 仁安 + 嘉応 + 承安 + 安元 + 治承 + 養和 + 寿永 + 元暦 + 文治 + 建久 + 正治 + 建仁 + 元久 + 建永 + 承元 + 建暦 + 建保 + 承久 + 貞応 + 元仁 + 嘉禄 + 安貞 + 寛喜 + 貞永 + 天福 + 文暦 + 嘉禎 + 暦仁 + 延応 + 仁治 + 寛元 + 宝治 + 建長 + 康元 + 正嘉 + 正元 + 文応 + 弘長 + 文永 + 建治 + 弘安 + 正応 + 永仁 + 正安 + 乾元 + 嘉元 + 徳治 + 延慶 + 応長 + 正和 + 文保 + 元応 + 元亨 + 正中 + 嘉暦 + 元徳 + 元弘 + 建武 + 延元 + 興国 + 正平 + 建徳 + 文中 + 天授 + 康暦 + 弘和 + 元中 + 至徳 + 嘉慶 + 康応 + 明徳 + 応永 + 正長 + 永享 + 嘉吉 + 文安 + 宝徳 + 享徳 + 康正 + 長禄 + 寛正 + 文正 + 応仁 + 文明 + 長享 + 延徳 + 明応 + 文亀 + 永正 + 大永 + 享禄 + 天文 + 弘治 + 永禄 + 元亀 + 天正 + 文禄 + 慶長 + 元和 + 寛永 + 正保 + 慶安 + 承応 + 明暦 + 万治 + 寛文 + 延宝 + 天和 + 貞享 + 元禄 + 宝永 + 正徳 + 享保 + 元文 + 寛保 + 延享 + 寛延 + 宝暦 + 明和 + 安永 + 天明 + 寛政 + 享和 + 文化 + 文政 + 天保 + 弘化 + 嘉永 + 安政 + 万延 + 文久 + 元治 + 慶応 + 明治 + 大正 + 昭和 + 平成 + + + + + + + Gy'年'M'月'd'日'EEEE + + + + + Gy'年'M'月'd'日' + + + + + G yy/MM/dd + + + + + G yy/MM/dd + + + + + + + + H'時'mm'分'ss'秒'z + + + + + H:mm:ss:z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + 太平洋標準時 + 太平洋夏時間 + + + PST + PDT + + ロサンゼルス + + + + 太平洋標準時 + 太平洋夏時間 + + + PST + PDT + + ロサンゼルス + + + + 山地標準時 + 山地夏時間 + + + MST + MDT + + デンバー + + + + 山地標準時 + 山地夏時間 + + + MST + MDT + + デンバー + + + + 山地標準時 + 山地標準時 + + + MST + MST + + フェニックス + + + + 山地標準時 + 山地標準時 + + + MST + MST + + フェニックス + + + + 中部標準時 + 中部夏時間 + + + CST + CDT + + シカゴ + + + + 中部標準時 + 中部夏時間 + + + CST + CDT + + シカゴ + + + + 東部標準時 + 東部夏時間 + + + EST + EDT + + ニューヨーク + + + + 東部標準時 + 東部夏時間 + + + EST + EDT + + ニューヨーク + + + + 東部標準時 + 東部標準時 + + + EST + EST + + インディアナポリス + + + + 東部標準時 + 東部標準時 + + + EST + EST + + インディアナポリス + + + + ハワイ標準時 + ハワイ標準時 + + + HST + HST + + ホノルル + + + + ハワイ標準時 + ハワイ標準時 + + + HST + HST + + ホノルル + + + + アラスカ標準時 + アラスカ夏時間 + + + AST + ADT + + アンカレッジ + + + + アラスカ標準時 + アラスカ夏時間 + + + AST + ADT + + アンカレッジ + + + + 大西洋標準時 + 大西洋夏時間 + + + AST + ADT + + ハリファクス + + + + ニューファンドランド島標準時 + ニューファンドランド島夏時間 + + + CNT + CDT + + セントジョンズ + + + + ニューファンドランド島標準時 + ニューファンドランド島夏時間 + + + CNT + CDT + + セントジョンズ + + + + 中欧標準時 + 中欧夏時間 + + + CET + CEST + + パリ + + + + 中欧標準時 + 中欧夏時間 + + + CET + CEST + + パリ + + + + グリニッジ標準時 + グリニッジ標準時 + + + GMT + GMT + + ロンドン + + + + グリニッジ標準時 + グリニッジ標準時 + + + GMT + GMT + + カサブランカ + + + + イスラエル標準時 + イスラエル夏時間 + + + IST + IDT + + エルサレム + + + + 日本標準時 + 日本標準時 + + + JST + JST + + 東京 + + + + 日本標準時 + 日本標準時 + + + JST + JST + + 東京 + + + + 東欧標準時 + 東欧夏時間 + + + EET + EEST + + ブカレスト + + + + 中国標準時 + 中国標準時 + + + CTT + CDT + + 上海 + + + + 中国標準時 + 中国標準時 + + + CTT + CDT + + 上海 + + + + + + + アンドラ ディナール + ADD + + + アンドラ ペセタ + ADP + + + UAE ディルハム + AED + + + アフガニー (1927-2002) + AFA + + + アフガニー + Af + + + アファールおよびイサス フラン + AIF + + + アルバニア レク (1946-1961) + ALK + + + アルバニア レク + ALL + + + アルバニア レク (Valute) + ALV + + + アルバニア レク (f) + ALX + + + アルメニア ドラム + AMD + + + オランダ領アンティル ギルダー + ANG + + + クワンザ (AOA) + AOA + + + クワンザ (1977-1990) + AOK + + + アンゴラ 新クワンザ (1990-2000) + AON + + + アンゴラ 旧クワンザ (1995-1999) + AOR + + + アンゴラ エスクード + AOS + + + アルゼンチン アゥストラール + ARA + + + アルゼンチン ペソ (MN) + ARM + + + アルゼンチン ペソ (1983-1985) + ARP + + + アルゼンチン ペソ + ARS + + + オーストラリア ドル + AUD + + + オーストラリア ポンド + AUP + + + アルバ ギルダー + AWG + + + アゼルバイジャン マナト + AZM + + + ボスニア ディナール + BAD + + + ボスニア マルク (BAM) + BAM + + + ボスニア 新ディナール + BAN + + + バルバドス ドル + BBD + + + バングラデシュ タカ + BDT + + + ベルギー フラン (BEC) + BEC + + + ベルギー フラン + BEF + + + ベルギー フラン (BEL) + BEL + + + ブルガリア レフ (BGL) + BGL + + + ブルガリア レフ (BGM) + BGM + + + ブルガリア 新レフ + BGN + + + ブルガリア レフ (1879-1952) + BGO + + + ブルガリア レフ (FEC) + BGX + + + バーレーン ディナール + BHD + + + ブルンジ フラン + BIF + + + バミューダ ドル + BMD + + + バミューダ ポンド + BMP + + + ブルネイ ドル + BND + + + ボリビアノ + BOB + + + ボリビア ボリビアノ (1863-1962) + BOL + + + ボリビア ペソ + BOP + + + ボリビア Mvdol + BOV + + + ブラジル 新クルゼイロ (BRB、1967-1986) + BRB + + + ブラジル クルゼイロ (BRC) + BRC + + + ブラジル クルゼイロ (BRE、1990-1993) + BRE + + + ブラジル レアル + BRL + + + ブラジル 新クルゼイロ (BRN) + BRN + + + ブラジル クルゼイロ レアル + BRR + + + ブラジル クルゼイロ (BRZ、1942-1967) + BRZ + + + バハマ ドル + BSD + + + バハマ ポンド + BSP + + + ブータン ニュルタム + BTN + + + ブータン ルピー + BTR + + + ビルマ チャット + BUK + + + ビルマ ルピー + BUR + + + ボツワナ プラ + BWP + + + ベラルーシ ルーブル (BYB、1994-1999) + BYB + + + ベラルーシ ルーブル (BYL、1992-1994) + BYL + + + ベラルーシ ルーブル + BYR + + + ベリーズ ドル + BZD + + + 英領ホンジュラス ドル + BZH + + + カナダ ドル + CAD + + + コンゴ フラン + CDF + + + コンゴ共和国 フラン + CDG + + + コンゴ ザイール + CDL + + + 中央アフリカ共和国 CFA フラン + CFF + + + スイス フラン + CHF + + + クック諸島 ドル + CKD + + + チリ ペソ (CLC) + CLC + + + チリ エスクード + CLE + + + チリ ウニダ デ フォメント + CLF + + + チリ ペソ + CLP + + + カメルーン CFA フラン + CMF + + + 中国人民元 + CNP + + + 中国 米ドル (FEC) + CNX + + + 中国人民元 + CNY + + + コロンビア ペソ (COB) + COB + + + コンゴ CFA フラン + COF + + + コロンビア ペソ + COP + + + コスタリカ コロン + CRC + + + チェコスロバキア コルナ (CSC) + CSC + + + チェコスロバキア コルナ (CSK) + CSK + + + キューバ ペソ + CUP + + + キューバ (FEC) + CUX + + + カーボベルデ エスクード + CVE + + + キュラソー島 ギルダー + CWG + + + キプロス ポンド + CYP + + + チェコ コルナ + CZK + + + 東ドイツ マルク + DDM + + + ドイツ マルク + DEM + + + ドイツ マルク (Sperrmark) + DES + + + ジブチ フラン + DJF + + + デンマーク クローネ + DKK + + + ドミニカ ペソ + DOP + + + アルジェリア ディナール + DZD + + + アルジェリア 新フラン + DZF + + + アルジェリア フラン (DZG) + DZG + + + エクアドル スクレ + ECS + + + エクアドル UVC + ECV + + + エストニア クルーン + EEK + + + エジプト ポンド + EGP + + + エリトリア ナクファ + ERN + + + スペイン ペセタ + ESP + + + エチオピア ブル + ETB + + + エチオピア ドル + ETD + + + ユーロ + + + + フィンランド マルカ + FIM + + + フィンランド マルカ (1860-1962) + FIN + + + フィジー諸島 ドル + FJD + + + フィジー諸島 ポンド + FJP + + + フォークランド(マルビナス)諸島 ポンド + FKP + + + フェロー諸島 クローナ + FOK + + + フランス フラン + FRF + + + フランス フラン (Franc Germinal/Franc Poincare) + FRG + + + ガボン CFA フラン + GAF + + + 英国ポンド + £ + + + グルジア クーポン ラリ + GEK + + + グルジア ラリ + GEL + + + ガーナ セディ + GHC + + + ガーナ 旧セディ + GHO + + + ガーナ ポンド + GHP + + + ガーナ 新セディ + GHR + + + ジブラルタル ポンド + GIP + + + グリーンランド クローネ + GLK + + + ガンビア ダラシ + GMD + + + ガンビア ポンド + GMP + + + ギニア フラン + GNF + + + ギニア フラン (1960-1972) + GNI + + + ギニア シリー + GNS + + + グアドループ フラン + GPF + + + 赤道ギニア ギニー + GQE + + + 赤道ギニア フラン + GQF + + + 赤道ギニア ペセタ + GQP + + + ギリシャ ドラクマ + GRD + + + ギリシャ 新ドラクマ + GRN + + + グアテマラ ケツァル + GTQ + + + 仏領ギアナ フラン + GUF + + + ポルトガル領ギニア エスクード + GWE + + + ポルトガル領ギニア ミルレイス + GWM + + + ギニアビサウ ペソ + GWP + + + ガイアナ ドル + GYD + + + 香港ドル + HKD + + + ホンジュラス レンピラ + HNL + + + クロアチア ディナール + HRD + + + クロアチア クーナ + HRK + + + ハイチ グールド + HTG + + + ハンガリー フォリント + HUF + + + 北アイルランド ポンド + IBP + + + インドネシア ニカギルダー + IDG + + + インドネシア ジャワ ルピア + IDJ + + + インドネシア 新ルピア + IDN + + + インドネシア ルピア + IDR + + + アイリッシュ ポンド + IEP + + + イスラエル シェケル + ILL + + + イスラエル ポンド + ILP + + + イスラエル新シェケル + ILS + + + マン島 ポンド + IMP + + + インド ルピー + =0#Rs.|1#Re.|1<Rs. + + + イラク ディナール + IQD + + + イラン リアル + IRR + + + アイスランド クローナ + ISK + + + イタリア リラ + + + + ジャージー島 ポンド + JEP + + + ジャマイカ ドル + JMD + + + ジャマイカ ポンド + JMP + + + ヨルダン ディナール + JOD + + + + + + + ケニア シリング + KES + + + キルギスタン ソム + KGS + + + カンボジア 旧リエル + KHO + + + カンボジア リエル + KHR + + + キリバス ドル + KID + + + コモロ フラン + KMF + + + 北朝鮮 人民ウォン + KPP + + + 北朝鮮 ウォン + KPW + + + 韓国 ホアン + KRH + + + 韓国 旧ウォン + KRO + + + 韓国 ウォン + + + + クウェート ディナール + KWD + + + ケイマン諸島 ドル + KYD + + + カザフスタン ルーブル + KZR + + + カザフスタン テンゲ + KZT + + + ラオス キープ + LAK + + + レバノン ポンド + LBP + + + リヒテンシュタイン フラン + LIF + + + スリランカ ルピー + LKR + + + セイロン ルピー + LNR + + + リベリア ドル + LRD + + + レソト ロティ + LSL + + + リトアニア リタス + LTL + + + リトアニア タロナ + LTT + + + ルクセンブルグ フラン + LUF + + + ラトビア ラッツ + LVL + + + ラトビア ルーブル + LVR + + + リビア リラ (British Military Authority) + LYB + + + リビア ディナール + LYD + + + リピア ポンド + LYP + + + モロッコ ディルハム + MAD + + + モロッコ フラン + MAF + + + モナコ フラン + MCF + + + モナコ フラン (MCG) + MCG + + + モルドバ レイ クーポン + MDC + + + モルドバ レイ + MDL + + + モルドバ レイ クーポン + MDR + + + マダガスカル アリアリ + MGA + + + マダガスカル フラン + MGF + + + マーシャル諸島 ドル + MHD + + + マケドニア デナル + MKD + + + マケドニア デナル (1992-1993) + MKN + + + マリ フラン + MLF + + + ミャンマー チャット + MMK + + + ミャンマー ドル (FEC) + MMX + + + モンゴル トグログ + MNT + + + マカオ パタカ + MOP + + + マルティニーク島 フラン + MQF + + + モーリタニア ウギア + MRO + + + マルタ リラ + MTL + + + マルタ ポンド + MTP + + + モーリシャス ルピー + MUR + + + モルディブ諸島 ルピー + MVP + + + モルディブ諸島 ルフィア + MVR + + + マラウィ クワチャ + MK + + + マラウィ ポンド + MWP + + + メキシコ ペソ + MXN + + + メキシコ ペソ (MXP、1861-1992) + MXP + + + メキシコ UDI + MXV + + + マレーシア リンギット + MYR + + + モザンピーク エスクード + MZE + + + モザンピーク メティカル + MZM + + + ナミビア ドル + NAD + + + ニューカレドニア CFP フラン + NCF + + + ナイジェリア ナイラ + NGN + + + ナイジェリア ポンド + NGP + + + ニューヘブリディーズ諸島 CFP フラン + NHF + + + ニカラグア コルドバ + NIC + + + ニカラグア コルドバ (NIG) + NIG + + + ニカラグア コルドバ オロ + NIO + + + オランダ ギルダー + NLG + + + ノルウェー クローネ + NOK + + + ネパール ルピー + NPR + + + ニュージーランド ドル + NZD + + + ニュージーランド ポンド + NZP + + + オマーン リアル + OMR + + + オマーン リアル (OMS) + OMS + + + パナマ バルボア + PAB + + + 沿ドニエストル ルーブル (PDK) + PDK + + + 沿ドニエストル 新ルーブル (PDN) + PDN + + + 沿ドニエストル ルーブル (PDR) + PDR + + + ペルー インティ + PEI + + + ペルー 新ソル + PEN + + + ペルー ソル + PES + + + パプアニューギニア キナ + PGK + + + フィリピン ペソ + PHP + + + パキスタン ルピー + PKR + + + ポーランド ズウォティ + PLN + + + ポーランド 米ドル (FEC) + PLX + + + ポーランド ズウォティ (1950-1995) + PLZ + + + パレスチナ ポンド + PSP + + + ポルトガル コント + PTC + + + ポルトガル エスクード + PTE + + + パラグアイ グアラニ + PYG + + + カタール リアル + QAR + + + レユニオン島 フラン + REF + + + ルーマニア レイ + ROL + + + ルーマニア 新レイ + RON + + + ロシア ルーブル (1991-1998) + RUR + + + ルワンダ フラン + RWF + + + サウジ リヤル + SAR + + + サウジ リヤル (SAS) + SAS + + + ソロモン諸島 ドル + SBD + + + セイシェル ルピー + SCR + + + スーダン ディナール + SDD + + + スーダン ポンド + SDP + + + スウェーデン クローナ + SEK + + + シンガポール ドル + SGD + + + セントヘレナ島 ポンド + SHP + + + スロベニア トラール (SIB) + SIB + + + スロベニア トラール + SIT + + + スロバキア コルナ + SKK + + + シエラレオネ レオン + SLL + + + サンマリノ リラ + SML + + + ソマリア シリング + SOS + + + ソマリランド シリング + SQS + + + スリナム ギルダー + SRG + + + スコットランド ポンド + SSP + + + サントメ・プリンシペ ドブラ + STD + + + サントメ・プリンシペ エスクード + STE + + + ソ連 新ルーブル + SUN + + + ソ連 ルーブル + SUR + + + エルサルバドル コロン + SVC + + + シリア ポンド + SYP + + + スワジランド リランゲニ + SZL + + + タークス・カイコス諸島 クローン + TCC + + + チャド CFA フラン + TDF + + + タイ バーツ + THB + + + タジキスタン ルーブル + TJR + + + タジキスタン ソモニ + TJS + + + トルクメニスタン マナト + TMM + + + チュニジア ディナール + TND + + + トンガ パ・アンガ + TOP + + + トンガ ポンド + TOS + + + ティモール エスクード + TPE + + + ティモール パタカ + TPP + + + トルコ リラ + TRL + + + トリニダードトバゴ ドル + TTD + + + トリニダードトバゴ 旧ドル + TTO + + + ツバル ドル + TVD + + + 新台湾ドル + TWD + + + タンザニア シリング + TZS + + + ウクライナ グリブナ + UAH + + + ウクライナ カルボバネツ + UAK + + + ウガンダ シリング (1966-1987) + UGS + + + ウガンダ シリング + UGX + + + 米ドル + $ + + + 米ドル (翌日) + USN + + + 米ドル (当日) + USS + + + ウルグアイ ペソ (UYF) + UYF + + + ウルグアイ ペソ (1975-1993) + UYP + + + ウルグアイ ペソ + UYU + + + ウズベキスタン スム (UZC) + UZC + + + ウズベキスタン スム + UZS + + + バチカン リラ + VAL + + + 北ベトナム ドン (VDD) + VDD + + + 北ベトナム 新ドン + VDN + + + 北ベトナム ドン (VDP) + VDP + + + ベネズエラ ボリバル + VEB + + + 英領バージン諸島 ドル + VGD + + + ベトナム ドン + đ + + + ベトナム 新ドン + VNN + + + ベトナム共和国 ドン + VNR + + + ベトナム自治区 ドン + VNS + + + バヌアツ バツ + VUV + + + 西サモア ポンド + WSP + + + 西サモア タラ + WST + + + アジア ディナール勘定単位 + XAD + + + CFA フラン BEAC + XAF + + + アジア通貨単位 (AMU) + XAM + + + + XAU + + + ヨーロッパ混合単位 (EURCO) + XBA + + + ヨーロッパ通貨単位 (EMU-6) + XBB + + + ヨーロッパ勘定単位 (EUA-9) + XBC + + + ヨーロッパ勘定単位 (EUA-17) + XBD + + + 東カリブ ドル + EC$ + + + CFA 新フラン + XCF + + + 特別引き出し権 (Special Drawing Rights) + XDR + + + CFA フラン (BCEAEC) + XEF + + + ヨーロッパ通貨単位 + XEU + + + フランス フラン (XFO) + XFO + + + フランス UIC フラン + XFU + + + イスラム ディナール + XID + + + フランス 新フラン (XMF) + XMF + + + 仏領アンティル諸島 CFA フラン + XNF + + + CFA フラン BCEAO + XOF + + + CFP フラン + XPF + + + コメコン振替ルーブル + XTR + + + イエメン ディナール + YDD + + + イエメン リアル (YEI) + YEI + + + イエメン リアル + YER + + + ユーゴスラビア ディナール (YUD) + YUD + + + ユーゴスラビア ディナール (YUF) + YUF + + + ユーゴスラビア ディナール (YUG) + YUG + + + ユーゴスラビア スーパー ディナール + YUM + + + ユーゴスラビア 新ディナール (YUN) + YUN + + + ユーゴスラビア ディナール (YUO) + YUO + + + ユーゴスラビア ディナール (YUR) + YUR + + + 南アフリカ ランド (ZAL) + ZAL + + + 南アフリカ ポンド + ZAP + + + 南アフリカ ランド + ZAR + + + ザンビア ポンド + ZMP + + + ザイール 新ザイール + ZRN + + + ザイール ザイール + ZRZ + + + ジンバブエ ドル + ZWD + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ka_GE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ka_GE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ka_GE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ka_GE.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ka.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ka.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ka.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ka.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,232 @@ + + + + + + + + + + + ქართული + + + ანდორა + არაბეთის გაერთიანებული ემირატები + ავღანეთი + ანტიგუა და ბარბუდა + ალბანეთი + სასომხეთი + ანგოლა + არგენტინა + ავსტრია + ავსტრალია + აზერბაიჯანი + ბოსნია და ჰერცეგოვინა + ბარბადოსი + ბანგლადეში + ბელგია + ბურკინა-ფასო + ბულგარეთი + ბაჰრეინი + ბურუნდი + ბენინი + ბრუნეი + ბოლივია + ბრაზილია + ბაჰამის კუნძულები + ბუტანი + ბოტსვანა + ბელორუსია + ბელიზი + კანადა + ცენტრალური აფრიკის რესპუბლიკა + კონგო + შვეიცარია + სპილოს ძვლის სანაპირო + ჩილი + კამერუნი + ჩინეთი + კოლუმბია + კოსტა-რიკა + კუბა + კაბო-ვერდე + კვიპროსი + ჩეხეთის რესპუბლიკა + გერმანია + ჯიბუტი + დანია + დომინიკა + დომინიკანის რესპუბლიკა + ალჟირი + ეკვადორი + ესტონეთი + ეგვიპტე + დასავლეთი საჰარა + ერიტრეა + ესპანეთი + ეთიოპია + ფინეთი + ფიჯი + მიკრონეზია + საფრანგეთი + გაბონი + გრენადა + საქართველო + განა + გამბია + გვინეა + ეკვატორული გვინეა + საბერძნეთი + გვატემალა + გვინეა-ბისაუ + გაიანა + ჰონდურასი + ჰორვატია + ჰაიტი + უნგრეთი + ინდონეზია + ირლანდია + ისრაელი + ინდოეთი + ერაყი + ირანი + ისლანდია + იტალია + იამაიკა + იორდანია + იაპონია + კენია + ყირგიზეთი + კამბოჯა + კირიბატი + კომორის კუნძულები + სენტ-კიტსი და ნევისი + ჩრდილოეთ კორეა + სამხრეთ კორეა + კუვეიტი + ყაზახეთი + ლაოსი + ლიბანი + სენტ-ლუსია + ლიხტენშტეინი + შრი-ლანკა + ლიბერია + ლესოტო + ლიტვა + ლუქსემბურგი + ლატვია + ლიბია + მაროკო + მონაკო + მოლდოვა + მადაგასკარი + მარშალის კუნძულები + მაკედონია + მალი + მიანმარი + მონღოლეთი + მავრიტანია + მალტა + მავრიკია + მალდივის კუნძულები + მალავი + მექსიკა + მალაიზია + მოზამბიკი + ნამიბია + ნიგერი + ნიგერია + ნიკარაგუა + ნიდერლანდები + ნორვეგია + ნეპალი + ნაურუ + ახალი ზელანდია + ომანი + პანამა + პერუ + პაპუა-ახალი გვინეა + ფილიპინები + პაკისტანი + პოლონეთი + პორტუგალია + პალაუ + პარაგვაი + კატარი + რუმინეთი + რუსეთი + რუანდა + საუდის არაბეთი + სოლომონის კუნძულები + სეიშელის კუნძულები + სუდანი + შვეცია + სინგაპური + სლოვენია + სლოვაკეთი + სიერა-ლეონე + სან-მარინო + სენეგალი + სომალი + სერბია + სურინამი + საო-ტომე და პრინსიპი + სალვადორი + სირია + სვაზილენდი + ჩადი + ტოგო + ტაილანდი + ტაჯიკეთი + თურქმენეთი + ტუნისი + ტონგა + თურქეთი + ტრინიდადი და ტობაგო + ტუვალუ + ტაივანი + ტანზანია + უკრაინა + უგანდა + ამერიკის შეერთებული შტატები + ურუგვაი + უზბაკეთი + ვატიკანი + სენტ-ვინსენტი და გრენადინები + ვენესუელა + ვიეტნამი + ვანუატუ + სამოა + იემენი + სამხრეთ აფრიკა + ზამბია + ზიმბაბვე + + + + [:Geor:] + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + GEL + Lari + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kk_KZ.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kk_KZ.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kk_KZ.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kk_KZ.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-¤ #,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kk.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kk.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kk.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kk.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,159 @@ + + + + + + + + + + + Қазақ + + + Қазақстан + + + + [а-яыэёіқңүұә] + + + + + + + + қаң. + ақп. + нау. + сәу. + мам. + мау. + шіл. + там. + қыр. + қаз. + қар. + желт. + + + қаңтар + ақпан + наурыз + сәуір + мамыр + маусым + шілде + тамыз + қыркүйек + қазан + қараша + желтоқсан + + + + + + + жс. + дс. + сс. + ср. + бс. + жм. + сһ. + + + жексені + дуйсенбі + сейсенбі + сәренбі + бейсенбі + жұма + сенбі + + + + + + + + + + + + EEEE, d MMMM yyyy 'ж.' + + + + + d MMMM yyyy 'ж.' + + + + + dd.MM.yyyy + + + + + dd.MM.yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + KZT + тңг. + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kl_GL.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kl_GL.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kl_GL.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kl_GL.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + MMM dd,yy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;¤ -#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kl.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kl.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kl.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kl.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,102 @@ + + + + + + + + + + + kalaallisut + + + Kalaallit Nunaat + + + + [a-záéíúâêîôûæåøãĩũĸ] + + + + + + + + jan + feb + mar + apr + maj + jun + jul + aug + sep + okt + nov + dec + + + januari + februari + martsi + aprili + maji + juni + juli + augustusi + septemberi + oktoberi + novemberi + decemberi + + + + + + + sab + ata + mar + pin + sis + tal + arf + + + sabaat + ataasinngorneq + marlunngorneq + pingasunngorneq + sisamanngorneq + tallimanngorneq + arfininngorneq + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + DKK + kr + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kn_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kn_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kn_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kn_IN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-M-yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kn.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kn.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kn.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kn.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,97 @@ + + + + + + + + + + + ಕನ್ನಡ + + + ಆಸ್ಟ್ರೆಲಿಯ + ಚೀನ + ಎಸ್ತೊನಿಯ + ಭಾರತ + ಮಾಲ್ಡಿವ ದ್ವೀಪಗಳು + ನೆಪಾಳ + ಸಿಂಗಪುರ + ತುರ್ಕಿ + + + + [:Knda:] + + + + + + + + ಜನವರೀ + ಫೆಬ್ರವರೀ + ಮಾರ್ಚ್ + ಎಪ್ರಿಲ್ + ಮೆ + ಜೂನ್ + ಜುಲೈ + ಆಗಸ್ಟ್ + ಸಪ್ಟೆಂಬರ್ + ಅಕ್ಟೋಬರ್ + ನವೆಂಬರ್ + ಡಿಸೆಂಬರ್ + + + ಜನವರೀ + ಫೆಬ್ರವರೀ + ಮಾರ್ಚ್ + ಎಪ್ರಿಲ್ + ಮೆ + ಜೂನ್ + ಜುಲೈ + ಆಗಸ್ಟ್ + ಸಪ್ಟೆಂಬರ್ + ಅಕ್ಟೋಬರ್ + ನವೆಂಬರ್ + ಡಿಸೆಂಬರ್ + + + + + + + ರ. + ಸೋ. + ಮಂ. + ಬು. + ಗು. + ಶು. + ಶನಿ. + + + ರವಿವಾರ + ಸೋಮವಾರ + ಮಂಗಳವಾರ + ಬುಧವಾರ + ಗುರುವಾರ + ಶುಕ್ರವಾರ + ಶನಿವಾರ + + + + ಪೂರ್ವಾಹ್ನ + ಅಪರಾಹ್ನ + + + + + + + INR + रु + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kok_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kok_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kok_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kok_IN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-M-yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ko_KR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ko_KR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ko_KR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ko_KR.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + yyyy'년' M'월' d'일' EEEE + + + + + yyyy'년' M'월' d'일' + + + + + yyyy. MM. dd + + + + + yy. MM. dd + + + + + + + + a hh'시' mm'분' ss'초' z + + + + + a hh'시' mm'분' ss'초' + + + + + a h:mm:ss + + + + + a h:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kok.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kok.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kok.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kok.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,247 @@ + + + + + + + + + + + अफार + अबखेज़ियन + अफ्रिकान्स + अमहारिक् + अरेबिक् + असामी + ऐमरा + अज़रबैजानी + बष्किर + बैलोरुसियन् + बल्गेरियन + बीहारी + बिसलमा + बंगाली + तिबेतियन + ब्रेटन + कटलान + कोर्शियन + ज़ेक् + वेळ्ष् + डानिष + जर्मन + भूटानी + ग्रीक् + आंग्ल + इस्परान्टो + स्पानिष + इस्टोनियन् + बास्क + पर्षियन् + फिन्निष् + फिजी + फेरोस् + फ्रेन्च + फ्रिशियन् + ऐरिष + स्काटस् गेलिक् + गेलीशियन + गौरानी + गुजराती + हौसा + हेब्रु + हिन्दी + क्रोयेषियन् + हंगेरियन् + आर्मीनियन् + इन्टरलिंग्वा + इन्डोनेषियन + इन्टरलिंग् + इनूपेयाक् + आईस्लान्डिक + इटालियन + इन्युकट्ट + जापनीस् + जावनीस् + जार्जियन् + कज़ख् + ग्रीनलान्डिक + कंबोडियन + कन्नडा + कोरियन् + कोंकणी + कश्मीरी + कुर्दिष + किर्गिज़ + लाटिन + लिंगाला + लाओतियन् + लिथुआनियन् + लाट्वियन् (लेट्टिष्) + मलागसी + माओरी + मसीडोनियन् + मळियाळम + मंगोलियन् + मोल्डावियन् + मराठी + मलय + मालतीस् + बर्मीज़् + नौरो + नेपाळी + डच् + नोर्वेजियन + ओसिटान् + ओरोमो (अफान) + ओरिया + पंजाबी + पोलिष + पाष्टो (पुष्टो) + पोर्चुगीज़् + क्वेच्वा + रहटो-रोमान्स् + किरुन्दी + रोमानियन् + रष्यन् + किन्यार्वान्डा + संस्कृत + सिंधी + सांग्रो + सेर्बो-क्रोयेषियन् + सिन्हलीस् + स्लोवाक + स्लोवेनियन् + समोन + शोना + सोमाळी + आल्बेनियन् + सेर्बियन् + सिस्वाती + सेसोथो + सुंदनीस + स्वीदीष + स्वाहिली + तमिळ + तेलुगू + तजिक + थाई + तिग्रिन्या + तुर्कमन + तगालोग + सेत्स्वाना + तोंगा + तुर्किष + त्सोगा + तटार + त्वि + उधूर + युक्रेनियन् + उर्दू + उज़बेक + वियत्नामीज़ + ओलापुक + उलोफ़ + झ़ौसा + इद्दिष् + यूरुबा + झ्हुन्ग + चीनीस् + जुलू + + + भारत + + + + [[:Deva:]‌‍] + + + + + + + + जानेवारी + फेबृवारी + मार्च + एप्रिल + मे + जून + जुलै + ओगस्ट + सेप्टेंबर + ओक्टोबर + नोव्हेंबर + डिसेंबर + + + जानेवारी + फेब्रुवारी + मार्च + एप्रिल + मे + जून + जुलै + ओगस्ट + सेप्टेंबर + ओक्टोबर + नोव्हेंबर + डिसेंबर + + + + + + + रवि + सोम + मंगळ + बुध + गुरु + शुक्र + शनि + + + आदित्यवार + सोमवार + मंगळार + बुधवार + गुरुवार + शुक्रवार + शनिवार + + + + म.पू. + म.नं. + + + क्रिस्तपूर्व + क्रिस्तशखा + + + + + + + + भारतीय समय + भारतीय समय + + + IST + IST + + + + + + + + INR + रु + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ko.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ko.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ko.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ko.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2399 @@ + + + + + + + + + + + 아파르어 + 압카즈어 + 남아공 공용어 + 아칸어 + 암하라어 + 아라곤어 + 아랍어 + 아샘어 + 아바릭어 + 아이마라어 + 아제르바이잔어 + 바슈키르어 + 벨로루시어 + 불가리아어 + 비하르어 + 비슬라마어 + 밤바라어 + 벵골어 + 티베트어 + 브르타뉴어 + 보스니아어 + 브린어 + 카탈로니아어 + 차모로어 + 체로키어 + 코르시카어 + 크리어 + 체코어 + 교회슬라브어 + 추바시어 + 웨일스어 + 덴마크어 + 독일어 + 디베히어 + 부탄어 + 에웨어 + 그리스어 + 영어 + 에스페란토어 + 스페인어 + 에스토니아어 + 바스크어 + 이란어 + 풀라어 + 핀란드어 + 피지어 + 페로스어 + 프랑스어 + 프리지아어 + 아일랜드어 + 스코갤릭어 + 게이즈어 + 갈리시아어 + 구아라니어 + 구자라트어 + 맹크스어 + 하우자어 + 하와이어 + 히브리어 + 힌디어 + 히리 모투어 + 크로아티아어 + 아이티어 + 헝가리어 + 아르메니아어 + 인터링거 + 인도네시아어 + 인터링게어 + 이그보어 + 시츄안 이어 + 이누피아크어 + 이도어 + 아이슬란드어 + 이탈리아어 + 이눅티투트어 + 일본어 + 자바어 + 그루지야어 + 콩고어 + 키쿠유어 + 쿠안야마어 + 카자흐어 + 그린랜드어 + 캄보디아어 + 카나다어 + 한국어 + 코카니어 + 칸누리어 + 카슈미르어 + 크르드어 + 코미어 + 콘월어 + 키르기스어 + 라틴어 + 룩셈부르크어 + 간다어 + 림버거어 + 링갈라어 + 라오어 + 리투아니아어 + 루바-카탄가어 + 라트비아어 + 마다가스카르어 + 마셜제도어 + 마오리어 + 마케도니아어 + 말라얄람어 + 몽골어 + 몰다비아어 + 마라티어 + 말레이어 + 몰타어 + 버마어 + 나우루어 + 보크말 노르웨이어 + 은데벨레어, 북부 + 네팔어 + 느동가어 + 네덜란드어 + 뉘노르스크 노르웨이어 + 노르웨이어 + 은데벨레어, 남부 + 나바호어 + 니안자어; 치츄어; 츄어 + 옥시트어 + 오지브웨이어 + 오로모어 (아판) + 오리야어 + 오세트어 + 펀잡어 + 팔리어 + 폴란드어 + 파시토어 (푸시토) + 포르투칼어 + 케추아어 + 레토로만어 + 반투어(부룬디) + 루마니아어 + 러시아어 + 반투어(루완다) + 산스크리트어 + 사르디니아어 + 신디어 + 북부 사미어 + 산고어 + 세르보크로아티아어 + 스리랑카어 + 시다모어 + 슬로바키아어 + 슬로베니아어 + 사모아어 + 쇼나어 + 소말리아어 + 알바니아어 + 세르비아어 + 시스와티어 + 세소토어 + 순단어 + 스웨덴어 + 스와힐리어 + 시리아어 + 타밀어 + 텔루구어 + 타지키스탄어 + 태국어 + 티그리냐어 + 티그레어 + 투르크멘어 + 타갈로그어 + 세츠와나어 + 통가어 + 터키어 + 통가어 + 타타르어 + 트위어 + 타히티어 + 위구르어 + 우크라이나어 + 우르두어 + 우즈베크어 + 벤다어 + 베트남어 + 볼라퓌크어 + 왈론어 + 올로프어 + 반투어(남아프리카) + 이디시어 + 요루바어 + 주앙어 + 중국어 + 줄루어 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 안도라 + 아랍에미리트 + 아프가니스탄 + 앤티가 바부다 + 안길라 + 알바니아 + 아르메니아 + 네덜란드령 안틸레스 + 앙골라 + 남극 대륙 + 아르헨티나 + 아메리칸 사모아 + 오스트리아 + 오스트레일리아 + 아루바 + 아제르바이잔 + 보스니아 헤르체고비나 + 바베이도스 + 방글라데시 + 벨기에 + 부르키나파소 + 불가리아 + 바레인 + 부룬디 + 베넹 + 버뮤다 + 브루나이 + 볼리비아 + 브라질 + 바하마 + 부탄 + 부베 + 보츠와나 + 벨라루스 + 벨리즈 + 캐나다 + 코코스제도 + 콩고민주공화국 + 중앙 아프리카 + 콩고 + 스위스 + 코트디부와르 + 쿡제도 + 칠레 + 카메룬 + 중국 + 콜롬비아 + 코스타리카 + 쿠바 + 까뽀베르데 + 크리스마스섬 + 사이프러스 + 체코 + 독일 + 지부티 + 덴마크 + 도미니카 + 도미니카 공화국 + 알제리 + 에쿠아도르 + 에스토니아 + 이집트 + 서사하라 + 에리트리아 + 스페인 + 이디오피아 + 핀란드 + 피지 + 포클랜드제도 + 마이크로네시아 + 페로제도 + 프랑스 + 가봉 + 영국 + 그레나다 + 그루지야 + 프랑스령 기아나 + 가나 + 지브롤터 + 그린란드 + 감비아 + 기니 + 과달로프 + 적도 기니 + 그리스 + 사우스조지아-사우스샌드위치제도 + 과테말라 + + 기네비쏘 + 가이아나 + 홍콩, 중국 특별행정구 + 허드섬-맥도널드제도 + 온두라스 + 크로아티아 + 하이티 + 헝가리 + 인도네시아 + 아일랜드 + 이스라엘 + 인도 + 영국령인도양식민지 + 이라크 + 이란 + 아이슬란드 + 이탈리아 + 자메이카 + 요르단 + 일본 + 케냐 + 키르기스스탄 + 캄보디아 + 키리바시 + 코모르 + 세인트크리스토퍼 네비스 + 조선 민주주의 인민 공화국 + 대한민국 + 쿠웨이트 + 케이맨제도 + 카자흐스탄 + 라오스 + 레바논 + 세인트루시아 + 리히텐슈타인 + 스리랑카 + 라이베리아 + 레소토 + 리투아니아 + 룩셈부르크 + 라트비아 + 리비아 + 모로코 + 모나코 + 몰도바 + 마다가스카르 + 마샬 군도 + 마케도니아어 + 말리 + 미얀마 + 몽골 + 마카오, 중국 특별행정구 + 북마리아나제도 + 말티니크 + 모리타니 + 몬트세라트 + 몰타 + 모리셔스 + 몰디브 + 말라위 + 멕시코 + 말레이지아 + 모잠비크 + 나미비아 + 뉴 칼레도니아 + 니제르 + 노퍽섬 + 나이지리아 + 니카라과 + 네덜란드 + 노르웨이 + 네팔 + 나우루 + 니우에 + 뉴질랜드 + 오만 + 파나마 + 페루 + 프랑스령 폴리네시아 + 파푸아뉴기니 + 필리핀 + 파키스탄 + 폴란드 + 세인트피에르-미케롱 + 핏케언섬 + 푸에르토리코 + 팔레스타인 지구 + 포르트칼 + 팔라우 + 파라과이 + 카타르 + 리유니온 + 루마니아 + 러시아 + 르완다 + 사우디아라비아 + 솔로몬 + 쉐이쉘 + 수단 + 스웨덴 + 싱가포르 + 세인트헬레나 + 슬로베니아 + 스발바르제도-얀마웬섬 + 슬로바키아 + 시에라리온 + 산마리노 + 세네갈 + 소말리아 + 세르비아 + 수리남 + 상투메 프린시페 + 엘살바도르 + 시리아 + 스와질랜드 + 터크스케이커스제도 + 차드 + 프랑스 남부 지방 + 토고 + 태국 + 타지키스탄 + 토켈라우 + 동티모르 + 투르크메니스탄 + 튀니지 + 통가 + 터키 + 트리니다드 토바고 + 투발루 + 대만 + 탄자니아 + 우크라이나 + 우간다 + 미국령 해외 제도 + 미국 + 우루과이 + 우즈베키스탄 + 바티칸 + 세인트빈센트그레나딘 + 베네수엘라 + 영국령 버진 아일랜드 + 미국령 버진 아일랜드 + 베트남 + 바누아투 + 왈리스-푸투나 제도 + 사모아 + 예멘 + 마요티 + 유고슬라비아 + 남아프리카 + 잠비아 + 짐바브웨 + + + 개정 + + + 달력 + 조합 + 통화 + + + 불교력 + 중국력 + 태양력 + 히브리력 + 이슬람력 + 이슬람 상용력 + 일본력 + 순서 + 전화번호부순 + 병음순 + 자획순 + 전통 역법 + + + + [가-힣] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + + 1월 + 2월 + 3월 + 4월 + 5월 + 6월 + 7월 + 8월 + 9월 + 10월 + 11월 + 12월 + + + + + + + + + + + + + + + + + + + + + + + + + 일요일 + 월요일 + 화요일 + 수요일 + 목요일 + 금요일 + 토요일 + + + + 오전 + 오후 + + + 기원전 + 서기 + + + + + + + yyyy'년' M'월' d'일' EEEE + + + + + yyyy'년' M'월' d'일' EE + + + + + yyyy-MM-dd + + + + + yy-MM-dd + + + + + + + + a hh'시' mm'분' ss'초' z + + + + + a hh'시' mm'분' ss'초' + + + + + a hh'시' mm'분' + + + + + a hh'시' mm'분' + + + + + + + {1} {0} + + + + + + + + + 태평양 표준시 + 태평양 기준시 + + + PST + PDT + + 로스앤젤레스 + + + + 태평양 표준시 + 태평양 기준시 + + + PST + PDT + + 로스앤젤레스 + + + + 산악 표준시 + 산악 기준시 + + + MST + MDT + + 덴버 + + + + 산악 표준시 + 산악 기준시 + + + MST + MDT + + 덴버 + + + + 산악 표준시 + 산악 표준시 + + + MST + MST + + 피닉스 + + + + 산악 표준시 + 산악 표준시 + + + MST + MST + + 피닉스 + + + + 중부 표준시 + 중부 기준시 + + + CST + CDT + + 시카고 + + + + 중부 표준시 + 중부 기준시 + + + CST + CDT + + 시카고 + + + + 동부 표준시 + 동부 기준시 + + + EST + EDT + + 뉴욕 + + + + 동부 표준시 + 동부 기준시 + + + EST + EDT + + 뉴욕 + + + + 동부 표준시 + 동부 표준시 + + + EST + EST + + 인디애나폴리스 + + + + 동부 표준시 + 동부 표준시 + + + EST + EST + + 인디애나폴리스 + + + + 하와이 표준시 + 하와이 표준시 + + + HST + HST + + 호놀룰루 + + + + 하와이 표준시 + 하와이 표준시 + + + HST + HST + + 호놀룰루 + + + + 알래스카 표준시 + 알래스카 기준시 + + + AST + ADT + + 앵커리지 + + + + 알래스카 표준시 + 알래스카 기준시 + + + AST + ADT + + 앵커리지 + + + + 대서양 표준시 + 대서양 기준시 + + + AST + ADT + + 핼리팩스 + + + + 뉴펀들랜드 표준시 + 뉴펀들랜드 기준시 + + + CNT + CDT + + St. Johns + + + + 뉴펀들랜드 표준시 + 뉴펀들랜드 기준시 + + + CNT + CDT + + St. Johns + + + + 중부유럽 표준시 + 중부유럽 기준시 + + + CET + CEST + + 파리 + + + + 중부유럽 표준시 + 중부유럽 기준시 + + + CET + CEST + + 파리 + + + + 그리니치 표준시 + 그리니치 표준시 + + + GMT + GMT + + 런던 + + + + 그리니치 표준시 + 그리니치 표준시 + + + GMT + GMT + + 카사블랑카 + + + + 이스라엘 표준시 + 이스라엘 기준시 + + + IST + IDT + + 예루살렘 + + + + 일본 표준시 + 일본 표준시 + + + JST + JST + + 도쿄 + + + + 일본 표준시 + 일본 표준시 + + + JST + JST + + 도쿄 + + + + 한국표준시 + 한국표준시 + + + KST + KST + + + + + 동부유럽 표준시 + 동부유럽 기준시 + + + EET + EEST + + 부쿠레슈티 + + + + 중국 표준시 + 중국 표준시 + + + CTT + CDT + + 상하이 + + + + 중국 표준시 + 중국 표준시 + + + CTT + CDT + + 상하이 + + + + + + + 안도라 디네르 + ADD + + + 안도라 페세타 + ADP + + + 아랍에미레이트 디나르 + AED + + + 아프가니 (1927-2002) + AFA + + + 아프가니 + AFN + + + 아파르와 이사스의 프랑 + AIF + + + 알바니아 레크 (1946-1961) + ALK + + + 알바니아 레크 + ALL + + + 알바니아 레크 발루트 + ALV + + + 알바니아 달러 태환권 + ALX + + + 아르메니아 드람 + AMD + + + 네델란드 안틸레스 굴덴 + ANG + + + 앙골라 콴자 + AOA + + + 앙골라 콴자 (1977-1990) + AOK + + + 앙골라 신귄 콴자 (1990-2000) + AON + + + 앙골라 콴자 Reajustado (1995-1999) + AOR + + + 앙골라 에스쿠도 + AOS + + + 아르헨티나 오스트랄 + ARA + + + 아르헨티나 페소 모네다 국영 + ARM + + + 아르헨티나 페소 (1983-1985) + ARP + + + 아르헨티나 페소 + ARS + + + 호주 실링 + ATS + + + 호주 달러 + AUD + + + 호주 파운드 + AUP + + + 아루바 길더 + AWG + + + 아제르바이젠 마나트 + AZM + + + 보스니아-헤르체고비나 디나르 + BAD + + + 보스니아-헤르체고비나 태환 마르크 + BAM + + + 보스니아-헤르체고비나 신 디나르 + BAN + + + 바베이도스 달러 + BBD + + + 방글라데시 타카 + BDT + + + 벨기에 프랑 (태환) + BEC + + + 벨기에 프랑 + BEF + + + 벨기에 프랑 (금융) + BEL + + + 불가리아 동전 렛 + BGL + + + 불가리아 사회주의 렛 + BGM + + + 불가리아 신권 렛 + BGN + + + 불가리아 렛 (1879-1952) + BGO + + + 불가리아 렛 태환권 + BGX + + + 바레인 디나르 + BHD + + + 부룬디 프랑 + BIF + + + 버뮤다 달러 + BMD + + + 버뮤다 파운드 + BMP + + + 부루나이 달러 + BND + + + 볼리비아노 + BOB + + + 볼리비아노 (1863-1962) + BOL + + + 볼리비아노 페소 + BOP + + + 볼리비아노 크루제이루 노보 (1967-1986) + BRB + + + 브라질 크루자두 + BRC + + + 브라질 크루제이루 (1990-1993) + BRE + + + 브라질 레알 + BRL + + + 브라질 크루자두 노보 + BRN + + + 브라질 크루제이루 + BRR + + + 브라질 크루제이루 (1942-1967) + BRZ + + + 바하마 달러 + BSD + + + 바하마 달러 파운드 + BSP + + + 부탄 눌투눔 + BTN + + + 부탄 루피 + BTR + + + 버마 차트 + BUK + + + 버마 루피 + BUR + + + 보츠와나 폴라 + BWP + + + 벨라루스 신권 루블 (1994-1999) + BYB + + + 벨라루스 루블 (1992-1994) + BYL + + + 벨라루스 루블 + BYR + + + 벨리즈 달러 + BZD + + + 영국령 혼두라스 달러 + BZH + + + 캐나다 달러 + CAD + + + 콩고 프랑 콩골라스 + CDF + + + 콩고 공화국 프랑 + CDG + + + 콩고 자이르 + CDL + + + 스위스 프랑달러 + CHF + + + 쿡 제도 달러 + CKD + + + 칠레 콘도르 + CLC + + + 칠레 에스쿠도 + CLE + + + 칠레 페소 + CLP + + + 중국 위안 인민폐 + CNY + + + 콜롬비아 지폐 페소 + COB + + + 콩고 CFA 프랑 + COF + + + 콜롬비아 페소 + COP + + + 코스타리카 콜론 + CRC + + + 체코슬로바키아 코루나 + CSC + + + 체코슬로바키아 동전 코루나 + CSK + + + 쿠바 페소 + CUP + + + 쿠바 태환권 마르크 + CUX + + + 카보베르데 에스쿠도 + CVE + + + 쿠라카오 길더 + CWG + + + 싸이프러스 파운드 + CYP + + + 체코 공화국 코루나 + CZK + + + 동독 오스트마르크 + DDM + + + 독일 마르크 + DEM + + + 독일 스퍼마르크 + DES + + + 지부티 프랑 + DJF + + + 덴마크 크로네 + DKK + + + 도미니카 페소 + DOP + + + 알제리 디나르 + DZD + + + 알제리 신권 프랑 + DZF + + + 알제리 프랑 제르미날 + DZG + + + 에쿠아도르 수크레 + ECS + + + 에스토니아 크룬 + EEK + + + 이집트 파운드 + EGP + + + 에리트리아 나크파 + ERN + + + 스페인 페세타 + ESP + + + 이디오피아 비르 + ETB + + + 이디오피아 달러 + ETD + + + 유로화 + + + + 핀란드 마르카 + FIM + + + 핀란드 마르카 (1860-1962) + FIN + + + 피지 달러 + FJD + + + 피지 파운드 + FJP + + + 포클랜드제도 파운드 + FKP + + + 페로제도 크로너 + FOK + + + 프랑스 프랑 + FRF + + + 프랑스 프랑 제르미날/프랑 포앙카레 + FRG + + + 가봉 CFA 프랑 + GAF + + + 영국령 파운드 스털링 + £ + + + 그루지야 지폐 라리트 + GEK + + + 그루지야 라리 + GEL + + + 가나 시디 + GHC + + + 가나 구권 시디 + GHO + + + 가나 파운드 + GHP + + + 가나 재평가 시디 + GHR + + + 지브롤터 파운드 + GIP + + + 그린란드 크로네 + GLK + + + 감비아 달라시 + GMD + + + 감비아 파운드 + GMP + + + 기니 프랑 + GNF + + + 기니 프랑 (1960-1972) + GNI + + + 기니 시리 + GNS + + + 과달로프 프랑 + GPF + + + 적도 기니 프랑 + GQF + + + 적도 기니 페세타 기니아나 + GQP + + + 그리스 드라크마 + GRD + + + 그리스 신권 드라크마 + GRN + + + 과테말라 케트살 + GTQ + + + 프랑스령 가이아나 프랑 기아나 + GUF + + + 포르투갈령 기니 에스쿠도 + GWE + + + 포르투갈령 기니 밀 레이스 + GWM + + + 기네비쏘 페소 + GWP + + + 가이아나 달러 + GYD + + + 홍콩 달러 + HKD + + + 온두라스 렘피라 + HNL + + + 크로아티아 디나르 + HRD + + + 크로아티아 쿠나 + HRK + + + 하이티 구르드 + HTG + + + 헝가리 포린트 + HUF + + + 북부 아일랜드 파운드 + IBP + + + 인도네시아 니카 길더 + IDG + + + 인도네시아 자바 루피아 + IDJ + + + 인도네시아 신권 루피아 + IDN + + + 인도네시아 루피아 + IDR + + + 아일랜드 파운드 + IR£ + + + 이스라엘 세켈 + ILL + + + 이스라엘 파운드 + ILP + + + 이스라엘 신권 세켈 + ILS + + + 맨도 파운드 스털링 + IMP + + + 인도 루피 + =0#Rs.|1#Re.|1<Rs. + + + 이라크 디나르 + IQD + + + 이란 리얄 + IRR + + + 아이슬란드 크로나 + ISK + + + 이탈리아 리라 + ITL + + + 저지 파운드 스털링 + JEP + + + 자메이카 달러 + JMD + + + 자메이카 파운드 + JMP + + + 요르단 디나르 + JOD + + + 일본 엔화 + + + + 케냐 실링 + KES + + + 키르기스스탄 솜 + KGS + + + 캄보디아 구권 리얄 + KHO + + + 캄보디아 리얄 + KHR + + + 키리바시 달러 + KID + + + 코모르 프랑 + KMF + + + 조선 민주주의 인민 공화국 원 + KPP + + + 조선 민주주의 인민 공화국 원 + KPW + + + 대한민국 환 + KRH + + + 대한민국 구권 원 + KRO + + + 대한민국 원 + + + + 쿠웨이트 디나르 + KWD + + + 케이맨 제도 달러 + KYD + + + 카자흐스탄 루블 + KZR + + + 카자흐스탄 텐게 + KZT + + + 라오스 키프 + LAK + + + 레바논 파운드 + LBP + + + 리히텐슈타인 프랑 + LIF + + + 스리랑카 루피 + LKR + + + 실론 루피 + LNR + + + 라이베리아 달러 + LRD + + + 레소토 로티 + LSL + + + 리투아니아 리타 + LTL + + + 룩셈부르크 타로나 + LTT + + + 룩셈부르크 프랑 + LUF + + + 라트비아 라트 + LVL + + + 라트비아 루블 + LVR + + + 리비아 영국 군사령 리라 + LYB + + + 리비아 디나르 + LYD + + + 리비아 파운드 + LYP + + + 모로코 디렘 + MAD + + + 모로코 프랑 + MAF + + + 모나코 프랑 누보 + MCF + + + 모나코 프랑 제르미날 + MCG + + + 몰도바 레이 지폐 + MDC + + + 몰도바 레이 + MDL + + + 몰도바 루블 지폐 + MDR + + + 마다가스카르 아리아리 + MGA + + + 마다가스카르 프랑 + MGF + + + 마샬 군도 달러 + MHD + + + 마케도니아 디나르 + MKD + + + 마케도니아 디나르 (1992-1993) + MKN + + + 말리 프랑 + MLF + + + 미얀마 키얏 + MMK + + + 미얀마 달러 태환권 + MMX + + + 몽골 투그릭 + MNT + + + 마카오 파타카 + MOP + + + 말티니크 프랑 + MQF + + + 모리타니 우기야 + UM + + + 몰타 리라 + MTL + + + 몰타 파운드 + MTP + + + 모리셔스 루피 + MUR + + + 몰디브 제도 루피 + MVP + + + 몰디브 제도 루피아 + MVR + + + 말라위 콰쳐 + MWK + + + 말라위 파운드 + MWP + + + 멕시코 페소 + MXN + + + 멕시코 실버 페소 (1861-1992) + MXP + + + 말레이지아 링기트 + MYR + + + 모잠비크 에스쿠도 + MZE + + + 모잠비크 메티칼 + MZM + + + 나미비아 달러 + NAD + + + 뉴 칼레도니아 프랑 제르미날 + NCF + + + 니제르 나이라 + NGN + + + 니제르 파운드 + NGP + + + 니카라과 코르도바 + NIC + + + 니카라과 코르도바 오로 + NIO + + + 네델란드 길더 + NLG + + + 노르웨이 크로네 + NOK + + + 네팔 루피 + NPR + + + 뉴질랜드 달러 + NZD + + + 뉴질랜드 파운드 + NZP + + + 오만 리얄 + OMR + + + 오만 리얄 사이디 + OMS + + + 파나마 발보아 + PAB + + + 페루 인티 + PEI + + + 페루 솔 누에보 + PEN + + + 페루 솔 + PES + + + 파푸아뉴기니 키나 + PGK + + + 필리핀 페소 + PHP + + + 파키스탄 루피 + PKR + + + 폴란드 즐로티 + PLN + + + 폴란드 즐로티 (1950-1995) + PLZ + + + 팔레스타인 파운드 + PSP + + + 포르투갈 콘토 + PTC + + + 포르투갈 에스쿠도 + PTE + + + 파라과이 과라니 + PYG + + + 카타르 리얄 + QAR + + + 리유니온 프랑 + REF + + + 루마니아 레이 + ROL + + + 루마니아 신권 레이 + RON + + + 러시아 루블 + RUB + + + 러시아 루블 (1991-1998) + RUR + + + 르완다 프랑 + RWF + + + 사우디아라비아 리얄 + SAR + + + 사우디아라비아 자치령 리얄 + SAS + + + 솔로몬 제도 달러 + SBD + + + 세이쉴 루피 + SCR + + + 수단 디나르 + SDD + + + 수단 파운드 + SDP + + + 스웨덴 크로나 + SEK + + + 싱가폴 달러 + SGD + + + 세인트헬레나 파운드 + SHP + + + 슬로베니아 톨라르 본스 + SIB + + + 슬로베니아 톨라르 + SIT + + + 슬로바키아 코루나 + SKK + + + 시에라리온 리온 + SLL + + + 산마리노 리라 + SML + + + 소말리아 실링 + SOS + + + 소말리랜드 실링 + SQS + + + 수리남 길더 + SRG + + + 스코틀랜드 파운드 + SSP + + + 상투메 프린시페 도브라 + STD + + + 상투메 프린시페 에스쿠도 + STE + + + 소련 신권 루블 + SUN + + + 소련 루블 + SUR + + + 엘살바도르 콜론 + SVC + + + 시리아 파운드 + SYP + + + 스와질란드 릴랑게니 + SZL + + + 터크스케이커스 크라운 + TCC + + + 태국 바트 + THB + + + 타지키스탄 루블 + TJR + + + 타지키스탄 소모니 + TJS + + + 투르크메니스탄 마나트 + TMM + + + 튀니지 디나르 + TND + + + 통가 파운드 스털링 + TOS + + + 티모르 에스쿠도 + TPE + + + 티모르 파타카 + TPP + + + 터기 리라 + TRL + + + 트리니다드 토바고 달러 + TTD + + + 트리니다드 토바고 구권 달러 + TTO + + + 투발루 달러 + TVD + + + 대만 신권 달러 + TWD + + + 탄자니아 실링 + TZS + + + 우크라이나 그리브나 + UAH + + + 우간다 실링 (1966-1987) + UGS + + + 우간다 실링 + UGX + + + 미국 달러 + US$ + + + 우루과이 페소 푸에르떼 + UYF + + + 우루과이 페소 (1975-1993) + UYP + + + 우루과이 페소 우루과요 + UYU + + + 우즈베키스탄 숨 쿠폰 + UZC + + + 우즈베키스탄 숨 + UZS + + + 바티칸 리라 + VAL + + + 북 베트남 피아스타 동 베트 + VDD + + + 북 베트남 신권 동 + VDN + + + 북 베트남 베트민 피아스타 동 베트 + VDP + + + 베네주엘라 볼리바르 + VEB + + + 영국령 버진 아일랜드 달러 + VGD + + + 베트남 동 + VND + + + 베트남 신권 동 + VNN + + + 베트남 공화국 동 + VNR + + + 베트남 국영 동 + VNS + + + 바누아투 바투 + VUV + + + 서 사모아 파운드 + WSP + + + 서 사모아 탈라 + WST + + + 아시아 디나르 계산 단위 + XAD + + + 아시아 기금 연합 통화 단위 + XAM + + + + XAU + + + 동카리브 달러 + XCD + + + 특별인출권 + XDR + + + 유럽 환율 단위 + XEU + + + 프랑스 Gold 프랑 + XFO + + + 프랑스 UIC-프랑 + XFU + + + 이슬람 디나르 + XID + + + 프랑스 메트로폴리탄 누보 프랑 + XMF + + + 예멘 디나르 + YDD + + + 예멘 리알 + YER + + + 유고슬라비아 동전 디나르 + YUD + + + 유고슬라비아 연합 디나르 + YUF + + + 유고슬라비아 1994 디나르 + YUG + + + 유고슬라비아 노비 디나르 + YUM + + + 유고슬라비아 전환 디나르 + YUN + + + 유고슬라비아 10월 디나르 + YUO + + + 유고슬라비아 개량 디나르 + YUR + + + 남아프리카 랜드 (금융) + ZAL + + + 남아프리카 파운드 + ZAP + + + 남아프리카 랜드 + ZAR + + + 쟘비아 콰쳐 + ZMK + + + 쟘비아 파운드 + ZMP + + + 자이르 신권 자이르 + ZRN + + + 자이르 자이르 + ZRZ + + + 짐비브웨 달러 + ZWD + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kw_GB.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kw_GB.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kw_GB.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kw_GB.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + dd/MM/yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/kw.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/kw.xml --- zope3-3.4.0/src/zope/i18n/locales/data/kw.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/kw.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,88 @@ + + + + + + + + + + + kernewek + + + Rywvaneth Unys + + + + [a-z] + + + + + + + + Gen + Whe + Mer + Ebr + Me + Efn + Gor + Est + Gwn + Hed + Du + Kev + + + Mys Genver + Mys Whevrel + Mys Merth + Mys Ebrel + Mys Me + Mys Efan + Mys Gortheren + Mye Est + Mys Gwyngala + Mys Hedra + Mys Du + Mys Kevardhu + + + + + + + Sul + Lun + Mth + Mhr + Yow + Gwe + Sad + + + De Sul + De Lun + De Merth + De Merher + De Yow + De Gwener + De Sadorn + + + + a.m. + p.m. + + + RC + AD + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ky_KG.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ky_KG.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ky_KG.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ky_KG.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ky.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ky.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ky.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ky.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,42 @@ + + + + + + + + + + + Кыргыз + + + Кыргызстан + + + + [а-я і є ї ґ] + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + KGS + сом + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/license.html zope3-3.5~bzr18/src/zope/i18n/locales/data/license.html --- zope3-3.4.0/src/zope/i18n/locales/data/license.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/license.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,49 @@ + + + +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + + + +

UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE

+
COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed
+under the Terms of Use in http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, and/or sell copies of the Data
+Files or Software, and to permit persons to whom the Data Files or
+Software are furnished to do so, provided that (a) the above copyright
+notice(s) and this permission notice appear with all copies of the Data
+Files or Software, (b) both the above copyright notice(s) and this
+permission notice appear in associated documentation, and (c) there is
+clear notice in each modified Data File or in the Software as well as
+in the documentation associated with the Data File(s) or Software that
+the data or software has been modified.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or other
+dealings in these Data Files or Software without prior written authorization
+of the copyright holder.
+
+Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be
+registered in some jurisdictions. All other trademarks and registered
+trademarks mentioned herein are the property of their respective owners.
+
+ + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/lt_LT.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/lt_LT.xml --- zope3-3.4.0/src/zope/i18n/locales/data/lt_LT.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/lt_LT.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + ##,##0.##;-##,##0.## + + + + + + + #E0 + + + + + + + #,##% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/lt.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/lt.xml --- zope3-3.4.0/src/zope/i18n/locales/data/lt.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/lt.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,435 @@ + + + + + + + + + + + Arabų + Bulgarų + Čekijos + Danų + Vokiečių + Graikų + Anglų + Ispanų + Estų + Suomių + Prancūzų + Hebrajų + Kroatų + Vengrų + Italų + Japonų + Korėjiečių + Lietuvių + Latvių + Olandų + Norvegų + Lenkų + Portugalų + Rumunų + Rusų + Slovakų + Slovėnų + Švedų + Turkų + Kinų + + + Andora + Jungtiniai Arabų Emyratai + Afganistanas + Antigva ir Barbuda + Angvila + Albanija + Armėnija + Nyderlandų Antilai + Angola + Antarktika + Argentina + Amerikos Samoa + Austrija + Australija + Aruba + Azerbaidžanas + Bosnija ir Hercegovina + Barbadosas + Bangladešas + Belgija + Burkina Fasas + Bulgarija + Bahreinas + Burundis + Beninas + Bermudai + Brunėjus + Bolivija + Brazilija + Bahamai + Butanas + Bouvet sala + Botsvana + Baltarusija + Belizas + Kanada + Kokosų salos + Kongo Demokratinė Respublika + Centrinės Afrikos Respublika + Kongas + Šveicarija + Kot d’Ivuar + Kuko salos + Čilė + Kamerūnas + Kinija + Kolumbija + Kosta Rika + Kuba + Žaliojo Kyšulio salos + Kalėdų sala + Kipras + Čekijos respublika + Vokietija + Džibutis + Danija + Dominika + Dominikos Respublika + Alžyras + Ekvadoras + Estija + Egiptas + Vakarų Sachara + Eritrėja + Ispanija + Etiopija + Suomija + Fidžis + Folklendo salos (Malvinai) + Mikronezijos Federacinės Valstijos + Farerų salos + Prancūzija + en + Gabonas + Didžioji Britanija + Grenada + Gruzija + Prancūzijos Gviana + Gana + Gibraltaras + Grenlandija + Gambija + Gvinėja + Gvadelupė + Ekvatorinė Gvinėja + Graikija + Rytų Džordžija ir Rytų Sandwich salos + Gvatemala + Guamas + Gvinėja-Bisau + Gviana + Kinijos S.A.R.Honkongas + Heard ir McDonald salos + Hondūras + Kroatija + Haitis + Vengrija + Indonezija + Airija + Izraelis + Indija + Britų Indijos vandenyno teritorija + Irakas + Irano + Islandija + Italija + Jamaika + Jordanija + Japonija + Kenija + Kirgiztanas + Kambodža + Kiribatis + Komorai + Sent Kitsas ir Nevis + Šiaurės Korėja + Pietų Korėja + Kuveitas + Kaimanų salos + Kazachstanas + Laoso Liaudies Demokratinė Respublika + Libanas + Šventoji Liucija + Lichtenšteinas + Šri Lanka + Liberija + Lesotas + Lietuva + Liuksemburgas + Latvija + Libijos Arabų Džamahirija + Marokas + Monakas + Moldovos Respublika + Madagaskaras + Maršalo salos + Makedonijos Respublika + Malis + Mjanma + Mongolija + Kinijos S.A.R. Makao + Šiaurinės Marianos salos + Martinika + Mauritanija + Montserat + Malta + Mauricijus + Maldivai + Malavis + Meksika + Malaizija + Mozambikas + Namibija + Naujoji Kaledonija + Nigeris + Norfolko sala + Nigerija + Nikaragva + Nyderlandai + Norvegija + Nepalas + Nauru + Niujė + Naujoji Zelandija + Omanas + Panama + Peru + Prancūzų Polinezija + Papua Naujoji Gvinėja + Filipinai + Pakistanas + Lenkija + Sen Pjeras ir Mikelonas + Pitkernas + Puerto Rikas + Palestinos teritorija + Portugalija + Palau + Paragvajus + Kataras + Rejunjonas + Rumunija + Rusijos Federacija + Ruanda + Saudo Arabija + Saliamono salos + Seišeliai + Sudanas + Švedija + Singapūras + Šventoji Elena + Slovėnija + Svalbardo ir Jan Majen salos + Slovakia + Siera Leonė + San Marinas + Senegalas + Somalis + Serbia + Surinamas + San Tomė ir Principė + El Salvadoras + Sirija + Svazilendas + Turks ir Kaikos salos + Čadas + Prancūzų pietinės teritorijos + Togas + Tailandas + Tadžikija + Tokelau + Rytų Timoras + Turkmėnistanas + Tunisas + Tonga + Turkija + Trinidadas ir Tobagas + Tuvalu + Taivanis, Kinijos provincija + Tanzanija + Ukraina + Uganda + JAV antraeilės teritorijos salos + United States + Urugvajus + Uzbekija + Vatikano Miesto Valstija + Šventasis Vincentas ir Grenadinai + Venesuela + Britų Virginijos salos + JAV Virginijos salos + Vietnamas + Vanuatu + Valiso ir Futuna salos + Samoa + Jemenas + Majotė + Jugoslavija + Pietų Afrika + Zambija + Zimbabvė + + + + [a-z ą ę į ų ė ū č š ž] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + Sau + Vas + Kov + Bal + Geg + Bir + Lie + Rgp + Rgs + Spa + Lap + Grd + + + Sausio + Vasario + Kovo + Balandžio + Gegužės + Birželio + Liepos + Rugpjūčio + Rugsėjo + Spalio + Lapkričio + Gruodžio + + + + + + + Sk + Pr + An + Tr + Kt + Pn + Št + + + Sekmadienis + Pirmadienis + Antradienis + Trečiadienis + Ketvirtadienis + Penktadienis + Šeštadienis + + + + + + + + + + pr.Kr. + po.Kr. + + + + + + + yyyy 'm.' MMMM d 'd.',EEEE + + + + + yyyy 'm.' MMMM d 'd.' + + + + + yyyy.MM.dd + + + + + yyyy.MM.dd + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + LTL + Lt + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/lv_LV.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/lv_LV.xml --- zope3-3.4.0/src/zope/i18n/locales/data/lv_LV.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/lv_LV.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/lv.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/lv.xml --- zope3-3.4.0/src/zope/i18n/locales/data/lv.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/lv.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,435 @@ + + + + + + + + + + + arābu + bulgāru + čehu + dāņu + vācu + grieķu + angļu + spāņu + igauņu + somu + franču + ivrits + horvātu + ungāru + itāliešu + japāņu + korejiešu + lietuviešu + latviešu + holandiešu + norvēģu + poļu + portugāļu + rumāņu + krievu + slovāku + slovēņu + zviedru + turku + ķīniešu + + + Andora + Apvienotie Arābu Emirāti + Afganistāna + Antigva un Barbuda + Angilja + Albānija + Armēnija + Antiļas + Angola + Antarktika + Argentīna + Amerikāņu Samoa + Austrija + Austrālija + Aruba + Azerbaidžāna + Bosnija un Hercegovina + Barbadosa + Bangladeša + Beļģija + Burkinafaso + Bulgārija + Bahreina + Burundi + Benina + Bermudu salas + Bruneja + Bolīvija + Brazīlija + Bahamas + Butāna + Buvē sala + Botsvāna + Baltkrievija + Beliza + Kanāda + Kokosu (Kīlinga) salas + Kongo Demokrātiskā Republika + Centrālāfrikas Republika + Kongo + Šveice + Kotdivuāra + Kuka salas + Čīle + Kamerūna + Ķīna + Kolumbija + Kostarika + Kuba + Kaboverde + Ziemsvētku sala + Kipra + Čehija + Vācija + Džibutija + Dānija + Dominika + Dominikānas Republika + Alžīrija + Ekvadora + Igaunija + Ēģipte + Rietumsahāra + Eritreja + Spānija + Etiopija + Somija + Fidži + Folklenda salas + Mikronēzijas Federatīvās Valstis + Farēru salas + Francija + en + Gabona + Lielbritānija + Grenāda + Gruzija + Franču Gviāna + Gana + Gibraltārs + Grenlande + Gambija + Gvineja + Gvadelupa + Ekvatoriālā Gvineja + Grieķija + Dienviddžordžija un Dienvidsendviču salas + Gvatemala + Guama + Gvineja-Bisava + Gajāna + Honkonga, Ķīnas īpašās pārvaldes apgabals + Hērda un Makdonalda salas + Hondurasa + Horvātija + Haiti + Ungārija + Indonēzija + Īrija + Izraēla + Indija + Britu Indijas okeāna teritorija + Irāka + Irāna + Islande + Itālija + Jamaika + Jordānija + Japāna + Kenija + Kirgīzija + Kambodža + Kiribati + Komoru salas + Sentkitsa un Nevisa + Ziemeļkoreja + Dienvidkoreja + Kuveita + Kaimanu salas + Kazahstāna + Laosa + Libāna + Sentlūsija + Lihtenšteina + Šrilanka + Libērija + Lesoto + Lietuva + Luksemburga + Latvija + Lībija + Maroka + Monako + Moldova + Madagaskara + Māršala salas + Maķedonija + Mali + Mjanma + Mongolija + Makao, Ķīnas īpašās pārvaldes apgabals + Ziemeļu Marianas + Martinika + Mauritānija + Montserrata + Malta + Maurīcija + Maldīvija + Malāvija + Meksika + Malaizija + Mozambika + Namībija + Jaunkaledonija + Nigēra + Norfolka + Nigērija + Nikaragva + Nīderlande + Norvēģija + Nepāla + Nauru + Niue + Jaunzēlande + Omāna + Panama + Peru + Franču Polinēzija + Papua-Jaungvineja + Filipīnas + Pakistāna + Polija + Senpjēra un Mikelona + Pitkērna + Puertoriko + Palestīniešu pašpārvaldes teritorija + Portugāle + Palau + Paragvaja + Katara + Reinjona + Rumānija + Krievija + Ruanda + Saūda Arābija + Zālamana salas + Seišeļu salas + Sudāna + Zviedrija + Singapūra + Sv. Helēnas sala + Slovēnija + Svalbāra un Jana Majena sala + Slovākija + Sjerraleone + Sanmarīno + Senegāla + Somālija + Serbia + Surinama + Santome un Prinsipi + Salvadora + Sīrija + Svazilenda + Tērksas un Kaikosas salas + Čada + Franču dienvidu teritorijas + Togo + Taizeme + Tadžikistāna + Tokelau + Austrumtimora + Turkmenistāna + Tunisija + Tonga + Turcija + Trinidāda un Tobāgo + Tuvalu + Taivāna, Ķīnas province + Tanzānija + Ukraina + Uganda + ASV mazās aizjūras teritorijas + United States + Urugvaja + Uzbekistāna + Vatikāns + Sentvinsenta un Grenadīnas + Venecuēla + Britu Virdžīnu salas + Amerikāņu Virdžīnu salas + Vjetnama + Vanuatu + Volisa un Futuna + Samoa + Jemena + Majota + Dienvidslāvija + Dienvidāfrika + Zambija + Zimbabve + + + + [a-z ā ē ī ō ū ģ ķ ļ ņ ŗ č š ž] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + Jan + Feb + Mar + Apr + Mai + Jūn + Jūl + Aug + Sep + Okt + Nov + Dec + + + janvāris + februāris + marts + aprīlis + maijs + jūnijs + jūlijs + augusts + septembris + oktobris + novembris + decembris + + + + + + + Sv + P + O + T + C + Pk + S + + + svētdiena + pirmdiena + otrdiena + trešdiena + ceturtdiena + piektdiena + sestdiena + + + + + + + + + + pmē + + + + + + + + EEEE, yyyy'. gada 'd. MMMM + + + + + yyyy'. gada 'd. MMMM + + + + + yyyy.d.M + + + + + yy.d.M + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + LVL + Ls + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/mk_MK.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/mk_MK.xml --- zope3-3.4.0/src/zope/i18n/locales/data/mk_MK.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/mk_MK.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;(#,##0.###) + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0.00;-¤ #,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/mk.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/mk.xml --- zope3-3.4.0/src/zope/i18n/locales/data/mk.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/mk.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,166 @@ + + + + + + + + + + + македонски + + + Македонија + + + + [а-и к-ш ѐ ѓ ѕ ј љ њ ќ ѝ џ] + + + GuMtkHmsSEDFwWahKzUeygAZ + + + + + + јан. + фев. + мар. + апр. + мај. + јун. + јул. + авг. + септ. + окт. + ноем. + декем. + + + јануари + февруари + март + април + мај + јуни + јули + август + септември + октомври + ноември + декември + + + + + + + нед. + пон. + вт. + сре. + чет. + пет. + саб. + + + недела + понеделник + вторник + среда + четврток + петок + сабота + + + + + + + + + + пр.н.е. + ае. + + + + + + + EEEE, dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd.M.yyyy + + + + + dd.M.yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + MKD + Den + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/mn_MN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/mn_MN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/mn_MN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/mn_MN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,18 @@ + + + + + + + + + + + + + MNT + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/mn.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/mn.xml --- zope3-3.4.0/src/zope/i18n/locales/data/mn.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/mn.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,36 @@ + + + + + + + + + + + Монгол хэл + + + Монгол улс + + + + [а-яієїґү] + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/mr_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/mr_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/mr_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/mr_IN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-M-yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/mr.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/mr.xml --- zope3-3.4.0/src/zope/i18n/locales/data/mr.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/mr.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,242 @@ + + + + + + + + + + + अफार + अबखेजियन + अफ्रिकान्स + अमहारिक + अरेबिक + असामी + ऐमरा + अज़रबाइजानी + बष्किर + बैलोरुसियन + बल्गेरियन + बीहारी + बिसलमा + बंगाली + तिबेटियन + ब्रेटन + कटलन + कोर्सिकन + ज़ेक + वेल्ष + डानिष + जर्मन + भूटानी + ग्रीक + इंग्रेजी + इस्परान्टो + स्पानिष + इस्टोनियन् + बास्क + पर्षियन् + फिन्निष + फिजी + फेरोस् + फ्रेन्च + फ्रिसियन् + ऐरिष + स्काटस् गेलिक + गेलीशियन + गौरानी + गुजराती + हौसा + हेबृ + हिन्दी + क्रोयेषियन् + हंगेरियन् + आर्मीनियन् + इन्टरलिंग्वा + इन्डोनेषियन + इन्टरलिंग + इनूपियाक + आईसलान्डिक + इटालियन + इनुकिटुट् + जापनीस् + जावनीस् + जार्जियन् + कज़क + ग्रीनलान्डिक + कंबोडियन + कन्नड + कोरियन् + कोंकणी + कश्मीरी + कुर्दिष + किर्गिज़ + लाटिन + लिंगाला + लाओतियन् + लिथुआनियन् + लाट्वियन् (लेट्टिष) + मलागसी + माओरी + मसीडोनियन् + मलियालम + मंगोलियन् + मोल्डावियन् + मराठी + मलय + मालतीस् + बर्मीस् + नौरो + नेपाली + डच + नोर्वेजियन + ओसिटान् + ओरोमो (अफान) + ओरिया + पंजाबी + पोलिष + पष्टो (पुष्टो) + पोर्चुगीस् + क्वेचओ + रहटो-रोमान्स् + किरुन्दी + रोमानियन् + मराठी + रष्यन् + किन्यार्वान्डा + संस्कृत + सिंधी + सांग्रो + सेर्बो-क्रोयेषियन् + सिन्हलीस् + स्लोवाक + स्लोवेनियन् + समोन + शोना + सोमाली + आल्बेनियन् + सेर्बियन् + सिस्वती + सेसोथो + सुंदनीस् + स्वीडिष + स्वाहिली + तमिळ + तेलंगू + तजिक + थाई + तिग्रिन्या + तुर्कमेन + तगालोग + सेत्स्वाना + तोंगा + तुर्किष + त्सोगा + टटार + त्वि + उधूर + युक्रेनियन् + उर्दू + उज़बेक + वियत्नामीज़ + ओलापुक + उलोफ + क्स्होसा + इद्दिष + यूरुबा + झ्हुन्ग + चिनीस् + जुलू + + + भारत + + + + [[:Deva:]-[क़-य़]‌‍] + + + + + + + + जानेवारी + फेबृवारी + मार्च + एप्रिल + मे + जून + जुलै + ओगस्ट + सेप्टेंबर + ओक्टोबर + नोव्हेंबर + डिसेंबर + + + जानेवारी + फेबृवारी + मार्च + एप्रिल + मे + जून + जुलै + ओगस्ट + सेप्टेंबर + ओक्टोबर + नोव्हेंबर + डिसेंबर + + + + + + + रवि + सोम + मंगळ + बुध + गुरु + शुक्र + शनि + + + रविवार + सोमवार + मंगळवार + बुधवार + गुरुवार + शुक्रवार + शनिवार + + + + म.पू. + म.नं. + + + + + + भारतीय समय + भारतीय समय + + + IST + IST + + + + + + + + INR + रु + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ms_BN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ms_BN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ms_BN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ms_BN.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yyyy + + + + + + + + h:mm:ss aa + + + + + H:mm:ss + + + + + H:mm:ss + + + + + H:mm:ss + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + BND + $ + + + USD + US$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ms_MY.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ms_MY.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ms_MY.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ms_MY.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE dd MMM yyyy + + + + + dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + dd/MM/yyyy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ms.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ms.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ms.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ms.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,328 @@ + + + + + + + + + + + Bahasa Melayu + + + Andorra + Emiriah Arab Bersatu + Afghanistan + Antigua dan Barbuda + Anguilla + Albania + Armenia + Netherlands Antilles + Angola + Antarctica + Argentina + American Samoa + Austria + Australia + Aruba + Azerbaijan + Bosnia dan Herzegovina + Barbados + Bangladesh + Belgium + Burkina Faso + Bulgaria + Bahrain + Burundi + Benin + Bermuda + Brunei + Bolivia + Brazil + Bahamas + Bhutan + Bouvet Island + Botswana + Belarus + Belize + Kanada + Cocos (Keeling) Islands + Democratic Republic of the Congo + Republik Afrika Tengah + Congo + Switzerland + Pantai Gading + Cook Islands + Cile + Kamerun + Cina + Colombia + Kosta Rika + Cuba + Cape Verde + Christmas Island + Kibris + Republik Czech + Jerman + Jibouti + Denmark + Dominica + Republik Dominican + Aljazair + Ecuador + Estonia + Mesir + Sahara Barat + Eritrea + Sepanyol + Ethiopia + Finland + Fiji + Falkland Islands + Micronesia + Faroe Islands + Perancis + en + Gabon + United Kingdom + Grenada + Georgia + French Guiana + Ghana + Gibraltar + Greenland + Gambia + Guinea + Guadeloupe + Equatorial Guinea + Yunani + South Georgia and the South Sandwich Islands + Guatemala + Guam + Guinea Bissau + Guyana + Hong Kong S.A.R., China + Heard Island and McDonald Islands + Honduras + Croatia + Haiti + Hungari + Indonesia + Ireland + Israel + Hindia + British Indian Ocean Territory + Iraq + Iran + Iceland + Itali + Jamaika + Jordan + Jepun + Kenya + Kyrgyzstan + Kemboja + Kiribati + Comoros + Saint Kitts dan Nevis + Utara Korea + Selatan Korea + Kuwait + Cayman Islands + Kazakhstan + Laos + Lubnan + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Lithuania + Luksembourg + Latvia + Libya + Maghribi + Monaco + Moldova + Madagaskar + Kepulauan Marshall + Macedonia + Mali + Myanmar + Mongolia + Macao S.A.R., China + Northern Mariana Islands + Martinique + Mauritania + Montserrat + Malta + Mauritius + Maldiv + Malawi + Meksiko + Malaysia + Mozambik + Namibia + New Caledonia + Niger + Norfolk Island + Nigeria + Nicaragua + Belanda + Norway + Nepal + Nauru + Niue + New Zealand + Oman + Panama + Peru + French Polynesia + Papua New Guinea + Filipina + Pakistan + Poland + Saint Pierre and Miquelon + Pitcairn + Puerto Rico + Palestinian Territory + Feringgi + Palau + Paraguay + Qatar + Réunion + Romania + Russia + Rwanda + Arab Saudi + Kepulauan Solomon + Seychelles + Sudan + Sweden + Singapura + Saint Helena + Slovenia + Svalbard and Jan Mayen + Slovakia + Siera Leon + San Marino + Senegal + Somalia + Serbia + Surinam + Sao Tome dan Principe + El Salvador + Syria + Swaziland + Turks and Caicos Islands + Cad + French Southern Territories + Togo + Thailand + Tadjikistan + Tokelau + Timor-Leste + Turkmenistan + Tunisia + Tonga + Turki + Trinidad dan Tobago + Tuvalu + Taiwan + Tanzania + Ukraine + Uganda + United States Minor Outlying Islands + Amerika Syarikat + Uruguay + Uzbekistan + Vatican + Saint Vincent dan Grenadines + Venezuela + British Virgin Islands + U.S. Virgin Islands + Vietnam + Vanuatu + Wallis and Futuna + Samoa + Yaman + Mayotte + Yugoslavia + Afrika Selatan + Zambia + Zimbabwe + + + + [a-z] + + + + + + + + Jan + Feb + Mac + Apr + Mei + Jun + Jul + Ogos + Sep + Okt + Nov + Dis + + + Januari + Februari + Mac + April + Mei + Jun + Julai + Ogos + September + Oktober + November + Disember + + + + + + + Ahd + Isn + Sel + Rab + Kha + Jum + Sab + + + Ahad + Isnin + Selasa + Rabu + Khamis + Jumaat + Sabtu + + + + + + + + + + Ringgit Malaysia + RM + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/mt_MT.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/mt_MT.xml --- zope3-3.4.0/src/zope/i18n/locales/data/mt_MT.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/mt_MT.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/mt.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/mt.xml --- zope3-3.4.0/src/zope/i18n/locales/data/mt.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/mt.xml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,873 @@ + + + + + + + + + + + en + Afar + Abkażjan + Aċiniż + Akoli + Adangme + Adyghe + Avestan + Afrikans + Afro-Asjatiku (Oħra) + Afriħili + Akan + Akkadjen + Aleut + Lingwi Algonqwinjani + Amħariku + Aragonese + Ingliż, Antik (ca.450-1100) + Lingwi Apaċi + Għarbi + Aramajk + Arawkanjan + Arapaħo + Artifiċjali (Oħra) + Arawak + Assamese + Asturian + Lingwi Atabaskani + Lingwi Awstraljani + Avarik + Awadħi + Ajmara + Ażerbajġani + Baxkir + Banda + Lingwi Bamileke + Baluċi + Bambara + Baliniż + Basa + Baltiku (Oħra) + Belarussu + Beja + Bemba + Beber + Bulgaru + Biħari + Bojpuri + Bislama + Bikol + Bini + Siksika + Bambara + Bengali + Bantu + Tibetjan + Brenton + Braj + Bosnijan + Batak + Burjat + Buginiż + Blin + Katalan + Kaddo + Amerika Ċentrali (Oħra) + Karib + Kawkasu (Oħra) + Ċeċen + Sibwano + Keltiku (Oħra) + Ċamorro + Ċibċa + Ċagataj + Ċukese + Mari + Ġargon taċ-Ċinuk + Ċostaw + Ċipewjan + Ċerokij + Xajenn + Lingwi Ċamiki + Korsiku + Koptiku + Kreoli u Piġini, Bbażat fuq l-Ingliż (Oħra) + Kreoli u Piġini, Bbażat fuq il-Franċiż (Oħra) + Creoles and pidgins, Portuguese-based (Other) + Krij + Crimean Turkish; Crimean Tatar + Kreoli u Piġini (Oħra) + Ċek + Kashubian + Slaviku tal-Knisja + Kuxtiku (Oħra) + Ċuvax + Welx + Daniż + Dakota + Dargwa + Dajak + Ġermaniż + Delawerjan + Slav + Dogrib + Dinka + Dogri + Dravidjan (Oħra) + Lower Sorbian + Dwala + Olandiż, Medjevali (ca. 1050-1350) + Diveħi + Djula + Dżongka + Ewe + Efik + Eġizzjan (Antik) + Ekajuk + Grieg + Elamit + Ingliż + Ingliż, Medjevali (1100-1500) + Esperanto + Spanjol + Estonjan + Bask + Ewondo + Persjan + Fang + Fanti + Fulaħ + Finlandiż + Finno - Ugrijan + Fiġi + Fawriż + Fon + Franċiż + Franċiż, Medjevali (ca. 1400-1600) + Franċiż, Antik (842-ca. 1400) + Frijuljan + Friżjan + Irlandiż + Ga + Gajo + Gbaja + Galliku Skoċċiż + Ġermaniku (Oħra) + Geez + Gilbertjan + Gallegjan + Ġermaniku, Medjevali Pulit (ca. 1050-1500) + Gwarani + Ġermaniku, Antik Pulit (ca. 750-1050) + Gondi + Gorontalo + Gotiku + Ġerbo + Grieg, Antik (to 1453) + Guġarati + Manks + Gwiċin + Ħawsa + Ħajda + Ħawajjan + Ebrajk + Ħindi + Hiligaynon + Ħimaċali + Ħittit + Ħmong + Ħiri Motu + Kroat + Upper Sorbian + Haitian + Ungeriż + Ħupa + Armenjan + Ħerero + Interlingua + Iban + Indoneżjan + Interlingue + Igbo + Sichuan Yi + Iġo + Inupjak + Iloko + Indjan (Oħra) + Indo-Ewropew + Ingush + Ido + Iranjan + Lingwi Irogwjani + Iżlandiż + Taljan + Inukitut + Ġappuniż + Lojban + Lhudi-Persjan + Lhudi-Għarbi + Ġavaniż + Ġorġjan + Kara-Kalpak + Kabuljan + Kaċin + Kamba + Karen + Kawi + Kabardian + Kongo + Kasi + Kojsan + Kotaniż + Kikuju + Kuanyama + Każak + Kalallisut + Kmer + Kimbundu + Kannada + Korejan + Konkani + Kosrejan + Kpelle + Kanuri + Karachay-Balkar + Kru + Kurusk + Kaxmiri + Kurdiż + Kumiku + Kutenaj + Komi + Korniku + Kirgiż + Latin + Ladino + Landa + Lamba + Letżburgiż + Leżgjan + Ganda + Limburgish + Lingaljan + Lao + Mongo + Lożi + Litwanjan + Luba-Katanga + Luba-Luluwa + Luwisinuż + Lunda + Luwa + Luxaj + Latvjan (Lettix) + Maduriż + Magaħi + Majtili + Makasar + Mandingwan + Awstronesjan + Masaj + Moksha + Mandar + Mende + Malagażi + Irlandiż, Medjevali (900-1200) + Marxall + Maori + Mikmek + Minangkabaw + Lingwi Oħra + Maċedonjan + Mon-Kmer (Oħra) + Malajalam + Mongoljan + Manċurjan + Manipuri + Lingwi Manobo + Moldavjan + Moħak + Mossi + Marati + Malajan + Malti + Lingwi Diversi + Lingwi tal-Munda + Kriek + Marwari + Burmiż + Majan + Erzya + Nawuru + Naħwatil + Indjan tal-Amerika ta' Fuq (Oħra) + Neapolitan + Bokmahal Norveġiż + Ndebele, ta' Fuq + Ġermaniż Komuni; Sassonu Komuni + Nepaliż + Newari + Ndonga + Nijas + Niġerjan - Kordofanjan + Nijuwejan + Olandiż + Ninorsk Norveġiż + Norveġiż + Nogai + Skandinav, Antik + Ndebele, t'Isfel + Soto, ta' Fuq + Lingwi Nubjani + Navaħo + Ċiċewa; Njanġa + Njamweżi + Nyankole + Njoro + Nżima + Provenzal (wara 1500) + Oġibwa + Oromo (Afan) + Orija + Ossettiku + Osaġjan + Tork (Imperu Ottoman) + Lingwi Otomjani + Punġabi + Papwan (Oħra) + Pangasinjan + Paħlavi + Pampamga + Papjamento + Palawjan + Persjan Antik (ca. 600-400 Q.K.) + Filippin (Oħra) + Feniċju + Pali + Pollakk + Ponpejan + Lingwi Prakriti + Provenzal, Antik (sa l-1500) + Paxtun + Portugiż + Keċwa + Raġastani + Rapanwi + Rarotongani + Reto-Romanz + Rundi + Rumen + Romanz (Oħra) + Żingaru + Għerq + Russu + Kinjarwanda + Sanskrit + Sandawe + Jakut + Indjan tal-Amerika t'Isfel (Oħra) + Salixan + Samritan + Saska + Santali + Sardinjan + Skoċċiż + Sindi + Sami ta' Fuq + Selkup + Semitiku + Sango + Irlandiż, Antik (sa l-900) + Lingwa tas-Sinjali + Serbo-Kroat + Xan + Sinħaliż + Sidamo + Lingwi Suwjani + Sino-Tibetjani (Oħra) + Slovakk + Slav + Slavic (Other) + Samojan + Southern Sami + Sami languages (Other) + Lule Sami + Inari Sami + Skolt Sami + Xona + Soninke + Somali + Sogdien + Songaj + Albaniż + Serb + Serer + Swati + Nilo-Saħaram + Soto, t'Isfel + Sundaniż + Sukuma + Susu + Sumerjan + Svediż + Swaħili + Sirjan + Tamil + Tai (Oħra) + Telugu + Timne + Tereno + Tetum + Taġik + Tajlandiż + Tigrinja + Tigre + Tiv + Turkmeni + Tokelau + Tagalog + Tlingit + Tamaxek + Zwana + Tongan (Gżejjer ta' Tonga) + Tonga (Njasa) + Tok Pisin + Tork + Tsonga + Zimxjan + Tatar + Tumbuka + Tupi languages + Altajk (Oħra) + Tuvalu + Twi + Taħitjan + Tuvinjan + Udmurt + Wigur + Ugaritiku + Ukranjan + Umbundu + Indeterminat + Urdu + Użbek + Vai + Venda + Vjetnamiż + Volapuk + Votik + Walloon + Lingwi Wakaxani + Walamo + Waraj + Waxo + Lingwi Sorbjani + Wolof + Kalmyk + Ħoża + Jao + Japese + Jiddix + Joruba + Lingwi Jupiċi + Żwang + Żapotek + Żenaga + Ċiniż + Żande + Żulu + Żuni + + + Andorra + Emirati Għarab Maqgħuda + Afganistan + Antigua and Barbuda + Angwilla + Albanija + Armenja + Antilles Olandiżi + Angola + Antarctica + Arġentina + Samoa Amerikana + Awstrija + Awstralja + Aruba + Ażerbajġan + Bożnija Ħerżegovina + Barbados + Bangladexx + Belġju + Burkina Faso + Bulgarija + Baħrajn + Burundi + Benin + Bermuda + Brunej + Bolivja + Brażil + Baħamas + Butan + Bouvet Island + Botswana + Bjelorussja + Beliże + Kanada + Cocos (Keeling) Islands + Democratic Republic of the Congo + Repubblika Afrikana Ċentrali + Kongo + Svizzera + Kosta ta' l-Avorju + Cook Islands + Ċili + Kamerun + Ċina + Kolumbja + Kosta Rika + Kuba + Kape Verde + Christmas Island + Ċipru + Repubblika Ċeka + Ġermanja + Ġibuti + Danimarka + Dominika + Republikka Domenikana + Alġerija + Ekwador + Estonja + Eġittu + Sahara tal-Punent + Eritreja + Spanja + Etijopja + Finlandja + Fiġi + Falkland Islands + Mikronesja + Gżejjer Faroe + Franza + en + Gabon + Ingilterra + Grenada + Ġorġja + Gujana Franċiża + Gana + Gibraltar + Grinlandja + Gambja + Gineja + Gwadelupe + Ginea Ekwatorjali + Greċja + South Georgia and the South Sandwich Islands + Gwatemala + Gwam + Ginea-Bissaw + Gujana + Ħong Kong S.A.R., Ċina + Heard Island and McDonald Islands + Ħonduras + Kroazja + Ħaiti + Ungerija + Indoneżja + Irlanda + Iżrael + Indja + British Indian Ocean Territory + Iraq + Iran + Islanda + Italja + Ġamajka + Ġordan + Ġappun + Kenja + Kirgistan + Kambodja + Kiribati + Komoros + Saint Kitts and Nevis + Koreja ta' Fuq + Koreja t'Isfel + Kuwajt + Cayman Islands + Każakstan + Laos + Libanu + Saint Lucia + Liechtenstein + Sri Lanka + Liberja + Lesoto + Litwanja + Lussemburgu + Latvja + Libja + Marokk + Monako + Maldova + Madagaskar + Gżejjer ta' Marshall + Maċedonja + Mali + Mjanmar + Mongolja + Macao S.A.R., China + Gżejjer Marjana ta' Fuq + Martinik + Mawritanja + Montserrat + Malta + Mawrizju + Maldives + Malawi + Messiku + Malasja + Możambik + Namibja + New Caledonia + Niġer + Norfolk Island + Niġerja + Nikaragwa + Olanda + Norveġja + Nepal + Nauru + Niue + New Zealand + Oman + Panama + Peru + Polinesja Franċiża + Papwa-Ginea Ġdida + Filippini + Pakistan + Polonja + Saint Pierre and Miquelon + Pitcairn + Puerto Rico + Palestinian Territory + Portugall + Palau + Paragwaj + Qatar + Réunion + Rumanija + Russja + Rwanda + Għarabja Sawdita + Solomon Islands + Seychelles + Sudan + Żvezja + Singapor + Saint Helena + Slovenja + Svalbard and Jan Mayen + Slovakkja + Sierra Leone + San Marino + Senegal + Somalja + Serbja + Surinam + Sao Tome and Principe + El Salvador + Sirja + Sważiland + Turks and Caicos Islands + Ċad + Territorji Franċiżi ta' Nofsinhar + Togo + Tajlandja + Taġikistan + Tokelaw + Timor tal-Lvant + Turkmenistan + Tuneż + Tonga + Turkija + Trinidad u Tobago + Tuvalu + Tajwan + Tanżanija + Ukraina + Uganda + United States Minor Outlying Islands + Stati Uniti + Urugwaj + Użbekistan + Vatikan + Saint Vincent and the Grenadines + Venezwela + British Virgin Islands + U.S. Virgin Islands + Vjetnam + Vanwatu + Wallis and Futuna + Samoa + Jemen + Majotte + Jugoslavja + Afrika t'Isfel + Żambja + Żimbabwe + + + + [[a-z à ì ù è ò ċ ġ ħ {għ} ż]-[cy]] + + + + + + + + Jan + Fra + Mar + Apr + Mej + Ġun + Lul + Awi + Set + Ott + Nov + Diċ + + + Jannar + Frar + Marzu + April + Mejju + Ġunju + Lulju + Awissu + Settembru + Ottubru + Novembru + Diċembru + + + + + + + Ħad + Tne + Tli + Erb + Ħam + Ġim + Sib + + + Il-Ħadd + It-Tnejn + It-Tlieta + L-Erbgħa + Il-Ħamis + Il-Ġimgħa + Is-Sibt + + + + + + + + + + QK + WK + + + + + + + EEEE, d 'ta''' MMMM yyyy + + + + + d 'ta''' MMMM yyyy + + + + + dd MMM yyyy + + + + + dd/MM/yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Ħin Ċentrali Ewropew + Ħin Ċentrali Ewropew tas-Sajf + + + CET + CEST + + Valletta + + + + + + + Lira Maltija + Lm + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/nb_NO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/nb_NO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/nb_NO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/nb_NO.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/nb.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/nb.xml --- zope3-3.4.0/src/zope/i18n/locales/data/nb.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/nb.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2229 @@ + + + + + + + + + + + afar + abkhasisk + avestisk + afrikaans + akan + amharisk + aragonsk + arabisk + assamisk + avarisk + aymara + aserbajdsjansk + basjkirsk + hviterussisk + bulgarsk + bihari + bislama + bambara + bengali + tibetansk + bretonsk + bosnisk + blin + katalansk + tsjetsjensk + chamorro + cherokee + korsikansk + cree + tsjekkisk + kirkeslavisk + tsjuvansk + walisisk + dansk + tysk + divehi + dzongkha + ewe + gresk + engelsk + esperanto + spansk + estisk + baskisk + persisk + fulani + finsk + fijiansk + færøysk + fransk + frisisk + irsk + skotsk gælisk + ges + galicisk + guarani + gujarati + manx + hawaiisk + hebraisk + hindi + hiri motu + kroatisk + haitisk + ungarsk + armensk + herero + interlingua + indonesisk + interlingue + ibo + sichuan-yi + unupiak + ido + islandsk + italiensk + inuktitut + japansk + javanesisk + georgisk + kikongo + kikuyu + kuanyama + kasakhisk + kalaallisut + khmer + kannada + koreansk + konkani + kanuri + kasjmiri + kurdisk + komi + kornisk + kirgisisk + latin + luxemburgsk + ganda + limburgisk + lingala + laotisk + litauisk + luba-katanga + latvisk + madagassisk + marshallesisk + maori + makedonsk + malayalam + mongolsk + moldavisk + marathi + malayisk + maltesisk + burmesisk + nauru + norsk bokmål + ndebele (nord) + nepalsk + ndonga + nederlandsk + norsk nynorsk + norsk + ndebele, sør + navajo + nyanja + oksitansk (etter 1500) + ojibwa + oromo + oriya + ossetisk + panjabi + pali + polsk + pashto + portugisisk + quechua + retoromansk + rundi + rumensk + russisk + kinjarwanda + sanskrit + sardinsk + sindhi + nordsamisk + sango + serbokroatisk + singalesisk + sidamo + slovakisk + slovensk + samoansk + shona + somalisk + albansk + serbisk + swati + sotho (sørlig) + sundanesisk + svensk + swahili + syrisk + tamil + telugu + tatsjikisk + thai + tigrinja + tigré + turkmensk + tagalog + tswana + tonga (Tonga-øyene) + tyrkisk + tsonga + tatarisk + twi + tahitisk + uigurisk + ukrainsk + urdu + usbekisk + venda + vietnamesisk + volapyk + vallonsk + wolof + xhosa + jiddisk + joruba + zhuang + kinesisk + zulu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + De forente arabiske emiratene + Afghanistan + Antigua og Barbuda + Anguilla + Albania + Armenia + De nederlandske antiller + Angola + Antarktis + Argentina + Amerikansk Samoa + Østerrike + Australia + Aruba + Aserbajdsjan + Bosnia og Hercegovina + Barbados + Bangladesh + Belgia + Burkina Faso + Bulgaria + Bahrain + Burundi + Benin + Bermuda + Brunei Darussalam + Bolivia + Brasil + Bahamas + Bhutan + Bouvetøya + Botswana + Hviterussland + Belize + Canada + Kokosøyene (Keelingøyene) + Kongo, Den demokratiske republikken + Den sentralafrikanske republikk + Kongo + Sveits + Elfenbenskysten + Cookøyene + Chile + Kamerun + Kina + Colombia + Costa Rica + Cuba + Kapp Verde + Christmasøya + Kypros + Tsjekkia + Tyskland + Djibouti + Danmark + Dominica + Den dominikanske republikk + Algerie + Ecuador + Estland + Egypt + Vest-Sahara + Eritrea + Spania + Etiopia + Finland + Fiji + Falklandsøyene (Malvinas) + Mikronesiaføderasjonen + Færøyene + Frankrike + en + Gabon + Storbritannia + Grenada + Georgia + Fransk Guyana + Ghana + Gibraltar + Grønland + Gambia + Guinea + Guadeloupe + Ekvatorial-Guinea + Hellas + Sør-Georgia og Sør-Sandwich-øyene + Guatemala + Guam + Guinea-Bissau + Guyana + Hong Kong S.A.R. (Kina) + Heard- og McDonaldsøyene + Honduras + Kroatia + Haiti + Ungarn + Indonesia + Irland + Israel + India + Britiske områder i det indiske hav + Irak + Iran + Island + Italia + Jamaica + Jordan + Japan + Kenya + Kirgisistan + Kambodsja + Kiribati + Komorene + St. Christopher og Nevis + Nord-Korea + Sør-Korea + Kuwait + Caymanøyene + Kasakhstan + Laos, Den folkedemokratiske republikken + Libanon + St. Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Litauen + Luxembourg + Latvia + Libya + Marokko + Monaco + Moldova + Madagaskar + Marshalløyene + Makedonia, Republikken + Mali + Myanmar + Mongolia + Macao S.A.R. (Kina) + Nord-Marianene + Martinique + Mauritania + Montserrat + Malta + Mauritius + Maldivene + Malawi + Mexico + Malaysia + Mosambik + Namibia + Ny-Caledonia + Niger + Norfolkøyene + Nigeria + Nicaragua + Nederland + Norge + Nepal + Nauru + Niue + New Zealand + Oman + Panama + Peru + Fransk Polynesia + Papua Ny-Guinea + Filippinene + Pakistan + Polen + St. Pierre og Miquelon + Pitcairn + Puerto Rico + Palestinsk territorium + Portugal + Palau + Paraguay + Qatar + Reunion + Romania + Den russiske føderasjon + Rwanda + Saudi Arabia + Salomonøyene + Seychellene + Sudan + Sverige + Singapore + Saint Helena + Slovenia + Svalbard og Jan Mayen + Slovakia + Sierra Leone + San Marino + Senegal + Somalia + Serbia + Surinam + Sao Tome og Principe + El Salvador + Syria + Swaziland + Turks- og Caicosøyene + Tchad + Franske sørområder + Togo + Thailand + Tadsjikistan + Tokelau + Øst-Timor + Turkmenistan + Tunisia + Tonga + Tyrkia + Trinidad og Tobago + Tuvalu + Taiwan + Tanzania + Ukraina + Uganda + USAs mindre øyer + USA + Uruguay + Usbekistan + Vatikanstaten + St. Vincent og Grenadinene + Venezuela + Jomfruøyene (britisk) + Jomfruøyene (USA) + Vietnam + Vanuatu + Wallis og Futuna + Samoa + Yemen + Mayotte + Jugoslavia + Sør-Afrika + Zambia + Zimbabwe + + + Revidert + + + Kalendar + Kollasjon + Valuta + + + Buddhistisk kalender + Kinesisk kalender + Gregoriansk kalender + Hebraisk kalender + Islamsk kalender + Islamsk sivil kalender + Japansk kalender + Direkte rekkefølge + Telefonkatalogrekkefølge + Pinyin-rekkefølge + Strekrekkefølge + Tradisjonell rekkefølge + + + + [a-zæåøéóôàüǎ] + + + + + + + + jan + feb + mar + apr + mai + jun + jul + aug + sep + okt + nov + des + + + J + F + M + A + M + J + J + A + S + O + N + D + + + januar + februar + mars + april + mai + juni + juli + august + september + oktober + november + desember + + + + + + + + ma + ti + on + to + fr + + + + S + M + T + O + T + F + L + + + søndag + mandag + tirsdag + onsdag + torsdag + fredag + lørdag + + + + + + + + + + f.Kr. + e.Kr. + + + + + + + EEEE d. MMMM yyyy + + + + + d. MMMM yyyy + + + + + d. MMM. yyyy + + + + + dd.MM.yy + + + + + + + + 'kl. 'HH.mm.ss z + + + + + HH.mm.ss z + + + + + HH.mm.ss + + + + + HH.mm + + + + + + + {1} {0} + + + + + + + + + Eastern European Standard Time + Eastern European Daylight Time + + + EET + EEST + + Bucuresti + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + Andorranske dinarer + ADD + + + Andorranske pesetas + ADP + + + UAE dirham + AED + + + Afghani (1927-2002) + AFA + + + Afghani + Af + + + Affar og Issa franc + AIF + + + Albanske lek (1946-1961) + ALK + + + Albanske lek + lek + + + Albanske lek valute + ALV + + + Albanske dollar (FEC) + ALX + + + Armenske dram + dram + + + Nederlandske antillegylden + NA f. + + + Angolanske kwanza + AOA + + + Angolanske kwanza (1977-1990) + AOK + + + Angolanske ny kwanza (1990-2000) + AON + + + Angolan Kwanza Reajustado (1995-1999) + AOR + + + Angolanske escudo + AOS + + + Argentinske australer + ARA + + + Argentinske Peso Moneda Nacional + ARM + + + Argentinske pesos (1983-1985) + ARP + + + Argentinske pesos + Arg$ + + + Østerrikske shilling + ATS + + + Australske dollar + $A + + + Australske pund + AUP + + + Arubiske gylden + AWG + + + Aserbajdsjanske Manat + AZM + + + Bosnia-Hercegovina dinarer + BAD + + + Bosnia-Hercegovina mark (konvertible) + KM + + + Bosnia-Hercegovina nye dinarer + BAN + + + Barbadisk dollar + BDS$ + + + Bangladeshiske taka + Tk + + + Belgiske franc (konvertible) + BEC + + + Belgiske franc + BF + + + Belgiske franc (økonomiske) + BEL + + + Bulgarske lev (hard) + lev + + + Bulgarske sosialist-lev + BGM + + + Bulgarske lev + BGN + + + Bulgarske lev (1879-1952) + BGO + + + Bulgarske lev (FEC) + BGX + + + Bahrainske dinarer + BD + + + Burundiske franc + Fbu + + + Bermudiske dollar + Ber$ + + + Bermudiske pund + BMP + + + Bruneiske dollar + BND + + + Boliviano + Bs + + + Boliviano (1863-1962) + BOL + + + Boliviansk pesos + BOP + + + Boliviansk mvdol + BOV + + + Brasiliansk cruzeiro novo (1967-1986) + BRB + + + Brasilianske cruzado + BRC + + + Brasilianske cruzeiro (1990-1993) + BRE + + + Brasilianske realer + R$ + + + Brasilianske cruzado novo + BRN + + + Brasilianske cruzeiro + BRR + + + Brasilianske cruzeiro (1942-1967) + BRZ + + + Bahamske dollar + BSD + + + Bahamske pund + BSP + + + Bhutanske ngultrum + Nu + + + Bhutanske rupier + BTR + + + Burmesiske kyat + BUK + + + Burmesiske rupier + BUR + + + Botswanske pula + BWP + + + Hviterussiske nye rubler (1994-1999) + BYB + + + Hviterussiske rubler (1992-1994) + BYL + + + Hviterussiske rubler + Rbl + + + Beliziske dollar + BZ$ + + + Britisk Honduras-dollar + BZH + + + Kanadiske dollar + Can$ + + + Kongolesiske franc (congolais) + CDF + + + Kongolesiske republikk-franc + CDG + + + Congolesiske zaire + CDL + + + Sentralafrikanske franc (CFA) + CFF + + + Sveitsiske franc + SwF + + + Cookøyene dollar + CKD + + + Chilenske condor + CLC + + + Chilenske escudo + CLE + + + Chilenske Unidades de Fomento + CLF + + + Chilenske pesos + Ch$ + + + Kamerunske franc (CFA) + CMF + + + Kinesiske Jen Min Piao Yuan + CNP + + + Kinesiske US dollar (FEC) + CNX + + + Kinesiske Yuan Renminbi + Y + + + Colombianske papir-pesos + COB + + + Kongolesiske franc (CFA) + COF + + + Colombianske pesos + Col$ + + + Costaricanske colon + C + + + Tsjekkoslovakiske koruna + CSC + + + Tsjekkoslovakiske koruna (hard) + CSK + + + Kubanske pesos + CUP + + + Kubanske Foreign Exchange Certificates + CUX + + + Kappverdiske escudo + CVEsc + + + Curacao-gylden + CWG + + + Kypriotiske pund + £C + + + Tsjekkiske koruna + CZK + + + Østtyske ostmark + DDM + + + Tyske mark + DEM + + + Tyske sperrmark + DES + + + Djiboutiske franc + DF + + + Danske kroner + DKr + + + Dominikanske pesos + RD$ + + + Algeriske dinarer + DA + + + Algeriske nye franc + DZF + + + Algeriske franc germinal + DZG + + + Ecuadorianske sucre + ECS + + + Ecuadorianske Unidad de Valor Constante (UVC) + ECV + + + Estiske kroon + EEK + + + Egyptiske pund + EGP + + + Eritreiske nakfa + ERN + + + Spanske peseta + ESP + + + Etiopiske birr + Br + + + Etiopiske dollar + ETD + + + Euro + + + + Finske mark + FIM + + + Finske mark (1860-1962) + FIN + + + Fijianske dollar + F$ + + + Fijianske pund + FJP + + + Falklandsøyene-pund + FKP + + + Færøyske kronur + FOK + + + Franske franc + FRF + + + Franske franc (Germinal/Franc Poincare) + FRG + + + Gabonske franc (CFA) + GAF + + + Britiske pund sterling + £ + + + Georgiske kupon larit + GEK + + + Georgiske lari + lari + + + Ghanesiske cedi + GHC + + + Ghanesiske gamle cedi + GHO + + + Ghanesiske pund + GHP + + + Ghanesiske revaluerte cedi + GHR + + + Gibraltarske pund + GIP + + + Grønlandske kroner + GLK + + + Gambiske dalasi + GMD + + + Gambiske pund + GMP + + + Guineanske franc + GF + + + Guineanske franc (1960-1972) + GNI + + + Guineanske syli + GNS + + + Guadeloupe-franc + GPF + + + Ekvatorialguineanske ekwele guineana + GQE + + + Ekvatorialguineanske franco + GQF + + + Ekvatorialguineanske peseta guineana + GQP + + + Greske drakmer + GRD + + + Greske nye drakmer + GRN + + + Guatemalanske quetzal + Q + + + Fransk Guyana-franc guiana + GUF + + + Portugisiske guinea escudo + GWE + + + Portugisiske Guinea Mil Reis + GWM + + + Guinea-Bissau-pesos + GWP + + + Guyanske dollar + G$ + + + Hongkong-dollar + HK$ + + + Hoduras Lempira + L + + + Kroatiske dinarer + HRD + + + Kroatiske kuna + HRK + + + Haitiske gourde + HTG + + + Ungarske forinter + Ft + + + Nordirske pund + IBP + + + Indonesiske nica-gylden + IDG + + + Indonesiske Java-rupier + IDJ + + + Indonesiske nye rupier + IDN + + + Indonesiske rupier + Rp + + + Irske pund + IR£ + + + Israelske shekler + ILL + + + Israelske pund + ILP + + + Israelske nye shekler + ILS + + + Manske pund sterling + IMP + + + Indiske rupier + =0#Rs.|1#Re.|1<Rs. + + + Irakske dinarer + ID + + + Iranske rialer + RI + + + Islandske kronar + ISK + + + Italienske lire + + + + Jersey pund sterling + JEP + + + Jamaikanske dollar + J$ + + + Jamaikanske pund + JMP + + + Jordanske dinarer + JD + + + Japanske yen + ¥ + + + Kenyanske shilling + K Sh + + + Kirgisiske som + som + + + Kambodsjanske gamle riel + KHO + + + Kambodsjanske riel + CR + + + Kiribatiske dollar + KID + + + Komoriske franc + CF + + + Nordkoreanske won (1947-1959) + KPP + + + Nordkoreanske won + KPW + + + Sørkoreanske hwan + KRH + + + Sørkoreanske gamle won + KRO + + + Sørkoreanske won + KRW + + + Kuwaitiske dinarer + KD + + + Caymanske dollar + KYD + + + Kasakhstanske rubler + KZR + + + Kasakhstanske tenge + T + + + Laotiske kip + LAK + + + Libanesiske pund + LL + + + Liechtensteinske franc + LIF + + + Srilankiske rupier + SL Re + + + Ceylonske rupier + LNR + + + Liberiske dollar + LRD + + + Lesothiske loti + M + + + Litauiske lita + LTL + + + Litauiske talonas + LTT + + + Luxemburgske franc + LUF + + + Latviske lats + LVL + + + Latviske rubler + LVR + + + Libyske British Military Authority-lira + LYB + + + Libyske dinarer + LD + + + Libyske pund + LYP + + + Marokkanske dirham + MAD + + + Marokkanske franc + MAF + + + Monegaskiske franc nouveau + MCF + + + Monegaskiske franc germinal + MCG + + + Moldovske leu cupon + MDC + + + Moldovske leu + MDL + + + Moldovske ruble cupon + MDR + + + Madagassiske ariary + MGA + + + Madagassiske franc + MGF + + + Marshalløyene-dollar + MHD + + + Makedonske denarer + MDen + + + Makedonske denarer (1992-1993) + MKN + + + Maliske franc + MLF + + + Myanmarske kyat + MMK + + + Myanmarske dollar (FEC) + MMX + + + Mongolske tugrik + Tug + + + Makaoske pataca + MOP + + + Martinique-franc + MQF + + + Mauritanske ouguiya + UM + + + Maltesiske lira + Lm + + + Maltesiske pund + MTP + + + Mauritiske rupier + MUR + + + Maldiviske rupier + MVP + + + Maldiviske rufiyaa + MVR + + + Malawisle kwacha + MK + + + Malawiske pund + MWP + + + Meksikanske pesos + MEX$ + + + Meksikanske sølvpesos (1861-1992) + MXP + + + Meksikanske Unidad de Inversion (UDI) + MXV + + + Malaysiske ringgit + RM + + + Mosambikiske escudo + MZE + + + Mosambikiske metical + Mt + + + Namibiske dollar + N$ + + + Kaledonske franc germinal + NCF + + + Nigerianske naira + NGN + + + Nigerianske pund + NGP + + + Ny-hebridene CFP-franc + NHF + + + Nicaraguanske cordoba + NIC + + + Nicaraguanske gullcordoba + NIG + + + Nicaraguanske cordoba oro + NIO + + + Nederlandske gylden + NLG + + + Norske kroner + kr + + + Nepalesiske rupier + Nrs + + + Nyzealandske dollar + $NZ + + + Nyzealandske pund + NZP + + + Omanske rialer + RO + + + Omanske rial saidi + OMS + + + Panamanske balboa + PAB + + + Transdniestriansk rubler (kupon) + PDK + + + Transdniestrianske nye rubler + PDN + + + Transdniestrianske rubler + PDR + + + Peruvianske inti + PEI + + + Peruvianske sol nuevo + PEN + + + Peruvianske sol + PES + + + Papuanske kina + PGK + + + Filippinske pesos + PHP + + + Pakistanske rupier + Pra + + + Polske zloty + Zl + + + Polske US dollar (FEC) + PLX + + + Polske zloty (1950-1995) + PLZ + + + Palestinske pund + PSP + + + Portugisiske conto + PTC + + + Portugisiske escudo + PTE + + + Paraguayanske guarani + PYG + + + Qatarske riyaler + QR + + + Reunionske franc + REF + + + Rumenske leu + leu + + + Rumenske nye leu + RON + + + Russiske rubler + RUB + + + Russiske rubler (1991-1998) + RUR + + + Rwandiske franc + RWF + + + Saudiarabiske riyaler + SRl + + + Saudiarabiske riyaler (1936-1952) + SAS + + + Salomonske dollar + SI$ + + + Seychelliske rupier + SR + + + Sudanesiske dinarer + SDD + + + Sudanesiske pund + SDP + + + Svenske kroner + SKr + + + Singaporske dollar + S$ + + + Sankthelenske pund + SHP + + + Slovenske tolar bons + SIB + + + Slovenske tolar + SIT + + + Slovakiske koruna + Sk + + + Sierraleonske leone + SLL + + + Sanmarinske lira + SML + + + Somaliske shilling + So. Sh. + + + Somalilandske shilling + SQS + + + Surinamske gylden + Sf + + + Skotske pund + SSP + + + Sao Tome og Principe-dobra + Db + + + Sao Tome og Principe-escudo + STE + + + Sovjetiske nye rubler + SUN + + + Sovjetiske rubler + SUR + + + Salvadoranske colon + SVC + + + Syriske pund + LS + + + Swazilandske lilangeni + E + + + Turks- og Caicosøyene-crown + TCC + + + Tsjadiske franc (CFA) + TDF + + + Thailandske baht + THB + + + Tadsjikiske rubler + TJR + + + Tadsjikiske somoni + TJS + + + Turkmenske manat + TMM + + + Tunisiske dinarer + TND + + + Tonganske paʻanga + T$ + + + Tonganske pund sterling + TOS + + + Timoresiske escudo + TPE + + + Timoresiske pataca + TPP + + + Tyrkiske lira + TL + + + Trinidadiske dollar + TT$ + + + Trinidadiske gamle dollar + TTO + + + Tuvalske dollar + TVD + + + Taiwanske nye dollar + NT$ + + + Tanzanianske shilling + T Sh + + + Ukrainsle hryvnia + UAH + + + Ukrainske karbovanetz + UAK + + + Ugandiske shilling (1966-1987) + UGS + + + Ugandiske shilling + U Sh + + + Amerikanske dollar + US$ + + + Amerikanske dollar (neste dag) + USN + + + Amerikanske dollar (samme dag) + USS + + + Uruguayanske peso fuerte + UYF + + + Uruguayanske pesos (1975-1993) + UYP + + + Uruguayanske peso uruguayo + Ur$ + + + Usbekiske kupong-som + UZC + + + Usbekiske sum + UZS + + + Vatikanstatens lira + VAL + + + Nordvietnamesiske piastre dong viet + VDD + + + Nordvietnamesiske nye dong + VDN + + + Nordvietnamesiske viet minh piastre dong viet + VDP + + + Venezuelanske bolivar + Be + + + De britiske jomfruøyene-dollar + VGD + + + Vietnamesiske dong + VND + + + Vietnamesiske nye dong + VNN + + + Vietnamesiske republikk-dong + VNR + + + Vietnamesiske nasjonale dong + VNS + + + Vanuatisk vatu + VT + + + Vestsamoisk pund + WSP + + + Vestsamoisk tala + WST + + + Asian Dinar Unit of Account + XAD + + + CFA Franc BEAC + XAF + + + Asian Monetary Unit + XAM + + + Gull + XAU + + + European Composite Unit + XBA + + + European Monetary Unit + XBB + + + European Unit of Account (XBC) + XBC + + + European Unit of Account (XBD) + XBD + + + Østkaribiske dollar + EC$ + + + CFA Nouveau Franc + XCF + + + Special Drawing Rights + XDR + + + CFA Franc BCEAEC + XEF + + + European Currency Unit + XEU + + + French Gold Franc + XFO + + + French UIC-Franc + XFU + + + Islamske dinarer + XID + + + French Metropolitan Nouveau Franc + XMF + + + Franske antiller-franc (CFA) + XNF + + + CFA Franc BCEAO + XOF + + + CFP Franc + CFPF + + + COMECON Transferable Ruble + XTR + + + Jemenittiske dinarer + YDD + + + Jemenittiske imadi-riyaler + YEI + + + Jemenittiske rialer + YRl + + + Jugoslaviske dinarer (hard) + YUD + + + Jugoslaviske føderasjonen-dinarer + YUF + + + Jugoslaviske 1994-dinarer + YUG + + + Jugoslaviske noviy-dinarer + YUM + + + Jugoslaviske konvertible dinarer + YUN + + + Jugoslaviske oktoberdinarer + YUO + + + Jugoslaviske reforerte dinarer + YUR + + + Sørafrikanske rand (økonomisk) + ZAL + + + Sørafrikanske pund + ZAP + + + Sørafrikanske rand + R + + + Zambiske kwacha + ZMK + + + Zambiske pund + ZMP + + + Zairiske nye zaire + ZRN + + + Zairiske zaire + ZRZ + + + Zimbabwiske dollar + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/nl_BE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/nl_BE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/nl_BE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/nl_BE.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + d-MMM-yy + + + + + d/MM/yy + + + + + + + + HH.mm' u. 'z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/nl_NL.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/nl_NL.xml --- zope3-3.4.0/src/zope/i18n/locales/data/nl_NL.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/nl_NL.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0.00;¤ #,##0.00- + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/nl.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/nl.xml --- zope3-3.4.0/src/zope/i18n/locales/data/nl.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/nl.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2650 @@ + + + + + + + + + + + Afar + Abchazisch + Avestisch + Afrikaans + Akan + Amhaars + Aragonees + Arabisch + Assamees + Avarisch + Aymara + Azerbeidzjaans + Basjkiers + Wit-Russisch + Bulgaars + Bihari + Bislama + Bambara + Bengalees + Tibetaans + Bretons + Bosnisch + Blin + Catalaans + Chechen + Chamorro + Cherokee + Corsicaans + Cree + Tsjechisch + Kerkslavisch + Tsjoevasjisch + Welsh + Deens + Duits + Divehi + Dzongkha + Ewe + Grieks + Engels + Esperanto + Spaans + Estlands + Baskisch + Perzisch + Fulah + Fins + Fijisch + Faeröers + Frans + Fries + Iers + Schots Gaelic + Geez + Galicisch + Guarani + Gujarati + Manx + Hausa + Hawaïaans + Hebreeuws + Hindi + Hiri Motu + Kroatisch + Haïtiaans + Hongaars + Armeens + Herero + Interlingua + Indonesisch + Interlingue + Igbo + Sichuan Yi + Inupiaq + Ido + IJslands + Italiaans + Inuktitut + Japans + Javaans + Georgisch + Kongo + Kikuyu + Kuanyama + Kazachs + Kalaallisut + Khmer + Kannada + Koreaans + Konkani + Kanuri + Kashmiri + Koerdisch + Komi + Cornish + Kirgizisch + Latijn + Luxemburgs + Ganda + Limburgs + Lingala + Lao + Litouws + Luba-Katanga + Letlands + Malagasisch + Marshallees + Maori + Macedonisch + Malayalam + Mongools + Moldavisch + Marathi + Maleis + Maltees + Birmees + Nauru + Noors - Bokmål + Ndebele, noord- + Nepalees + Ndonga + Nederlands + Noors - Nynorsk + Noors + Ndebele, zuid- + Navajo + Nyanja + Langue d’Oc (na 1500) + Ojibwa + Oromo + Oriya + Ossetisch + Punjabi + Pali + Pools + Pashto + Portugees + Quechua + Retoromaans + Rundi + Roemeens + Russisch + Kinyarwanda + Sanskrit + Sardinisch + Sindhi + Noord-Samisch + Sango + Servokroatisch + Singalees + Sidamo + Slowaaks + Sloveens + Samoaans + Shona + Somalisch + Albanees + Servisch + Swati + Sotho, zuid + Sundanees + Zweeds + Swahili + Syriac + Tamil + Teloegoe + Tadzjik + Thai + Tigrinya + Tigre + Turkmeens + Tagalog + Tswana + Tonga (Tonga-eilanden) + Turks + Tsonga + Tataars + Twi + Tahitisch + Uighur + Oekraïens + Urdu + Oezbeeks + Venda + Vietnamees + Volapük + Wallonisch + Wolof + Xhosa + Jiddisch + Joruba + Zhuang + Chinees + Zulu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + Verenigde Arabische Emiraten + Afghanistan + Antigua en Barbuda + Anguilla + Albanië + Armenië + Nederlandse Antillen + Angola + Antarctica + Argentinië + Amerikaans Samoa + Oostenrijk + Australië + Aruba + Azerbeidzjan + Bosnië Herzegovina + Barbados + Bangladesh + België + Burkina Faso + Bulgarije + Bahrein + Burundi + Benin + Bermuda + Brunei Darussalam + Bolivia + Brazilië + Bahama’s + Bhutan + Bouveteiland + Botswana + Wit-Rusland + Belize + Canada + Cocoseilanden + Congo, Democratische Republiek + Centraal-Afrikaanse Republiek + Congo + Zwitserland + Ivoorkust + Cookeilanden + Chili + Kameroen + China + Colombia + Costa Rica + Cuba + Kaapverdië + Christmaseiland + Cyprus + Tsjechië + Duitsland + Djibouti + Denemarken + Dominica + Dominicaanse Republiek + Algerije + Ecuador + Estland + Egypte + West-Sahara + Eritrea + Spanje + Ethiopië + Finland + Fiji + Falklandeilanden + Micronesia, Federale Staten van + Faeröer + Frankrijk + en + Gabon + Verenigd Koninkrijk + Grenada + Georgië + Frans-Guyana + Ghana + Gibraltar + Groenland + Gambia + Guinea + Guadeloupe + Equatoriaal-Guinea + Griekenland + Zuid-Georgië en Zuidelijke Sandwicheilanden + Guatemala + Guam + Guinee-Bissau + Guyana + Hongkong S.A.R. van China + Heardeiland en McDonaldeiland + Honduras + Kroatië + Haïti + Hongarije + Indonesië + Ierland + Israël + India + Brits Territorium in de Indische Oceaan + Irak + Iran + IJsland + Italië + Jamaica + Jordanië + Japan + Kenia + Kirgizstan + Cambodja + Kiribati + Comoren + Saint Kitts en Nevis + Noord-Korea + Zuid-Korea + Koeweit + Caymaneilanden + Kazachstan + Laos + Libanon + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Litouwen + Luxemburg + Letland + Libië + Marokko + Monaco + Republiek Moldavië + Madagaskar + Marshalleilanden + Macedonië, Republiek + Mali + Myanmar + Mongolië + Macao S.A.R. van China + Noordelijke Marianeneilanden + Martinique + Mauritanië + Montserrat + Malta + Mauritius + Maldiven + Malawi + Mexico + Maleisië + Mozambique + Namibië + Nieuw-Caledonië + Niger + Norfolkeiland + Nigeria + Nicaragua + Nederland + Noorwegen + Nepal + Nauru + Niue + Nieuw-Zeeland + Oman + Panama + Peru + Frans-Polynesië + Papoea-Nieuw-Guinea + Filipijnen + Pakistan + Polen + Saint Pierre en Miquelon + Pitcairn + Puerto Rico + Palestijns Gebied + Portugal + Palau + Paraguay + Qatar + Réunion + Roemenië + Russische Federatie + Rwanda + Saoedi-Arabië + Salomonseilanden + Seychellen + Soedan + Zweden + Singapore + Saint Helena + Slovenië + Svalbard en Jan Mayen + Slowakije + Sierra Leone + San Marino + Senegal + Somalië + Servië + Suriname + Sao Tomé en Principe + El Salvador + Syrië + Swaziland + Turks- en Caicoseilanden + Tsjaad + Franse Gebieden in de zuidelijke Indische Oceaan + Togo + Thailand + Tadzjikistan + Tokelau + Oost-Timor + Turkmenistan + Tunesië + Tonga + Turkije + Trinidad en Tobago + Tuvalu + Taiwan + Tanzania + Oekraïne + Oeganda + Amerikaanse ondergeschikte afgelegen eilanden + Verenigde Staten + Uruguay + Oezbekistan + Vaticaanstad + Saint Vincent en de Grenadines + Venezuela + Britse Maagdeneilanden + Amerikaanse Maagdeneilanden + Vietnam + Vanuatu + Wallis en Futuna + Samoa + Jemen + Mayotte + Joegoslavië + Zuid-Afrika + Zambia + Zimbabwe + + + Gewijzigd + + + Kalender + Volgorde + Munteenheid + + + Boeddhistische kalender + Chinese kalender + Gregoriaanse kalender + Joodse kalender + Islamitische kalender + Islamitische kalender (cyclisch) + Japanse kalender + Directe volgorde + Telefoonboekvolgorde + Pinyinvolgorde + Streekvolgorde + Traditioneelvolgorde + + + + [a-záéíóúäëïöüij] + + + + + + + + jan + feb + mrt + apr + mei + jun + jul + aug + sep + okt + nov + dec + + + J + F + M + A + M + J + J + A + S + O + N + D + + + januari + februari + maart + april + mei + juni + juli + augustus + september + oktober + november + december + + + + + + + zo + ma + di + wo + do + vr + za + + + Z + M + D + W + D + V + Z + + + zondag + maandag + dinsdag + woensdag + donderdag + vrijdag + zaterdag + + + + + + + + + + v. Chr. + n. Chr. + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + d-MMM-yyyy + + + + + d-M-yy + + + + + + + + H:mm:ss' uur' z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + Tisjrie + Chesjwan + Kislev + Tevet + Sjevat + Adar + Adar B + Nisan + Ijar + Sivan + Tammoez + Av + Elloel + + + Tisjrie + Chesjwan + Kislev + Tevet + Sjevat + Adar + Adar B + Nisan + Ijar + Sivan + Tammoez + Av + Elloel + + + + + + + + + Moeharram + Safar + Rabiʻa al awal + Rabiʻa al thani + Joemadʻal awal + Joemadʻal thani + Rajab + Sjaʻaban + Ramadan + Sjawal + Doe al kaʻaba + Doe al hizja + + + Moeharram + Safar + Rabiʻa al awal + Rabiʻa al thani + Joemadʻal awal + Joemadʻal thani + Rajab + Sjaʻaban + Ramadan + Sjawal + Doe al kaʻaba + Doe al hizja + + + + + + Saʻna Hizjria + + + + + + + + Moeharram + Safar + Rabiʻa al awal + Rabiʻa al thani + Joemadʻal awal + Joemadʻal thani + Rajab + Sjaʻaban + Ramadan + Sjawal + Doe al kaʻaba + Doe al hizja + + + Moeharram + Safar + Rabiʻa al awal + Rabiʻa al thani + Joemadʻal awal + Joemadʻal thani + Rajab + Sjaʻaban + Ramadan + Sjawal + Doe al kaʻaba + Doe al hizja + + + + + + Saʻna Hizjria + + + + + + + + Pacific-standaardtijd + Pacific-zomertijd + + + PST + PDT + + Los Angeles + + + + Pacific-standaardtijd + Pacific-zomertijd + + + PST + PDT + + Los Angeles + + + + Mountain-standaardtijd + Mountain-zomertijd + + + MST + MDT + + Denver + + + + Mountain-standaardtijd + Mountain-zomertijd + + + MST + MDT + + Denver + + + + Mountain-standaardtijd + Mountain-standaardtijd + + + MST + MST + + Phoenix + + + + Mountain-standaardtijd + Mountain-standaardtijd + + + MST + MST + + Phoenix + + + + Central-standaardtijd + Central-zomertijd + + + CST + CDT + + Chicago + + + + Central-standaardtijd + Central-zomertijd + + + CST + CDT + + Chicago + + + + Eastern-standaardtijd + Eastern-zomertijd + + + EST + EDT + + New York + + + + Eastern-standaardtijd + Eastern-zomertijd + + + EST + EDT + + New York + + + + Eastern-standaardtijd + Eastern-standaardtijd + + + EST + EST + + Indianapolis + + + + Eastern-standaardtijd + Eastern-standaardtijd + + + EST + EST + + Indianapolis + + + + Hawaï-standaardtijd + Hawaï-standaardtijd + + + HST + HST + + Honolulu + + + + Hawaï-standaardtijd + Hawaï-standaardtijd + + + HST + HST + + Honolulu + + + + Alaska-standaardtijd + Alaska-zomertijd + + + AST + ADT + + Anchorage + + + + Alaska-standaardtijd + Alaska-zomertijd + + + AST + ADT + + Anchorage + + + + Atlantic-standaardtijd + Atlantic-zomertijd + + + AST + ADT + + Halifax + + + + Newfoundland-standaardtijd + Newfoundland-zomertijd + + + CNT + CDT + + St. Johns + + + + Newfoundland-standaardtijd + Newfoundland-zomertijd + + + CNT + CDT + + St. Johns + + + + Midden-Europese standaardtijd + Midden-Europese zomertijd + + + CET + CEST + + Paris + + + + Midden-Europese standaardtijd + Midden-Europese zomertijd + + + CET + CEST + + Parijs + + + + Greenwich Mean Time + Greenwich Mean Time + + + GMT + GMT + + Londen + + + + Greenwich Mean Time + Greenwich Mean Time + + + GMT + GMT + + Casablanca + + + + Israëlische standaardtijd + Israëlische zomertijd + + + IST + IDT + + Jeruzalem + + + + Japanse standaardtijd + Japanse standaardtijd + + + JST + JST + + Tokyo + + + + Japanse standaardtijd + Japanse standaardtijd + + + JST + JST + + Tokyo + + + + Oost-Europese standaardtijd + Oost-Europese zomertijd + + + EET + EEST + + Boekarest + + + + Chinese standaardtijd + Chinese standaardtijd + + + CTT + CDT + + Shanghai + + + + Chinese standaardtijd + Chinese standaardtijd + + + CTT + CDT + + Shanghai + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + Andorrese diner + ADD + + + Andorrese peseta + ADP + + + Verenigde Arabische Emiraten-dirham + AED + + + Afghani (1927-2002) + AFA + + + Afghani + Af + + + Affars en Issas-franc + AIF + + + Albanese lek (1946-1961) + ALK + + + Albanese lek + lek + + + Albanese lek valute + ALV + + + Albanese dollarwisselcertificaten + ALX + + + Armeense dram + dram + + + Nederlands-Antilliaanse gulden + NA f. + + + Angolese kwanza + AOA + + + Angolese kwanza (1977-1990) + AOK + + + Angolese nieuwe kwanza (1990-2000) + AON + + + Angolese kwanza reajustado (1995-1999) + AOR + + + Angolese escudo + AOS + + + Argentijnse austral + ARA + + + Argentijnse peso moneda nacional + ARM + + + Argentijnse peso (1983-1985) + ARP + + + Argentijnse peso + Arg$ + + + Oostenrijkse schilling + ATS + + + Australische dollar + $A + + + Australisch pond + AUP + + + Arubaanse gulden + AWG + + + Azerbeidzjaanse manat + AZM + + + Bosnische dinar + BAD + + + Bosnische convertibele mark + KM + + + Bosnische nieuwe dinar + BAN + + + Barbadaanse dollar + BDS$ + + + Bengalese taka + Tk + + + Belgische frank (convertibel) + BEC + + + Belgische frank + BF + + + Belgische frank (financieel) + BEL + + + Bulgaarse harde lev + lev + + + Bulgaarse socialistische lev + BGM + + + Bulgaarse nieuwe lev + BGN + + + Bulgaarse lev (1879-1952) + BGO + + + Bulgaarse levwisselcertificaten + BGX + + + Bahreinse dinar + BD + + + Burundese franc + Fbu + + + Bermuda-dollar + Ber$ + + + Bermuda-pond + BMP + + + Bruneise dollar + BND + + + Boliviano + Bs + + + Boliviano (1863-1962) + BOL + + + Boliviaanse peso + BOP + + + Boliviaanse mvdol + BOV + + + Braziliaanse cruzeiro novo (1967-1986) + BRB + + + Braziliaanse cruzado + BRC + + + Braziliaanse cruzeiro (1990-1993) + BRE + + + Braziliaanse real + R$ + + + Braziliaanse cruzado novo + BRN + + + Braziliaanse cruzeiro + BRR + + + Braziliaanse cruzeiro (1942-1967) + BRZ + + + Bahamaanse dollar + BSD + + + Bahamaans pond + BSP + + + Bhutaanse ngultrum + Nu + + + Bhutaanse rupee + BTR + + + Birmese kyat + BUK + + + Birmese rupee + BUR + + + Botswaanse pula + BWP + + + Wit-Russische nieuwe roebel (1994-1999) + BYB + + + Wit-Russische roebel (1992-1994) + BYL + + + Wit-Russische roebel + Rbl + + + Belizaanse dollar + BZ$ + + + Brits-Hondurese dollar + BZH + + + Canadese dollar + Can$ + + + Congolese franc congolais + CDF + + + Congolese franc + CDG + + + Congolese zaïre + CDL + + + Centraal-Afrikaanse CFA-franc + CFF + + + Zwitserse franc + SwF + + + Cookeilandse dollar + CKD + + + Chileense condor + CLC + + + Chileense escudo + CLE + + + Chileense unidades de fomento + CLF + + + Chileense peso + Ch$ + + + Kameroense CFA-franc + CMF + + + Chinese jen min piao yuan + CNP + + + Chinese Amerikaanse-dollarwisselcertificaten + CNX + + + Chinese yuan renminbi + Y + + + Colombiaanse papieren peso + COB + + + Congolese CFA-franc + COF + + + Colombiaanse peso + Col$ + + + Costaricaanse colón + C + + + Tsjechoslowaakse koruna + CSC + + + Tsjechoslowaakse harde koruna + CSK + + + Cubaanse peso + CUP + + + Cubaanse wisselcertificaten + CUX + + + Kaapverdische escudo + CVEsc + + + Curaçao-gulden + CWG + + + Cyprisch pond + £C + + + Tsjechische koruna + CZK + + + Oost-Duitse ostmark + DDM + + + Duitse mark + DEM + + + Duitse sperrmark + DES + + + Djiboutiaanse franc + DF + + + Deense kroon + DKr + + + Dominicaanse peso + RD$ + + + Algerijnse dinar + DA + + + Algerijnse nieuwe franc + DZF + + + Algerijnse franc germinal + DZG + + + Ecuadoraanse sucre + ECS + + + Ecuadoraanse unidad de valor constante (UVC) + ECV + + + Estlandse kroon + EEK + + + Egyptisch pond + EGP + + + Eritrese nakfa + ERN + + + Spaanse peseta + ESP + + + Ethiopische birr + Br + + + Ethiopische dollar + ETD + + + Euro + + + + Finse markka + FIM + + + Finse markka (1860-1962) + FIN + + + Fijische dollar + F$ + + + Fijisch pond + FJP + + + Falklandeilands pond + FKP + + + Faeröerse kroon + FOK + + + Franse franc + FRF + + + Franse franc germinal/franc poincare + FRG + + + Gabonese CFA-franc + GAF + + + Brits pond sterling + £ + + + Georgische kupon larit + GEK + + + Georgische lari + lari + + + Ghanese cedi + GHC + + + Ghanese oude cedi + GHO + + + Ghanees pond + GHP + + + Ghanese hergewaardeerde cedi + GHR + + + Gibraltarees pond + GIP + + + Groenlandse kroon + GLK + + + Gambiaanse dalasi + GMD + + + Gambiaans pond + GMP + + + Guinese franc + GF + + + Guinese franc (1960-1972) + GNI + + + Guinese syli + GNS + + + Guadeloupse franc + GPF + + + Equatoriaal-Guinese ekwele guineana + GQE + + + Equatoriaal-Guinese franco + GQF + + + Equatoriaal-Guinese peseta puineana + GQP + + + Griekse drachme + GRD + + + Griekse nieuwe drachme + GRN + + + Guatemalteekse quetzal + Q + + + Frans-Guyaanse franc guiana + GUF + + + Portuguees-Guinese escudo + GWE + + + Portuguees-Guinese mil reis + GWM + + + Guinee-Bissause peso + GWP + + + Guyaanse dollar + G$ + + + Hongkongse dollar + HK$ + + + Hodurese lempira + L + + + Kroatische dinar + HRD + + + Kroatische kuna + HRK + + + Haïtiaanse gourde + HTG + + + Hongaarse forint + Ft + + + Noord-Iers pond + IBP + + + Indonesische nica-gulden + IDG + + + Indonesische Java-rupiah + IDJ + + + Indonesische nieuwe rupiah + IDN + + + Indonesische rupiah + Rp + + + Iers pond + IR£ + + + Israëlische shekel + ILL + + + Israëlisch pond + ILP + + + Israëlische nieuwe shekel + ILS + + + Isle of Man pond sterling + IMP + + + Indiase rupee + =0#Rs.|1#Re.|1<Rs. + + + Iraakse dinar + ID + + + Iraanse rial + RI + + + IJslandse kroon + ISK + + + Italiaanse lire + + + + Jersey pond sterling + JEP + + + Jamaicaanse dollar + J$ + + + Jamaicaans pond + JMP + + + Jordaanse dinar + JD + + + Japanse yen + ¥ + + + Kenyaanse shilling + K Sh + + + Kirgizische som + som + + + Cambodjaanse oude riel + KHO + + + Cambodjaanse riel + CR + + + Kiribatische dollar + KID + + + Comorese franc + CF + + + Noord-Koreaanse Volksrepubliek-won + KPP + + + Noord-Koreaanse won + KPW + + + Zuid-Koreaanse hwan + KRH + + + Zuid-Koreaanse oude won + KRO + + + Zuid-Koreaanse won + KRW + + + Koeweitse dinar + KD + + + Caymaneilandse dollar + KYD + + + Kazachstaanse roebel + KZR + + + Kazachstaanse tenge + T + + + Laotiaanse kip + LAK + + + Libanees pond + LL + + + Liechtensteinse frank + LIF + + + Srilankaanse rupee + SL Re + + + Ceylon-rupee + LNR + + + Liberiaanse dollar + LRD + + + Lesothaanse loti + M + + + Litouwse litas + LTL + + + Litouwse talonas + LTT + + + Luxemburgse frank + LUF + + + Letse lats + LVL + + + Letse roebel + LVR + + + Libische Britse Militaire Autoriteit-lire + LYB + + + Libische dinar + LD + + + Libisch pond + LYP + + + Marokkaanse dirham + MAD + + + Marokkaanse franc + MAF + + + Monegaskische nieuwe franc + MCF + + + Monegaskische franc germinal + MCG + + + Monegaskische leu cupon + MDC + + + Moldavische leu + MDL + + + Moldavische roebel-cupon + MDR + + + Malagassische ariary + MGA + + + Malagassische franc + MGF + + + Marshalleilandse dollar + MHD + + + Macedonische denar + MDen + + + Macedonische denar (1992-1993) + MKN + + + Malinese franc + MLF + + + Myanmarese kyat + MMK + + + Myanmarese dollarwisselcertificaten + MMX + + + Mongoolse tugrik + Tug + + + Macause pataca + MOP + + + Martinikaanse franc + MQF + + + Mauritaanse ouguiya + UM + + + Maltese lire + Lm + + + Maltees pond + MTP + + + Mauritiaanse rupee + MUR + + + Maldivische rupee + MVP + + + Maldivische rufiyaa + MVR + + + Malawische kwacha + MK + + + Malawisch pond + MWP + + + Mexicaanse peso + MEX$ + + + Mexicaanse zilveren peso (1861-1992) + MXP + + + Mexicaanse unidad de inversion (UDI) + MXV + + + Maleisische ringgit + RM + + + Mozambikaanse escudo + MZE + + + Mozambikaanse metical + Mt + + + Namibische dollar + N$ + + + Nieuw-Caledonische franc germinal + NCF + + + Nigeriaanse naira + NGN + + + Nigeriaans pond + NGP + + + Nieuw-Hebridiaanse CFP-franc + NHF + + + Nicaraguaanse córdoba + NIC + + + Nicaraguaanse gouden córdoba + NIG + + + Nicaraguaanse córdoba oro + NIO + + + Nederlandse gulden + fl + + + Noorse kroon + NKr + + + Nepalese rupee + Nrs + + + Nieuw-Zeelandse dollar + $NZ + + + Nieuw-Zeelands pond + NZP + + + Omaanse rial + RO + + + Omaanse rial saidi + OMS + + + Panamese balboa + PAB + + + Trans-Djnestrische roebel-kupon + PDK + + + Trans-Djnestrische nieuwe roebel + PDN + + + Trans-Djnestrische roebel + PDR + + + Peruaanse inti + PEI + + + Peruaanse nieuwe sol + PEN + + + Peruaanse sol + PES + + + Papuaanse kina + PGK + + + Filipijnse peso + PHP + + + Pakistaanse rupee + Pra + + + Poolse zloty + Zl + + + Poolse Amerikaanse-dollarwisselcertificaten + PLX + + + Poolse zloty (1950-1995) + PLZ + + + Palestijns pond + PSP + + + Portugese conto + PTC + + + Portugese escudo + PTE + + + Paraguayaanse guarani + PYG + + + Qatarese rial + QR + + + Réunionse franc + REF + + + Roemeense leu + leu + + + Roemeense nieuwe leu + RON + + + Russische roebel + RUB + + + Russische roebel (1991-1998) + RUR + + + Rwandese franc + RWF + + + Saoedische rial + SRl + + + Saoedische souvereine rial + SAS + + + Salomonseilandse dollar + SI$ + + + Seychelse rupee + SR + + + Soedanese dinar + SDD + + + Soedanees pond + SDP + + + Zweedse kroon + SKr + + + Singaporese dollar + S$ + + + Sint-Heleense pond + SHP + + + Sloveense tolar bons + SIB + + + Sloveense tolar + SIT + + + Slowaakse koruna + Sk + + + Sierraleoonse leone + SLL + + + Sanmarinese lire + SML + + + Somalische shilling + So. Sh. + + + Somalilandse shilling + SQS + + + Surinaamse gulden + Sf + + + Schotse pond + SSP + + + Santomese dobra + Db + + + Santomese escudo + STE + + + Nieuwe sovjet-roebel + SUN + + + Sovjet-roebel + SUR + + + Salvadoraanse colón + SVC + + + Syrisch pond + LS + + + Swazische lilangeni + E + + + Turks en Caicos-kroon + TCC + + + Tsjaadse CFA-franc + TDF + + + Thaise baht + THB + + + Tadzjikistaanse roebel + TJR + + + Tadzjikistaanse somoni + TJS + + + Turkmeense manat + TMM + + + Tunesische dinar + TND + + + Tongaanse paʻanga + T$ + + + Tongaans pond sterling + TOS + + + Timorese escudo + TPE + + + Timorese pataca + TPP + + + Turkse lire + TL + + + Trinidad en Tobago-dollar + TT$ + + + Trinidad en Tobago-oude dollar + TTO + + + Tuvaluaanse dollar + TVD + + + Nieuwe Taiwanese dollar + NT$ + + + Tanzaniaanse shilling + T Sh + + + Oekraïense hryvnia + UAH + + + Oekraïense karbovanetz + UAK + + + Oegandese shilling (1966-1987) + UGS + + + Oegandese shilling + U Sh + + + Amerikaanse dollar + US$ + + + Amerikaanse dollar (volgende dag) + USN + + + Amerikaanse dollar (zelfde dag) + USS + + + Uruguayaanse peso fuerte + UYF + + + Uruguayaanse peso (1975-1993) + UYP + + + Uruguayaanse peso uruguayo + Ur$ + + + Oezbekistaanse coupon-som + UZC + + + Oezbekistaanse sum + UZS + + + Vaticaanse lire + VAL + + + Noord-Vietnamese piastre dong viet + VDD + + + Noord-Vietnamese nieuwe dong + VDN + + + Noord-Vietnamese viet minh piastre dong viet + VDP + + + Venezolaanse bolivar + Be + + + Britse Maagdeneilandse dollar + VGD + + + Vietnamese dong + VND + + + Vietnamese nieuwe dong + VNN + + + Vietnamese Republiek-dong + VNR + + + Vietnamese nationale dong + VNS + + + Vanuatuaanse vatu + VT + + + West-Samoaans pond + WSP + + + West-Samoaanse tala + WST + + + Aziatische dinar-rekeneenheid + XAD + + + CFA-franc BEAC + XAF + + + Aziatische monetaire eenheid + XAM + + + Goud + XAU + + + Europese samengestelde eenheid + XBA + + + Europese monetaire eenheid + XBB + + + Europese rekeneenheid (XBC) + XBC + + + Europese rekeneenheid (XBD) + XBD + + + Oost-Caribische dollar + EC$ + + + CFA nieuwe franc + XCF + + + Special Drawing Rights + XDR + + + CFA-franc BCEAEC + XEF + + + European Currency Unit + XEU + + + Franse gouden franc + XFO + + + Franse UIC-franc + XFU + + + Islamitische dinar + XID + + + Franse metropolische nieuwe franc + XMF + + + Franse antillen CFA-franc + XNF + + + CFA-franc BCEAO + XOF + + + CFP-franc + CFPF + + + COMECON transferable roebel + XTR + + + Jemenitische dinar + YDD + + + Jemenitische imadi rial + YEI + + + Jemenitische rial + YRl + + + Joegoslavische harde dinar + YUD + + + Joegoslavische federale dinar + YUF + + + Joegoslavische 1994-dinar + YUG + + + Joegoslavische noviy-dinar + YUM + + + Joegoslavische convertibele dinar + YUN + + + Joegoslavische oktober-dinar + YUO + + + Joegoslavische hervormde dinar + YUR + + + Zuid-Afrikaanse rand (financieel) + ZAL + + + Zuid-Afrikaans pond + ZAP + + + Zuid-Afrikaanse rand + R + + + Zambiaanse kwacha + ZMK + + + Zambiaans pond + ZMP + + + Zaïrese nieuwe zaïre + ZRN + + + Zaïrese zaïre + ZRZ + + + Zimbabwaanse dollar + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/nn_NO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/nn_NO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/nn_NO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/nn_NO.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/nn.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/nn.xml --- zope3-3.4.0/src/zope/i18n/locales/data/nn.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/nn.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,276 @@ + + + + + + + + + + + norsk bokmål + norsk nynorsk + norsk + + + De forente arabiske emiratene + Antigua og Barbuda + De nederlandske antiller + Antarktis + Amerikansk Samoa + Østerrike + Aserbajdsjan + Bosnia og Hercegovina + Belgia + Brunei Darussalam + Brasil + Bouvetøya + Hviterussland + Kokosøyene (Keelingøyene) + Kongo, Den demokratiske republikken + Den sentralafrikanske republikk + Kongo + Sveits + Elfenbenskysten + Cookøyene + Kamerun + Kina + Kapp Verde + Christmasøya + Kypros + Tsjekkia + Tyskland + Danmark + Den dominikanske republikk + Algerie + Estland + Vest-Sahara + Spania + Etiopia + Falklandsøyene (Malvinas) + Mikronesiaføderasjonen + Færøyene + Frankrike + nb + Storbritannia + Fransk Guyana + Grønland + Ekvatorial-Guinea + Hellas + Sør-Georgia og Sør-Sandwich-øyene + Hong Kong S.A.R. (Kina) + Heard- og McDonaldsøyene + Kroatia + Ungarn + Irland + Britiske områder i det indiske hav + Irak + Island + Italia + Kirgisistan + Kambodsja + Komorene + St. Christopher og Nevis + Nord-Korea + Sør-Korea + Caymanøyene + Kasakhstan + Laos, Den folkedemokratiske republikken + Libanon + St. Lucia + Litauen + Marokko + Madagaskar + Marshalløyene + Makedonia, Republikken + Macao S.A.R. (Kina) + Nord-Marianene + Maldivene + Mosambik + Ny-Caledonia + Norfolkøyene + Nederland + Noreg + Fransk Polynesia + Papua Ny-Guinea + Filippinene + Polen + St. Pierre og Miquelon + Palestinsk territorium + Reunion + Den russiske føderasjon + Salomonøyene + Seychellene + Sverige + Svalbard og Jan Mayen + Surinam + Sao Tome og Principe + Turks- og Caicosøyene + Tchad + Franske sørområder + Tadsjikistan + Øst-Timor + Tyrkia + Trinidad og Tobago + Ukraina + USAs mindre øyer + USA + Usbekistan + Vatikanstaten + St. Vincent og Grenadinene + Jomfruøyene (britisk) + Jomfruøyene (USA) + Wallis og Futuna + Jugoslavia + Sør-Afrika + + + + [a-zæåøéóôàüǎ] + + + + + + + + jan + feb + mar + apr + mai + jun + jul + aug + sep + okt + nov + des + + + januar + februar + mars + april + mai + juni + juli + august + september + oktober + november + desember + + + + + + + su + + ty + on + to + fr + la + + + sundag + måndag + tysdag + onsdag + torsdag + fredag + laurdag + + + + + + + + + + f.Kr. + e.Kr. + + + + + + + EEEE d. MMMM yyyy + + + + + d. MMMM yyyy + + + + + d. MMM. yyyy + + + + + dd.MM.yy + + + + + + + + 'kl. 'HH.mm.ss z + + + + + HH.mm.ss z + + + + + HH.mm.ss + + + + + HH.mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + NOK + kr + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/no_NO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/no_NO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/no_NO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/no_NO.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/no.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/no.xml --- zope3-3.4.0/src/zope/i18n/locales/data/no.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/no.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2229 @@ + + + + + + + + + + + afar + abkhasisk + avestisk + afrikaans + akan + amharisk + aragonsk + arabisk + assamisk + avarisk + aymara + aserbajdsjansk + basjkirsk + hviterussisk + bulgarsk + bihari + bislama + bambara + bengali + tibetansk + bretonsk + bosnisk + blin + katalansk + tsjetsjensk + chamorro + cherokee + korsikansk + cree + tsjekkisk + kirkeslavisk + tsjuvansk + walisisk + dansk + tysk + divehi + dzongkha + ewe + gresk + engelsk + esperanto + spansk + estisk + baskisk + persisk + fulani + finsk + fijiansk + færøysk + fransk + frisisk + irsk + skotsk gælisk + ges + galicisk + guarani + gujarati + manx + hawaiisk + hebraisk + hindi + hiri motu + kroatisk + haitisk + ungarsk + armensk + herero + interlingua + indonesisk + interlingue + ibo + sichuan-yi + unupiak + ido + islandsk + italiensk + inuktitut + japansk + javanesisk + georgisk + kikongo + kikuyu + kuanyama + kasakhisk + kalaallisut + khmer + kannada + koreansk + konkani + kanuri + kasjmiri + kurdisk + komi + kornisk + kirgisisk + latin + luxemburgsk + ganda + limburgisk + lingala + laotisk + litauisk + luba-katanga + latvisk + madagassisk + marshallesisk + maori + makedonsk + malayalam + mongolsk + moldavisk + marathi + malayisk + maltesisk + burmesisk + nauru + norsk bokmål + ndebele (nord) + nepalsk + ndonga + nederlandsk + norsk nynorsk + norsk + ndebele, sør + navajo + nyanja + oksitansk (etter 1500) + ojibwa + oromo + oriya + ossetisk + panjabi + pali + polsk + pashto + portugisisk + quechua + retoromansk + rundi + rumensk + russisk + kinjarwanda + sanskrit + sardinsk + sindhi + nordsamisk + sango + serbokroatisk + singalesisk + sidamo + slovakisk + slovensk + samoansk + shona + somalisk + albansk + serbisk + swati + sotho (sørlig) + sundanesisk + svensk + swahili + syrisk + tamil + telugu + tatsjikisk + thai + tigrinja + tigré + turkmensk + tagalog + tswana + tonga (Tonga-øyene) + tyrkisk + tsonga + tatarisk + twi + tahitisk + uigurisk + ukrainsk + urdu + usbekisk + venda + vietnamesisk + volapyk + vallonsk + wolof + xhosa + jiddisk + joruba + zhuang + kinesisk + zulu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + De forente arabiske emiratene + Afghanistan + Antigua og Barbuda + Anguilla + Albania + Armenia + De nederlandske antiller + Angola + Antarktis + Argentina + Amerikansk Samoa + Østerrike + Australia + Aruba + Aserbajdsjan + Bosnia og Hercegovina + Barbados + Bangladesh + Belgia + Burkina Faso + Bulgaria + Bahrain + Burundi + Benin + Bermuda + Brunei Darussalam + Bolivia + Brasil + Bahamas + Bhutan + Bouvetøya + Botswana + Hviterussland + Belize + Canada + Kokosøyene (Keelingøyene) + Kongo, Den demokratiske republikken + Den sentralafrikanske republikk + Kongo + Sveits + Elfenbenskysten + Cookøyene + Chile + Kamerun + Kina + Colombia + Costa Rica + Cuba + Kapp Verde + Christmasøya + Kypros + Tsjekkia + Tyskland + Djibouti + Danmark + Dominica + Den dominikanske republikk + Algerie + Ecuador + Estland + Egypt + Vest-Sahara + Eritrea + Spania + Etiopia + Finland + Fiji + Falklandsøyene (Malvinas) + Mikronesiaføderasjonen + Færøyene + Frankrike + en + Gabon + Storbritannia + Grenada + Georgia + Fransk Guyana + Ghana + Gibraltar + Grønland + Gambia + Guinea + Guadeloupe + Ekvatorial-Guinea + Hellas + Sør-Georgia og Sør-Sandwich-øyene + Guatemala + Guam + Guinea-Bissau + Guyana + Hong Kong S.A.R. (Kina) + Heard- og McDonaldsøyene + Honduras + Kroatia + Haiti + Ungarn + Indonesia + Irland + Israel + India + Britiske områder i det indiske hav + Irak + Iran + Island + Italia + Jamaica + Jordan + Japan + Kenya + Kirgisistan + Kambodsja + Kiribati + Komorene + St. Christopher og Nevis + Nord-Korea + Sør-Korea + Kuwait + Caymanøyene + Kasakhstan + Laos, Den folkedemokratiske republikken + Libanon + St. Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Litauen + Luxembourg + Latvia + Libya + Marokko + Monaco + Moldova + Madagaskar + Marshalløyene + Makedonia, Republikken + Mali + Myanmar + Mongolia + Macao S.A.R. (Kina) + Nord-Marianene + Martinique + Mauritania + Montserrat + Malta + Mauritius + Maldivene + Malawi + Mexico + Malaysia + Mosambik + Namibia + Ny-Caledonia + Niger + Norfolkøyene + Nigeria + Nicaragua + Nederland + Norge + Nepal + Nauru + Niue + New Zealand + Oman + Panama + Peru + Fransk Polynesia + Papua Ny-Guinea + Filippinene + Pakistan + Polen + St. Pierre og Miquelon + Pitcairn + Puerto Rico + Palestinsk territorium + Portugal + Palau + Paraguay + Qatar + Reunion + Romania + Den russiske føderasjon + Rwanda + Saudi Arabia + Salomonøyene + Seychellene + Sudan + Sverige + Singapore + Saint Helena + Slovenia + Svalbard og Jan Mayen + Slovakia + Sierra Leone + San Marino + Senegal + Somalia + Serbia + Surinam + Sao Tome og Principe + El Salvador + Syria + Swaziland + Turks- og Caicosøyene + Tchad + Franske sørområder + Togo + Thailand + Tadsjikistan + Tokelau + Øst-Timor + Turkmenistan + Tunisia + Tonga + Tyrkia + Trinidad og Tobago + Tuvalu + Taiwan + Tanzania + Ukraina + Uganda + USAs mindre øyer + USA + Uruguay + Usbekistan + Vatikanstaten + St. Vincent og Grenadinene + Venezuela + Jomfruøyene (britisk) + Jomfruøyene (USA) + Vietnam + Vanuatu + Wallis og Futuna + Samoa + Yemen + Mayotte + Jugoslavia + Sør-Afrika + Zambia + Zimbabwe + + + Revidert + + + Kalendar + Kollasjon + Valuta + + + Buddhistisk kalender + Kinesisk kalender + Gregoriansk kalender + Hebraisk kalender + Islamsk kalender + Islamsk sivil kalender + Japansk kalender + Direkte rekkefølge + Telefonkatalogrekkefølge + Pinyin-rekkefølge + Strekrekkefølge + Tradisjonell rekkefølge + + + + [a-zæåøéóôàüǎ] + + + + + + + + jan + feb + mar + apr + mai + jun + jul + aug + sep + okt + nov + des + + + J + F + M + A + M + J + J + A + S + O + N + D + + + januar + februar + mars + april + mai + juni + juli + august + september + oktober + november + desember + + + + + + + + ma + ti + on + to + fr + + + + S + M + T + O + T + F + L + + + søndag + mandag + tirsdag + onsdag + torsdag + fredag + lørdag + + + + + + + + + + f.Kr. + e.Kr. + + + + + + + EEEE d. MMMM yyyy + + + + + d. MMMM yyyy + + + + + d. MMM. yyyy + + + + + dd.MM.yy + + + + + + + + 'kl. 'HH.mm.ss z + + + + + HH.mm.ss z + + + + + HH.mm.ss + + + + + HH.mm + + + + + + + {1} {0} + + + + + + + + + Eastern European Standard Time + Eastern European Daylight Time + + + EET + EEST + + Bucuresti + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + Andorranske dinarer + ADD + + + Andorranske pesetas + ADP + + + UAE dirham + AED + + + Afghani (1927-2002) + AFA + + + Afghani + Af + + + Affar og Issa franc + AIF + + + Albanske lek (1946-1961) + ALK + + + Albanske lek + lek + + + Albanske lek valute + ALV + + + Albanske dollar (FEC) + ALX + + + Armenske dram + dram + + + Nederlandske antillegylden + NA f. + + + Angolanske kwanza + AOA + + + Angolanske kwanza (1977-1990) + AOK + + + Angolanske ny kwanza (1990-2000) + AON + + + Angolan Kwanza Reajustado (1995-1999) + AOR + + + Angolanske escudo + AOS + + + Argentinske australer + ARA + + + Argentinske Peso Moneda Nacional + ARM + + + Argentinske pesos (1983-1985) + ARP + + + Argentinske pesos + Arg$ + + + Østerrikske shilling + ATS + + + Australske dollar + $A + + + Australske pund + AUP + + + Arubiske gylden + AWG + + + Aserbajdsjanske Manat + AZM + + + Bosnia-Hercegovina dinarer + BAD + + + Bosnia-Hercegovina mark (konvertible) + KM + + + Bosnia-Hercegovina nye dinarer + BAN + + + Barbadisk dollar + BDS$ + + + Bangladeshiske taka + Tk + + + Belgiske franc (konvertible) + BEC + + + Belgiske franc + BF + + + Belgiske franc (økonomiske) + BEL + + + Bulgarske lev (hard) + lev + + + Bulgarske sosialist-lev + BGM + + + Bulgarske lev + BGN + + + Bulgarske lev (1879-1952) + BGO + + + Bulgarske lev (FEC) + BGX + + + Bahrainske dinarer + BD + + + Burundiske franc + Fbu + + + Bermudiske dollar + Ber$ + + + Bermudiske pund + BMP + + + Bruneiske dollar + BND + + + Boliviano + Bs + + + Boliviano (1863-1962) + BOL + + + Boliviansk pesos + BOP + + + Boliviansk mvdol + BOV + + + Brasiliansk cruzeiro novo (1967-1986) + BRB + + + Brasilianske cruzado + BRC + + + Brasilianske cruzeiro (1990-1993) + BRE + + + Brasilianske realer + R$ + + + Brasilianske cruzado novo + BRN + + + Brasilianske cruzeiro + BRR + + + Brasilianske cruzeiro (1942-1967) + BRZ + + + Bahamske dollar + BSD + + + Bahamske pund + BSP + + + Bhutanske ngultrum + Nu + + + Bhutanske rupier + BTR + + + Burmesiske kyat + BUK + + + Burmesiske rupier + BUR + + + Botswanske pula + BWP + + + Hviterussiske nye rubler (1994-1999) + BYB + + + Hviterussiske rubler (1992-1994) + BYL + + + Hviterussiske rubler + Rbl + + + Beliziske dollar + BZ$ + + + Britisk Honduras-dollar + BZH + + + Kanadiske dollar + Can$ + + + Kongolesiske franc (congolais) + CDF + + + Kongolesiske republikk-franc + CDG + + + Congolesiske zaire + CDL + + + Sentralafrikanske franc (CFA) + CFF + + + Sveitsiske franc + SwF + + + Cookøyene dollar + CKD + + + Chilenske condor + CLC + + + Chilenske escudo + CLE + + + Chilenske Unidades de Fomento + CLF + + + Chilenske pesos + Ch$ + + + Kamerunske franc (CFA) + CMF + + + Kinesiske Jen Min Piao Yuan + CNP + + + Kinesiske US dollar (FEC) + CNX + + + Kinesiske Yuan Renminbi + Y + + + Colombianske papir-pesos + COB + + + Kongolesiske franc (CFA) + COF + + + Colombianske pesos + Col$ + + + Costaricanske colon + C + + + Tsjekkoslovakiske koruna + CSC + + + Tsjekkoslovakiske koruna (hard) + CSK + + + Kubanske pesos + CUP + + + Kubanske Foreign Exchange Certificates + CUX + + + Kappverdiske escudo + CVEsc + + + Curacao-gylden + CWG + + + Kypriotiske pund + £C + + + Tsjekkiske koruna + CZK + + + Østtyske ostmark + DDM + + + Tyske mark + DEM + + + Tyske sperrmark + DES + + + Djiboutiske franc + DF + + + Danske kroner + DKr + + + Dominikanske pesos + RD$ + + + Algeriske dinarer + DA + + + Algeriske nye franc + DZF + + + Algeriske franc germinal + DZG + + + Ecuadorianske sucre + ECS + + + Ecuadorianske Unidad de Valor Constante (UVC) + ECV + + + Estiske kroon + EEK + + + Egyptiske pund + EGP + + + Eritreiske nakfa + ERN + + + Spanske peseta + ESP + + + Etiopiske birr + Br + + + Etiopiske dollar + ETD + + + Euro + + + + Finske mark + FIM + + + Finske mark (1860-1962) + FIN + + + Fijianske dollar + F$ + + + Fijianske pund + FJP + + + Falklandsøyene-pund + FKP + + + Færøyske kronur + FOK + + + Franske franc + FRF + + + Franske franc (Germinal/Franc Poincare) + FRG + + + Gabonske franc (CFA) + GAF + + + Britiske pund sterling + £ + + + Georgiske kupon larit + GEK + + + Georgiske lari + lari + + + Ghanesiske cedi + GHC + + + Ghanesiske gamle cedi + GHO + + + Ghanesiske pund + GHP + + + Ghanesiske revaluerte cedi + GHR + + + Gibraltarske pund + GIP + + + Grønlandske kroner + GLK + + + Gambiske dalasi + GMD + + + Gambiske pund + GMP + + + Guineanske franc + GF + + + Guineanske franc (1960-1972) + GNI + + + Guineanske syli + GNS + + + Guadeloupe-franc + GPF + + + Ekvatorialguineanske ekwele guineana + GQE + + + Ekvatorialguineanske franco + GQF + + + Ekvatorialguineanske peseta guineana + GQP + + + Greske drakmer + GRD + + + Greske nye drakmer + GRN + + + Guatemalanske quetzal + Q + + + Fransk Guyana-franc guiana + GUF + + + Portugisiske guinea escudo + GWE + + + Portugisiske Guinea Mil Reis + GWM + + + Guinea-Bissau-pesos + GWP + + + Guyanske dollar + G$ + + + Hongkong-dollar + HK$ + + + Hoduras Lempira + L + + + Kroatiske dinarer + HRD + + + Kroatiske kuna + HRK + + + Haitiske gourde + HTG + + + Ungarske forinter + Ft + + + Nordirske pund + IBP + + + Indonesiske nica-gylden + IDG + + + Indonesiske Java-rupier + IDJ + + + Indonesiske nye rupier + IDN + + + Indonesiske rupier + Rp + + + Irske pund + IR£ + + + Israelske shekler + ILL + + + Israelske pund + ILP + + + Israelske nye shekler + ILS + + + Manske pund sterling + IMP + + + Indiske rupier + =0#Rs.|1#Re.|1<Rs. + + + Irakske dinarer + ID + + + Iranske rialer + RI + + + Islandske kronar + ISK + + + Italienske lire + + + + Jersey pund sterling + JEP + + + Jamaikanske dollar + J$ + + + Jamaikanske pund + JMP + + + Jordanske dinarer + JD + + + Japanske yen + ¥ + + + Kenyanske shilling + K Sh + + + Kirgisiske som + som + + + Kambodsjanske gamle riel + KHO + + + Kambodsjanske riel + CR + + + Kiribatiske dollar + KID + + + Komoriske franc + CF + + + Nordkoreanske won (1947-1959) + KPP + + + Nordkoreanske won + KPW + + + Sørkoreanske hwan + KRH + + + Sørkoreanske gamle won + KRO + + + Sørkoreanske won + KRW + + + Kuwaitiske dinarer + KD + + + Caymanske dollar + KYD + + + Kasakhstanske rubler + KZR + + + Kasakhstanske tenge + T + + + Laotiske kip + LAK + + + Libanesiske pund + LL + + + Liechtensteinske franc + LIF + + + Srilankiske rupier + SL Re + + + Ceylonske rupier + LNR + + + Liberiske dollar + LRD + + + Lesothiske loti + M + + + Litauiske lita + LTL + + + Litauiske talonas + LTT + + + Luxemburgske franc + LUF + + + Latviske lats + LVL + + + Latviske rubler + LVR + + + Libyske British Military Authority-lira + LYB + + + Libyske dinarer + LD + + + Libyske pund + LYP + + + Marokkanske dirham + MAD + + + Marokkanske franc + MAF + + + Monegaskiske franc nouveau + MCF + + + Monegaskiske franc germinal + MCG + + + Moldovske leu cupon + MDC + + + Moldovske leu + MDL + + + Moldovske ruble cupon + MDR + + + Madagassiske ariary + MGA + + + Madagassiske franc + MGF + + + Marshalløyene-dollar + MHD + + + Makedonske denarer + MDen + + + Makedonske denarer (1992-1993) + MKN + + + Maliske franc + MLF + + + Myanmarske kyat + MMK + + + Myanmarske dollar (FEC) + MMX + + + Mongolske tugrik + Tug + + + Makaoske pataca + MOP + + + Martinique-franc + MQF + + + Mauritanske ouguiya + UM + + + Maltesiske lira + Lm + + + Maltesiske pund + MTP + + + Mauritiske rupier + MUR + + + Maldiviske rupier + MVP + + + Maldiviske rufiyaa + MVR + + + Malawisle kwacha + MK + + + Malawiske pund + MWP + + + Meksikanske pesos + MEX$ + + + Meksikanske sølvpesos (1861-1992) + MXP + + + Meksikanske Unidad de Inversion (UDI) + MXV + + + Malaysiske ringgit + RM + + + Mosambikiske escudo + MZE + + + Mosambikiske metical + Mt + + + Namibiske dollar + N$ + + + Kaledonske franc germinal + NCF + + + Nigerianske naira + NGN + + + Nigerianske pund + NGP + + + Ny-hebridene CFP-franc + NHF + + + Nicaraguanske cordoba + NIC + + + Nicaraguanske gullcordoba + NIG + + + Nicaraguanske cordoba oro + NIO + + + Nederlandske gylden + NLG + + + Norske kroner + kr + + + Nepalesiske rupier + Nrs + + + Nyzealandske dollar + $NZ + + + Nyzealandske pund + NZP + + + Omanske rialer + RO + + + Omanske rial saidi + OMS + + + Panamanske balboa + PAB + + + Transdniestriansk rubler (kupon) + PDK + + + Transdniestrianske nye rubler + PDN + + + Transdniestrianske rubler + PDR + + + Peruvianske inti + PEI + + + Peruvianske sol nuevo + PEN + + + Peruvianske sol + PES + + + Papuanske kina + PGK + + + Filippinske pesos + PHP + + + Pakistanske rupier + Pra + + + Polske zloty + Zl + + + Polske US dollar (FEC) + PLX + + + Polske zloty (1950-1995) + PLZ + + + Palestinske pund + PSP + + + Portugisiske conto + PTC + + + Portugisiske escudo + PTE + + + Paraguayanske guarani + PYG + + + Qatarske riyaler + QR + + + Reunionske franc + REF + + + Rumenske leu + leu + + + Rumenske nye leu + RON + + + Russiske rubler + RUB + + + Russiske rubler (1991-1998) + RUR + + + Rwandiske franc + RWF + + + Saudiarabiske riyaler + SRl + + + Saudiarabiske riyaler (1936-1952) + SAS + + + Salomonske dollar + SI$ + + + Seychelliske rupier + SR + + + Sudanesiske dinarer + SDD + + + Sudanesiske pund + SDP + + + Svenske kroner + SKr + + + Singaporske dollar + S$ + + + Sankthelenske pund + SHP + + + Slovenske tolar bons + SIB + + + Slovenske tolar + SIT + + + Slovakiske koruna + Sk + + + Sierraleonske leone + SLL + + + Sanmarinske lira + SML + + + Somaliske shilling + So. Sh. + + + Somalilandske shilling + SQS + + + Surinamske gylden + Sf + + + Skotske pund + SSP + + + Sao Tome og Principe-dobra + Db + + + Sao Tome og Principe-escudo + STE + + + Sovjetiske nye rubler + SUN + + + Sovjetiske rubler + SUR + + + Salvadoranske colon + SVC + + + Syriske pund + LS + + + Swazilandske lilangeni + E + + + Turks- og Caicosøyene-crown + TCC + + + Tsjadiske franc (CFA) + TDF + + + Thailandske baht + THB + + + Tadsjikiske rubler + TJR + + + Tadsjikiske somoni + TJS + + + Turkmenske manat + TMM + + + Tunisiske dinarer + TND + + + Tonganske paʻanga + T$ + + + Tonganske pund sterling + TOS + + + Timoresiske escudo + TPE + + + Timoresiske pataca + TPP + + + Tyrkiske lira + TL + + + Trinidadiske dollar + TT$ + + + Trinidadiske gamle dollar + TTO + + + Tuvalske dollar + TVD + + + Taiwanske nye dollar + NT$ + + + Tanzanianske shilling + T Sh + + + Ukrainsle hryvnia + UAH + + + Ukrainske karbovanetz + UAK + + + Ugandiske shilling (1966-1987) + UGS + + + Ugandiske shilling + U Sh + + + Amerikanske dollar + US$ + + + Amerikanske dollar (neste dag) + USN + + + Amerikanske dollar (samme dag) + USS + + + Uruguayanske peso fuerte + UYF + + + Uruguayanske pesos (1975-1993) + UYP + + + Uruguayanske peso uruguayo + Ur$ + + + Usbekiske kupong-som + UZC + + + Usbekiske sum + UZS + + + Vatikanstatens lira + VAL + + + Nordvietnamesiske piastre dong viet + VDD + + + Nordvietnamesiske nye dong + VDN + + + Nordvietnamesiske viet minh piastre dong viet + VDP + + + Venezuelanske bolivar + Be + + + De britiske jomfruøyene-dollar + VGD + + + Vietnamesiske dong + VND + + + Vietnamesiske nye dong + VNN + + + Vietnamesiske republikk-dong + VNR + + + Vietnamesiske nasjonale dong + VNS + + + Vanuatisk vatu + VT + + + Vestsamoisk pund + WSP + + + Vestsamoisk tala + WST + + + Asian Dinar Unit of Account + XAD + + + CFA Franc BEAC + XAF + + + Asian Monetary Unit + XAM + + + Gull + XAU + + + European Composite Unit + XBA + + + European Monetary Unit + XBB + + + European Unit of Account (XBC) + XBC + + + European Unit of Account (XBD) + XBD + + + Østkaribiske dollar + EC$ + + + CFA Nouveau Franc + XCF + + + Special Drawing Rights + XDR + + + CFA Franc BCEAEC + XEF + + + European Currency Unit + XEU + + + French Gold Franc + XFO + + + French UIC-Franc + XFU + + + Islamske dinarer + XID + + + French Metropolitan Nouveau Franc + XMF + + + Franske antiller-franc (CFA) + XNF + + + CFA Franc BCEAO + XOF + + + CFP Franc + CFPF + + + COMECON Transferable Ruble + XTR + + + Jemenittiske dinarer + YDD + + + Jemenittiske imadi-riyaler + YEI + + + Jemenittiske rialer + YRl + + + Jugoslaviske dinarer (hard) + YUD + + + Jugoslaviske føderasjonen-dinarer + YUF + + + Jugoslaviske 1994-dinarer + YUG + + + Jugoslaviske noviy-dinarer + YUM + + + Jugoslaviske konvertible dinarer + YUN + + + Jugoslaviske oktoberdinarer + YUO + + + Jugoslaviske reforerte dinarer + YUR + + + Sørafrikanske rand (økonomisk) + ZAL + + + Sørafrikanske pund + ZAP + + + Sørafrikanske rand + R + + + Zambiske kwacha + ZMK + + + Zambiske pund + ZMP + + + Zairiske nye zaire + ZRN + + + Zairiske zaire + ZRZ + + + Zimbabwiske dollar + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/om_ET.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/om_ET.xml --- zope3-3.4.0/src/zope/i18n/locales/data/om_ET.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/om_ET.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,107 @@ + + + + + + + + + + + + + WD + WB + + + + + EEEE, MMMM d, yyyy + + + + + dd MMMM yyyy + + + + + dd-MMM-yy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + ETB + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/om_KE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/om_KE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/om_KE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/om_KE.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + + + + + + + + + WD + WB + + + + + EEEE, MMMM d, yyyy + + + + + dd MMMM yyyy + + + + + dd-MMM-yy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/om.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/om.xml --- zope3-3.4.0/src/zope/i18n/locales/data/om.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/om.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + Oromoo + + + Itoophiyaa + Keeniyaa + + + + [a-z] + + + + + + + + Ama + Gur + Bit + Elb + Cam + Wax + Ado + Hag + Ful + Onk + Sad + Mud + + + Amajjii + Guraandhala + Bitooteessa + Elba + Caamsa + Waxabajjii + Adooleessa + Hagayya + Fuulbana + Onkololeessa + Sadaasa + Muddee + + + + + + + Dil + Wix + Qib + Rob + Kam + Jim + San + + + Dilbata + Wiixata + Qibxata + Roobii + Kamiisa + Jimaata + Sanbata + + + + + + + + + + KD + KB + + + + + + + + + KES + Ksh + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/pa_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/pa_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/pa_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/pa_IN.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-M-yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤##,##,##0.00;-¤##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/pa.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/pa.xml --- zope3-3.4.0/src/zope/i18n/locales/data/pa.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/pa.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,132 @@ + + + + + + + + + + + ਪੰਜਾਬੀ + + + ਭਾਰਤ + + + + [[:Guru:]‌‍] + + + + + + + + ਜਨਵਰੀ + ਫ਼ਰਵਰੀ + ਮਾਰਚ + ਅਪ੍ਰੈਲ + ਮਈ + ਜੂਨ + ਜੁਲਾਈ + ਅਗਸਤ + ਸਤੰਬਰ + ਅਕਤੂਬਰ + ਨਵੰਬਰ + ਦਸੰਬਰ + + + ਜਨਵਰੀ + ਫ਼ਰਵਰੀ + ਮਾਰਚ + ਅਪ੍ਰੈਲ + ਮਈ + ਜੂਨ + ਜੁਲਾਈ + ਅਗਸਤ + ਸਤੰਬਰ + ਅਕਤੂਬਰ + ਨਵੰਬਰ + ਦਸੰਬਰ + + + + + + + ਐਤ. + ਸੋਮ. + ਮੰਗਲ. + ਬੁਧ. + ਵੀਰ. + ਸ਼ੁਕਰ. + ਸ਼ਨੀ. + + + ਐਤਵਾਰ + ਸੋਮਵਾਰ + ਮੰਗਲਵਾਰ + ਬੁਧਵਾਰ + ਵੀਰਵਾਰ + ਸ਼ੁੱਕਰਵਾਰ + ਸ਼ਨੀਚਰਵਾਰ + + + + ਸਵੇਰੇ + ਸ਼ਾਮ + + + + + + . + , + ; + % + + # + + + - + E + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + + ਰੁਪਿਯ + ਰੁ. + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/pl_PL.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/pl_PL.xml --- zope3-3.4.0/src/zope/i18n/locales/data/pl_PL.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/pl_PL.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/pl.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/pl.xml --- zope3-3.4.0/src/zope/i18n/locales/data/pl.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/pl.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,492 @@ + + + + + + + + + + + arabski + bułgarski + czeski + duński + niemiecki + grecki + angielski + hiszpański + estoński + fiński + francuski + hebrajski + chorwacki + węgierski + włoski + japoński + koreański + litewski + łotewski + holenderski + norweski + polski + portugalski + rumuński + rosyjski + słowacki + słoweński + szwedzki + turecki + chiński + + + Andora + Zjednoczone Emiraty Arabskie + Afganistan + Antigua i Barbuda + Anguilla + Albania + Armenia + Antyle Holenderskie + Angola + Antarktyka + Argentyna + Samoa Amerykańskie + Austria + Australia + Aruba + Azerbejdżan + Bośnia i Hercegowina + Barbados + Bangladesz + Belgia + Burkina Faso + Bułgaria + Bahrajn + Burundi + Benin + Bermudy + Brunei Darussalam + Boliwia + Brazylia + Bahamy + Bhutan + Wyspa Bouveta + Botswana + Białoruś + Belize + Kanada + Wyspy Kokosowe (Keelinga) + Kongo, Republika Demokratyczna + Republika Środkowej Afryki + Kongo + Szwajcaria + Wybrzeże Kości Słoniowej + Wyspy Cooka + Chile + Kamerun + Chiny + Kolumbia + Kostaryka + Kuba + Wyspy Zielonego Przylądka + Wyspa Bożego Narodzenia + Cypr + Republika Czeska + Niemcy + Dżibuti + Dania + Dominika + Republika Dominikańska + Algieria + Ekwador + Estonia + Egipt + Sahara Zachodnia + Erytrea + Hiszpania + Etiopia + Finlandia + Fidżi + Falklandy (Malwiny) + Mikronezja, Stany Sfederowane + Wyspy Owcze + Francja + en + Gabon + Wielka Brytania + Grenada + Gruzja + Gujana Francuska + Ghana + Gibraltar + Grenlandia + Gambia + Gwinea + Gwadelupa + Gwinea Równikowa + Grecja + Wyspy Georgia Południowa i Sandwich Południowy + Gwatemala + Guam + Gwinea Bissau + Gujana + Hongkong, Specjalny Region Administracyjny Chin + Wyspy Heard i McDonald + Honduras + Chorwacja + Haiti + Węgry + Indonezja + Irlandia + Izrael + Indie + Terytorium Brytyjskie Oceanu Indyjskiego + Irak + Iran + Islandia + Włochy + Jamajka + Jordania + Japonia + Kenia + Kirgistan + Kambodża + Kiribati + Komory + Saint Kitts i Nevis + Korea Północna + Korea Południowa + Kuwejt + Kajmany + Kazachstan + Laos (Demokratyczna Republika Ludowa) + Liban + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Litwa + Luksemburg + Łotwa + Libijska + Maroko + Monako + Mołdawia, Republika + Madagaskar + Wyspy Marshalla + Macedonia, Republika + Mali + Birma + Mongolia + Makau, Specjalny Region Administracyjny Chin + Wspólnota Marianów Północnych + Martynika + Mauretania + Montserrat + Malta + Mauritius + Malediwy + Malawi + Meksyk + Malezja + Mozambik + Namibia + Nowa Kaledonia + Niger + Wyspa Norfolk + Nigeria + Nikaragua + Holandia + Norwegia + Nepal + Nauru + Niue + Nowa Zelandia + Oman + Panama + Peru + Polinezja Francuska + Papua Nowa Gwinea + Filipiny + Pakistan + Polska + St. Pierre i Miquelon + Pitcairn + Puerto Rico + Terytoria Palestyńskie + Portugalia + Palau + Paragwaj + Katar + Reunion + Rumunia + Federacja Rosyjska + Rwanda + Arabia Saudyjska + Wyspy Salomona + Seszele + Sudan + Szwecja + Singapur + Wyspa Świętej Heleny + Słowenia + Svalbard i Wyspy Jan Mayen + Słowacja + Sierra Leone + San Marino + Senegal + Somalia + Serbia + Surinam + Wyspy Świętego Tomasza i Książęca + Salwador + Syria + Suazi + Turks i Caicos + Czad + Francuskie Terytoria Południowe + Togo + Tajlandia + Tadżykistan + Tokelau + Timor Wschodni + Turkmenia + Tunezja + Tonga + Turcja + Trinidad i Tobago + Tuvalu + Tajwan + Tanzania + Ukraina + Uganda + United States Minor Outlying Islands + Stany Zjednoczone + Urugwaj + Uzbekistan + Stolica Apostolska (Państwo Watykańskie) + Saint Vincent and the Grenadines + Wenezuela + Brytyjskie Wyspy Dziewicze + Wyspy Dziewicze, Stanów Zjednoczonych + Wietnam + Vanuatu + Wallis i Futuna + Samoa + Jemen + Majotta + Jugosławia + Afryka Południowa + Zambia + Zimbabwe + + + + [a-z ó ą ę ć ń ś ź ł ż] + + + + + + + + st + lut + mrz + kw + maj + cz + lip + sier + wrz + paź + lis + gr + + + s + l + m + k + m + c + l + s + w + p + l + g + + + stycznia + lutego + marca + kwietnia + maja + czerwca + lipca + sierpnia + września + października + listopada + grudnia + + + + + st + lut + mrz + kw + maj + cz + lip + sier + wrz + paź + lis + gr + + + s + l + m + k + m + c + l + s + w + p + l + g + + + Styczeń + Luty + Marzec + Kwiecień + Maj + Czerwiec + Lipiec + Sierpień + Wrzesień + Październik + Listopad + Grudzień + + + + + + + N + Pn + Wt + Śr + Cz + Pt + So + + + niedziela + poniedziałek + wtorek + środa + czwartek + piątek + sobota + + + + + + + + + + p.n.e. + n.e. + + + + + + + EEEE, d MMMM yyyy + + + + + d MMMM yyyy + + + + + yyyy-MM-dd + + + + + yy-MM-dd + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + PLN + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ps_AF.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ps_AF.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ps_AF.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ps_AF.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + ٫ + ٬ + ; + ٪ + ۰ + # + + + + ×۱۰^ + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0 ¤;-#,##0 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ps.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ps.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ps.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ps.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,259 @@ + + + + + + + + + + + عربي + بلوڅي + الماني + یوناني + انګلیسي + حبشي + فارسي + فینلنډي + فرانسوي + عبري + هندي + ارمني + هند و اروپایي + ایټالوي + جاپانی + کردي + لاتیني + ملغاسي + مقدوني + مغولي + ملایا + پولنډي + پښتو + پورتګالي + روسي + سنسکریټ + سویډنی + تاجک + ترکمني + تاتار + ازبکي + چیني + + + افغانستان + البانیه + انګولا + انتارکتیکا + اتریش + بنګله‌دیش + بلغاریه + کاناډا + سویس + چین + کولمبیا + کیوبا + المان + ډنمارک + الجزایر + مصر + هسپانیه + حبشه + فنلینډ + فرانسه + برتانیه + ګانا + ګیانا + یونان + ګواتیمالا + هانکانګ + هانډوراس + مجارستان + اندونیزیا + هند + عراق + آیسلینډ + ایټالیه + جمیکا + جاپان + کمبودیا + کویټ + لاوس + لبنان + لایبریا + لیبیا + مراکش + مغولستان + مالیزیا + نایجیریا + نکاراګوا + هالېنډ + ناروې + نیپال + نیوزیلنډ + پاکستان + پولنډ + فلسطین + پورتګال + روسیه + روندا + سعودی عربستان + سویډن + سالوېډور + سوریه + تاجکستان + تنزانیا + یوروګوای + یمن + + + + + + + [ء-ؤئ-غفقل-وي-ْٰٔټپځڅ-چډړږژښکګڼی-ۍې ‌‍‏‎] + + + + + + + + جنو + فبر + مار + اپر + مـی + جون + جول + اګس + سپت + اکت + نوم + دسم + + + جنوري + فبروري + مارچ + اپریل + می + جون + جولای + اګست + سپتمبر + اکتوبر + نومبر + دسمبر + + + + + + + ی. + د. + س. + چ. + پ. + ج. + ش. + + + یکشنبه + دوشنبه + سه‌شنبه + چهارشنبه + پنجشنبه + جمعه + شنبه + + + + غ.م. + غ.و. + + + ق.م. + م. + + + + + + + EEEE د yyyy د MMMM d + + + + + د yyyy د MMMM d + + + + + d MMMM yyyy + + + + + yyyy/M/d + + + + + + + + H:mm:ss (z) + + + + + H:mm:ss (z) + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + د افغانستان په وخت + د افغانستان په وخت + + + AFT + AFT + + کابل + + + + + + + افغانۍ + افغانۍ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/pt_BR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/pt_BR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/pt_BR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/pt_BR.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + EEEE, d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yy + + + + + + + + HH'h'mm'min'ss's' z + + + + + H'h'm'min's's' z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/pt_PT.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/pt_PT.xml --- zope3-3.4.0/src/zope/i18n/locales/data/pt_PT.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/pt_PT.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,186 @@ + + + + + + + + + + + + árabe + checo + estónio + letão + polaco + esloveno + + + Emiratos Árabes Unidos + Antígua e Barbuda + Arménia + Antárctica + Azerbeijão + Bósnia-Herzegovina + Benim + Bielorrússia + Camarões + Ilha do Natal + República Checa + Estónia + Egipto + Sahara Ocidental + Eritreia + Ilhas Falkland + Gronelândia + Ilhas South Georgia e South Sandwich + Guiné-Bissau + Hong Kong - Região Administrativa Especial da China + Quénia + Quirguizistão + Camboja + Saint Kitts e Nevis + Coreia do Norte + Coreia do Sul + Ilhas Caimão + Cazaquistão + Lao, República Popular Democrática + Letónia + Mónaco + Moldávia, República da + Madagáscar + Macedónia, República da + Macau - Região Administrativa Especial da China + Ilhas Mariana do Norte + Maurícias + Nova Caledónia + Papua Nova Guiné + Polónia + Território Palestiniano + Reunion + Roménia + Seicheles + Singapura + Eslovénia + São Marino + Ilhas Turcas e Caicos + Tchade + Territórios Franceses a Sul + Tajiquistão + Turquemenistão + Formosa, Província Chinesa + Ilhas Minor Outlying (E.U.A) + Uzbaquistão + Santa Sé (Estado da Cidade do Vaticano) + Saint Vincent e Grenadines + Ilhas Virgin Britânicas + Ilhas Virgin E.U.A. + Vietname + Iémen + Jugoslávia + + + + + + + + + + + + + + EEEE, d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + yyyy/MM/dd + + + + + yy/MM/dd + + + + + + + + HH'H'mm'm'ss's' z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + + Escudo português + Esc. + #,##0.00 ¤;-#,##0.00 ¤ + #,##0.00 ¤;-#,##0.00 ¤ + , + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/pt.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/pt.xml --- zope3-3.4.0/src/zope/i18n/locales/data/pt.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/pt.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2789 @@ + + + + + + + + + + + afar + abkhazian + achinese + acoli + adangme + adyghe + avéstico + africâner + afro-asiático (outros) + Afrihili + Akan + acadiano + aleúte + idiomas algonquianos + amárico + aragonês + inglês, arcaico (aprox. 450-1100) + idiomas apache + árabe + aramaico + araucano + arapaho + artificiais (outros) + arauaqui + assamês + asturiano + idiomas atabascanos + idiomas australianos + avaric + Awadhi + aimara + azerbaijano + bashkir + banda + bamileke Languages + balúchi + bambara + balinês + basa + bálticos (outros) + bielo-russo + beja + bemba + berbere + búlgaro + biari + bhojpuri + bislamá + bikol + bini + siksika + bambara + bengali + banto + tibetano + bretão + braj + bósnio + bataque + Buriat + Buginese + Blin + catalão + caddo + indígenas centro-americanos (outros) + caribe + caucasianos (outros) + chechene + cebuano + célticos (outros) + chamorro + chibcha + chagatai + chuukese + mari + chinook jargon + choctaw + chipewyan + cheroqui + cheiene + chamic languages + córsico + copta + crioulos e pídgin, inglês (outros) + crioulos e pídgin, francês (outros) + crioulos e pídgin, português (outros) + cree + crimean turkish; crimean tatar + crioulos e pídgins (outros) + tcheco + kashubian + eslavo eclesiástico + cuxitas (outros) + chuvash + galês + dinamarquês + dacota + dargwa + dayak + alemão + delaware + slave + dogrib + dinka + dogri + dravídicos (outros) + Lower Sorbian + duala + holandês, medieval (aprox. 1050-1350) + divehi + diúla + dzonga + eve + efique + egípcio (arcaico) + ekajuk + grego + elamite + inglês + inglês, medieval (1100-1500) + esperanto + espanhol + estoniano + basco + ewondo + persa + fangue + fanti + fula + finlandês + ugro-finês (outros) + fijiano + feroês + fom + francês + francês, medieval (aprox.1400-1600) + francês, arcaico (842-aprox.1400) + friulano + frisão + irlandês + ga + gayo + gbaia + gaélico escocês + germânicos (outros) + geez + gilbertês + galego + alemão, medieval alto (aprox.1050-1500) + guarani + alemão, arcaico alto (aprox.750-1050) + gondi + gorontalo + gótico + Gerbo + grego, arcaico (até 1453) + guzerate + manx + gwichʻin + hauçá + haida + havaiano + hebraico + hindi + hiligaynon + himachali + hitita + hmong + hiri motu + croata + upper sorbian + haitiano + húngaro + hupa + armênio + herero + interlíngua + Iban + indonésio + interlingue + ibo + sichuan yi + Inupiaq + ilocano + índicos (outros) + indo-europeus (outros) + inguche + ido + iraniano + idiomas iroqueses + islandês + italiano + inuktitut + japonês + lojban + judaico-persa + judaico-arábico + georgiano + kara-Kalpak + kabyle + kachin + kamba + karen + kawi + kabardian + congolês + khasi + khoisan (other) + khotanese + quicuio + Kuanyama + cazaque + groenlandês + cmer + quimbundo + canarês + coreano + concani + kosraean + kpelle + canúri + karachay-Balkar + kru + kurukh + kashmiri + curdo + kumyk + kutenai + komi + córnico + quirguiz + latim + ladino + lahnda + lamba + luxemburguês + lezghian + luganda + limburgish + lingala + laosiano + mongo + lozi + lituano + luba-catanga + luba-Lulua + luiseno + lunda + lushai + letão + madurês + magahi + maithili + makasar + mandinga + austronésio + massai + mocsa + mandar + mende + malgaxe + irlandês, medieval (900-1200) + marshallês + maori + miquemaque + minangkabau + idiomas diversos + macedônio + mon-khmer (other) + malaiala + mongol + manchu + manipuri + manobo languages + moldávio + mohawk + mossi + marata + malaio + maltês + idiomas múltiplos + idiomas munda + creek + marwari + birmanês + maia + erzya + nauruano + náuatle + indígenas norte-americanos (outros) + napolitano + bokmål norueguês + ndebele, north + alto alemão; baixo saxão + nepali + newari + dongo + nias + niger - kordofanian (other) + niueano + holandês + nynorsk norueguês + norueguês + nogai + norse, old + ndebele, south + soto, setentrional + idiomas núbios + navajo + nianja; chicheua; cheua + nyamwezi + nyankole + nyoro + nzima + occitânico (após 1500); provençal + ojibwa + oromo + oriya + ossetic + osage + turco, otomano (1500-1928) + idiomas otomanos + panjabi + papuanos (outros) + pangasinã + pálavi + pampanga + papiamento + palauano + persa arcaico (aprox. 600-400 a.C.) + filipinos (outros) + fenício + páli + polonês + pohnpeian + idiomas prácrito + provençal, arcaico (até 1500) + pashto (pushto) + português + quíchua + rajastani + rapanui + rarotongano + rhaeto-romance + rundi + romeno + romances (outros) + romani + russo + kinyarwanda + sânscrito + sandawe + iacuto + indígenas sul-americanos (outros) + salishan languages + aramaico samaritano + sasak + santali + sardo + escocês + sindi + northern sami + selkup + semíticos (outros) + sango + irlandês, arcaico (até 900) + linguages de sinais + servo-croata + shan + cingalês + sidamo + idiomas sioux + sino-tibetanos (outros) + eslovaco + eslovênio + somali + sogdien + songai + albanês + sérvio + serere + swati + nilo-saarianos (outros) + soto, do sul + sundanês + sukuma + sosso + sumério + sueco + suaíli + siríaco + tâmil + tai (outros) + telugu + timne + tereno + tétum + tadjique + tailandês + tigrínia + tigré + turcomano + toquelauano + tlinguite + tamaxeque + tswana + tonga (ilhas tonga) + toganês (Nyasa) + tok pisin + turco + tsonga + tsimshian + tatar + tumbuka + idiomas tupi + altaicos (outros) + tuvaluano + twi + taitiano + tuvinian + udmurt + uighur + ugarítico + ucraniano + umbundu + indeterminado + urdu + usbeque + venda + vietnamita + volapuque + votic + walloon + wakashan languages + walamo + waray + washo + sorbian languages + uolofe + kalmyk + xosa + iao + yapese + iídiche + ioruba + idiomas iúpique + zhuang + zapoteca + zenaga + chinês + zande + zulu + zunhi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + Emirados Árabes Unidos + Afeganistão + Antígua e Barbuda + Anguilla + Albânia + Armênia + Antilhas Holandesas + Angola + Antártida + Argentina + Samoa Americana + Áustria + Austrália + Aruba + Azerbaijão + Bósnia-Herzegóvina + Barbados + Bangladesh + Bélgica + Burquina Faso + Bulgária + Bareine + Burundi + Benin + Bermudas + Brunei + Bolívia + Brasil + Bahamas + Butão + Ilha Bouvet + Botsuana + Belarus + Belize + Canadá + Ilhas Cocos (Keeling) + Congo, República Democrática do + República Centro-Africana + Congo + Suíça + Costa do Marfim + Ilhas Cook + Chile + República dos Camarões + China + Colômbia + Costa Rica + Cuba + Cabo Verde + Ilhas Natal + Chipre + República Tcheca + Alemanha + Djibuti + Dinamarca + Dominica + República Dominicana + Argélia + Equador + Estônia + Egito + Saara Ocidental + Eritréia + Espanha + Etiópia + Finlândia + Fiji + Ilhas Malvinas + Micronésia, Estados Federados da + Ilhas Faroe + França + en + Gabão + Reino Unido + Granada + Geórgia + Guiana Francesa + Gana + Gibraltar + Groênlandia + Gâmbia + Guiné + Guadalupe + Guiné Equatorial + Grécia + Geórgia do Sul e Ilhas Sandwich do Sul + Guatemala + Guam + Guiné Bissau + Guiana + Hong Kong, Região Admin. Especial da China + Ilha Heard e Ilhas McDonald + Honduras + Croácia + Haiti + Hungria + Indonésia + Irlanda + Israel + Índia + Território Britânico do Oceano Índico + Iraque + Irã + Islândia + Itália + Jamaica + Jordânia + Japão + Quênia + Quirguistão + Camboja + Quiribati + Comores + São Cristovão e Nevis + Coréia, Norte + Coréia, Sul + Kuwait + Ilhas Caiman + Casaquistão + República Democrática Popular de Lao + Líbano + Santa Lúcia + Liechtenstein + Sri Lanka + Libéria + Lesoto + Lituânia + Luxemburgo + Letônia + Líbia + Marrocos + Mônaco + Moldova, República de + Madagascar + Ilhas Marshall + Macedônia, República da + Mali + Mianmá + Mongólia + Macau, Região Admin. Especial da China + Ilhas Marianas do Norte + Martinica + Mauritânia + Montserrat + Malta + Maurício + Maldivas + Malawi + México + Malásia + Moçambique + Namíbia + Nova Caledônia + Níger + Ilha Norfolk + Nigéria + Nicarágua + Países Baixos + Noruega + Nepal + Nauru + Niue + Nova Zelândia + Omã + Panamá + Peru + Polinésia Francesa + Papua-Nova Guiné + Filipinas + Paquistão + Polônia + Saint Pierre e Miquelon + Pitcairn + Porto Rico + Território da Palestina + Portugal + Palau + Paraguai + Catar + Reunião + Romênia + Rússia + Ruanda + Arábia Saudita + Ilhas Salomão + Seychelles + Sudão + Suécia + Cingapura + Santa Helena + Eslovênia + Svalbard e Jan Mayen + Eslováquia + Serra Leoa + San Marino + Senegal + Somália + Sérvia + Suriname + São Tomé e Príncipe + El Salvador + Síria + Suazilândia + Ilhas Turks e Caicos + Chade + Territórios Franceses do Sul + Togo + Tailândia + Tadjiquistão + Tokelau + Timor Leste + Turcomenistão + Tunísia + Tonga + Turquia + Trinidad e Tobago + Tuvalu + Taiwan + Tanzânia + Ucrânia + Uganda + Ilhas Menores Distantes dos Estados Unidos + Estados Unidos + Uruguai + Uzbequistão + Vaticano + São Vicente e Granadinas + Venezuela + Ilhas Virgens Britânicas + Ilhas Virgens dos EUA + Vietnã + Vanuatu + Wallis e Futuna + Samoa + Iêmen + Mayotte + Iugoslávia + África do Sul + Zâmbia + Zimbábwe + + + Revisado + + + Calendário + Intercalação + Moeda + + + Calendário Budista + Calendário Chinês + Calendário Gregoriano + Calendário Hebraico + Calendário Islâmico + Calendário Civil Islâmico + Calendário Japonês + Ordem Direta + Ordem de Lista Telefônica + Ordem Pin-yin + Ordem dos Traços + Ordem Tradicional + + + + [a-zãõçáéíóúàâêôüò] + + + + + + + + jan + fev + mar + abr + mai + jun + jul + ago + set + out + nov + dez + + + J + F + M + A + M + J + J + A + S + O + N + D + + + janeiro + fevereiro + março + abril + maio + junho + julho + agosto + setembro + outubro + novembro + dezembro + + + + + + + dom + seg + ter + qua + qui + sex + sáb + + + D + S + T + Q + Q + S + S + + + domingo + segunda-feira + terça-feira + quarta-feira + quinta-feira + sexta-feira + sábado + + + + + + a.C. + d.C. + + + + + + + EEEE, d' de 'MMMM' de 'yyyy + + + + + d' de 'MMMM' de 'yyyy + + + + + d/MMM/yyyy + + + + + dd-MM-yyyy + + + + + + + + HH'H'mm'm'ss's' z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + Horário Padrão do Pacífico + Horário de Verão do Pacífico + + + PST + PDT + + Los Angeles + + + + Horário Padrão do Pacífico + Horário de Verão do Pacífico + + + PST + PDT + + Los Angeles + + + + Horário Padrão Montanha + Horário de Verão Montanha + + + MST + MDT + + Denver + + + + Horário Padrão Montanha + Horário de Verão Montanha + + + MST + MDT + + Denver + + + + Horário Padrão Montanha + Horário Padrão Montanha + + + MST + MST + + Phoenix + + + + Horário Padrão Montanha + Horário Padrão Montanha + + + MST + MST + + Phoenix + + + + Horário Padrão Central + Horário de Verão Central + + + CST + CDT + + Chicago + + + + Horário Padrão Central + Horário de Verão Central + + + CST + CDT + + Chicago + + + + Horário Padrão Oriental + Horário de Verão Oriental + + + EST + EDT + + Nova Iorque + + + + Horário Padrão Oriental + Horário de Verão Oriental + + + EST + EDT + + Nova Iorque + + + + Horário Padrão Oriental + Horário Padrão Oriental + + + EST + EST + + Indianapolis + + + + Horário Padrão Oriental + Horário Padrão Oriental + + + EST + EST + + Indianápolis + + + + Horário Padrão do Havaí + Horário Padrão do Havaí + + + HST + HST + + Honolulu + + + + Horário Padrão do Havaí + Horário Padrão do Havaí + + + HST + HST + + Honolulu + + + + Horário Padrão do Alasca + Horário de Verão do Alasca + + + AST + ADT + + Anchorage + + + + Horário Padrão do Alasca + Horário de Verão do Alasca + + + AST + ADT + + Anchorage + + + + Horário Padrão Atlântico + Horário de Verão Atlântico + + + AST + ADT + + Halifax + + + + Horário Padrão de Terra Nova + Horário de Verão de Terra Nova + + + CNT + CDT + + St. Johns + + + + Horário Padrão de Terra Nova + Horário de Verão de Terra Nova + + + CNT + CDT + + St. Johns + + + + Horário Padrão Europa Central + Horário de Verão Europa Central + + + CET + CEST + + Paris + + + + Horário Padrão Europa Central + Horário de Verão Europa Central + + + CET + CEST + + Paris + + + + Horário do Meridiano de Greenwich + Horário do Meridiano de Greenwich + + + GMT + GMT + + Londres + + + + Horário do Meridiano de Greenwich + Horário do Meridiano de Greenwich + + + GMT + GMT + + Casablanca + + + + Horário Padrão de Israel + Horário de Verão de Israel + + + IST + IDT + + Jerusalém + + + + Horário Padrão do Japão + Horário Padrão do Japão + + + JST + JST + + Tóquio + + + + Horário Padrão do Japão + Horário Padrão do Japão + + + JST + JST + + Tóquio + + + + Horário Padrão da Europa Oriental + Horário de Verão da Europa Oriental + + + EET + EEST + + Bucareste + + + + Horário Padrão da China + Horário Padrão da China + + + CTT + CDT + + Xangai + + + + Horário Padrão da China + Horário Padrão da China + + + CTT + CDT + + Xangai + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + Diner de Andorra + ADD + + + Peseta de Andorra + ADP + + + Dirém dos Emirados Árabes Unidos + AED + + + Afegane (1927-2002) + AFA + + + Afegane + AFA + + + Franco de Affars e Issas + AIF + + + Lek Albanês + ALL + + + Lek Valute Albanês + ALV + + + Certificados de câmbio albaneses em dólares + ALX + + + Dram Arménio + AMD + + + Guilder das Antilhas Holandesas + ANG + + + Cuanza angolano + AOA + + + Cuanza angolano (1977-1990) + AOK + + + Cuanza novo angolano (1990-2000) + AON + + + Cuanza angolano reajustado (1995-1999) + AOR + + + Escudo angolano + AOS + + + Austral argentino + ARA + + + Peso moneda nacional argentino + ARM + + + Peso argentino (1983-1985) + ARP + + + Peso argentino + ARS + + + Xelim austríaco + ATS + + + Dólar australiano + AUD + + + Libra australiana + AUP + + + Guilder de Aruba + AWG + + + Manat azerbaijano + AZM + + + Dinar da Bósnia-Herzegóvina + BAD + + + Marco bósnio-herzegóvino conversível + BAM + + + Dinar novo da Bósnia-Herzegóvina + BAN + + + Dólar de Barbados + BBD + + + Taka de Bangladesh + BDT + + + Franco belga (conversível) + BEC + + + Franco belga + BEF + + + Franco belga (financeiro) + BEL + + + Lev forte búlgaro + BGL + + + Lev socialista búlgaro + BGM + + + Lev novo búlgaro + BGN + + + Lev búlgaro (1879-1952) + BGO + + + Certificados de câmbio búlgaros em leva + BGX + + + Dinar bareinita + BHD + + + Franco do Burundi + BIF + + + Dólar das Bermudas + BMD + + + Libra das Bermudas + BMP + + + Dólar do Brunei + BND + + + Boliviano + BOB + + + Boliviano (1863-1962) + BOL + + + Peso boliviano + BOP + + + Mvdol boliviano + BOV + + + Cruzeiro novo brasileiro(1967-1986) + BRB + + + Cruzado brasileiro + BRC + + + Cruzeiro brasileiro (1990-1993) + BRE + + + Real brasileiro + R$ + + + Cruzado novo brasileiro + BRN + + + Cruzeiro brasileiro + BRR + + + Cruzeiro brasileiro (1942-1967) + BRZ + + + Dólar das Bahamas + BSD + + + Libra das Bahamas + BSP + + + Ngultrum do Butão + BTN + + + Rupia do Butão + BTR + + + Kyat birmanês + BUK + + + Rupia birmanesa + BUR + + + Pula botsuanesa + BWP + + + Rublo novo bielo-russo (1994-1999) + BYB + + + Rublo bielo-russo (1992-1994) + BYL + + + Rublo bielo-russo + BYR + + + Dólar do Belize + BZD + + + Dólar de Honduras Britânica + BZH + + + Dólar canadense + CAD + + + Franco congolês + CDF + + + Franco da República do Congo + CDG + + + Zaire congolês + CDL + + + Franco da República Centro-Africana CFA + CFF + + + Franco suíço + CHF + + + Dólar das Ilhas Cook + CKD + + + Condor chileno + CLC + + + Escudo chileno + CLE + + + Unidades de Fomento chilenas + CLF + + + Peso chileno + CLP + + + Franco dos Camarões CFA + CMF + + + Jen Min Piao Yuan chinês + CNP + + + Certificados de câmbio chineses em dólares dos EUA + CNX + + + Yuan Renminbi chinês + CNY + + + Peso de Papel colombiano + COB + + + Franco do Congo CFA + COF + + + Peso colombiano + COP + + + Colon da Costa Rica + CRC + + + Coroa checoslovaca + CSC + + + Coroa Forte checoslovaca + CSK + + + Peso cubano + CUP + + + Certificados de câmbio cubanos + CUX + + + Escudo cabo-verdiano + CVE + + + Guilder de Curaçau + CWG + + + Libra de Chipre + CYP + + + Coroa da República Checa + CZK + + + Ostmark da Alemanha Oriental + DDM + + + Marco alemão + DEM + + + Sperrmark alemão + DES + + + Franco do Djibuti + DJF + + + Coroa dinamarquesa + DKK + + + Peso dominicano + DOP + + + Dinar argelino + DZD + + + Franco Novo argelino + DZF + + + Franco Germinal argelino + DZG + + + Sucre equatoriano + ECS + + + Unidad de Valor Constante (UVC) do Equador + ECV + + + Coroa estoniana + EEK + + + Libra egípcia + EGP + + + Nakfa da Eritréia + ERN + + + Peseta espanhola + ESP + + + Birr etíope + ETB + + + Dólar etíope + ETD + + + Euro + + + + Marca finlandesa + FIM + + + Marca finlandesa (1860-1962) + FIN + + + Dólar de Fiji + FJD + + + Libra de Fiji + FJP + + + Libra das Malvinas + FKP + + + Coroa das Ilhas Feroé + FOK + + + Franco francês + FRF + + + Franco Germinal francês/Franco Poincaré + FRG + + + Franco do Gabão CFA + GAF + + + Libra esterlina britânica + £ + + + Cupom Lari georgiano + GEK + + + Lari georgiano + GEL + + + Cedi de Gana + GHC + + + Cedi Antigo de Gana + GHO + + + Libra de Gana + GHP + + + Cedi reajustado de Gana + GHR + + + Libra de Gibraltar + GIP + + + Coroa de Groenlândia + GLK + + + Dalasi de Gâmbia + GMD + + + Libra de Gâmbia + GMP + + + Franco de Guiné + GNF + + + Franco de Guiné (1960-1972) + GNI + + + Syli de Guiné + GNS + + + Franco de Guadalupe + GPF + + + Ekwele de Guiné Equatorial + GQE + + + Franco de Guiné Equatorial + GQF + + + Peseta Guineana de Guiné Equatorial + GQP + + + Dracma grego + GRD + + + Dracma Novo grego + GRN + + + Quetçal da Guatemala + GTQ + + + Franco da Guiana Francesa + GUF + + + Escudo da Guiné Portuguesa + GWE + + + Mil-réis da Guiné Portuguesa + GWM + + + Peso da Guiné-Bissau + GWP + + + Dólar da Guiana + GYD + + + Dólar de Hong Kong + HKD + + + Lempira de Honduras + HNL + + + Dinar croata + HRD + + + Kuna croata + HRK + + + Gurde do Haiti + HTG + + + Forinte húngaro + HUF + + + Libra da Irlanda do Norte + IBP + + + Guilder Nica indonésio + IDG + + + Rupia Java indonésia + IDJ + + + Rupia Nova indonésia + IDN + + + Rupia indonésia + IDR + + + Libra irlandesa + IEP + + + Sheqel israelita + ILL + + + Libra israelita + ILP + + + Sheqel Novo israelita + ILS + + + Libra esterlina da Ilha de Man + IMP + + + Rupia indiana + =0#Rs.|1#Re.|1<Rs. + + + Dinar iraquiano + IQD + + + Rial iraniano + IRR + + + Coroa islandesa + ISK + + + Lira italiana + + + + Libra esterlina de Jersey + JEP + + + Dólar jamaicano + JMD + + + Libra jamaicana + JMP + + + Dinar jordaniano + JOD + + + Iene japonês + ¥ + + + Xelim queniano + KES + + + Som de Quirguistão + KGS + + + Riel Antigo do Camboja + KHO + + + Riel cambojano + KHR + + + Dólar do Quiribati + KID + + + Franco de Comores + KMF + + + Won da República Popular da Coréia do Norte + KPP + + + Won norte-coreano + KPW + + + Hwan sul-coreano + KRH + + + Won Antigo sul-coreano + KRO + + + Won sul-coreano + KRW + + + Dinar coveitiano + KWD + + + Dólar das Ilhas Caimão + KYD + + + Rublo do Cazaquistão + KZR + + + Tenge do Cazaquistão + KZT + + + Kip de Laos + LAK + + + Libra libanesa + LBP + + + Franco de Liechtenstein + LIF + + + Rupia de Sri Lanka + LKR + + + Rupia do Ceilão + LNR + + + Dólar liberiano + LRD + + + Loti de Lesoto + LSL + + + Lita lituano + LTL + + + Talonas lituano + LTT + + + Franco luxemburguês + LUF + + + Lats letão + LVL + + + Rublo letão + LVR + + + Lira líbia da Autoridade Militar Britânica + LYB + + + Dinar líbio + LYD + + + Libra líbia + LYP + + + Dirém marroquino + MAD + + + Franco marroquino + MAF + + + Franco Novo de Mônaco + MCF + + + Franco Germinal de Mônaco + MCG + + + Cupom leu moldávio + MDC + + + Leu de Moldávia + MDL + + + Cupom rublo molávio + MDR + + + Ariary de Madagascar + MGA + + + Franco de Madagascar + MGF + + + Dólar das Ilhas Marshall + MHD + + + Dinar macedônio + MKD + + + Dinar macedônio (1992-1993) + MKN + + + Franco de Mali + MLF + + + Kyat de Mianmar + MMK + + + Certificados de câmbio birmaneses em dólares + MMX + + + Tugrik mongol + MNT + + + Pataca macaense + MOP + + + Franco da Martinica + MQF + + + Ouguiya da Mauritânia + MRO + + + Lira maltesa + MTL + + + Libra maltesa + MTP + + + Rupia de Maurício + MUR + + + Rupia das Ilhas Maldivas + MVP + + + Rupias das Ilhas Maldivas + MVR + + + Cuacha do Maláui + MWK + + + Libra do Maláui + MWP + + + Peso mexicano + MXN + + + Peso Plata mexicano (1861-1992) + MXP + + + Unidad de Inversion (UDI) mexicana + MXV + + + Ringgit malaio + MYR + + + Escudo de Moçambique + MZE + + + Metical de Moçambique + MZM + + + Dólar da Namíbia + NAD + + + Franco Germinal da Nova Caledônia + NCF + + + Naira nigeriana + NGN + + + Libra nigeriana + NGP + + + Franco CFP das Novas Hébridas + NHF + + + Córdoba nicaraguano + NIC + + + Córdoba Ouro nicaraguano + NIG + + + Córdoba Ouro nicaraguano + NIO + + + Guilder holandês + NLG + + + Coroa norueguesa + NOK + + + Rupia nepalesa + NPR + + + Dólar da Nova Zelândia + NZD + + + Libra da Nova Zelândia + NZP + + + Rial de Omã + OMR + + + Rial Saidi de Omã + OMS + + + Balboa panamenho + PAB + + + Cupom rublo de Transdniestria + PDK + + + Rublo Novo de Transdniestria + PDN + + + Rublo de Transdniestria + PDR + + + Inti peruano + PEI + + + Sol Novo peruano + PEN + + + Sol peruano + PES + + + Kina da Papua-Nova Guiné + PGK + + + Peso filipino + PHP + + + Rupia paquistanesa + PKR + + + Zloti polonês + PLN + + + Certificados de câmbio poloneses em dólares + PLX + + + Zloti polonês (1950-1995) + PLZ + + + Libra palestina + PSP + + + Conto português + PTC + + + Escudo português + Esc. + + + Guarani paraguaio + PYG + + + Rial catariano + QAR + + + Franco de Reunião + REF + + + Leu romeno + ROL + + + Leu Novo romeno + RON + + + Rublo russo + RUB + + + Rublo russo (1991-1998) + RUR + + + Franco ruandês + RWF + + + Rial saudita + SAR + + + Rial Soberano saudita + SAS + + + Dólar das Ilhas Salomão + SBD + + + Rupia das Seychelles + SCR + + + Dinar sudanês + SDD + + + Libra sudanesa + SDP + + + Coroa sueca + SEK + + + Dólar de Cingapura + SGD + + + Libra de Santa Helena + SHP + + + Tolar Bons esloveno + SIB + + + Tolar Bons esloveno + SIT + + + Coroa eslovaca + SKK + + + Leone de Serra Leoa + SLL + + + Lira de San Marino + SML + + + Xelim somali + SOS + + + Xelim de Somalilândia + SQS + + + Guilder do Suriname + SRG + + + Libra escocesa + SSP + + + Dobra de São Tomé e Príncipe + STD + + + Escudo de São Tomé e Príncipe + STE + + + Rublo Novo soviético + SUN + + + Rublo soviético + SUR + + + Colom salvadorenho + SVC + + + Libra síria + SYP + + + Lilangeni da Suazilândia + SZL + + + Coroa de Turcas e Caicos + TCC + + + Franco CFA de Chade + TDF + + + Baht tailandês + THB + + + Rublo do Tadjiquistão + TJR + + + Somoni tadjique + TJS + + + Manat do Turcomenistão + TMM + + + Dinar tunisiano + TND + + + Paʻanga de Tonga + TOP + + + Libra esterlina de Tonga + TOS + + + Escudo timorense + TPE + + + Pataca timorense + TPP + + + Lira turca + TRL + + + Dólar de Trinidad e Tobago + TTD + + + Dólar Antigo de Trinidad e Tobago + TTO + + + Dólar de Tuvalu + TVD + + + Dólar Novo de Taiwan + TWD + + + Xelim de Tanzânia + TZS + + + Hryvnia ucraniano + UAH + + + Karbovanetz ucraniano + UAK + + + Xelim ugandense (1966-1987) + UGS + + + Xelim ugandense + UGX + + + Dólar norte-americano + $ + + + Dólar norte-americano (Dia seguinte) + USN + + + Dólar norte-americano (Mesmo dia) + USS + + + Peso Fuerte uruguaio + UYF + + + Peso uruguaio (1975-1993) + UYP + + + Peso uruguaio + UYU + + + Coupon Som do Usbequistão + UZC + + + Sum do Usbequistão + UZS + + + Lira da Cidade do Vaticano + VAL + + + Piastre Dong Viet do Vietnã do Norte + VDD + + + Dong Novo do Vietnã do Norte + VDN + + + Viet Minh Piastre Dong Viet do Vietnã do Norte + VDP + + + Bolívar venezuelano + VEB + + + Dólar das Ilhas Virgens Britânicas + VGD + + + Dong vietnamita + đ + + + Dong Novo vietnamita + VNN + + + Dong da República do Vietnã + VNR + + + Dong Nacional vietnamita + VNS + + + Vatu de Vanuatu + VUV + + + Libra de Samoa Ocidental + WSP + + + Tala de Samoa Ocidental + WST + + + Unidade de Conta asiática em dinares + XAD + + + Franco CFA BEAC + XAF + + + Unidade Monetária Asiática + XAM + + + Ouro + XAU + + + Unidade Composta Européia + XBA + + + Unidade Monetária Européia + XBB + + + Unidade de Conta Européia (XBC) + XBC + + + Unidade de Conta Européia (XBD) + XBD + + + Dólar do Caribe Oriental + XCD + + + Franco Novo CFA + XCF + + + Direitos Especiais de Giro + XDR + + + Franco CFA BCEAEC + XEF + + + Unidade Monetária Européia + XEU + + + Franco-ouro francês + XFO + + + Franco UIC francês + XFU + + + Dinar islâmico + XID + + + Franco Novo Metropolitano francês + XMF + + + Franco CFA das Antilhas Francesas + XNF + + + Franco CFA BCEAO + XOF + + + Franco CFP + CFPF + + + Rublo transferível do COMECON + XTR + + + Dinar iemenita + YDD + + + Rial Imadi iemenita + YEI + + + Rial iemenita + YRl + + + Dinar forte iugoslavo + YUD + + + Dinar da Federação Iugoslava + YUF + + + Dinar iugoslavo de 1994 + YUG + + + Super Dinar iugoslavo + YUM + + + Dinar conversível iugoslavo + YUN + + + Dinar de outubro iugoslavo + YUO + + + Dinar reformado iugoslavo + YUR + + + Rand sul-africano (financeiro) + ZAL + + + Libra sul-africana + ZAP + + + Rand sul-africano + ZAR + + + Cuacha zambiano + ZMK + + + Libra zambiana + ZMP + + + Zaire Novo zairense + ZRN + + + Zaire zairense + ZRZ + + + Dólar do Zimbábwe + ZWD + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/root.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/root.xml --- zope3-3.4.0/src/zope/i18n/locales/data/root.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/root.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1665 @@ + + + + + + + + + + + aa + ab + ace + ach + ada + ady + ae + af + afa + afh + ak + akk + ale + alg + am + an + ang + apa + ar + arc + arn + arp + art + arw + as + ast + ath + aus + av + awa + ay + az + ba + bad + bai + bal + bam + ban + bas + bat + be + bej + bem + ber + bg + bh + bho + bi + bik + bin + bla + bm + bn + bnt + bo + br + bra + bs + btk + bua + bug + byn + ca + cad + cai + car + cau + ce + ceb + cel + ch + chb + chg + chk + chm + chn + cho + chp + chr + chy + cmc + co + cop + cpe + cpf + cpp + cr + crh + crp + cs + csb + cu + cus + cv + cy + da + dak + dar + day + de + del + den + dgr + din + doi + dra + dsb + dua + dum + dv + dyu + dz + ee + efi + egy + eka + el + elx + en + enm + eo + es + et + eu + ewo + fa + fan + fat + ff + fi + fiu + fj + fo + fon + fr + frm + fro + fur + fy + ga + gaa + gay + gba + gd + gem + gez + gil + gl + gmh + gn + goh + gon + gor + got + grb + grc + gu + gv + gwi + ha + hai + haw + he + hi + hil + him + hit + hmn + ho + hr + hsb + ht + hu + hup + hy + hz + ia + iba + id + ie + ig + ii + ijo + ik + ilo + inc + ine + inh + io + ira + iro + is + it + iu + ja + jbo + jpr + jrb + jv + ka + kaa + kab + kac + kam + kar + kaw + kbd + kg + kha + khi + kho + ki + kj + kk + kl + km + kmb + kn + ko + kok + kos + kpe + kr + krc + kro + kru + ks + ku + kum + kut + kv + kw + ky + la + lad + lah + lam + lb + lez + lg + li + ln + lo + lol + loz + lt + lu + lua + lui + lun + luo + lus + lv + mad + mag + mai + mak + man + map + mas + mdf + mdr + men + mg + mga + mh + mi + mic + min + mis + mk + mkh + ml + mn + mnc + mni + mno + mo + moh + mos + mr + ms + mt + mul + mun + mus + mwr + my + myn + myv + na + nah + nai + nap + nb + nd + nds + ne + new + ng + nia + nic + niu + nl + nn + no + nog + non + nr + nso + nub + nv + ny + nym + nyn + nyo + nzi + oc + oj + om + or + os + osa + ota + oto + pa + paa + pag + pal + pam + pap + pau + peo + phi + phn + pi + pl + pon + pra + pro + ps + pt + qu + raj + rap + rar + rm + rn + ro + roa + rom + root + ru + rw + sa + sad + sah + sai + sal + sam + sas + sat + sc + sco + sd + se + sel + sem + sg + sga + sgn + sh + shn + si + sid + sio + sit + sk + sl + sla + sm + sma + smi + smj + smn + sms + sn + snk + so + sog + son + sq + sr + srr + ss + ssa + st + su + suk + sus + sux + sv + sw + syr + ta + tai + te + tem + ter + tet + tg + th + ti + tig + tiv + tk + tkl + tl + tli + tmh + tn + to + tog + tpi + tr + ts + tsi + tt + tum + tup + tut + tvl + tw + ty + tyv + udm + ug + uga + uk + umb + und + ur + uz + vai + ve + vi + vo + vot + wa + wak + wal + war + was + wen + wo + xal + xh + yao + yap + yi + yo + ypk + za + zap + zen + zh + znd + zu + zun + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AD + AE + AF + AG + AI + AL + AM + AN + AO + AQ + AR + AS + AT + AU + AW + AZ + BA + BB + BD + BE + BF + BG + BH + BI + BJ + BM + BN + BO + BR + BS + BT + BV + BW + BY + BZ + CA + CC + CD + CF + CG + CH + CI + CK + CL + CM + CN + CO + CR + CU + CV + CX + CY + CZ + DE + DJ + DK + DM + DO + DZ + EC + EE + EG + EH + ER + ES + ET + FI + FJ + FK + FM + FO + FR + GA + GB + GD + GE + GF + GH + GI + GL + GM + GN + GP + GQ + GR + GS + GT + GU + GW + GY + HK + HM + HN + HR + HT + HU + ID + IE + IL + IN + IO + IQ + IR + IS + IT + JM + JO + JP + KE + KG + KH + KI + KM + KN + KP + KR + KW + KY + KZ + LA + LB + LC + LI + LK + LR + LS + LT + LU + LV + LY + MA + MC + MD + MG + MH + MK + ML + MM + MN + MO + MP + MQ + MR + MS + MT + MU + MV + MW + MX + MY + MZ + NA + NC + NE + NF + NG + NI + NL + NO + NP + NR + NU + NZ + OM + PA + PE + PF + PG + PH + PK + PL + PM + PN + PR + PS + PT + PW + PY + QA + RE + RO + RU + RW + SA + SB + SC + SD + SE + SG + SH + SI + SJ + SK + SL + SM + SN + SO + SP + SR + ST + SV + SY + SZ + TC + TD + TF + TG + TH + TJ + TK + TL + TM + TN + TO + TR + TT + TV + TW + TZ + UA + UG + UM + US + UY + UZ + VA + VC + VE + VG + VI + VN + VU + WF + WS + YE + YT + YU + ZA + ZM + ZW + + + POSIX + REVISED + + + CALENDAR + COLLATION + CURRENCY + + + BUDDHIST + CHINESE + GREGORIAN + HEBREW + ISLAMIC + ISLAMIC-CIVIL + JAPANESE + DIRECT + PHONEBOOK + PINYIN + STROKE + TRADITIONAL + + + + + + + [] + + + + + 297 + 210 + + + + GyMdkHmsSEDFwWahKzYeugAZ + + + + + BE + + + + + + + EEEE, MMMM d, yyyy G + + + + + MMMM d, yyyy G + + + + + MMM d, yyyy G + + + + + M/d/yyyy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + EEEE y'x'G-Ml-d + + + + + y'x'G-Ml-d + + + + + y'x'G-Ml-d + + + + + y'x'G-Ml-d + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + + + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + + + + + + + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + + + + + + + AM + PM + + + BCE + CE + + + + + + + EEEE, yyyy MMMM dd + + + + + yyyy MMMM d + + + + + yyyy MMM d + + + + + yy/MM/dd + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + + + Tishri + Heshvan + Kislev + Tevet + Shevat + Adar I + Adar + Nisan + Iyar + Sivan + Tamuz + Av + Elul + + + Tishri + Heshvan + Kislev + Tevet + Shevat + Adar I + Adar + Nisan + Iyar + Sivan + Tamuz + Av + Elul + + + + + + AM + + + + + + + + + + Muharram + Safar + Rabiʻ I + Rabiʻ II + Jumada I + Jumada II + Rajab + Shaʻban + Ramadan + Shawwal + Dhuʻl-Qiʻdah + Dhuʻl-Hijjah + + + Muharram + Safar + Rabiʻ I + Rabiʻ II + Jumada I + Jumada II + Rajab + Shaʻban + Ramadan + Shawwal + Dhuʻl-Qiʻdah + Dhuʻl-Hijjah + + + + + + AH + + + + + + + + + + Muharram + Safar + Rabiʻ I + Rabiʻ II + Jumada I + Jumada II + Rajab + Shaʻban + Ramadan + Shawwal + Dhuʻl-Qiʻdah + Dhuʻl-Hijjah + + + Muharram + Safar + Rabiʻ I + Rabiʻ II + Jumada I + Jumada II + Rajab + Shaʻban + Ramadan + Shawwal + Dhuʻl-Qiʻdah + Dhuʻl-Hijjah + + + + + + AH + + + + + + + Taika + Hakuchi + Hakuhō + Shuchō + Taihō + Keiun + Wadō + Reiki + Yōrō + Jinki + Tempyō + Tempyō-kampō + Tempyō-shōhō + Tempyō-hōji + Temphō-jingo + Jingo-keiun + Hōki + Ten-ō + Enryaku + Daidō + Kōnin + Tenchō + Shōwa + Kajō + Ninju + Saiko + Tennan + Jōgan + Genkei + Ninna + Kampyō + Shōtai + Engi + Enchō + Shōhei + Tengyō + Tenryaku + Tentoku + Ōwa + Kōhō + Anna + Tenroku + Ten-en + Jōgen + Tengen + Eikan + Kanna + Ei-en + Eiso + Shōryaku + Chōtoku + Chōhō + Kankō + Chōwa + Kannin + Jian + Manju + Chōgen + Chōryaku + Chōkyū + Kantoku + Eishō + Tengi + Kōhei + Jiryaku + Enkyū + Shōho + Shōryaku + Eiho + Ōtoku + Kanji + Kaho + Eichō + Shōtoku + Kōwa + Chōji + Kashō + Tennin + Ten-ei + Eikyū + Gen-ei + Hoan + Tenji + Daiji + Tenshō + Chōshō + Hoen + Eiji + Kōji + Tenyō + Kyūan + Ninpei + Kyūju + Hogen + Heiji + Eiryaku + Ōho + Chōkan + Eiman + Nin-an + Kaō + Shōan + Angen + Jishō + Yōwa + Juei + Genryuku + Bunji + Kenkyū + Shōji + Kennin + Genkyū + Ken-ei + Shōgen + Kenryaku + Kenpō + Shōkyū + Jōō + Gennin + Karoku + Antei + Kanki + Jōei + Tempuku + Bunryaku + Katei + Ryakunin + En-ō + Ninji + Kangen + Hōji + Kenchō + Kōgen + Shōka + Shōgen + Bun-ō + Kōchō + Bun-ei + Kenji + Kōan + Shōō + Einin + Shōan + Kengen + Kagen + Tokuji + Enkei + Ōchō + Shōwa + Bunpō + Genō + Genkyō + Shōchū + Kareki + Gentoku + Genkō + Kemmu + Engen + Kōkoku + Shōhei + Kentoku + Bunchũ + Tenju + Kōryaku + Kōwa + Genchũ + Meitoku + Kakei + Kōō + Meitoku + Ōei + Shōchō + Eikyō + Kakitsu + Bun-an + Hōtoku + Kyōtoku + Kōshō + Chōroku + Kanshō + Bunshō + Ōnin + Bunmei + Chōkyō + Entoku + Meiō + Bunki + Eishō + Taiei + Kyōroku + Tenmon + Kōji + Eiroku + Genki + Tenshō + Bunroku + Keichō + Genwa + Kan-ei + Shōho + Keian + Shōō + Meiryaku + Manji + Kanbun + Enpō + Tenwa + Jōkyō + Genroku + Hōei + Shōtoku + Kyōhō + Genbun + Kanpō + Enkyō + Kan-en + Hōryaku + Meiwa + An-ei + Tenmei + Kansei + Kyōwa + Bunka + Bunsei + Tenpō + Kōka + Kaei + Ansei + Man-en + Bunkyū + Genji + Keiō + Meiji + Taishō + Shōwa + Heisei + + + + + + + EEEE, MMMM d, y G + + + + + MMMM d, y G + + + + + MMM d, y G + + + + + M/d/yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + + + + + + + + + + + + . + , + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0.00;-¤ #,##0.00 + + + + + + EUR + + + + GBP + £ + + + INR + =0#Rs.|1#Re.|1<Rs. + + + ITL + + + + JPY + ¥ + + + USD + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ro_RO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ro_RO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ro_RO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ro_RO.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ro.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ro.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ro.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ro.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,435 @@ + + + + + + + + + + + Arabă + Bulgară + Cehă + Daneză + Germană + Greacă + Engleză + Spaniolă + Estoniană + Finlandeză + Franceză + Ebraică + Croată + Maghiară + Italiană + Japoneză + Coreeană + Lituaniană + Letonă + Olandeză + Norvegiană + Poloneză + Portugheză + Română + Rusă + Slovacă + Slovenă + Suedeză + Turcă + Chineză + + + Andorra + Emiratele Arabe Unite + Afganistan + Antigua şi Barbuda + Anguilla + Albania + Armenia + Antilele Olandeze + Angola + Antarctica + Argentina + Samoa Americană + Austria + Australia + Aruba + Azerbaidjan + Bosnia şi Herzegovina + Barbados + Bangladesh + Belgia + Burkina Faso + Bulgaria + Bahrain + Burundi + Benin + Bermuda + Brunei + Bolivia + Brazilia + Bahamas + Bhutan + Insula Bouvet + Botswana + Bielorusia + Belize + Canada + Insulele Cocos (Keeling) + Congo, Republica Democratică + Republica Central Africană + Congo + Eleveţia + Coasta de Fildeş + Insulele Cook + Chile + Camerun + China + Columbia + Costa Rica + Cuba + Capul Verde + Insula Christmas + Cipru + Republica Cehă + Germania + Djibouti + Danemarca + Dominica + Republica Dominicană + Algeria + Ecuador + Estonia + Egipt + Sahara de Vest + Eritrea + Spania + Etiopia + Finlanda + Fiji + Insulele Falkland + Micronezia, Statele Federate + Insulele Feroe + Franţa + en + Gabon + Regatul Unit + Grenada + Georgia + Guyana Franceză + Ghana + Gibraltar + Groenlanda + Gambia + Guineea + Guadeloupe + Guineea Ecuatorială + Grecia + Insulele South Georgia şi South Sandwich + Guatemala + Guam + Guineea-Bissau + Guyana + R.A.S. Hong Kong a Chinei + Insula Heard şi Insulele McDonald + Honduras + Croaţia + Haiti + Ungaria + Indonezia + Irlanda + Israel + India + Teritoriile Britanice din Oceanul Indian + Iraq + Iran + Islanda + Italia + Jamaica + Iordania + Japonia + Kenya + Kirghizia + Cambodgia + Kiribati + Comoros + Saint Kitts şi Nevis + Coreea de Nord + Coreea de Sud + Kuweit + Insulele Cayman + Kazahstan + Lao, Republica Democratică Populară + Liban + Saint Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Lituania + Luxemburg + Letonia + Libia, Jamahiriya Arabă + Maroc + Monaco + Moldova, Republica + Madagascar + Insulele Marshall + Macedonia + Mali + Myanmar + Mongolia + R.A.S. Macao a Chinei + Insulele Northern Mariana + Martinica + Mauritania + Montserrat + Malta + Mauritius + Maldive + Malawi + Mexic + Malaezia + Mozambic + Namibia + Noua Caledonie + Niger + Insulele Norfolk + Nigeria + Nicaragua + Olanda + Norvegia + Nepal + Nauru + Niue + Noua Zeelandă + Oman + Panama + Peru + Polinezia Franceză + Papua Noua Guinee + Filipine + Pakistan + Polonia + Saint Pierre şi Miquelon + Pitcairn + Porto Rico + Teritoriul Palestinian + Portugalia + Palau + Paraguay + Qatar + Reunion + România + Federaţia Rusă + Rwanda + Arabia Saudită + Insulele Solomon + Seychelles + Sudan + Suedia + Singapore + Saint Helena + Slovenia + Svalbard şi Jan Mayen + Slovacia + Sierra Leone + San Marino + Senegal + Somalia + Serbia + Surinam + Sao Tome şi Principe + El Salvador + Siria + Swaziland + Insulele Turks şi Caicos + Ciad + Teritoriile Franceze de Sud + Togo + Tailanda + Tadjikistan + Tokelau + Timorul de Est + Turkmenistan + Tunisia + Tonga + Turcia + Trinidad şi Tobago + Tuvalu + Taiwan, Provincia Chineză + Tanzania + Ucraina + Uganda + United States Minor Outlying Islands + Statele Unite + Uruguay + Uzbekistan + Sfântul Scaun (Statul Vatican) + Saint Vincent şi Grenadines + Venezuela + Insulele Virgine Britanice + Insulele Virgine S.U.A. + Vietnam + Vanuatu + Wallis şi Futuna + Samoa + Yemen + Mayotte + Iugoslavia + Africa de Sud + Zambia + Zimbabwe + + + + [a-z â î ă ş ţ] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + Ian + Feb + Mar + Apr + Mai + Iun + Iul + Aug + Sep + Oct + Nov + Dec + + + ianuarie + februarie + martie + aprilie + mai + iunie + iulie + august + septembrie + octombrie + noiembrie + decembrie + + + + + + + D + L + Ma + Mi + J + V + S + + + duminică + luni + marţi + miercuri + joi + vineri + sîmbătă + + + + + + + + + + d.C. + î.d.C. + + + + + + + d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd.MM.yyyy + + + + + dd.MM.yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + ROL + lei + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ru_RU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ru_RU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ru_RU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ru_RU.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,54 @@ + + + + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00¤;-#,##0.00¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ru_UA.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ru_UA.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ru_UA.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ru_UA.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + EEEE, d MMMM yyyy 'г.' + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + dd.MM.yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ru.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ru.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ru.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ru.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,591 @@ + + + + + + + + + + + Афар + Абхазский + Африкаанс + Амхарский + Арабский + Ассамский + Аямара + Азербайджанский + Башкирский + Белорусский + Болгарский + Бихарский + Бислама + Бенгальский + Тибетский + Бретонский + Каталанский + Корсиканский + Чешский + Валлийский + Датский + Немецкий + Бутанский + Греческий + Английский + Эсперанто + Испанский + Эстонский + Баскский + Персидский + Финский + Фиджи + Фарерский + Французский + Фризский + Ирландский + Гаэльский + Галицийский + Гуарани + Гуярати + Хоса + Иврит + Хинди + Хорватский + Венгерский + Армянский + Смешанный язык + Индонезийский + Смешанный язык + Инапиак + Исландский + Итальянский + Инактитут + Японский + Яванский + Грузинский + Казахский + Гренландский + Камбоджийский + Канада + Корейский + Кашмирский + Курдиш + Киргизский + Латинский + Лингала + Лаосский + Литовский + Латвийский + Малагасийский + Маори + Македонский + Малаялам + Монгольский + Молдавский + Маратийский + Малайский + Мальтийский + Бирманский + Науру + Непальский + Голландский + Норвежский + Окитан + Оромо (Афан) + Ория + Панджабский + Польский + Пашто (Пушто) + Португальский + Кечуа + Раето-романский + Кирундийский + Румынский + Русский + Кинярванда + Санскрит + Синди + Санго + Сербско-хорватский + Сингальский + Словацкий + Словенский + Самоа + Шона + Сомали + Албанский + Сербский + Сисвати + Сесото + Санданизский + Шведский + Суахили + Тамильский + Телугу + Таджикский + Тайский + Тигриниа + Туркменский + Тагалог + Сетсвана + Тонга + Турецкий + Тсонга + Татарский + Тви + Уйгурский + Украинский + Урду + Узбекский + Вьетнамский + Волапак + Волоф + Хоза + Идиш + Йоруба + Зуанг + Китайский + Зулусский + + + Андорра + Объединенные Арабские Эмираты + Афганистан + Антигуа и Барбуда + Ангуилла + Албания + Армения + Голландские Антильские Острова + Ангола + Антарктида + Аргентина + Американское Самоа + Австрия + Австралия + Аруба + Азербайджан + Босния + Барбадос + Бангладеш + Бельгия + Буркина Фасо + Болгария + Бахрейн + Бурунди + Бенин + Бермудские Острова + Бруней Даруссалам + Боливия + Бразилия + Багамские острова + Бутан + Остров Буве + Ботсвана + Беларусь + Белиз + Канада + Кокосовые Острова (Киилинг) + Конго, Демократическая Республика + Центрально-Африканская Республика + Конго + Швейцария + Кот д’Ивуар + Острова Кука + Чили + Камерун + Китай + Колумбия + Коста-Рика + Куба + Острова Зеленого Мыса + Остров Рождества + Кипр + Чешская Республика + Германия + Джибути + Дания + Остров Доминика + Доминиканская Республика + Алжир + Эквадор + Эстония + Египет + Западная Сахара + Эритрея + Испания + Эфиопия + Финляндия + Фиджи + Фольклендские Острова + Федеративное Государство Микронезия + Фарерские острова + Франция + Габон + Великобритания + Гренада + Грузия + Французская Гвиана + Гана + Гибралтар + Гренландия + Гамбия + Гвинея + Гваделупа + Экваториальная Гвинея + Греция + Южная Джорджия и Южные Сандвичевы Острова + Гватемала + Гуам + Гвинея-Биссау + Гайана + Гонконг (Область с Особым Административным Управлением, Китай) + Острова Херд и Мак-Дональд + Гондурас + Хорватия + Гаити + Венгрия + Индонезия + Ирландия + Израиль + Индия + Британские Территории в Индийском Океане + Ирак + Иран + Исландия + Италия + Ямайка + Иордания + Япония + Кения + Кыргызстан + Камбоджа + Кирибати + Коморские Острова + Сент-Киттс и Невис + Северная Корея + Южная Корея + Кувейт + Каймановы Острова + Казахстан + Лаос + Ливан + Сент-Люсия + Лихтенштейн + Шри-Ланка + Либерия + Лесото + Литва + Люксембург + Латвия + Ливия + Марокко + Монако + Молдова + Мадагаскар + Маршалловы Острова + Македония + Мали + Майанмар + Монголия + Макао (Область с Особым Административным Управлением, Китай) + Северные Марианские Острова + Мартиник + Мавритания + Монсеррат + Мальта + Маврикий + Мальдивы + Малави + Мексика + Малайзия + Мозамбик + Намибия + Новая Каледония + Нигер + Остров Норфолк + Нигерия + Никарагуа + Нидерланды + Норвегия + Непал + Науру + Ниуе + Новая Зеландия + Оман + Панама + Перу + Французская Полинезия + Папуа-Новая Гвинея + Филиппины + Пакистан + Польша + Сен-Пьер и Микелон + Остров Питкэрн + Пуэрто-Рико + Палестинская автономия + Португалия + Палау + Парагвай + Катар + Реюньон + Румыния + Россия + Руанда + Саудовская Аравия + Соломоновы Острова + Сейшельские Острова + Судан + Швеция + Сингапур + Остров Святой Елены + Словения + Острова Свалбард и Жан Майен + Словакия + Сьерра-Леоне + Сан-Марино + Сенегал + Сомали + Сербия + Суринам + Сан-Томе и Принсипи + Сальвадор + Сирийская Арабская Республика + Свазиленд + Острова Туркс и Кайкос + Чад + Французские Южные Территории + Того + Таиланд + Таджикистан + Токелау + Восточный Тимор + Туркменистан + Тунис + Тонга + Турция + Тринидад и Тобаго + Тувалу + Тайвань, Китайская Провинция + Танзания + Украина + Уганда + Внешние малые острова (США) + Соединенные Штаты + Уругвай + Узбекистан + Государство-город Ватикан + Сент-Винсент и Гренадины + Венесуэла + Британские Виргинские Острова + Американские Виргинские Острова + Вьетнам + Вануату + Эллис и Футуна + Самоа + Йемен + Майотта + Югославия + Южная Африка + Замбия + Зимбабве + + + + [а-я ё і ѣ ѳ ѵ] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + янв + фев + мар + апр + май + июн + июл + авг + сен + окт + ноя + дек + + + Я + Ф + М + А + М + И + И + А + С + О + Н + Д + + + января + февраля + марта + апреля + мая + июня + июля + августа + сентября + октября + ноября + декабря + + + + + янв + фев + мар + апр + май + июн + июл + авг + сен + окт + ноя + дек + + + Я + Ф + М + А + М + И + И + А + С + О + Н + Д + + + Январь + Февраль + Март + Апрель + Май + Июнь + Июль + Август + Сентябрь + Октябрь + Ноябрь + Декабрь + + + + + + + Вс + Пн + Вт + Ср + Чт + Пт + Сб + + + воскресенье + понедельник + вторник + среда + четверг + пятница + суббота + + + + + + + + + + до н.э. + н.э. + + + + + + + d MMMM yyyy 'г.' + + + + + d MMMM yyyy 'г.' + + + + + dd.MM.yyyy + + + + + dd.MM.yy + + + + + + + + H:mm:ss z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + + RUR + р. + + + UAH + грн. + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sa_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sa_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sa_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sa_IN.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-MM-yy + + + + + + + + hh:mm:ss a z + + + + + hh:mm:ss a z + + + + + hh:mm:ss a + + + + + hh:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤##,##,##0.00;-¤##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sa.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sa.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sa.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sa.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + संस्कृत + + + भारतम् + + + + [[[:Deva:][॑-॔]]-[क़-य़]‌‍] + + + + . + , + ; + % + + # + + + - + E + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + + INR + रु + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sh.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sh.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sh.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sh.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,475 @@ + + + + + + + + + + + Afrikanerski + Arapski + Beloruski + Bugarski + Bretonski + Katalonski + Korzikanski + Češki + Danski + Nemački + Grčki + Engleski + Španski + Estonski + Baskijski + Persijski + Finski + Francuski + Irski + Hebrejski + Hrvatski + Mađarski + Armenski + Indonezijski + Islandski + Italijanski + Japanski + Gruzijski + Kambodžanski + Korejski + Kurdski + Kirgiski + Latinski + Litvanski + Letonski + Makedonski + Mongolski + Moldavski + Burmanski + Holandski + Norveški + Poljski + Portugalski + Reto-Romanski + Rumunski + Ruski + Srpsko-Hrvatski + Slovački + Slovenački + Albanski + Srpski + Švedski + Svahili + Turski + Ukrajnski + Vijetnamski + Jidiš + Kineski + + + Andora + Ujedinjeni Arapski Emirati + Avganistan + Antigua and Barbuda + Anguilla + Albanija + Armenija + Holandski Antili + Angola + Antarctica + Argentina + American Samoa + Austrija + Australija + Aruba + Azerbejdžan + Bosna i Hercegovina + Barbados + Bangladeš + Belgija + Burkina Faso + Bugarska + Bahrein + Burundi + Benin + Bermuda + Brunej + Bolivija + Brazil + Bahami + Butan + Bouvet Island + Bocvana + Belorusija + Belise + Kanada + Cocos (Keeling) Islands + Democratic Republic of the Congo + Centralno Afrička Republika + Kongo + Švajcarska + Obala Slonovače + Cook Islands + Čile + Kamerun + Kina + Kolumbija + Kostarika + Kuba + Cape Verde + Christmas Island + Kipar + Češka + Nemačka + Džibuti + Danska + Dominika + Dominikanska Republika + Alžir + Ekvador + Estonija + Egipat + Zapadna Sahara + Eritreja + Španija + Etiopija + Finska + Fidži + Falkland Islands + Mikronezija + Faroe Islands + Francuska + en + Gabon + Velika Britanija + Grenada + Gruzija + Francuska Gvajana + Gana + Gibraltar + Greenland + Gambija + Gvineja + Gvadelupe + Ekvatorijalna Gvineja + Grčka + South Georgia and the South Sandwich Islands + Gvatemala + Guam + Gvineja-Bisao + Gvajana + Hong Kong S.A.R., China + Heard Island and McDonald Islands + Honduras + Hrvatska + Haiti + Mađarska + Indonezija + Irska + Izrael + Indija + British Indian Ocean Territory + Irak + Iran + Island + Italija + Jamajka + Jordan + Japan + Kenija + Kirgistan + Kambodža + Kiribati + Comoros + Saint Kitts and Nevis + Severna Koreja + Južna Koreja + Kuvajt + Cayman Islands + Kazahstan + Laos + Liban + Saint Lucia + Lihenštajn + Šrilanka + Liberija + Lesoto + Litvanija + Luksemburg + Letonija + Libija + Maroko + Monako + Moldavija + Madagaskar + Marshall Islands + Makedonija + Mali + Mijnamar + Mongolija + Macao S.A.R., China + Northern Mariana Islands + Martinik + Mauritanija + Montserrat + Malta + Mauricius + Maldives + Malawi + Meksiko + Malezija + Mozambik + Namibija + Nova Kaledonija + Niger + Norfolk Island + Nigerija + Nikaragva + Holandija + Norveška + Nepal + Nauru + Niue + Novi Zeland + Oman + Panama + Peru + Francuska Polinezija + Papua Nova Gvineja + Filipini + Pakistan + Poljska + Saint Pierre and Miquelon + Pitcairn + Porto Riko + Palestinian Territory + Portugal + Palau + Paragvaj + Katar + Réunion + Rumunija + Rusija + Ruanda + Saudijska Arabija + Solomon Islands + Sejšeli + Sudan + Švedska + Singapur + Saint Helena + Slovenija + Svalbard and Jan Mayen + Slovačka + Sijera Leone + San Marino + Senegal + Somalija + Srbija + Surinam + Sao Tome and Principe + Salvador + Sirija + Svazilend + Turks and Caicos Islands + Čad + Francuske Južne Teritorije + Togo + Tajland + Tadžikistan + Tokelau + Timor-Leste + Turkmenistan + Tunis + Tonga + Turska + Trinidad i Tobago + Tuvalu + Tajvan + Tanzanija + Ukrajina + Uganda + United States Minor Outlying Islands + Sjedinjene Američke Države + Urugvaj + Uzbekistan + Vatikan + Saint Vincent and the Grenadines + Venecuela + Britanska Devičanska Ostrva + S.A.D. Devičanska Ostrva + Vijetnam + Vanuatu + Wallis and Futuna + Samoa + Jemen + Mayotte + Jugoslavija + Južna Afrika + Zambija + Zimbabve + + + + [a-p r-v z đ ć č ž š {lj} {nj} {dž}] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + jan + feb + mar + apr + maj + jun + jul + avg + sep + okt + nov + dec + + + januar + februar + mart + april + maj + juni + juli + avgust + septembar + oktobar + novembar + decembar + + + + + + + ned + pon + uto + sre + čet + pet + sub + + + nedelja + ponedeljak + utorak + sreda + četvrtak + petak + subota + + + + + + + + + + p. n. e. + n. e. + + + + + + + EEEE, dd. MMMM yyyy. + + + + + EEEE, d.MM.yyyy. + + + + + dd.MM.yyyy. + + + + + d.M.yy. + + + + + + + + HH.mm.ss z + + + + + HH.mm.ss z + + + + + HH.mm.ss + + + + + HH.mm + + + + + + + {1} {0} + + + + + + + + + Centralno Evropsko Vreme + Centralno Evropsko Letnje Vreme + + + CET + CET + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + YUN + Din + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sh_YU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sh_YU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sh_YU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sh_YU.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sid_ET.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sid_ET.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sid_ET.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sid_ET.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sid.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sid.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sid.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sid.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,153 @@ + + + + + + + + + + + Sidaamu Afo + + + Itiyoophiya + + + + [a-z] + + + + + + + + Jan + Feb + Mar + Apr + May + Jun + Jul + Aug + Sep + Oct + Nov + Dec + + + January + February + March + April + May + June + July + August + September + October + November + December + + + + + + + Sam + San + Mak + Row + Ham + Arb + Qid + + + Sambata + Sanyo + Maakisanyo + Roowe + Hamuse + Arbe + Qidaame + + + + + + + + soodo + hawwaro + + + YIA + YIG + + + + + + + EEEE, MMMM dd, yyyy + + + + + dd MMMM yyyy + + + + + dd-MMM-yyyy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + ETB + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sk_SK.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sk_SK.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sk_SK.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sk_SK.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sk.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sk.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sk.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sk.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,435 @@ + + + + + + + + + + + arabský + bulharský + český + dánsky + nemecký + grécky + anglický + španielsky + estónsky + fínsky + francúzsky + hebrejský + chorvátsky + maďarský + taliansky + japonský + kórejský + litovský + lotyšský + holandský + nórsky + poľský + portugalský + rumunský + ruský + slovenský + slovinský + švédsky + turecký + čínsky + + + Andorra + Spojené arabské emiráty + Afganistan + Antigua a Barbados + Anguilla + Albánsko + Arménsko + Holandské Antily + Angola + Antarctica + Argentína + Americká Samoa + Rakúsko + Austrália + Aruba + Azerbajdžan + Bosna a Hercegovina + Barbados + Bangladéš + Belgicko + Burkina Faso + Bulharsko + Bahrajn + Burundi + Benin + Bermudy + Brunej + Bolívia + Brazília + Bahamy + Bután + Bouvetov ostrov + Botswana + Bielorusko + Belize + Kanada + Kokosové (Keelingove) ostrovy + Konžská demokratická republika + Stredoafrická republika + Kongo + Švajčiarsko + Pobrežie Slonoviny + Cookove ostrovy + Čile + Kamerun + Čína + Kolumbia + Kostarika + Kuba + Kapverdy + Vianočný ostrov + Cyprus + Česká republika + Nemecko + Džibuti + Dánsko + Dominika + Dominikánska republika + Alžírsko + Ekvádor + Estónsko + Egypt + Západná Sahara + Eritrea + Španielsko + Etiópia + Fínsko + Fidži + Falklandské ostrovy + Mikronézia, Federatívne štáty + Faerské ostrovy + Francúzsko + en + Gabon + Spojené kráľovstvo + Grenada + Gruzínsko + Francúzska Guayana + Ghana + Gibraltár + Grónsko + Gambia + Guinea + Guadeloupe + Rovníková Guinea + Grécko + Južná Georgia a Južné Sandwichove ostrovy + Guatemala + Guam + Guinea-Bissau + Guayana + Hong Kong S.A.R. Číny + Heardove ostrovy a McDonaldove ostrovy + Honduras + Chorvátsko + Haiti + Maďarsko + Indonézia + Írsko + Izrael + India + Britské územie v Indickom oceáne + Irak + Irán + Island + Taliansko + Jamajka + Jordánsko + Japonsko + Keňa + Kirgizsko + Kambodža + Kiribati + Komory + Saint Kitts a Nevis + Kórea, Severná + Kórea, Južná + Kuvajt + Kajmanské ostrovy + Kazachstan + Laoská ľudovodemokratická republika + Libanon + Svätá Lucia + Lichtenštajnsko + Srí Lanka + Libéria + Lesotho + Litva + Luxembursko + Lotyšsko + Lýbijská arabská džamahírija + Maroko + Monako + Moldavsko, republika + Madagaskar + Marshallove ostrovy + Macedónsko, republika + Mali + Mjanmarsko + Mongolsko + Makao S.A.R. Číny + Severné Mariány + Martinik + Mauritánia + Montserrat + Malta + Maurícius + Maldivy + Malawi + Mexiko + Malajzia + Mozambik + Namíbia + Nová Kaledónia + Niger + Norfolkov ostrov + Nigéria + Nikaragua + Holandsko + Nórsko + Nepál + Nauru + Niue + Nový Zéland + Omán + Panama + Peru + Francúzska Polynézia + Papua Nová Guinea + Filipíny + Pakistan + Poľsko + Saint Pierre a Miquelon + Pitcairnove ostrovy + Portoriko + Palestínske územie + Portugalsko + Palau + Paraguaj + Katar + Reunion + Rumunsko + Ruská federácia + Rwanda + Saudská Arábia + Šalamúnove ostrovy + Seychelské ostrovy + Sudán + Švédsko + Singapur + Svätá Helena + Slovinsko + Špicbergy a Jan Mayen + Slovenská republika + Sierra Leone + San Maríno + Senegal + Somálsko + Serbia + Surinam + Svätý Tomáš a Princove ostrovy + Salvador + Sýrska arabská republika + Svazijsko + Turks a Caicos + Čad + Francúzske južné územia + Togo + Thajsko + Tadžikistan + Tokelau + Východný Timor + Turkménsko + Tunisko + Tonga + Turecko + Trinidad a Tobago + Tuvalu + Tajwan + Tanzánia + Ukrajina + Uganda + Menšie odľahlé ostrovy USA + Spojené štáty + Uruguaj + Uzbekistan + Svätá stolica (Vatikánsky mestský štát) + Svätý Vincent a Grenadíny + Venezuela + Britské panenské ostrovy + Panenské ostrovy - USA + Vietnam + Vanuatu + Wallis a Futuna + Samoa + Jemen + Mayotte + Juhoslávia + Južná Afrika + Zambia + Zimbabwe + + + + [a-z ý á é í ó ú ä ô ĺ ŕ č ď ľ ň š ť ž] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + jan + feb + mar + apr + máj + jún + júl + aug + sep + okt + nov + dec + + + január + február + marec + apríl + máj + jún + júl + august + september + október + november + december + + + + + + + Ne + Po + Ut + St + Št + Pi + So + + + Nedeľa + Pondelok + Utorok + Streda + Štvrtok + Piatok + Sobota + + + + + + + + + + pred n.l. + n.l. + + + + + + + EEEE, d. MMMM yyyy + + + + + d. MMMM yyyy + + + + + d.M.yyyy + + + + + d.M.yyyy + + + + + + + + H:mm:ss z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + SKK + Sk + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sl_SI.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sl_SI.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sl_SI.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sl_SI.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sl.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sl.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sl.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sl.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,435 @@ + + + + + + + + + + + Arabščina + Bolgarščina + Češčina + Danščina + Nemščina + Grščina + Angleščina + Španščina + Estonščina + Finščina + Francoščina + Hebrejščina + Hrvaščina + Madžarščina + Italijanščina + Japonščina + Korejščina + Litovščina + Letonščina + Nizozemščina + Norveščina + Poljščina + Portugalščina + Romunščina + Ruščina + Slovaščina + Slovenščina + Švedščina + Turščina + Kitajščina + + + Andora + Združeni arabski emirati + Afganistan + Antigva in Barbuda + Angvila + Albanija + Armenija + Nizozemski Antili + Angola + Antarktika + Argentina + Ameriška Samoa + Avstrija + Avstralija + Aruba + Azerbajdžan + Bosna in Hercegovina + Barbados + Bangladeš + Belgija + Burkina Faso + Bolgarija + Bahrajn + Burundi + Benin + Bermuda + Brunej + Bolivija + Brazilija + Bahami + Butan + Otok Bouvet + Bocvana + Belorusija + Belize + Kanada + Kokosovi otoki + Demokratična republika Kongo + Centralnoafriška republika + Kongo + Švica + Slonokoščena obala + Cookovi otoki + Čile + Kamerun + Kitajska + Kolumbija + Kostarika + Kuba + Kapverdski otoki + Božični otok + Ciper + Češka + Nemčija + Džibuti + Danska + Dominika + Dominikanska republika + Alžirija + Ekvador + Estonija + Egipt + Zahodna Sahara + Eritreja + Španija + Etiopija + Finska + Fidži + Falklandski (Malvinski) otoki + Mikronezija + Fererski otoki + Francija + en + Gabon + Velika Britanija + Grenada + Gruzija + Francoska Gvajana + Gana + Gibraltar + Grenlandija + Gambija + Gvineja + Guadeloupe + Ekvatorialna Gvineja + Grčija + Južna Georgija in Južni Sandwich Islands + Gvatemala + Guam + Gvineja Bissau + Gvajana + Kitajska republika Hong Kong + Heardov otok in McDonaldovi otoki + Honduras + Hrvaška + Haiti + Madžarska + Indonezija + Irska + Izrael + Indija + Britanska Indija + Irak + Iran + Islandija + Italija + Jamajka + Jordan + Japonska + Kenija + Kirgizistan + Kambodža + Kiribati + Komori + Saint Kitts in Nevis + Severna Koreja + Južna Koreja + Kuvajt + Kajmanski otoki + Kazahstan + Ljudska demokratična republika Laos + Libanon + Saint Lucia + Liechtenstein + Šrilanka + Liberija + Lesoto + Litva + Luxemburg + Latvija + Libija + Maroko + Monako + Republika Moldova + Madagaskar + Marshallovi otoki + Republika Makedonija + Mali + Myanmar + Mongolija + Kitajska republika Macao + Severni Marianski otoki + Martinik + Mavretanija + Montserrat + Malta + Mauritius + Maldivi + Malavi + Mehika + Malezija + Mozambik + Namibija + Nova Kaledonija + Nigerija + Otok Norfolk + Nigerija + Nikaragva + Nizozemska + Norveška + Nepal + Nauru + Niue + Nova Zelandija + Oman + Panama + Peru + Francoska Polinezija + Papua Nova Gvineja + Filipini + Pakistan + Poljska + Saint Pierre in Miquelon + Pitcairn + Portoriko + Palestinsko ozemlje + Portugalska + Palau + Paragvaj + Katar + Reunion + Romunija + Ruska federacija + Ruanda + Saudova Arabija + Salomonovo otočje + Sejšeli + Sudan + Švedska + Singapur + Sveta Helena + Slovenija + Svalbard in Jan Mayen + Slovaška + Sierra Leone + San Marino + Senegal + Somalija + Serbia + Surinam + Sao Tome in Principe + Salvador + Sirija + Svazi + Otočji Turks in Caicos + Čad + Francoski zahodni teritorij + Togo + Tajska + Tadžikistan + Tokelau + Vzhodni Timor + Turkmenistan + Tunizija + Tonga + Turčija + Trinidad in Tobago + Tuvalu + Tajvan + Tanzanija + Ukrajina + Uganda + Ameriški manjši oddaljeni otoki + Združene države Amerike + Urugvaj + Uzbekistan + Vatikan + Saint Vincent in Grenadine + Venezuela + Britanski Deviški otoki + Ameriški Deviški otoki + Vietnam + Vanuatu + Wallis in Futuna + Samoa + Jemen + Mayotte + Jugoslavija + Južna Afrika + Zambija + Zimbabve + + + + [a-p r-v z č š ž] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + jan + feb + mar + apr + maj + jun + jul + avg + sep + okt + nov + dec + + + januar + februar + marec + april + maj + junij + julij + avgust + september + oktober + november + december + + + + + + + ned + pon + tor + sre + čet + pet + sob + + + nedelja + ponedeljek + torek + sreda + četrtek + petek + sobota + + + + + + + + + + pr.n.š. + po Kr. + + + + + + + EEEE, dd. MMMM yyyy + + + + + dd. MMMM yyyy + + + + + yyyy.M.d + + + + + yy.M.d + + + + + + + + H:mm:ss z + + + + + H:mm:ss z + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + SIT + SIT + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/so_DJ.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/so_DJ.xml --- zope3-3.4.0/src/zope/i18n/locales/data/so_DJ.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/so_DJ.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + DJF + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/so_ET.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/so_ET.xml --- zope3-3.4.0/src/zope/i18n/locales/data/so_ET.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/so_ET.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + ETB + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/so_KE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/so_KE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/so_KE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/so_KE.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/so_SO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/so_SO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/so_SO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/so_SO.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + SOS + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/so.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/so.xml --- zope3-3.4.0/src/zope/i18n/locales/data/so.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/so.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,252 @@ + + + + + + + + + + + Soomaali + + + Imaaraadka Carabta ee Midoobay + Afgaanistaan + Armeeniya + Angoola + Osteeriya + Awstraaliya + Boosniya Heersigoviina + Baarbadoos + Bangaala-Deesh + Beljiyam + Baxrayn + Beniin + Braasiil + Kanada + Swiiserlaand + Jili + Kameruun + Shiinaha + Kuuba + Jarmal + Jabuuti + Danmaark + Masar + Isbeyn + Itoobiya + Fiinlaand + Faransiis + Giriinaada + Gini + Giriigga + Korweeshiya + Hangeri + Indoneesiya + Ayrlaanda + Israa'iil + Hindiya + Ciraaq + Iiraan + Iislaand + Talyaani + Jameyka + Urdun + Jabbaan + Kiiniya + Kamboodiya + Kuuriyada Waqooyi + Kuuriyada Koonfureed + Kuwayt + Kasaakhistaan + Lubnaan + Siirilaanka + Laybeeriya + Losooto + Luksemboorg + Laatfiya + Liibiya + Marooko + Moonako + Makadooniya + Maali + Muritaaniya + Maalda + Maaldiqeen + Malaawi + Meksiko + Musambiig + Namiibiya + Nayjeeriya + Nikaraaguwa + Noorweey + Neyuusilaand + Cumaan + Filibiin + Bakistaan + Booland + Bortuqaal + Qadar + Rumaaniya + Ruush + Sacuudi Carabiya + Sudaan + Iswidhan + Siraaliyoon + Soomaaliya + Suuriya + Jaad + Toogo + Taylaand + Tuniisiya + Turki + Tansaaniya + Ugaanda + Qaramada Midoobey ee Maraykanka + Faatikaan + Fenisuweela + Fiyetnaam + Yaman + Koonfur Afrika + Saambiya + Simbaabwe + + + + [a-z] + + + + + + + + Kob + Lab + Sad + Afr + Sha + Lix + Tod + Sid + Sag + Tob + KIT + LIT + + + Bisha Koobaad + Bisha Labaad + Bisha Saddexaad + Bisha Afraad + Bisha Shanaad + Bisha Lixaad + Bisha Todobaad + Bisha Sideedaad + Bisha Sagaalaad + Bisha Tobnaad + Bisha Kow iyo Tobnaad + Bisha Laba iyo Tobnaad + + + + + + + Axa + Isn + Sal + Arb + Kha + Jim + Sab + + + Axad + Isniin + Salaaso + Arbaco + Khamiis + Jimco + Sabti + + + + + + + + sn + gn + + + Ciise ka hor + Ciise ka dib + + + + + + + EEEE, MMMM dd, yyyy + + + + + dd MMMM yyyy + + + + + dd-MMM-yyyy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + KES + Ksh + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sq_AL.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sq_AL.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sq_AL.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sq_AL.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sq.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sq.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sq.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sq.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,319 @@ + + + + + + + + + + + shqipe + + + Andorrë + Emiratet Arabe te Bashkuara + Afganistan + Antigua e Barbuda + Shqipëria + Armeni + Angolë + Argjentinë + Austri + Australi + Azerbajxhan + Bosnja dhe Hercegovina + Belgjikë + Bullgari + Bahrein + Brunej + Bolivi + Butan + Botsvana + Bjellorusi + Kanada + Republika Qendrore e Afrikës + Kongo + Zvicër + Bregu i Fildishtë + Kili + Kamerun + Kinë + Kolumbi + Kosta Rika + Kubë + Kap Verde + Qipro + Republika e Çekisë + Gjermani + Xhibuti + Danimarkë + Dominikë + Republika Dominikanë + Algjeri + Ekuator + Estoni + Egjipt + Saharaja Perëndimore + Eritre + Spanjë + Etiopi + Finlandë + Fixhi + Mikronezi + Francë + Gjabon + Gjeorgji + Ganë + Gambi + Guine + Guineja Ekuatoriale + Greqi + Guatemalë + Guine Bisau + Guajana + Kroaci + Hungari + Indonezi + Irlandë + Izrael + Indi + Irak + Islandë + Itali + Xhamajkë + Jordani + Japoni + Kenia + Kirgistan + Kamboxhi + Qiribati + Komore + Saint Kitts e Nevis + Koreja e Veriut + Koreja e Jugut + Kuvajt + Kazakistan + Liban + Lihtënshtajn + Liberi + Lesoto + Lituani + Luksemburg + Letoni + Libi + Maroko + Monako + Moldavi + Madagaskar + Ishujt Marshall + Maqedoni + Mongoli + Mauritani + Maltë + Maldivit + Malavi + Meksikë + Malajzi + Mozambik + Namibi + Nigeri + Nikaragua + Vendet e Ulëta + Norvegji + Zelanda e Re + Papua Guineja e Re + Filipine + Poloni + Portugali + Paraguaj + Katar + Rumani + Rusi + Ruanda + Arabia Saudite + Ishujt Solomon + Sishel + Suedi + Singapor + Slloveni + Sllovaki + Siera Leone + Somali + Serbië + Sao Tome e Prinsipe + Siri + Svazilandë + Çad + Togo + Tajlandë + Taxhikistan + Tunisi + Turqi + Trinidad e Tobago + Tajvan + Tanzani + Ukrainë + Shtetet e Bashkuara të Amerikës + Uruguaj + Vatikan + Saint Vincent e Grenadinet + Venezuelë + Jemen + Afrika e Jugut + Zambi + Zimbabve + + + + [a-zçë{dh}{gj}{ll}{nj}{rr}{sh}{th}{xh}{zh}] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + Jan + Shk + Mar + Pri + Maj + Qer + Kor + Gsh + Sht + Tet + Nën + Dhj + + + janar + shkurt + mars + prill + maj + qershor + korrik + gusht + shtator + tetor + nëntor + dhjetor + + + + + + + Die + Hën + Mar + Mër + Enj + Pre + Sht + + + e diel + e hënë + e martë + e mërkurë + e enjte + e premte + e shtunë + + + + + + + + PD + MD + + + p.e.r. + n.e.r. + + + + + + + EEEE, dd MMMM yyyy + + + + + dd MMMM yyyy + + + + + yyyy-MM-dd + + + + + yy-MM-dd + + + + + + + + h.mm.ss.a z + + + + + h.mm.ss.a z + + + + + h:mm:ss.a + + + + + h.mm.a + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + ALL + Lek + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sr.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sr.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sr.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sr.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,479 @@ + + + + + + + + + + + Африканерски + Арапски + Белоруски + Бугарски + Бретонски + Каталонски + Корзикански + Чешки + Дански + Немачки + Грчки + Енглески + Есперанто + Шпански + Естонски + Баскијски + Персијски + Фински + Француски + Ирски + Хебрејски + Хрватски + Мађарски + Арменски + Индонезијски + Исландски + Италијански + Јапански + Грузијски + Камбоџански + Корејски + Курдски + Киргиски + Латински + Литвански + Летонски + Македонски + Монголски + Молдавски + Бурмански + Холандски + Норвешки + Пољски + Португалски + Рето-Романски + Румунски + Руски + Санскрит + Српско-Хрватски + Словачки + Словеначки + Албански + Српски + Шведски + Свахили + Турски + Украјински + Вијетнамски + Јидиш + Кинески + + + Андора + Уједињени Арапски Емирати + Авганистан + Албанија + Арменија + Холандски Антили + Ангола + Аргентина + Аустрија + Аустралија + Аруба + Азербејџан + Босна и Херцеговина + Барбадос + Бангладеш + Белгија + Буркина Фасо + Бугарска + Бахреин + Бурунди + Бенин + Бермуда + Брунеј + Боливија + Браѕил + Бахами + Бутан + Боцвана + Белорусија + Белисе + Канада + Централно Афричка Република + Конго + Швајцарска + Обала Слоноваче + Чиле + Камерун + Кина + Колумбија + Костарика + Куба + Кипар + Чешка + Немачка + Џибути + Данска + Доминика + Доминиканска Република + Алжир + Еквадор + Естонија + Египат + Западна Сахара + Еритреја + Шпанија + Етиопија + Финска + Фиџи + Микронезија + Француска + Габон + Велика Британија + Грузија + Француска Гвајана + Гана + Гамбија + Гвинеја + Гваделупе + Екваторијална Гвинеја + Грчка + Гватемала + Гвинеја-Бисао + Гвајана + Хондурас + Хрватска + Хаити + Мађарска + Индонезија + Ирска + Израел + Индија + Ирак + Иран + Исланд + Италија + Јамајка + Јордан + Јапан + Кенија + Киргизстан + Камбоџа + Северна Кореја + Јужна Кореја + Кувајт + Казахстан + Лаос + Либан + Лихенштајн + Шри Ланка + Либерија + Лесото + Литванија + Луксембург + Летонија + Либија + Мароко + Монако + Молдавија + Мадагаскар + Македонија + Мали + Мијнамар + Монголија + Мартиник + Мауританија + Малта + Маурицијус + Мексико + Малезија + Мозамбик + Намибија + Нова Каледонија + Нигер + Нигерија + Никарагва + Холандија + Норвешка + Непал + Нови Зеланд + Оман + Панама + Перу + Француска Полинезија + Папуа Нова Гвинеја + Филипини + Пакистан + Пољска + Порто Рико + Португал + Парагвај + Катар + Румунија + Русија + Руанда + Саудијска Арабија + Сејшели + Судан + Шведска + Сингапур + Словенија + Словачка + Сијера Леоне + Сенегал + Сомалија + Србија + Суринам + Салвадор + Сирија + Свазиленд + Чад + Француске Јужне Територије + Того + Тајланд + Таџикистан + Туркменистан + Тунис + Турска + Тринидад и Тобаго + Тајван + Танзанија + Украјина + Уганда + Сједињене Америчке Државе + Уругвај + Узбекистан + Ватикан + Венецуела + Британска Девичанска Острва + С.А.Д. Девичанска Острва + Вијетнам + Јемен + Југославија + Јужна Африка + Замбија + Зимбабве + + + + [а-и к-ш ђ ј љ њ ћ џ] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + јан + феб + мар + апр + мај + јун + јул + абг + сеп + окт + ноб + дец + + + ј + ф + м + а + м + ј + ј + а + с + о + н + д + + + јануара + фебруара + марта + априла + маја + јуна + јула + августа + септембра + октобра + новембра + децембра + + + + + јан + феб + мар + апр + мај + јун + јул + абг + сеп + окт + ноб + дец + + + ј + ф + м + а + м + ј + ј + а + с + о + н + д + + + јануар + фебруар + март + април + мај + јун + јул + август + септембар + октобар + новембар + децембар + + + + + + + нед + пон + уто + сре + чет + пет + суб + + + недеља + понедељак + уторак + среда + четвртак + петак + субота + + + + + + + + + + п. н. е. + н. е + + + + + + + EEEE, dd.MMMM.yyyy. + + + + + dd.MM.yyyy. + + + + + dd.MM.yyyy. + + + + + d.M.yy. + + + + + + + + HH.mm.ss z + + + + + HH.mm.ss z + + + + + HH.mm.ss + + + + + HH.mm + + + + + + + {1} {0} + + + + + + + + + Централно Европско Време + Централно Европско Време + + + CET + CET + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + YUN + Дин + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sr_YU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sr_YU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sr_YU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sr_YU.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sv_FI.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sv_FI.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sv_FI.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sv_FI.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sv_SE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sv_SE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sv_SE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sv_SE.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sv.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sv.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sv.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sv.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2750 @@ + + + + + + + + + + + afar + abkhaziska + achinese + acholi + adangme + adygeiska + avestiska + afrikaans + afro-asiatiskt (andra) + afrihili + akan + akkadiska + aleutiska + Algonkinspråk + amhariska + aragonesiska + fornengelska (ca. 450-1100) + Apache-språk + arabiska + arameiska + araukanska + arapaho + artificiellt (annat) + arawakiska + assami + asturiska + Athapaskiska språk + Australiska språk + avariska + awadhi + aymara + azerbadzjanska + basjkiriska + banda + Bamilekiska språk + baluchi + bambara + balinesiska + basa + baltiskt (annat) + vitryska + beyja + bemba + berber + bulgariska + bihari + bhojpuri + bislama + bikol + bini + siksika + bambara + bengali + bantuspråk + tibetanska + bretonska + braj + bosniska + batak + buriat + buginesiska + blin + katalanska + caddo + centralamerikanskt indianskt (annat) + karibiska + kaukasiskt (annat) + tjetjenska + cebuano + keltiskt (annat) + chamorro + chibcha + chagatai + chuukesiska + mari + chinook + choctaw + chipewyan + cherokesiska + cheyenne + Chami-språk + korsiska + koptiska + kreolska och pidgin, engelsk-baserat (annat) + kreolska och pidgin, fransk-baserat (annat) + kreolska och pidgin, portugisisk-baserat (annat) + cree + krimturkiska; krimtatar + kreolska och pidgin (annat) + tjeckiska + kasjubiska + kyrkoslaviska + kushitiska (annat) + tjuvasjiska + walesiska + danska + dakota + dargwa + dayak + tyska + delaware + slave + dogrib + dinka + dogri + dravidiskt (annat) + lågsorbiska + duala + medelnederländska (ca. 1050-1350) + divehi + dyula + dzongkha + ewe + efik + fornegyptiska + ekajuk + grekiska + elamitiska + engelska + medelengelska (1100-1500) + esperanto + spanska + estniska + baskiska + ewondo + farsi + fang + fanti + fulani + finska + finskugriskt (annat) + fidjianska + färöiska + franska + medelfranska (ca.1400-1600) + fornfranska (842- ca.1400) + friuilian + frisiska + irländsk gaeliska + ga + gayo + gbaya + skotsk gaeliska + germanskt (annat) + geez + gilbertesiska; kiribati + galiciska + medelhögtyska (ca.1050-1500) + guaraní + fornhögtyska (ca.750-1050) + gondi + gorontalo + gotiska + grebo + forngrekiska (till 1453) + gujarati + manx gaeliska + gwichʻin + haussa + haida + hawaiiska + hebreiska + hindi + hiligaynon + himachali + hettitiska + hmong + hiri motu + kroatiska + högsorbiska + haitiska + ungerska + hupa + armeniska + herero + interlingua + iban + indonesiska + interlingue + ibo + sichuan yi + inupiaq + iloko + indiskt (annat) + indo-europeiskt (annat) + ingusjiska + ido + iranska + irokesiska + isländska + italienska + inuktitut + japanska + lojban + judisk farsi + judisk arabiska + javanska + georgiska + karakalpakiska + kabyliska + kachin + kamba + karen + kawi + kabardinska + kikongo + khasi + khoisan (annat) + khotanesiska + kikuyu + kuanyama + kazakiska + grönländska + kambodjanska; khmer + kinbundu + kanaresiska; kannada + koreanska + konkani + kosreanska + kpelle + kanuri + karachay-balkar + kru + kurukh + kashmiri + kurdiska + kumyk + kutenai + kome + korniska + kirgisiska + latin + ladino + lahnda + lamba + luxemburgiska + lezghien + luganda + limburgiska + lingala + laotiska + lolo; mongo + lozi + litauiska + luba-katanga + luba-lulua + luiseño + lunda + lushai + lettiska + madurese + magahi + maithili + makasar + mande + austronesiska + massajiska + moksja + mandar + mende + malagassiska + medeliriska (900-1200) + marshalliska + maori + mic-mac + minangkabau + Blandade språk + makedonska + mon-khmer (annat) + malayalam + mongoliska + manchu + manipuri + manobo-sråk + moldaviska + mohawk + mossi + marathi + malajiska + maltesiska + Flera språk + Munda-språk + muskogee + marwari + burmanska + maya + erjya + nauru + nahuatl; aztekiska + nordamerikanskt indianspråk (annat) + napolitanska + norskt bokmål + nord­ndebele + lågtyska; lågsaxiska + nepali + newari + ndonga + nias + kordofanspråk (annat) + niuean + nederländska + ny­norsk + norska + nogai + fornnordiska + syd­ndebele + sotho, nord + Nubiska språk + navaho + nyanja + nyamwezi + nyankole + nyoro + nzima + provensalska (efter 1500 + odjibwa; chippewa + oromo + oriya + ossetiska + osage + ottomanturkiska (1500-1928) + Oto-mangue-språk + panjabi + papuaspråk (annat) + pangasinan + pahlavi + pampanga + papiamento + palau + fornpersiska (ca.600-400 b.c.) + filippinskt språk (annat) + kananeiska; feniciska + pali + polska + ponape + Prakritspråk + fornprovensalska (till 1500) + pashto; afghanska + portugisiska + quechua + rajasthani + rapanui + rarotongan + räto­romanska + rundi + rumänska + romanskt (annat) + romani + ryska + rwanda; kinjarwanda + sanskrit + sandawe + jakutiska + nordamerikanskt indianskt (annat) + salish-språk + samaritanska + sasak + santali + sardiska + skotska + sindhi + nord­samiska + selkup + semitiskt (annat) + sango + forniriska (till 900) + teckenspråk + serbokroatiska + shan + singalesiska + sidamo + siouxspråk + Sinotibetanska språk + slovakiska + slovenska + slaviskt (annat) + samoanska + sydsamiska + samiskt språk (annat) + lulesamiska + enaresamiska + skoltsamiska + shona; manshona + soninke + somali + sogdiska + songhai + albanska + serbiska + serer + swati + nilosahariskt (annat) + syd­sotho + sundanesiska + sukuma + susu + sumeriska + svenska + swahili + syriska + tamil + tai (annat) + telugu + temne + tereno + tetum + tadzjikiska + thailändska + tigrinja + tigré + tivi + turkmeniska + tokelau + tagalog + tlingit + tamashek + tswana + tonga + tonga-Nyasa + tok pisin + turkiska + tsonga + tsimshian + tatariska + tumbuka + Tupi-språk + altaiskt (annat) + tuvaluan + twi + tahitiska + tuviniska + udmurtiska + uiguriska + ugaritiska + ukrainska + umbundu + obestämt + urdu + uzbekiska + venda + vietnamesiska + volapük + votiska + walloon + wakash + walamo + waray + washo + Sorbiska språk + wolof + kalmuckiska + xhosa + jiddisch + yoruba + Yupiska språk + zhuang + zapotek + zenaga + kinesiska + zandé + zulu + zuñi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Andorra + Förenade Arabemiraten + Afganistan + Antigua och Barbuda + Anguilla + Albanien + Armenien + Nederländska Antillerna + Angola + Antarktis + Argentina + Amerikanska Samoa + Österrike + Australien + Aruba + Azerbajdzjan + Bosnien och Hercegovina + Barbados + Bangladesh + Belgien + Burkina Faso + Bulgarien + Bahrain + Burundi + Benin + Bermuda + Brunei + Bolivia + Brasilien + Bahamas + Bhutan + Bouvetön + Botswana + Vitryssland + Belize + Kanada + Kokosöarna (Keelingöarna) + Demokratiska republiken Kongo + Centralafrikanska republiken + Kongo + Schweiz + Elfenbenskusten + Cooköarna + Chile + Kamerun + Kina + Colombia + Costa Rica + Kuba + Kap Verde + Julön + Cypern + Tjeckien + Tyskland + Djibouti + Danmark + Dominica + Dominikanska republiken + Algeriet + Ecuador + Estland + Egypten + Västra Sahara + Eritrea + Spanien + Etiopien + Finland + Fiji + Falklandsöarna + Mikronesien + Färöarna + Frankrike + en + Gabon + Storbritannien + Grenada + Georgien + Franska Guyana + Ghana + Gibraltar + Grönland + Gambia + Guinea + Guadelope + Ekvatorialguinea + Grekland + Sydgeorgien och Södra Sandwichöarna + Guatemala + Guam + Guinea-Bissau + Guyana + Hongkong (S.A.R. Kina) + Heard- och McDonaldöarna + Honduras + Kroatien + Haiti + Ungern + Indonesien + Irland + Israel + Indien + Brittiska Indiska oceanöarna + Irak + Iran + Island + Italien + Jamaica + Jordanien + Japan + Kenya + Kirgisistan + Kambodja + Kiribati + Komorerna + S:t Christopher och Nevis + Nordkorea + Sydkorea + Kuwait + Kajmanöarna + Kazachstan + Laos + Libanon + S:t Lucia + Liechtenstein + Sri Lanka + Liberia + Lesotho + Litauen + Luxemburg + Lettland + Libyen + Marocko + Monaco + Moldavien + Madagaskar + Marshallöarna + Makedonien + Mali + Myanmar + Mongoliet + Macao (S.A.R. Kina) + Nordmarianerna + Martinique + Mauretanien + Montserrat + Malta + Mauritius + Maldiverna + Malawi + Mexiko + Malaysia + Moçambique + Namibia + Nya Kaledonien + Niger + Norfolkön + Nigeria + Nicaragua + Nederländerna + Norge + Nepal + Nauru + Niueön + Nya Zeeland + Oman + Panama + Peru + Franska Polynesien + Papua Nya Guinea + Filippinerna + Pakistan + Polen + S:t Pierre och Miquelon + Pitcairn + Puerto Rico + Palestinska territoriet + Portugal + Palau + Paraguay + Qatar + Réunion + Rumänien + Ryssland + Rwanda + Saudi-Arabien + Salomonöarna + Seychellerna + Sudan + Sverige + Singapore + S:t Helena + Slovenien + Svalbard och Jan Mayen + Slovakien + Sierra Leone + San Marino + Senegal + Somalia + Serbien + Surinam + São Tomé och Príncipe + El Salvador + Syrien + Swaziland + Turks- och Caicosöarna + Tchad + Franska Sydterritorierna + Togo + Thailand + Tadzjikistan + Tokelauöarna + Östtimor + Turkmenistan + Tunisien + Tonga + Turkiet + Trinidad och Tobago + Tuvalu + Taiwan + Tanzania + Ukraina + Uganda + Små, avlägset belägna öar som tillhör Förenta staterna + Amerikas Förenta Stater + Uruguay + Uzbekistan + Vatikanstaten + S:t Vincent och Grenadinerna + Venezuela + Brittiska Jungfruöarna + Amerikanska Jungfruöarna + Vietnam + Vanuatu + Wallis och Futunaöarna + Samoa + Jemen + Mayotte + Jugoslavien + Sydafrika + Zambia + Zimbabwe + + + Reviderad + + + Kalendar + Sortera + Valuta + + + Buddistisk kalender + Kinesisk kalender + Gregoriansk kalender + Hebreisk kalender + Islamisk kalender + Islamisk civil kalender + Japansk kalender + Direkt ordning + Telefonboksordning + Pinyinordning + Raderingsordning + Traditionell ordning + + + + [a-zäöåáéëü] + + + + + + + + jan + feb + mar + apr + maj + jun + jul + aug + sep + okt + nov + dec + + + J + F + M + A + M + J + J + A + S + O + N + D + + + januari + februari + mars + april + maj + juni + juli + augusti + september + oktober + november + december + + + + + + + + + ti + on + to + fr + + + + S + M + T + O + T + F + L + + + söndag + måndag + tisdag + onsdag + torsdag + fredag + lördag + + + + + + + + fm + em + + + f.Kr. + e.Kr. + + + + + + + 'den 'd MMMM yyyy + + + + + 'den 'd MMM yyyy + + + + + yyyy-MM-dd + + + + + yyyy-MM-dd + + + + + + + + 'kl. 'HH.mm.ss z + + + + + HH.mm.ss z + + + + + HH.mm.ss + + + + + HH.mm + + + + + + + {1} {0} + + + + + + + + + Pacific, normaltid + Pacific, sommartid + + + PST + PDT + + Los Angeles + + + + Pacific, normaltid + Pacific, sommartid + + + PST + PDT + + Los Angeles + + + + Mountain, normaltid + Mountain, sommartid + + + MST + MDT + + Denver + + + + Mountain, normaltid + Mountain, sommartid + + + MST + MDT + + Denver + + + + Mountain, normaltid + Mountain, sommartid + + + MST + MST + + Phoenix + + + + Mountain, normaltid + Mountain, sommartid + + + MST + MST + + Phoenix + + + + Central, normaltid + Central, sommartid + + + CST + CDT + + Chicago + + + + Central, normaltid + Central, sommartid + + + CST + CDT + + Chicago + + + + Eastern, normaltid + Eastern, sommartid + + + EST + EDT + + New York + + + + Eastern, normaltid + Eastern, sommartid + + + EST + EDT + + New York + + + + Eastern, normaltid + Eastern, normaltid + + + EST + EST + + Indianapolis + + + + Eastern, normaltid + Eastern, normaltid + + + EST + EST + + Indianapolis + + + + Hawaii, normaltid + Hawaii, normaltid + + + HST + HST + + Honolulu + + + + Hawaii, normaltid + Hawaii, normaltid + + + HST + HST + + Honolulu + + + + Alaska, normaltid + Alaska, sommartid + + + AST + ADT + + Anchorage + + + + Alaska, normaltid + Alaska, sommartid + + + AST + ADT + + Anchorage + + + + Atlantic, normaltid + Atlantic, sommartid + + + AST + ADT + + Halifax + + + + Newfoundland, normaltid + Newfoundland, sommartid + + + CNT + CDT + + St. Johns + + + + Newfoundland, normaltid + Newfoundland, sommartid + + + CNT + CDT + + St. Johns + + + + Centraleuropa, normaltid + Centraleuropa, sommartid + + + CET + CEST + + Paris + + + + Centraleuropa, normaltid + Centraleuropa, sommartid + + + CET + CEST + + Paris + + + + Greenwichtid + Greenwichtid + + + GMT + GMT + + London + + + + Greenwichtid + Greenwichtid + + + GMT + GMT + + Casablanca + + + + Israel, normaltid + Israel, sommartid + + + IST + IDT + + Jerusalem + + + + Japan, normaltid + Japan, normaltid + + + JST + JST + + Tokyo + + + + Japan, normaltid + Japan, normaltid + + + JST + JST + + Tokyo + + + + Östeuropa, normaltid + Östeuropa, sommartid + + + EET + EEST + + Bukarest + + + + Kina, normaltid + Kina, normaltid + + + CTT + CDT + + Shanghai + + + + Kina, normaltid + Kina, normaltid + + + CTT + CDT + + Shanghai + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + Andorransk diner + ADD + + + Andorransk peseta + ADP + + + Förenade arabemiratens dirham + AED + + + Affars and Issas franc + AIF + + + Albansk lek (1946-1961) + ALK + + + Albansk lek + lek + + + Albansk lek – Valute) + ALV + + + Albansk dollar – Foreign Exchange Certificates) + ALX + + + Armenisk dram + dram + + + Nederländsk antillisk gulden + NA f. + + + Angolansk kwanza + AOA + + + Angolansk kwanza (1977-1990) + AOK + + + Angolansk ny kwanza (1990-2000) + AON + + + Angolansk kwanza – Reajustado (1995-1999) + AOR + + + Angolansk escudo + AOS + + + Argentinsk austral + ARA + + + Argentinsk peso – Moneda nacional + ARM + + + Argentinsk peso (1983-1985) + ARP + + + Argentinsk peso + Arg$ + + + Österrikisk schilling + ATS + + + Australisk dollar + $A + + + Australiskt pund + AUP + + + Aruba-florin + AWG + + + Azerbajdzansk manat + AZM + + + Bosnisk-Hercegovinsk dinar + BAD + + + Bosnisk-Hercegovinsk konvertibel mark + KM + + + Bosnisk-Hercegovinsk ny dinar + BAN + + + Barbadisk dollar + BDS$ + + + Bangladeshisk taka + Tk + + + Belgisk franc (konvertibel) + BEC + + + Belgisk franc + BF + + + Belgisk franc (finansiell) + BEL + + + Bulgarisk hård lev + lev + + + Bulgarisk socialistisk lev + BGM + + + Bulgarisk ny lev + BGN + + + Bulgarisk lev (1879-1952) + BGO + + + Bulgarisk lev – Foreign Exchange Certificates + BGX + + + Bahrainsk dinar + BD + + + Burundisk franc + Fbu + + + Bermuda-dollar + Ber$ + + + Bermuda-pund + BMP + + + Bruneisk dollar + BND + + + Boliviansk peso + BOP + + + Boliviansk mvdol + BOV + + + Brasiliansk cruzeiro novo (1967-1986) + BRB + + + Brasiliansk cruzado + BRC + + + Brasiliansk cruzeiro (1990-1993) + BRE + + + Brasiliansk real + R$ + + + Brasiliansk cruzado novo + BRN + + + Brasiliansk cruzeiro + BRR + + + Brasiliansk cruzeiro (1942-1967) + BRZ + + + Bahamansk dollar + BSD + + + Bahamanskt pund + BSP + + + Bhutanesisk ngultrum + Nu + + + Bhutanesisk rupie + BTR + + + Burmesisk kyat + BUK + + + Burmesisk rupee + BUR + + + Botswansk pula + BWP + + + Vitrysk ny rubel (1994-1999) + BYB + + + Vitrysk rubel (1992-1994) + BYL + + + Vitrysk rubel + Rbl + + + Belizisk dollar + BZ$ + + + Brittiska Honduras-dollar + BZH + + + Kanadensisk dollar + Can$ + + + Kongolesisk franc congolais + CDF + + + Kongolesisk franc + CDG + + + Kongolesisk zaire + CDL + + + Centralafrikanska CFA-franc + CFF + + + Schweizisk franc + SwF + + + Cooköisk dollar + CKD + + + Chilensk condor + CLC + + + Chilensk escudo + CLE + + + Chilensk unidades de fomento + CLF + + + Chilensk peso + Ch$ + + + Kamerunsk CFA-franc + CMF + + + Kinesisk jen min piao yuan + CNP + + + Kinesiska US Dollar Foreign Exchange Certificates + CNX + + + Kinesisk yuan renminbi + Y + + + Colombiansk papperspeso + COB + + + Kongolesisk CFA-franc + COF + + + Colombiansk peso + Col$ + + + Costarikansk colon + C + + + Tjeckisk koruna + CSC + + + Tjeckisk hård koruna + CSK + + + Kubansk peso + CUP + + + Kubansk Foreign Exchange Certificates + CUX + + + Kapverdisk escudo + CVEsc + + + Curacaoisk gulden + CWG + + + Cypriotiskt pund + £C + + + Tjeckisk koruna + CZK + + + Östtysk mark + DDM + + + Tysk mark + DEM + + + Tysk sperrmark + DES + + + Djiboutisk franc + DF + + + Dansk krona + DKr + + + Dominikansk peso + RD$ + + + Algerisk dinar + DA + + + Algerisk ny franc + DZF + + + Algerisk franc germinal + DZG + + + Ecuadoriansk sucre + ECS + + + Ecuadoriansk Unidad de Valor Constante (UVC) + ECV + + + Estnisk krona + EEK + + + Egyptisk pund + EGP + + + Eritreansk nakfa + ERN + + + Spansk peseta + ESP + + + Etiopisk birr + Br + + + Etiopisk dollar + ETD + + + Euro + + + + Finska mark + FIM + + + Finska mark (1860-1962) + FIN + + + Fijiansk dollar + F$ + + + Fijianskt pund + FJP + + + Falklandsöarnas pund + FKP + + + Färöisk kronar + FOK + + + Fransk franc + FRF + + + Fransk Franc Germinal/Franc Poincare + FRG + + + Gabonesisk CFA-franc + GAF + + + Britiskt pound sterling + £ + + + Georgisk kupon larit + GEK + + + Georgisk lari + lari + + + Ghanansk cedi + GHC + + + Ghanansk gammal cedi + GHO + + + Ghananskt pund + GHP + + + Ghanansk omvärderad cedi + GHR + + + Gibraltiskt pund + GIP + + + Grönländsk krona + GLK + + + Gambisk dalasi + GMD + + + Gambiskt pund + GMP + + + Guineansk franc + GF + + + Guineansk franc (1960-1972) + GNI + + + Guineansk syli + GNS + + + Guadeloupisk franc + GPF + + + Ekvatorialguineansk ekwele guineana + GQE + + + Ekvatorialguineansk franco + GQF + + + Ekvatorialguineansk peseta guineana + GQP + + + Grekisk drachma + GRD + + + Grekisk ny drachma + GRN + + + Guatemalansk quetzal + Q + + + Franska Guyanas Franc Guiana + GUF + + + Portugisisk guineas escudo + GWE + + + Portugisiska guineas mil reis + GWM + + + Guinea-Bissauisk peso + GWP + + + Guyanansk dollar + G$ + + + Hongkong-dollar + HK$ + + + Hoduransk lempira + L + + + Kroatisk dinar + HRD + + + Kroatisk kuna + HRK + + + Haitisk gourde + HTG + + + Ungersk forint + Ft + + + Nordirländskt pund + IBP + + + Indonesisk nica gulden + IDG + + + Indonesisk java rupiah + IDJ + + + Indonesisk ny rupiah + IDN + + + Indonesisk rupiah + Rp + + + Irländskt pund + IR£ + + + Israelisk shekel + ILL + + + Israeliskt pund + ILP + + + Israelisk ny shekel + ILS + + + Isle of Man pund sterling + IMP + + + Indisk rupie + =0#Rs.|1#Re.|1<Rs. + + + Irakisk dinar + ID + + + Iransk rial + RI + + + Isländsk krona + ISK + + + Italiensk lira + + + + Jersey pound sterling + JEP + + + Jamaicansk dollar + J$ + + + Jamaicanskt pund + JMP + + + Jordansk dinar + JD + + + Japansk yen + ¥ + + + Kenyansk shilling + K Sh + + + Kirgizistansk som + som + + + Kambodjansk gammal riel + KHO + + + Kambodjansk riel + CR + + + Kiribatisk dollar + KID + + + Komorisk franc + CF + + + Nordkoreansk won + KPP + + + Nordkoreansk won + KPW + + + Sydkoreansk hwan + KRH + + + Sydkoreansk gammal won + KRO + + + Sydkoreansk won + KRW + + + Kuwaitisk dinar + KD + + + Caymanöisk dollar + KYD + + + Kazakisk rubel + KZR + + + Kazakisk tenge + T + + + Laotisk kip + LAK + + + Libanesiskt pund + LL + + + Liechtensteinsk franc + LIF + + + Srilankesisk rupie + SL Re + + + Ceylonesisk rupie + LNR + + + Liberisk dollar + LRD + + + Lesothisk loti + M + + + Lettisk lita + LTL + + + Lettisk talonas + LTT + + + Luxemburgsk franc + LUF + + + Lettisk lats + LVL + + + Lettisk rubel + LVR + + + Libyska brittiska militärmyndighetens lira + LYB + + + Libysk dinar + LD + + + Libyskt pund + LYP + + + Marockansk dirham + MAD + + + Marockansk franc + MAF + + + Monegaskisk franc nouveau + MCF + + + Monegaskisk franc germinal + MCG + + + Moldavisk leu-kupong + MDC + + + Moldavisk leu + MDL + + + Moldavisk rubelkupong + MDR + + + Madagaskisk ariary + MGA + + + Madagaskisk franc + MGF + + + Marshallöisk dollar + MHD + + + Makedonisk denar + MDen + + + Makedonisk denar (1992-1993) + MKN + + + Malisk franc + MLF + + + Myanmarisk kyat + MMK + + + Myanmarisk dollar – Foreign Exchange Certificates + MMX + + + Mongolisk tugrik + Tug + + + Macaoisk pataca + MOP + + + Martiniqueisk franc + MQF + + + Mauretansk ouguiya + UM + + + Maltesisk lira + Lm + + + Maltesiskt pund + MTP + + + Mauritisk rupie + MUR + + + Maldivisk rupie + MVP + + + Maldivisk rufiyaa + MVR + + + Malawisk kwacha + MK + + + Malawiskt pund + MWP + + + Mexikansk peso + MEX$ + + + Mexikansk silverpeso (1861-1992) + MXP + + + Mexikansk Unidad de Inversion (UDI) + MXV + + + Malaysisk ringgit + RM + + + Moçambikisk escudo + MZE + + + Moçambikisk metical + Mt + + + Namibisk dollar + N$ + + + Nya Kaledonisk franc germinal + NCF + + + Nigeriansk naira + NGN + + + Nigerianskt pund + NGP + + + Nya Hebridiska CFP-franc + NHF + + + Nicaraguansk córdoba + NIC + + + Nicaraguansk guldcordoba + NIG + + + Nicaraguansk córdoba oro + NIO + + + Nederländsk gulden + NLG + + + Norsk krona + NKr + + + Nepalesisk rupie + Nrs + + + Nyzeeländsk dollar + $NZ + + + Nyzeeländsk pund + NZP + + + Omansk rial + RO + + + Omansk rial saidi + OMS + + + Panamansk balboa + PAB + + + Transdniestrisk rubekupong + PDK + + + Transdniestrisk ny rubel + PDN + + + Transdniestrisk rubel + PDR + + + Peruansk inti + PEI + + + Peruansk sol nuevo + PEN + + + Peruansk sol + PES + + + Papuansk kina + PGK + + + Filippinsk peso + PHP + + + Pakistansk rupie + Pra + + + Polsk zloty + Zl + + + Polsk US-dollar Foreign Exchange Certificates + PLX + + + Polsk zloty (1950-1995) + PLZ + + + Palestinskt pund + PSP + + + Portugisisk conto + PTC + + + Portugisisk escudo + PTE + + + Paraguaysk guarani + PYG + + + Qatarisk rial + QR + + + Reunion-franc + REF + + + Rumänsk leu + leu + + + Rumänsk ny leu + RON + + + Rysk rubel + RUB + + + Rysk rubel (1991-1998) + RUR + + + Rwandisk franc + RWF + + + Saudisk riyal + SRl + + + Saudisk sovereign riyal + SAS + + + Salomonöisk dollar + SI$ + + + Seychellisk rupie + SR + + + Sudanesisk dinar + SDD + + + Sudanesiskt pund + SDP + + + Svensk krona + kr + + + Singaporiansk dollar + S$ + + + Saint Helena-pund + SHP + + + Slovensk tolar bons + SIB + + + Slovensk tolar + SIT + + + Slovakisk koruna + Sk + + + Sierraleonsk leone + SLL + + + Sanmarinsk lira + SML + + + Somalisk shilling + So. Sh. + + + Somaliländsk shilling + SQS + + + Surinamesisk gulden + Sf + + + Skottskt pund + SSP + + + São Tomé och Príncipe-dobra + Db + + + São Tomé och Príncipe-escudo + STE + + + Sovjetisk ny rubel + SUN + + + Sovjetisk rubel + SUR + + + Salvadoransk colon + SVC + + + Syriskt pund + LS + + + Swaziländsk lilangeni + E + + + Turks and Caicos-crown + TCC + + + Tchadisk CFA-franc + TDF + + + Thailändsk baht + THB + + + Tadzjikisk rubel + TJR + + + Tadzjikisk somoni + TJS + + + Turkmensk manat + TMM + + + Tunisisk dinar + TND + + + Tongansk paʻanga + T$ + + + Tongansk pound sterling + TOS + + + Timoriansk escudo + TPE + + + Timoriansk pataca + TPP + + + Turkisk lira + TL + + + Trinidadisk dollar + TT$ + + + Trinidadisk gammal dollar + TTO + + + Tuvaluansk dollar + TVD + + + Taiwanesisk ny dollar + NT$ + + + Tanzanisk shilling + T Sh + + + Ukrainsk hryvnia + UAH + + + Ukrainsk karbovanetz + UAK + + + Ugandisk shilling (1966-1987) + UGS + + + Ugandisk shilling + U Sh + + + US-dollar + US$ + + + US-dollar (nästa dag) + USN + + + US-dollar (samma dag) + USS + + + Uruguayansk peso fuerte + UYF + + + Uruguayansk peso (1975-1993) + UYP + + + Uruguayansk peso uruguayo + Ur$ + + + Uzbekisk coupon som + UZC + + + Uzbekisk sum + UZS + + + Heliga Stolen-lira + VAL + + + Nordvietnamesisk piastre dong viet + VDD + + + Nordvietnamesisk ny dong + VDN + + + Nordvietnamesisk viet minh piastre dong viet + VDP + + + Venezuelansk bolivar + Be + + + Jungfruöisk dollar + VGD + + + Vietnamesisk dong + VND + + + Vietnamesisk ny dong + VNN + + + Vietnamesisk dong + VNR + + + Vietnamesisk nationell dong + VNS + + + Vanuatisk vatu + VT + + + Västsamoanskt pund + WSP + + + Västsamoansk tala + WST + + + CFA Franc BEAC + XAF + + + Östkaribisk dollar + EC$ + + + CFA Nouveau Franc + XCF + + + CFA Franc BCEAEC + XEF + + + Fransk guldfranc + XFO + + + French UIC-Franc + XFU + + + Islamisk dinar + XID + + + French Metropolitan Nouveau Franc + XMF + + + Fransk Antillisk CFA-franc + XNF + + + CFA Franc BCEAO + XOF + + + CFP-franc + CFPF + + + Jemenitisk dinar + YDD + + + Jemenitisk imadi riyal + YEI + + + Jemenitisk rial + YRl + + + Jugoslavisk hård dinar + YUD + + + Jugoslavisk Federation-dinar + YUF + + + Jugoslavisk 1994 dinar + YUG + + + Jugoslavisk noviy dinar + YUM + + + Jugoslavisk konvertibel dinar + YUN + + + Jugoslavisk oktober dinar + YUO + + + Jugoslavisk reformed dinar + YUR + + + Sydafrikansk rand (finansiell) + ZAL + + + Sydafrikansk rand + ZAP + + + Sydafrikansk rand + R + + + Zambisk kwacha + ZMK + + + Zambiskt pund + ZMP + + + Zairisk ny zaire + ZRN + + + Zairisk zaire + ZRZ + + + Zimbabwisk dollar + Z$ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sw_KE.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sw_KE.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sw_KE.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sw_KE.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sw_TZ.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sw_TZ.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sw_TZ.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sw_TZ.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/sw.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/sw.xml --- zope3-3.4.0/src/zope/i18n/locales/data/sw.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/sw.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,192 @@ + + + + + + + + + + + Kiswahili + + + Muugano wa Falme za Nchi za Kiarabu + Antigua na Barbuda + Ajentina + Bosnia na Herzegowina + Ubelgiji + Brazili + Visiwa vya Bahama + Kanada + Jamhuri ya Afrika ya Kati + Kongo + Uswisi + Pwani ya Pembe + Kamerun + Uchina + Kolombia + Rasi Verde + Jamhuri ya Czech + Udachi + Jibuti + Udenmarki + Dominika + Jamhuri ya Dominikan + Ekvado + Misri + Uhispania + Uhabeshi + Ufaransa + Uingereza + Guinea ya Ikweta + Kroatia + Hungaria + Uyahudi + Uhindi + Iraki + Uajemi + Barafu + Uitaliani + Jamaika + Ujapani + Kenya + Kampuchea + Visiwa vya Komoro + Saint Kitts na Nevis + Korea ya Kaskazini + Korea ya Kusini + Luksemburg + Moroko + Monako + Visiwa vya Marshall + Meksiko + Malasya + Msumbiji + Nikaragua + Uholanzi + Unorwe + Nepali + Papua Guinea Mpya + Filipino + Ureno + Paragwai + Urusi + Arabuni Saudi + Visiwa vya Solomon + Visiwa vya Shelisheli + Uswidi + Somali + Sao Tome na Principe + Chadi + Timor ya Mashariki + Uturuki + Trinidad na Tobago + Tanzania + Muungano wa Nchi za Amerika + Urugwai + Vatikano + Saint Vincent na Grenadines + Yemeni + Afrika ya Kusini + + + + [a-z] + + + + + + + + Jan + Feb + Mar + Apr + Mei + Jun + Jul + Ago + Sep + Okt + Nov + Des + + + Januari + Februari + Machi + Aprili + Mei + Juni + Julai + Agosti + Septemba + Oktoba + Novemba + Desemba + + + + + + + Jpi + Jtt + Jnn + Jtn + Alh + Iju + Jmo + + + Jumapili + Jumatatu + Jumanne + Jumatano + Alhamisi + Ijumaa + Jumamosi + + + + + + + + + + KK + BK + + + + + + + + Saa za Africa Mashariki + Saa za Africa Mashariki + + + EAT + EAT + + Nairobi + + + + + + + KES + KSh + + + TZS + TSh + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/syr_SY.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/syr_SY.xml --- zope3-3.4.0/src/zope/i18n/locales/data/syr_SY.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/syr_SY.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + dd MMMM, yyyy + + + + + dd MMMM, yyyy + + + + + dd/MM/yyyy + + + + + dd/MM/yyyy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss + + + + + h:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;#,##0.###- + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤ #,##0.00;¤ #,##0.00- + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/syr.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/syr.xml --- zope3-3.4.0/src/zope/i18n/locales/data/syr.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/syr.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,52 @@ + + + + + + + + + + + ܣܘܪܝܝܐ + + + ܣܘܪܝܝܐ + + + + [[:Syrc:]‌‍‏‎] + + + + + + + + ܏ܟܢ ܏ܒ + ܫܒܛ + ܐܕܪ + ܢܝܣܢ + ܐܝܪ + ܚܙܝܪܢ + ܬܡܘܙ + ܐܒ + ܐܝܠܘܠ + ܏ܬܫ ܏ܐ + ܏ܬܫ ܏ܒ + ܏ܟܢ ܏ܐ + + + + + + + + + + SYP + ل.س.‏ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ta_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ta_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ta_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ta_IN.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + d-M-yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ta.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ta.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ta.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ta.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,440 @@ + + + + + + + + + + + அபார் + அப்காஸின் + ஆப்ரிகன்ஸ் + அம்ஹாரிக் + அரபு + அஸ்ஸாமி + அயமரா + அசர்பாய்ஜானி + பாஷ்கிர்0 + பைலோருஷ்ன் + பல்கேரியன் + பிஹாரி + பிஸ்லாமா + வங்காளம் + திபெத்து + பிரிடன் + காடலான் + கார்சியன் + செக் + வெல்ஷ் + டானிஷ் + ஜெர்மன் + புடானி + கிரேக்கம் + ஆங்கிலம் + எஸ்பரேன்டோ + ஸ்பேனிஷ் + எஸ்டோனியன் + பஸ்க் + பர்ஸியன் + பின்னிஷ் + பிஜி + பைரோஸி + பிரெஞ்சு + பிரிஷியன் + ஐரிஷ் + ஸ்காட்ஸ் காலெக் + கெலிஸியன் + குரானி + குஜராத்தி + ஹொஸா + ஹுப்ரு + இந்தி + கரோஷியன் + ஹங்கேரியன் + ஆர்மேனியன் + இன்டர்லிங்குவா + இந்தோனேஷியன் + இன்டர்லிங்குவா + இனுபெக் + ஐஸ்லென்டிக் + இத்தாலியன் + இனுகிடட் + ஜப்பானீஸ் + ஜாவானீஸ் + கன்னடம் + கசாக் + கிரின்லென்டிக் + கம்போடியன் + கன்னடா + கொரியன் + கொங்கனி + காஷ்மிரி + குர்திஷ் + கிர்கிஷ் + லாதின் + லிங்காலா + லோத்தியன் + லுத்தேனியன் + லேட்வியன் (லேட்டிஷ்) + மலகெஸி + மோரி + மெக்கடோனியன் + மலையாளம் + மங்கோலியன் + மோல்டேவியன் + மராத்தி + மலாய் + மால்டிஸ் + பர்மிஸ் + நாரூ + நேப்பாலி + டச்சு + நார்வேகியன் + ஆகிடியன் + ஒரோம (அபன்) + ஒரியா + பஞ்சாபி + போலிஷ் + பேஷ்டோ (புஷ்டோ) + போர்த்துகீஸ் + கியுசா + ரைட்டோ-ரோமென்ஸ் + கிருந்தி + ரோமேனியன் + தமிழ் + ரஷியன் + கின்யர்வென்டா + சமஸ்கிருதம் + சிந்தி + சென்க்ரோ + செர்போ-க்ரோஷியன் + சிங்களம் + ஸ்லோவெக் + ஸ்லோவினேயின் + ஸெமோன் + ஷோனா + சோமாலி + அல்பெனியன் + சர்பியன் + ஷிஸ்வாதி + ஷெஸ்ஸோதோ + சுடானீஸ் + ஷீவிடிஸ் + சுவாஹிலி + தமிழ் + தெலுங்கு + தாஜிக் + தாய் + டிக்ரின்யா + டர்க்மென் + டாகாலோக் + ஸெட்ஸ்வானா + டோங்கா + டர்கிஷ் + ஸோங்கா + டாடர் + த்திவி + யுகுர் + உக்ரேனியன் + உருது + உஸ்பெக் + வியட்நாமிஸ் + ஒலபுக் + ஒலோப் + ஹோஷா + ஈத்திஷ + யோருப்பா + ஜுவாங் + சீனம் + ஜூலூ + + + அன்டோரா + ஐக்கிய அரபு கூட்டாட்சி + ஆப்கானிஸ்தான் + ஆன்டிகுவா பார்புடா + அல்பேனியா + ஆர்மீனியா + அங்கோலா + அர்ஜெண்டினா + ஆஸ்திரியா + ஆஸ்திரேலியா + அஜர்பைஜான் + போஸ்னியா ஹெர்ஸிகோவினா + பார்படோஸ் + பங்களாதேஷ் + பெல்ஜியம் + பர்கினோ பாஸோ + பல்கேரியா + பஹ்ரைன் + புருண்டி + பெனின் + புரூனேய் + பொலிவியா + பிரேஸில் + பஹாமாஸ் + பூடான் + போட்ஸ்வானா + பெலாரூஸ் + பெலிஸ் + கனடா + மத்திய ஆப்ரிக்கக் குடியரசு + காங்கோ + ஸ்விட்சர்லாந்து + ஐவரி கோஸ்ட் + சிலி + கேமரூன் + சீன + கொலம்பியா + கோஸ்டாரிகா + கியூபா + கேப் வெர்டே + சைப்ரஸ் + செக் குடியரசு + ஜெர்மன் + ஜிபௌடி + டென்மார்க் + டொமினிகா + டொமினிகன் குடியரசு + அல்ஜீரியா + ஈக்வடார் + எஸ்டோனியா + எகிப்து + ஸ்பெயின் + எதியோப்பியா + பின்லாந்து + பிஜி + பிரான்ஸ் + காபோன் + பிரிடிஷ் கூட்டரசு + கிரனெடா + ஜார்ஜியா + கானா + காம்பியா + கினி + ஈக்குவிடோரியல் கினி + கிரீஸ் + குவாத்தாமாலா + கினி-பிஸ்ஸாவ் + கயானா + ஹாண்டுராஸ் + குரோசியா + ஹெய்தி + ஹங்கேரி + இந்தோனேஷியா + அயர்லாந்து + இஸ்ரேல் + இந்தியா + இராக் + ஈரான் + ஐஸ்லாந்து + இத்தாலி + ஜமாய்க்கா + ஜொர்டான் + ஜப்பான் + கென்யா + கிர்கிஸ்தான் + கம்போடியா + கிரிபடி + கோமரோஸ் + வட கொரியா + தென் கொரியா + குவைத்து + கஜகஸ்தான் + லாவோஸ் + லெபனான் + லிச்டெண்ஸ்டீன் + இலங்கை + லைபீரியா + லெசோதோ + லிதுவேனியா + லக்ஸ்சம்பர்க் + லாட்வியா + லிப்யா + மொரோக்கோ + மொனாக்கோ + மால்டோவா + மசெடோணியா + மாலீ + மியான்மார் + மங்கோலியா + மால்டா + மாலத்தீவு + மலாவீ + மெக்சிகோ + மலேஷியா + னாமீபியா + நிகாராகுவா + நெதர்லாந்து + நார்வே + நேபாளம் + நௌரு + நியூசிலாந்து + ஓமான் + பணாமா + பெரு + பாப்புவா-நியூகினி + பிலிப்பைன்ஸ் + பாகிஸ்தான் + போலந்து + போர்ச்சுக்கல் + பாரகுவே + காடார் + ருமேனியா + ரஷ்யா + சவூதி அரேபியா + சாலமன் தீவுகள் + ஸ்வீடன் + சிங்கப்பூர் + ஸ்லோவேனியா + ஸ்லோவாகியா + சான்மெரினோ + சூரினாம் + எல் சால்வடார் + சிரியா + சாட் + தாய்லாந்து + தாஜிகிஸ்தான் + துர்க்மெனிஸ்தான் + துனிசியா + தொங்கா + துருக்கி + திரினிடாட் தொபாகோ + துவாலூ + தைவான் + உக்ரைன் + ஐக்கிய அமெரிக்கா குடியரசு + உருகுவே + உஸ்பெகிஸ்தான் + வாடிகன் + வெனஜுவேலா + வியட்நாம் + வனுவாட்டு + சமோவா + யேமன் + தென் ஆப்ரிக்கா + ஜிம்பாப்வே + + + + [[:Taml:]‌‍] + + + + + + + + ஜன. + பிப். + மார். + ஏப். + மே + ஜூன் + ஜூலை + ஆக. + செப். + அக். + நவ. + டிச. + + + ஜனவரி + பிப்ரவரி + மார்ச் + ஏப்ரல் + மே + ஜூன் + ஜூலை + ஆகஸ்ட் + செப்டம்பர் + அக்டோபர் + நவம்பர் + டிசம்பர் + + + + + + + ஞா + தி + செ + பு + வி + வெ + + + + ஞாயிறு + திங்கள் + செவ்வாய் + புதன் + வியாழன் + வெள்ளி + சனி + + + + காலை + மாலை + + + கிமு + கிபி + + + + + + + + இந்திய நேரப்படி + இந்திய நேரப்படி + + + IST + IST + + + + + + + + + #,##,##0.###;-#,##,##0.### + + + + + + + #E0 + + + + + + + #,##,##0% + + + + + + + ¤ #,##,##0.00;-¤ #,##,##0.00 + + + + + + INR + ரூ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/te_IN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/te_IN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/te_IN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/te_IN.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd-MM-yyyy + + + + + dd-MM-yy + + + + + + + + h:mm:ss a z + + + + + h:mm:ss a z + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + ##,##,##0.###;-##,##,##0.### + + + + + + + #E0 + + + + + + + ##,##,##0% + + + + + + + ¤ ##,##,##0.00;-¤ ##,##,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/te.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/te.xml --- zope3-3.4.0/src/zope/i18n/locales/data/te.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/te.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,104 @@ + + + + + + + + + + + తెలుగు + + + భారత దేళ౦ + + + + [[:Telu:]‌‍] + + + + + + + + జనవరి + ఫిబ్రవరి + మార్చి + ఏప్రిల్ + మే + జూన్ + జూలై + ఆగస్టు + సెప్టెంబర్ + అక్టోబర్ + నవంబర్ + డిసెంబర్ + + + జనవరి + ఫిబ్రవరి + మార్చి + ఏప్రిల్ + మే + జూన్ + జూలై + ఆగస్టు + సెప్టెంబర్ + అక్టోబర్ + నవంబర్ + డిసెంబర్ + + + + + + + ఆది + సోమ + మంగళ + బుధ + గురు + శుక్ర + శని + + + ఆదివారం + సోమవారం + మంగళవారం + బుధవారం + గురువారం + శుక్రవారం + శనివారం + + + + పూర్వాహ్న + అపరాహ్న + + + + + + . + , + ; + % + + # + + + - + E + + + + + + + INR + రూ. + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/th_TH.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/th_TH.xml --- zope3-3.4.0/src/zope/i18n/locales/data/th_TH.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/th_TH.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;¤-#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/th.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/th.xml --- zope3-3.4.0/src/zope/i18n/locales/data/th.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/th.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,545 @@ + + + + + + + + + + + อาฟา + แอบกาเซีย + แอฟริกัน + อัมฮาริค + อาระบิค + อัสสัมมิส + ไอมารา + อาเซอร์ไบจานี + บาสช์กีร์ + บายโลรัสเซีย + บัลแกเรีย + บิฮารี + บิสลามา + เบนการี + ทิเบต + บรีทัน + แคตาแลน + คอร์ซิกา + เช็ค + เวลส์ + เดนมาร์ก + เยอรมัน + ภูฐานี + กรีก + อังกฤษ + เอสเปอรันโต + สเปน + เอสโตเนีย + แบสก์ + เปอร์เซีย + ฟิน + ฟิจิ + ฟาโรส + ฝรั่งเศส + ฟรีสแลนด์ + ไอริช + สก็อตส์เกลิค + กะลีเชีย + กัวรานี + กูจาราติ + โฮซา + ยิว + ฮีนดิ + โครเอเทีย + ฮังการี + อาร์มีเนีย + อินเตอร์ลิงกวา + อินโดนีเชีย + อินเตอร์ลิงค์ + ไอนูเปียก + ไอซ์แลนด์ดิค + อิตาลี + ไอนุกติตัท + ญี่ปุ่น + ชวา + จอร์เจียน + คาซัค + กรีนแลนด์ดิค + เขมร + กานาดา + เกาหลี + คัชมีรี + เคิด + เคอร์กิซ + ละติน + ลิงกาลา + ลาว + ลิธัวเนีย + แลตเวีย (เลททิสช์) + มาลากาซี + เมารี + แมซีโดเนีย + แมละยาลัม + มองโกล + โมดาเวีย + มาราที + มลายู + มอลตา + พม่า + นอรู + เนปาล + ฮอลันดา + นอร์เวย์ + ออกซิทัน + โอโรโม (อาฟาน) + โอริยา + ปัญจาป + โปแลนด์ + พาสช์โต (พุสช์โต) + โปรตุเกส + คิวชัว + เรโต-โรแมนซ์ + คิรันดี + โรมัน + รัสเซีย + คินยาวันดา + สันสกฤต + ซินดิ + สันโค + เซอร์โบ-โครเอเทียน + สิงหล + สโลวัค + สโลเวเนีย + ซามัว + โซนา + โซมาลี + แอลเบเนีย + เซอร์เบีย + ซีสวาติ + เซโสโท + ซันดานีส + สวีเดน + ซวาฮิรี + ทมิฬ + ทิลูกู + ทาจิค + ไทย + ทิกรินยา + เติร์กเมน + ตากาล็อก + เซตสวานา + ทองก้า + ตุรกี + ซองกา + ตาด + ทวี + อุยกัว + ยูเครน + อิรดู + อุสเบค + เวียดนาม + โวลาพุก + วูลอฟ + โซสา + ยีดิช + โยรูบา + จวง + จีน + ซูลู + + + อันดอร์รา + สหรัฐอาหรับเอมิเรตส์ + อัฟกานิสถาน + อันกิล่า + แอลเบเนีย + อาร์มีเนีย + เนเธอร์แลนด์แอนทิลล์ + อันโกลา + อาร์เจนติน่า + ออสเตรีย + ออสเตรเลีย + อารูบา + อาเซอร์ไบจัน + บอสเนีย และ เฮิร์ซโกวิเนีย + บาร์บาดอส + บังคลาเทศ + เบลเยี่ยม + เบอร์กินาฟาโซ + บัลแกเรีย + บาห์เรน + บูรันดิ + เบนิน + เบอร์มิวด้า + บรูไน + โบลิเวีย + บราซิล + บาฮามาส + ภูฐาน + บอตสวานา + เบลลารัส + เบลิซ + แคนาดา + สาธารณรัฐแอฟริกากลาง + คองโก + สวิสเซอร์แลนด์ + ฝั่งทะเลไอวอริ + ชิลี + คาเมรูน + จีน + โคลัมเบีย + คอสตาริก้า + คิวบา + เคพเวอร์ด + ไซปรัส + สาธารณรัฐเช็ค + เยอรมนี + ดิโบติ + เดนมาร์ก + โดมินิก้า + สาธารณรัฐโดมินิกัน + แอลจีเรีย + เอกวาดอร์ + เอสโตเนีย + อียิปต์ + ซาฮาร่าตะวันตก + อิริทรี + สเปน + เอธิโอเปีย + ฟินแลนด์ + ฟิจิ + ไมโครนิเซีย + ฝรั่งเศส + กาบอน + สหราชอาณาจักร + จอร์เจีย + เฟร็นชกิวน่า + กาน่า + แกมเบีย + กิวนี + กัวเดอลูป + เอควาโทเรียลกินี + กรีซ + กัวเตมาลา + กิวนี-บิสโซ + กูยาน่า + ฮ่องกง + ฮอนดูรัส + โครเอเชีย + ไฮตี + ฮังการี + อินโดนีเซีย + ไอร์แลนด์ + อิสราเอล + อินเดีย + อิรัก + อิหร่าน + ไอซแลนด์ + อิตาลี + จาไมก้า + จอร์แดน + ญี่ปุ่น + เคนย่า + เคอร์กิสถาน + กัมพูชา + คิรีบาติ + โคโมรอส + เกาหลีเหนือ + เกาหลีใต้ + คูเวต + คาซัคสถาน + ลาว + เลบานอน + ไลเทนสไตน์ + ศรีลังกา + ลิเบอร์เลีย + เลโซโท + ลิเทอร์เนีย + ลักซ์เซมเบอร์ก + ลาตเวีย + ลิเบีย + โมรอคโค + โมนาโค + โมลโดวา + มาดากาสก้า + แมซีโดเนีย + มาลี + สหภาพพม่า + มองโกเลีย + มาเก๊า + มาร์ตินิก + มอริทาเนีย + มอนต์เซอราต + มัลต้า + มอริเตียส + แม็กซิโก + มาเลเซีย + โมแซมบิค + นามิเบีย + นิวคาลิโดเนีย + ไนเจอร์ + ไนจีเรีย + นิคารากัว + เนเธอร์แลนด์ + นอร์เวย์ + เนปาล + นียู + นิวซีแลนด์ + โอมาน + ปานามา + เปรู + เฟร็นชโพลินีเซีย + ปาปัวนิวกีนี + ฟิลิปปินส์ + ปากีสถาน + โปแลนด์ + เปอร์โตริโก + โปตุกัล + ปารากวัย + กาตาร์ + รูเมเนีย + รัสเซีย + ราวัลดา + ซาอุดิอาระเบีย + เซย์แชลล์ + ซูดาน + สวีเดน + สิงคโปร์ + สโลวิเนีย + สโลวาเกีย + เซียร์ร่าลีออน + ซินีกัล + โซมาเลีย + เซอร์เบีย + ซูรินามิ + เอลซาวาดอร์ + ซีเรีย + สวาซิแลนด์ + ชาด + อาณาเขตทางใต้ของฝรั่งเศส + โตโก + ประเทศไทย + ทาจิกิสถาน + โทกิโล + ติมอร์ตะวันออก + เติร์กเมนิสถาน + ตูนิเซีย + ทองก้า + ตุรกี + ทรินิแดด และโทบาโก + ไต้หวัน + ทานซาเนีย + ยูเครน + อูกานดา + สหรัฐอเมริกา + อุรูกวัย + อุซเบกิสถาน + วาติกัน + เวเนซูเอล่า + บริทิชเวอร์จินไอส์แลนด์ + ยูเอสเวอร์จินไอส์แลนด์ + เวียดนาม + วานัวตู + เยเมน + มายอต + ยูโกสลาเวีย + แอฟริกาใต้ + แซมเบีย + ซิมบาบเว + + + + [:Thai:] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + พ.ศ. + + + + + + + EEEE'ที่ 'd MMMM G yyyy + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + d/M/yyyy + + + + + + + + H' นาฬิกา 'm' นาที 'ss' วินาที' + + + + + H' นาฬิกา 'm' นาที' + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1}, {0} + + + + + + + + + ม.ค. + ก.พ. + มี.ค. + เม.ย. + พ.ค. + มิ.ย. + ก.ค. + ส.ค. + ก.ย. + ต.ค. + พ.ย. + ธ.ค. + + + มกราคม + กุมภาพันธ์ + มีนาคม + เมษายน + พฤษภาคม + มิถุนายน + กรกฎาคม + สิงหาคม + กันยายน + ตุลาคม + พฤศจิกายน + ธันวาคม + + + + + + + อา. + จ. + อ. + พ. + พฤ. + ศ. + ส. + + + วันอาทิตย์ + วันจันทร์ + วันอังคาร + วันพุธ + วันพฤหัสบดี + วันศุกร์ + วันเสาร์ + + + + ก่อนเที่ยง + หลังเที่ยง + + + ปีก่อนคริสต์กาลที่ + ค.ศ. + + + + + + + EEEE'ที่ 'd MMMM G yyyy + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + d/M/yyyy + + + + + + + + H' นาฬิกา 'm' นาที 'ss' วินาที' + + + + + H' นาฬิกา 'm' นาที' + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1}, {0} + + + + + + + + + + บาท + ฿ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ti_ER.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ti_ER.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ti_ER.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ti_ER.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + ጥሪ + ለካቲ + መጋቢ + ሚያዝ + ግንቦ + ሰነ + ሓምለ + ነሓሰ + መስከ + ጥቅም + ሕዳር + ታሕሳ + + + ጥሪ + ለካቲት + መጋቢት + ሚያዝያ + ግንቦት + ሰነ + ሓምለ + ነሓሰ + መስከረም + ጥቅምቲ + ሕዳር + ታሕሳስ + + + + + + + ሰንበ + ሰኑይ + ሰሉስ + ረቡዕ + ሓሙስ + ዓርቢ + ቀዳም + + + ሰንበት + ሰኑይ + ሰሉስ + ረቡዕ + ሓሙስ + ዓርቢ + ቀዳም + + + + + + + + EEEE፡ dd MMMM መዓልቲ yyyy G + + + + + dd MMMM yyyy + + + + + dd-MMM-yy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + ERN + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ti_ET.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ti_ET.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ti_ET.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ti_ET.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + EEEE፣ dd MMMM መዓልቲ yyyy G + + + + + dd MMMM yyyy + + + + + dd-MMM-yy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + ETB + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/tig_ER.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/tig_ER.xml --- zope3-3.4.0/src/zope/i18n/locales/data/tig_ER.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/tig_ER.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE፡ dd MMMM ዮም yyyy G + + + + + dd MMMM yyyy + + + + + dd-MMM-yyyy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/tig.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/tig.xml --- zope3-3.4.0/src/zope/i18n/locales/data/tig.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/tig.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,371 @@ + + + + + + + + + + + am + አፋርኛ + አብሐዚኛ + አፍሪቃንስኛ + አምሐረኛ + ዐርቢኛ + አሳሜዛዊ + አያማርኛ + አዜርባይጃንኛ + ባስኪርኛ + ቤላራሻኛ + ቡልጋሪኛ + ቢሃሪ + ቢስላምኛ + በንጋሊኛ + ትበትንኛ + ብሬቶንኛ + ብሊን + ካታላንኛ + ኮርሲካኛ + ቼክኛ + ወልሽ + ዴኒሽ + ጀርመን + ድዞንግኻኛ + ግሪክኛ + እንግሊዝኛ + ኤስፐራንቶ + ስፓኒሽ + ኤስቶኒአን + ባስክኛ + ፐርሲያኛ + ፊኒሽ + ፊጂኛ + ፋሮኛ + ፈረንሳይኛ + ፍሪስኛ + አይሪሽ + እስኮትስ ጌልክኛ + ግዕዝኛ + ጋለጋኛ + ጓራኒኛ + ጉጃርቲኛ + ሃውሳኛ + ዕብራስጥ + ሐንድኛ + ክሮሽያንኛ + ሀንጋሪኛ + አርመናዊ + ኢንቴርሊንጓ + እንዶኒሲኛ + እንተርሊንግወ + እኑፒያቅኛ + አይስላንድኛ + ጣሊያንኛ + እኑክቲቱትኛ + ጃፓንኛ + ጃቫንኛ + ጊዮርጊያን + ካዛክኛ + ካላሊሱትኛ + ክመርኛ + ካናዳኛ + ኮሪያኛ + ካሽሚርኛ + ኩርድሽኛ + ኪርጊዝኛ + ላቲንኛ + ሊንጋላኛ + ላውስኛ + ሊቱአኒያን + ላትቪያን + ማላጋስኛ + ማዮሪኛ + ማከዶኒኛ + ማላያላምኛ + ሞንጎላዊኛ + ሞልዳቫዊና + ማራዚኛ + ማላይኛ + ማልቲስኛ + ቡርማኛ + ናኡሩ + ኔፓሊኛ + ደች + ኖርዌጂያን + ኦኪታንኛ + ኦሮምኛ + ኦሪያኛ + ፓንጃቢኛ + ፖሊሽ + ፑሽቶኛ + ፖርቱጋሊኛ + ኵቿኛ + ሮማንስ + ሩንዲኛ + ሮማኒያን + ራሽኛ + ኪንያርዋንድኛ + ሳንስክሪትኛ + ሲንድሂኛ + ሳንጎኛ + ስንሃልኛ + ሲዳምኛ + ስሎቫክኛ + ስሎቪኛ + ሳሞአኛ + ሾናኛ + ሱማልኛ + ልቤኒኛ + ሰርቢኛ + ስዋቲኛ + ሶዞኛ + ሱዳንኛ + ስዊድንኛ + ስዋሂሊኛ + ታሚልኛ + ተሉጉኛ + ታጂኪኛ + ታይኛ + ትግርኛ + ትግረ + ቱርክመንኛ + ታጋሎገኛ + ጽዋናዊኛ + ቶንጋ + ቱርክኛ + ጾንጋኛ + ታታርኛ + ትዊኛ + ኡዊግሁርኛ + ዩክረኒኛ + ኡርዱኛ + ኡዝበክኛ + ቪትናምኛ + ቮላፑክኛ + ዎሎፍኛ + ዞሳኛ + ይዲሻዊኛ + ዮሩባዊኛ + ዡዋንግኛ + ቻይንኛ + ዙሉኛ + + + አንዶራ + የተባበሩት አረብ ኤምሬትስ + አልባኒያ + አርሜኒያ + ኔዘርላንድስ አንቲልስ + አርጀንቲና + ኦስትሪያ + አውስትሬሊያ + አዘርባጃን + ቦስኒያ እና ሄርዞጎቪኒያ + ባርቤዶስ + ቤልጄም + ቡልጌሪያ + ባህሬን + ቤርሙዳ + ቦሊቪያ + ብራዚል + ቡህታን + ቤላሩስ + ቤሊዘ + ኮንጎ + የመካከለኛው አፍሪካ ሪፐብሊክ + ስዊዘርላንድ + ቺሊ + ካሜሩን + ቻይና + ኮሎምቢያ + ኬፕ ቬርዴ + ሳይፕረስ + ቼክ ሪፑብሊክ + ጀርመን + ዴንማርክ + ዶሚኒካ + ዶሚኒክ ሪፑብሊክ + አልጄሪያ + ኢኳዶር + ኤስቶኒያ + ግብጽ + ምዕራባዊ ሳህራ + ኤርትራ + ስፔን + ኢትዮጵያ + ፊንላንድ + ፊጂ + ሚክሮኔዢያ + እንግሊዝ + ጆርጂያ + የፈረንሳይ ጉዊአና + ጋምቢያ + ጊኒ + ኢኳቶሪያል ጊኒ + ግሪክ + ቢሳዎ + ጉያና + ሆንግ ኮንግ + ክሮኤሽያ + ሀይቲ + ሀንጋሪ + ኢንዶኔዢያ + አየርላንድ + እስራኤል + ህንድ + ኢራቅ + አይስላንድ + ጣሊያን + ጃማይካ + ጆርዳን + ጃፓን + ካምቦዲያ + ኮሞሮስ + ደቡብ ኮሪያ + ሰሜን ኮሪያ + ክዌት + ሊባኖስ + ሊቱዌኒያ + ላትቪያ + ሊቢያ + ሞሮኮ + ሞልዶቫ + ማከዶኒያ + ሞንጎሊያ + ማካዎ + ሞሪቴኒያ + ማልታ + ማሩሸስ + ሜክሲኮ + ማሌዢያ + ናሚቢያ + ኒው ካሌዶኒያ + ናይጄሪያ + ኔዘርላንድ + ኖርዌ + ኔፓል + ኒው ዚላንድ + ፔሩ + የፈረንሳይ ፖሊኔዢያ + ፓፑዋ ኒው ጊኒ + ፖላንድ + ፖርታ ሪኮ + ሮሜኒያ + ራሺያ + ሳውድአረቢያ + ሱዳን + ስዊድን + ሲንጋፖር + ስሎቬኒያ + ስሎቫኪያ + ሴኔጋል + ሱማሌ + ሰርቢያ + ሲሪያ + ቻድ + የፈረንሳይ ደቡባዊ ግዛቶች + ታይላንድ + ታጃኪስታን + ምስራቅ ቲሞር + ቱኒዚያ + ቱርክ + ትሪኒዳድ እና ቶባጎ + ታንዛኒያ + ዩጋንዳ + አሜሪካ + ዩዝበኪስታን + ቬንዙዌላ + የእንግሊዝ ድንግል ደሴቶች + የአሜሪካ ቨርጂን ደሴቶች + የመን + ዩጎዝላቪያ + ደቡብ አፍሪካ + ዛምቢያ + + + + [:Ethi:] + + + + + + + + ጃንዩ + ፌብሩ + ማርች + ኤፕረ + ሜይ + ጁን + ጁላይ + ኦገስ + ሴፕቴ + ኦክተ + ኖቬም + ዲሴም + + + ጃንዩወሪ + ፌብሩወሪ + ማርች + ኤፕረል + ሜይ + ጁን + ጁላይ + ኦገስት + ሴፕቴምበር + ኦክተውበር + ኖቬምበር + ዲሴምበር + + + + + + + ሰ/ዓ + ሰኖ + ታላሸ + ኣረር + ከሚሽ + ጅምዓ + ሰ/ን + + + ሰንበት ዓባይ + ሰኖ + ታላሸኖ + ኣረርባዓ + ከሚሽ + ጅምዓት + ሰንበት ንኢሽ + + + + + + + + ቀደም ሰርምዕል + ሓቆ ስርምዕል + + + ዓ/ዓ + ዓ/ም + + + + + + + + + ERN + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ti.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ti.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ti.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ti.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,362 @@ + + + + + + + + + + + አፋርኛ + አብሐዚኛ + አፍሪቃንስኛ + አማርኛ + ዐርቢኛ + አሳሜዛዊ + አያማርኛ + አዜርባይጃንኛ + ባስኪርኛ + ቤላራሻኛ + ቡልጋሪኛ + ቢሃሪ + ቢስላምኛ + በንጋሊኛ + ትበትንኛ + ብሬቶንኛ + ብሊን + ካታላንኛ + ኮርሲካኛ + ቼክኛ + ወልሽ + ዴኒሽ + ጀርመን + ድዞንግኻኛ + ግሪክኛ + እንግሊዝኛ + ኤስፐራንቶ + ስፓኒሽ + ኤስቶኒአን + ባስክኛ + ፐርሲያኛ + ፊኒሽ + ፊጂኛ + ፋሮኛ + ፈረንሳይኛ + ፍሪስኛ + አይሪሽ + እስኮትስ ጌልክኛ + ግዕዝኛ + ጋለጋኛ + ጓራኒኛ + ጉጃርቲኛ + ሃውሳኛ + ዕብራስጥ + ሐንድኛ + ክሮሽያንኛ + ሀንጋሪኛ + አርመናዊ + ኢንቴርሊንጓ + እንዶኒሲኛ + እንተርሊንግወ + እኑፒያቅኛ + አይስላንድኛ + ጣሊያንኛ + እኑክቲቱትኛ + ጃፓንኛ + ጃቫንኛ + ጊዮርጊያን + ካዛክኛ + ካላሊሱትኛ + ክመርኛ + ካናዳኛ + ኮሪያኛ + ካሽሚርኛ + ኩርድሽኛ + ኪርጊዝኛ + ላቲንኛ + ሊንጋላኛ + ላውስኛ + ሊቱአኒያን + ላትቪያን + ማላጋስኛ + ማዮሪኛ + ማከዶኒኛ + ማላያላምኛ + ሞንጎላዊኛ + ሞልዳቫዊና + ማራዚኛ + ማላይኛ + ማልቲስኛ + ቡርማኛ + ናኡሩ + ኔፓሊኛ + ደች + ኖርዌጂያን + ኦኪታንኛ + ኦሮምኛ + ኦሪያኛ + ፓንጃቢኛ + ፖሊሽ + ፑሽቶኛ + ፖርቱጋሊኛ + ኵቿኛ + ሮማንስ + ሩንዲኛ + ሮማኒያን + ራሽኛ + ኪንያርዋንድኛ + ሳንስክሪትኛ + ሲንድሂኛ + ሳንጎኛ + ስንሃልኛ + ሲዳምኛ + ስሎቫክኛ + ስሎቪኛ + ሳሞአኛ + ሾናኛ + ሱማልኛ + ልቤኒኛ + ሰርቢኛ + ስዋቲኛ + ሶዞኛ + ሱዳንኛ + ስዊድንኛ + ስዋሂሊኛ + ታሚልኛ + ተሉጉኛ + ታጂኪኛ + ታይኛ + ትግርኛ + ትግረ + ቱርክመንኛ + ታጋሎገኛ + ጽዋናዊኛ + ቶንጋ + ቱርክኛ + ጾንጋኛ + ታታርኛ + ትዊኛ + ኡዊግሁርኛ + ዩክረኒኛ + ኡርዱኛ + ኡዝበክኛ + ቪትናምኛ + ቮላፑክኛ + ዎሎፍኛ + ዞሳኛ + ይዲሻዊኛ + ዮሩባዊኛ + ዡዋንግኛ + ቻይንኛ + ዙሉኛ + + + አንዶራ + የተባበሩት አረብ ኤምሬትስ + አልባኒያ + አርሜኒያ + ኔዘርላንድስ አንቲልስ + አርጀንቲና + ኦስትሪያ + አውስትሬሊያ + አዘርባጃን + ቦስኒያ እና ሄርዞጎቪኒያ + ባርቤዶስ + ቤልጄም + ቡልጌሪያ + ባህሬን + ቤርሙዳ + ቦሊቪያ + ብራዚል + ቡህታን + ቤላሩስ + ቤሊዘ + ኮንጎ + የመካከለኛው አፍሪካ ሪፐብሊክ + ስዊዘርላንድ + ቺሊ + ካሜሩን + ቻይና + ኮሎምቢያ + ኬፕ ቬርዴ + ሳይፕረስ + ቼክ ሪፑብሊክ + ጀርመን + ዴንማርክ + ዶሚኒካ + ዶሚኒክ ሪፑብሊክ + አልጄሪያ + ኢኳዶር + ኤስቶኒያ + ግብጽ + ምዕራባዊ ሳህራ + ኤርትራ + ስፔን + ኢትዮጵያ + ፊንላንድ + ፊጂ + ሚክሮኔዢያ + እንግሊዝ + ጆርጂያ + የፈረንሳይ ጉዊአና + ጋምቢያ + ጊኒ + ኢኳቶሪያል ጊኒ + ግሪክ + ቢሳዎ + ጉያና + ሆንግ ኮንግ + ክሮኤሽያ + ሀይቲ + ሀንጋሪ + ኢንዶኔዢያ + አየርላንድ + እስራኤል + ህንድ + ኢራቅ + አይስላንድ + ጣሊያን + ጃማይካ + ጆርዳን + ጃፓን + ካምቦዲያ + ኮሞሮስ + ደቡብ ኮሪያ + ሰሜን ኮሪያ + ክዌት + ሊባኖስ + ሊቱዌኒያ + ላትቪያ + ሊቢያ + ሞሮኮ + ሞልዶቫ + ማከዶኒያ + ሞንጎሊያ + ማካዎ + ሞሪቴኒያ + ማልታ + ማሩሸስ + ሜክሲኮ + ማሌዢያ + ናሚቢያ + ኒው ካሌዶኒያ + ናይጄሪያ + ኔዘርላንድ + ኖርዌ + ኔፓል + ኒው ዚላንድ + ፔሩ + የፈረንሳይ ፖሊኔዢያ + ፓፑዋ ኒው ጊኒ + ፖላንድ + ፖርታ ሪኮ + ሮሜኒያ + ራሺያ + ሳውድአረቢያ + ሱዳን + ስዊድን + ሲንጋፖር + ስሎቬኒያ + ስሎቫኪያ + ሴኔጋል + ሱማሌ + ሰርቢያ + ሲሪያ + ቻድ + የፈረንሳይ ደቡባዊ ግዛቶች + ታይላንድ + ታጃኪስታን + ምስራቅ ቲሞር + ቱኒዚያ + ቱርክ + ትሪኒዳድ እና ቶባጎ + ታንዛኒያ + ዩጋንዳ + አሜሪካ + ዩዝበኪስታን + ቬንዙዌላ + የእንግሊዝ ድንግል ደሴቶች + የአሜሪካ ቨርጂን ደሴቶች + የመን + ዩጎዝላቪያ + ደቡብ አፍሪካ + ዛምቢያ + + + + [:Ethi:] + + + + + + + + ጃንዩ + ፌብሩ + ማርች + ኤፕረ + ሜይ + ጁን + ጁላይ + ኦገስ + ሴፕቴ + ኦክተ + ኖቬም + ዲሴም + + + ጃንዩወሪ + ፌብሩወሪ + ማርች + ኤፕረል + ሜይ + ጁን + ጁላይ + ኦገስት + ሴፕቴምበር + ኦክተውበር + ኖቬምበር + ዲሴምበር + + + + + + + ሰንበ + ሰኑይ + ሠሉስ + ረቡዕ + ኃሙስ + ዓርቢ + ቀዳም + + + ሰንበት + ሰኑይ + ሠሉስ + ረቡዕ + ኃሙስ + ዓርቢ + ቀዳም + + + + + + + + ንጉሆ ሰዓተ + ድሕር ሰዓት + + + ዓ/ዓ + ዓ/ም + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/tr_TR.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/tr_TR.xml --- zope3-3.4.0/src/zope/i18n/locales/data/tr_TR.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/tr_TR.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/tr.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/tr.xml --- zope3-3.4.0/src/zope/i18n/locales/data/tr.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/tr.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,547 @@ + + + + + + + + + + + Afar + Abazca + Afrikaan Dili + Amharik + Arapça + Aymara + Azerice + Başkırt Dili + Beyaz Rusça + Bulgarca + Bihari + Bislama + Bengal Dili + Tibetçe + Breton Dili + Katalan Dili + Korsika Dili + Çekçe + Gal Dili + Danca + Almanca + Bhutan Dili + Yunanca + İngilizce + Esperanto + İspanyolca + Estonya Dili + Bask Dili + Farsça + Fince + Fiji Dili + Faroe Dili + Fransızca + Frizye Dili + İrlanda Dili + İskoç Gal Dili + Galiçya Dili + Guarani + Gujarati + Hausa + İbranice + Hint Dili + Hırvatça + Macarca + Ermenice + Interlingua + Endonezya Dili + Interlingue + Inupiak + İzlandaca + İtalyanca + Inuktitut + Japonca + Java Dili + Gürcüce + Kazak Dili + Grönland Dili + Kamboçya Dili + Kannada + Korece + Keşmirce + Kürtçe + Kırgızca + Latince + Lingala + Laos Dili + Litvanya Dili + Letonya Dili + Malaga Dili + Maori + Makedonca + Malayalam + Moğol Dili + Moldavya Dili + Marathi + Malay + Malta Dili + Birmanya Dili + Nauru + Nepal Dili + Hollanda Dili + Norveççe + Occitan + Oromo (Afan) + Oriya + Pencap Dili + Polonya Dili + Peştun Dili + Portekizce + Quechua + Rhaeto-Roman Dili + Kirundi + Romence + Rusça + Kinyarwanda + Sanskritçe + Sindhi + Sangho + Sırp-Hırvat Dili + Sinhal Dili + Slovakça + Slovence + Samoa Dili + Shona + Somali Dili + Arnavutça + Sırpça + Siswati + Sesotho + Sudan Dili + İsveççe + Swahili + Tamil + Telugu + Tacik Dili + Tay Dili + Tigrinya + Türkmence + Tagalog + Setswana + Tonga + Türkçe + Tsonga + Tatarca + Twi + Uygurca + Ukraynaca + Urduca + Özbekçe + Vietnam Dili + Volapuk + Wolof + Xhosa + Yiddiş + Yoruba + Zhuang + Çince + Zulu + + + Andora + Birleşik Arap Emirlikleri + Afganistan + Antigua ve Barbuda + Anguilla + Arnavutluk + Ermenistan + Hollanda Antilleri + Angola + Antarktika + Arjantin + Amerikan Samoası + Avusturya + Avustralya + Aruba + Azerbaycan + Bosna Hersek + Barbados + Bangladeş + Belçika + Burkina Faso + Bulgaristan + Bahreyn + Burundi + Benin + Bermuda + Brunei + Bolivya + Brezilya + Bahamalar + Bhutan + Bouvet Adası + Botswana + Belarus + Belize + Kanada + Cocos (Keeling) Adaları + Kongo Demokratik Cumhuriyeti + Orta Afrika Cumhuriyeti + Kongo + İsviçre + Fildişi Sahilleri + Cook Adaları + Şili + Kamerun + Çin + Kolombiya + Kosta Rika + Küba + Cape Verde + Christmas Adası + Kıbrıs + Çek Cumhuriyeti + Almanya + Cibuti + Danimarka + Dominik + Dominik Cumhuriyeti + Cezayir + Ekvador + Estonya + Mısır + Batı Sahara + Eritre + İspanya + Etiyopya + Finlandiya + Fiji + Falkland Adaları (Malvinalar) + Mikronezya Federal Eyaletleri + Faroe Adaları + Fransa + en + Gabon + Birleşik Krallık + Granada + Gürcistan + Fransız Ginesi + Gana + Cebelitarık + Grönland + Gambia + Gine + Guadeloupe + Ekvator Ginesi + Yunanistan + Güney Georgia ve Güney Sandwich Adaları + Guatemala + Guam + Gine-Bissau + Guyana + Hong Kong SAR - Çin + Heard Adası ve McDonald Adaları + Honduras + Hırvatistan + Haiti + Macaristan + Endonezya + İrlanda + İsrail + Hindistan + Hint Okyanusu İngiliz Bölgesi + Irak + İran + İzlanda + İtalya + Jamaika + Ürdün + Japonya + Kenya + Kırgızistan + Kamboçya + Kiribati + Komorlar + Saint Kittler ve Neviler + Kore, Kuzey + Kore, Güney + Kuveyt + Cayman Adaları + Kazakistan + Lao Demokratik Halk Cumhuriyeti + Lübnan + Saint Lucia + Liechtenstein + Sri Lanka + Liberya + Lesotho + Litvanya + Lüksemburg + Letonya + Libya + Fas + Monako + Moldovya Cumhuriyeti + Madagaskar + Marshall Adaları + Makedonya Cumhuriyeti + Mali + Myanmar + Moğolistan + Macao S.A.R. - Çin + Kuzey Mariana Adaları + Martinik + Moritanya + Montserrat + Malta + Mauritius + Maldivler + Malavi + Meksika + Malezya + Mozambik + Namibya + Yeni Kaledonya + Nijer + Norfolk Adası + Nijerya + Nikaragua + Hollanda + Norveç + Nepal + Nauru Adası + Niue Adaları + Yeni Zelanda + Umman + Panama + Peru + Fransız Polinezyası + Papua Yeni Gine + Filipinler + Pakistan + Polonya + Saint Pierre ve Miquelon + Pitcairn + Porto Riko + Filistin Bölgesi + Portekiz + Palau + Paraguay + Katar + Reunion + Romanya + Rusya Federasyonu + Ruanda + Suudi Arabistan + Solomon Adaları + Seyşeller + Sudan + İsveç + Singapur + Saint Helena + Slovenya + Svalbard ve Jan Mayen + Slovakya + Sierra Leone + San Marino + Senegal + Somali + Serbia + Surinam + Sao Tome ve Principe + El Salvador + Suriye + Swaziland + Turks ve Caicos Adaları + Çad + Fransız Güney Bölgeleri + Togo + Tayland + Tacikistan + Tokelau + Doğu Timor + Türkmenistan + Tunus + Tonga + Türkiye + Trinidad ve Tobago + Tuvalu + Tayvan, Çin Bölgesi + Tanzanya + Ukrayna + Uganda + Amerika Birleşik Devletleri Küçük Dış Adaları + Amerika Birleşik Devletleri + Uruguay + Özbekistan + Kutsal Devlet (Vatikan Şehir Devleti) + Saint Vincent ve Grenadinler + Venezuela + İngiliz Virgin Adaları + ABD Virgin Adaları + Vietnam + Vanuatu + Wallis ve Futuna + Samoa + Yemen + Mayotte + Yugoslavya + Güney Afrika + Zambiya + Zimbabwe + + + + [a-zâûöüıçşğ] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + Oca + Şub + Mar + Nis + May + Haz + Tem + Ağu + Eyl + Eki + Kas + Ara + + + Ocak + Şubat + Mart + Nisan + Mayıs + Haziran + Temmuz + Ağustos + Eylül + Ekim + Kasım + Aralık + + + + + + + Paz + Pzt + Sal + Çar + Per + Cum + Cmt + + + Pazar + Pazartesi + Salı + Çarşamba + Perşembe + Cuma + Cumartesi + + + + + + + + + + + MS + + + + + + + dd MMMM yyyy EEEE + + + + + dd MMMM yyyy EEEE + + + + + dd.MMM.yyyy + + + + + dd.MM.yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + ITL + ITL + + + TRL + TL + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/tt_RU.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/tt_RU.xml --- zope3-3.4.0/src/zope/i18n/locales/data/tt_RU.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/tt_RU.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + d MMMM yyyy + + + + + d MMMM yyyy + + + + + dd.MM.yyyy + + + + + dd.MM.yyyy + + + + + + + + h:mm:ss a + + + + + H:mm:ss + + + + + H:mm:ss + + + + + H:mm:ss + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00¤;-#,##0.00¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/tt.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/tt.xml --- zope3-3.4.0/src/zope/i18n/locales/data/tt.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/tt.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,42 @@ + + + + + + + + + + + Татар + + + Россия + + + + [а-яёіѣѳѵә] + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + RUR + р. + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/uk_UA.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/uk_UA.xml --- zope3-3.4.0/src/zope/i18n/locales/data/uk_UA.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/uk_UA.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/uk.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/uk.xml --- zope3-3.4.0/src/zope/i18n/locales/data/uk.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/uk.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,599 @@ + + + + + + + + + + + Афарська + Абхазька + Африканс + Амхарік + Арабська + Ассамська + Аумара + Азербайджанська + Башкирська + Білоруська + Болгарська + Біхарійська + Бісламійська + Бенгальська + Тібетська + Бретонська + Каталонська + Корсиканська + Чеська + Валлійська + Датська + Німецька + Бхутані + Грецька + Англійська + Есперанто + Іспанська + Естонська + Басква + Перська + Фінська + Фіджі + Фарерська + Французька + Фризька + Ірландська + Гаельська + Галісійська + Гуарані + Гуяраті + Хауса + Іврит + Хінді + Хорватська + Угорська + Вірменська + Інтерлінгва + Індонезійська + Інтерлінгва + Інупіак + Ісландська + Італійська + Японська + Яванська + Грузинська + Казахська + Гринландік + Кампучійська + Дравідійська + Корейська + Кашмірська + Курдська + Киргизька + Латинська + Лінгала + Лаоська + Литовська + Латвійська + Малагасійська + Маорі + Македонська + Малайялам + Монгольська + Молдавська + Маратхі + Малайська + Мальтійська + Бурмісійська + Науру + Непальська + Голландська + Норвезька + Окитан + Оромо + Орія + Панджабі + Польська + Пашто + Португальська + Кечуа + Ретороманська + Кірундійська + Румунська + Російська + Кінаруанда + Санскрит + Сіндтхі + Сангро + Сербсько-хорватська + Сингальська + Словацька + Словенська + Самоанська + Шона + Сомалі + Албанська + Сербська + Сісваті + Сесотхо + Суданська + Шведська + Суахілі + Тамільська + Телугу + Таджицька + Тайська + Тигріні + Туркменська + Тагальська + Сетсванська + Тонга + Турецька + Тсонго + Татарська + Тві + Уйгурська + Українська + Урду + Узбецька + Вʼєтнамська + Волапак + Волоф + Кхоса + Ідиш + Йоруба + Зуанг + Китайська + Зулуська + + + Андорра + Сполучені Арабські Емірати + Афганістан + Антигуа і Барбуда + Ангілья + Албанія + Вірменія + Нідерландські Антіли + Ангола + Антарктика + Аргентина + Американські Самоа + Австрія + Австралія + Аруба + Азербайджан + Боснія і Герцеговина + Барбадос + Бангладеш + Бельгія + Буркіна-Фасо + Болгарія + Бахрейн + Бурунді + Бенін + Бермуди + Бруней + Болівія + Бразилія + Багами + Бутан + Буве, острів + Ботсвана + Білорусь + Беліз + Канада + Кокосові острови + Конго + Центрально-Африканська Республіка + Конго + Швейцарія + Кот-д’Івуар + Кука, острови + Чилі + Камерун + Китай + Колумбія + Коста-Рика + Куба + Зеленого Мису, острови + Різдвяні Острови + Кіпр + Чехія + Німеччина + Джибуті + Данія + Домінік + Домініканська Республіка + Алжир + Еквадор + Естонія + Єгипет + Західна Сахара + Ерітрея + Іспанія + Ефіопія + Фінляндія + Фіджі + Фолклендські Острови (Мальвіни) + Мікронезія + Фаро, острови + Франція + Габон + Великобританія + Гренада + Грузія + Французька Гвіана + Гана + Гібралтар + Гренландія + Гамбія + Гвінея + Гваделупа + Екваторіальна Гвінея + Греція + Південна Джоржія та Острови Південний Сандвіч + Гватемала + Гуам + Гвінея-Біссау + Гуана + Гонконг + Острови Херда і Макдональдса + Гондурас + Хорватія + Гаїті + Угорщина + Індонезія + Ірландія + Ізраїль + Індія + Британські території Індійського океану + Ірак + Іран + Ісландія + Італія + Ямайка + Йорданія + Японія + Кенія + Киргизстан + Камбоджа + Кірибаті + Коморос + Св. Кіттс і Невіс + Корея, Демократична Республіка + Корея, Республіка + Кувейт + Кайманові острови + Казахстан + Лаоська Народно-Демократична Республіка + Ліван + Санта Лючія + Ліхтенштейн + Шрі-Ланка + Ліберія + Лесото + Литва + Люксембург + Латвія + Лівійська Арабська Джамахірія + Марокко + Монако + Молдова + Мадагаскар + Маршалові Острови + Македонія + Малі + Мʼянмар + Монголія + Макао + Північна Маріана, острови + Мартиніка + Мавританія + Монсеррат + Мальта + Маврикій + Мальдіви + Малави + Мексика + Малайзія + Мозамбік + Намібія + Нова Каледонія + Нігерія + Норфолькські Острови + Нігерія + Нікарагуа + Нідерланди + Норвегія + Непал + Науру + Нія + Нова Зеландія + Оман + Панама + Перу + Французька Полінезія + Папуа Нова Гвінея + Філіппіни + Пакистан + Польща + Св. Пʼєр і Мікулон + Піткаїрн + Пуерто-Ріко + Палестина + Португалія + Палау + Парагвай + Катар + Реюньйон + Румунія + Росія + Руанда + Саудівська Аравія + Соломонові Острови + Сейшели + Судан + Швеція + Сінгапур + Св. Єлена + Словенія + Свалбард і Ян Майєн, острови + Словакія + Сьєрра-Леоне + Сан-Маріно + Сенегал + Сомалі + Сурінам + Сао Том і Прінсіп + Сальвадор + Сирійська Арабська Республіка + Свазіленд + Турок та Какіос, острови + Чад + Французькі Південні Території + Того + Тайланд + Таджикистан + Токелау + Східний Тимор + Туркменистан + Туніс + Тонга + Туреччина + Тринідад і Табаго + Тувалу + Тайвань + Танзанія, Обʼєднана Республіка + Україна + Уганда + Віддалені Острови США + США + Уругвай + Узбекистан + Ватикан + Св. Вінсент і Гренадини + Венесуела + Віргінські острови (Британія) + Віргінські острови (США) + Вʼєтнам + Вануату + Валліс і Футуна, острови + Самоа + Йємен + Майот + Югославія + ПАР + Замбія + Зімбабве + + + + [а-щюьяєіїґ] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + Січ + Лют + Бер + Кві + Тра + Чер + Лип + Сер + Вер + Жов + Лис + Гру + + + С + Л + Б + К + Т + Ч + Л + С + В + Ж + Л + Г + + + січня + лютого + березня + квітня + травня + червня + липня + серпня + вересня + жовтня + листопада + грудня + + + + + Січ + Лют + Бер + Кві + Тра + Чер + Лип + Сер + Вер + Жов + Лис + Гру + + + С + Л + Б + К + Т + Ч + Л + С + В + Ж + Л + Г + + + Січень + Лютий + Березень + Квітень + Травень + Червень + Липень + Серпень + Вересень + Жовтень + Листопад + Грудень + + + + + + + Нд + Пн + Вт + Ср + Чт + Пт + Сб + + + Неділя + Понеділок + Вівторок + Середа + Четвер + Пʼятниця + Субота + + + + + + + + + + до н.е. + н.е. + + + + + + + EEEE, d MMMM yyyy 'р.' + + + + + d MMMM yyyy + + + + + d MMM yyyy + + + + + dd.MM.yy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {1} {0} + + + + + + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + UAH + грн. + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ur_PK.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ur_PK.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ur_PK.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ur_PK.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/ur.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/ur.xml --- zope3-3.4.0/src/zope/i18n/locales/data/ur.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/ur.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,28 @@ + + + + + + + + + + + اردو + + + پاکستان + + + + [[:Arab:]‌‍‏‎] + + + + + PKR + Rs + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/uz_AF.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/uz_AF.xml --- zope3-3.4.0/src/zope/i18n/locales/data/uz_AF.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/uz_AF.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,209 @@ + + + + + + + + + + + + دری + پشتو + اۉزبېک + + + افغانستان + + + + [ء-ؤئ-غفقل-ويً-ْٰٔټپځڅ-چډړږژښکګگڼۇۉی-ۍې] + + + + + + + + جنو + فبر + مار + اپر + مـی + جون + جول + اگس + سپت + اکت + نوم + دسم + + + جنوری + فبروری + مارچ + اپریل + می + جون + جولای + اگست + سپتمبر + اکتوبر + نومبر + دسمبر + + + + + + + ی. + د. + س. + چ. + پ. + ج. + ش. + + + یکشنبه + دوشنبه + سه‌شنبه + چهارشنبه + پنجشنبه + جمعه + شنبه + + + + + + + + + + ق.م. + م. + + + + + + + yyyy نچی ییل d نچی MMMM EEEE کونی + + + + + d نچی MMMM yyyy + + + + + d MMMM yyyy + + + + + yyyy/M/d + + + + + + + + H:mm:ss (z) + + + + + H:mm:ss (z) + + + + + H:mm:ss + + + + + H:mm + + + + + + + {1} {0} + + + + + + + + + افغانستان وقتی + افغانستان وقتی + + + AFT + AFT + + کابل + + + + + + ٫ + ٬ + ; + ٪ + ۰ + # + + + + ×۱۰^ + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0 ¤;-#,##0 ¤ + + + + + + افغانی + افغانی + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/uz_UZ.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/uz_UZ.xml --- zope3-3.4.0/src/zope/i18n/locales/data/uz_UZ.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/uz_UZ.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/uz.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/uz.xml --- zope3-3.4.0/src/zope/i18n/locales/data/uz.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/uz.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,42 @@ + + + + + + + + + + + Ўзбек + + + Ўзбекистон + + + + [а-яёіѣѳѵў] + + + + , +   + ; + % + 0 + # + + + - + E + + + + + + + UZS + сўм + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/vi_VN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/vi_VN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/vi_VN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/vi_VN.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/vi.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/vi.xml --- zope3-3.4.0/src/zope/i18n/locales/data/vi.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/vi.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,440 @@ + + + + + + + + + + + Tiếng A-rập + Tiếng Ai-déc-bai-gian + Tiếng Bê-la-rút + Tiếng Bun-ga-ri + Tiếng Tây Tạng + Tiếng Ca-ta-lăng + Tiếng Séc + Tiếng Đan Mạch + Tiếng Đức + Tiếng Hy Lạp + Tiếng Anh + Tiếng Quốc Tế Ngữ + Tiếng Tây Ban Nha + Tiếng E-xtô-ni-a + Tiếng Ba Tư + Tiếng Phần Lan + Tiếng Pháp + Tiếng Ai-len + Tiếng Hê-brơ + Tiếng Hin-đi + Tiếng Crô-a-ti-a + Tiếng Hung-ga-ri + Tiếng Ác-mê-ni + Tiếng Khoa Học Quốc Tế + Tiếng In-đô-nê-xia + Tiếng Ai-xơ-len + Tiếng Ý + Tiếng Nhật + Tiếng Gia-va + Tiếng Campuchia + Tiếng Kan-na-đa + Tiếng Hàn Quốc + Tiếng La-tinh + Tiếng Lào + Tiếng Lít-va + Tiếng Lát-vi-a + Tiếng Ma-xê-đô-ni-a + Tiếng Mông Cổ + Tiếng Ma-lay-xi-a + Tiếng Nê-pan + Tiếng Hà Lan + Tiếng Na Uy + Tiếng Ba Lan + Tiếng Bồ Đào Nha + Tiếng Ru-ma-ni + Tiếng Nga + Tiếng Phạn + Tiếng Xlô-vác + Tiếng Xlô-ven + Tiếng Xô-ma-li + Tiếng An-ba-ni + Tiếng Séc-bi + Tiếng Thụy Điển + Tiếng Thái + Tiếng Thổ Nhĩ Kỳ + Tiếng U-crai-na + Tiếng U-dơ-bếch + Tiếng Việt + Tiếng Y-đit + Tiếng Trung Quốc + + + Các Tiểu Vương quốc A-rập Thống nhất + Áp-ga-ni-xtan + An-ti-gu-a và Ba-bu-đa + An-ba-ni + Ác-mê-ni-a + Ăng-gô-la + Ác-hen-ti-na + Áo + Úc + Ai-déc-bai-gian + Bô-xni-a Héc-xê-gô-vi-na + Bác-ba-đốt + Băng-la-đét + Bỉ + Buốc-ki-na Pha-xô + Bun-ga-ri + Ba-ren + Bu-run-đi + Bê-nanh + Bru-nây + Bô-li-vi-a + Bra-xin + Ba-ha-ma + Bốt-xoa-na + Bê-la-rút + Bê-li-xê + Ca-na-đa + Cộng hòa Trung Phi + Công-gô + Thụy Sĩ + Bờ Biển Ngà + Chi-lê + Ca-mơ-run + Trung Quốc + Cô-lôm-bi-a + Cốt-xta Ri-ca + Cu Ba + Cáp-ve + Síp + Cộng hòa Séc + Đức + Gi-bu-ti + Đan Mạch + An-giê-ri + Ê-cu-a-đo + E-xtô-ni-a + Ai Cập + Tây Sahara + Ê-ri-tơ-rê-a + Tây Ban Nha + Ê-ti-ô-pi-a + Phần Lan + Phi-gi + Mi-crô-nê-xi-a + Pháp + Ga-bông + Vương quốc Anh + Grê-na-đa + Gru-di-a + Gha-na + Găm-bi-a + Ghi-nê + Ghi-nê Xích-đạo + Hy Lạp + Goa-tê-ma-la + Ghi-nê Bít-xao + Guy-a-na + Hôn-đu-rát + Crô-a-ti-a + Ha-i-ti + Hung-ga-ri + Nam Dương + Ai-len + I-xra-en + Ấn Độ + I-rắc + I-ran + Ai-xơ-len + Ý + Ha-mai-ca + Gióc-đa-ni + Nhật Bản + Kê-ni-a + Cư-rơ-gư-xtan + Campuchia + Ki-ri-ba-ti + Cô-mô + Xan-kít và Nê-vi + Bắc Triều Tiên + Hàn Quốc + Cô-oét + Ka-dắc-xtan + Lào + Li-băng + Xan Lu-xi + Lich-ten-xtên + Xri Lan-ca + Li-bê-ri-a + Lê-xô-thô + Li-tu-a-ni-a + Lúc-xăm-bua + Lát-vi-a + Li-bi + Ma-rốc + Mô-na-cô + Môn-đô-va + Ma-đa-gát-xca + Quần đảo Mác-san + Ma-xê-đô-ni-a + Ma-li + Mi-an-ma + Mông Cổ + Mô-ri-ta-ni + Man-ta + Mô-ri-xơ + Man-đi-vơ + Ma-la-uy + Mê-hi-cô + Ma-lay-xi-a + Mô-dăm-bích + Nam-mi-bi-a + Ni-giê + Ni-giê-ri-a + Ni-ca-ra-goa + Hà Lan + Na Uy + Nê-pan + Niu Di-lân + Ô-man + Pa-na-ma + Pê-ru + Pa-pu-a Niu Ghi-nê + Phi-lip-pin + Pa-ki-xtan + Ba Lan + Bồ Đào Nha + Pa-ra-goay + Ca-ta + Ru-ma-ni + Nga + Ru-an-đa + A-rập Xê-út + Quần đảo Xô-lô-mông + Xây-sen + Xu-đăng + Thụy Điển + Xin-ga-po + Xlô-ven-ni-a + Xlô-va-ki-a + Xi-ê-ra Lê-ôn + Xan Ma-ri-nô + Xê-nê-gan + Xô-ma-li + Séc-bia + Xu-ri-nam + Xao Tô-mê và Prin-xi-pê + En-san-va-đo + Xi-ri + Xoa-di-len + Sát + Tô-gô + Thái Lan + Tát-gi-ki-xtan + Tuốc-mê-ni-xtan + Tuy-ni-di + Tông-ga + Thổ Nhĩ Kỳ + Tri-ni-đát và Tô-ba-gô + Tu-va-lu + Đài Loan + Tan-da-ni-a + U-crai-na + U-gan-đa + Hoa Kỳ + U-ru-goay + U-dơ-bê-ki-xtan + Va-ti-căng + Xan Vin-xen và Grê-na-din + Vê-nê-zu-ê-la + Việt Nam + Va-nu-a-tu + Xa-moa + Y-ê-men + Nam Tư + Nam Phi + Dăm-bi-a + Dim-ba-bu-ê + + + + [a-zẠ-ỹđơà-ãè-êìíò-õùúýăĩũư] + + + + + + + + thg 1 + thg 2 + thg 3 + thg 4 + thg 5 + thg 6 + thg 7 + thg 8 + thg 9 + thg 10 + thg 11 + thg 12 + + + tháng một + tháng hai + tháng ba + tháng tư + tháng năm + tháng sáu + tháng bảy + tháng tám + tháng chín + tháng mười + tháng mười một + tháng mười hai + + + + + + + CN + Th 2 + Th 3 + Th 4 + Th 5 + Th 6 + Th 7 + + + Chủ nhật + Thứ hai + Thứ ba + Thứ tư + Thứ năm + Thứ sáu + Thứ bảy + + + + + + + + SA + CH + + + tr. CN + sau CN + + + + + + + EEEE, 'ngày' dd MMMM 'năm' yyyy + + + + + 'Ngày' dd 'tháng' M 'năm' yyyy + + + + + dd-MM-yyyy + + + + + dd/MM/yyyy + + + + + + + + HH:mm:ss z + + + + + HH:mm:ss z + + + + + HH:mm:ss + + + + + HH:mm + + + + + + + {0} {1} + + + + + + + + + , + . + ; + % + 0 + # + + + - + E + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + #,##0.00 ¤;-#,##0.00 ¤ + + + + + + đồng + đ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/wal_ET.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/wal_ET.xml --- zope3-3.4.0/src/zope/i18n/locales/data/wal_ET.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/wal_ET.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + EEEE፥ dd MMMM ጋላሳ yyyy G + + + + + dd MMMM yyyy + + + + + dd-MMM-yyyy + + + + + dd/MM/yy + + + + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm:ss a + + + + + h:mm a + + + + + + + {1} {0} + + + + + + + + + + + #ወ##0.###;-#ወ##0.### + + + + + + + #E0 + + + + + + + #ወ##0% + + + + + + + ¤#ወ##0.00;-¤#ወ##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/wal.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/wal.xml --- zope3-3.4.0/src/zope/i18n/locales/data/wal.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/wal.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,197 @@ + + + + + + + + + + + ወላይታቱ + + + አንዶራ + የተባበሩት አረብ ኤምሬትስ + አልባኒያ + አርሜኒያ + ኔዘርላንድስ አንቲልስ + አርጀንቲና + ኦስትሪያ + አውስትሬሊያ + አዘርባጃን + ቦስኒያ እና ሄርዞጎቪኒያ + ባርቤዶስ + ቤልጄም + ቡልጌሪያ + ባህሬን + ቤርሙዳ + ቦሊቪያ + ብራዚል + ቡህታን + ቤላሩስ + ቤሊዘ + ኮንጎ + የመካከለኛው አፍሪካ ሪፐብሊክ + ስዊዘርላንድ + ቺሊ + ካሜሩን + ቻይና + ኮሎምቢያ + ኬፕ ቬርዴ + ሳይፕረስ + ቼክ ሪፑብሊክ + ጀርመን + ዴንማርክ + ዶሚኒካ + ዶሚኒክ ሪፑብሊክ + አልጄሪያ + ኢኳዶር + ኤስቶኒያ + ግብጽ + ምዕራባዊ ሳህራ + ኤርትራ + ስፔን + ኢትዮጵያ + ፊንላንድ + ፊጂ + ሚክሮኔዢያ + እንግሊዝ + ጆርጂያ + የፈረንሳይ ጉዊአና + ጋምቢያ + ጊኒ + ኢኳቶሪያል ጊኒ + ግሪክ + ቢሳዎ + ጉያና + ሆንግ ኮንግ + ክሮኤሽያ + ሀይቲ + ሀንጋሪ + ኢንዶኔዢያ + አየርላንድ + እስራኤል + ህንድ + ኢራቅ + አይስላንድ + ጣሊያን + ጃማይካ + ጆርዳን + ጃፓን + ካምቦዲያ + ኮሞሮስ + ደቡብ ኮሪያ + ሰሜን ኮሪያ + ክዌት + ሊባኖስ + ሊቱዌኒያ + ላትቪያ + ሊቢያ + ሞሮኮ + ሞልዶቫ + ማከዶኒያ + ሞንጎሊያ + ማካዎ + ሞሪቴኒያ + ማልታ + ማሩሸስ + ሜክሲኮ + ማሌዢያ + ናሚቢያ + ኒው ካሌዶኒያ + ናይጄሪያ + ኔዘርላንድ + ኖርዌ + ኔፓል + ኒው ዚላንድ + ፔሩ + የፈረንሳይ ፖሊኔዢያ + ፓፑዋ ኒው ጊኒ + ፖላንድ + ፖርታ ሪኮ + ሮሜኒያ + ራሺያ + ሳውድአረቢያ + ሱዳን + ስዊድን + ሲንጋፖር + ስሎቬኒያ + ስሎቫኪያ + ሴኔጋል + ሱማሌ + ሰርቢያ + ሲሪያ + ቻድ + የፈረንሳይ ደቡባዊ ግዛቶች + ታይላንድ + ታጃኪስታን + ምስራቅ ቲሞር + ቱኒዚያ + ቱርክ + ትሪኒዳድ እና ቶባጎ + ታንዛኒያ + ዩጋንዳ + አሜሪካ + ዩዝበኪስታን + ቬንዙዌላ + የእንግሊዝ ድንግል ደሴቶች + የአሜሪካ ቨርጂን ደሴቶች + የመን + ዩጎዝላቪያ + ደቡብ አፍሪካ + ዛምቢያ + + + + [:Ethi:] + + + + + + + + ወጋ + ሳይኖ + ማቆሳ + አሩዋ + ሃሙሳ + አርባ + ቄራ + + + ወጋ + ሳይኖ + ማቆሳኛ + አሩዋ + ሃሙሳ + አርባ + ቄራ + + + + + + + + ማለዶ + ቃማ + + + አዳ ዎዴ + ግሮተታ ላይታ + + + + + + + + + ETB + $ + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/zh_CN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_CN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/zh_CN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_CN.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + yyyy'年'M'月'd'日'EEEE + + + + + yyyy'年'M'月'd'日' + + + + + yyyy-M-d + + + + + yy-M-d + + + + + + + + ahh'时'mm'分'ss'秒' z + + + + + ahh'时'mm'分'ss'秒' + + + + + ahh:mm:ss + + + + + ah:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/zh_Hans_CN.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_Hans_CN.xml --- zope3-3.4.0/src/zope/i18n/locales/data/zh_Hans_CN.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_Hans_CN.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 安道爾 + 阿拉伯聯合大公國 + 安地卡及巴布達 + 安圭拉島 + 阿爾巴尼亞 + 亞美尼亞 + 荷屬安地列斯 + 南極洲 + 美屬薩摩亞群島 + 奧地利 + 澳洲 + 阿路巴 + 亞塞拜然 + 波士尼亞與赫塞格維納 + 巴貝多 + 孟加拉 + 比利時 + 布基納法索 + 保加利亞 + 浦隆地 + 貝南 + 百慕達 + 汶萊 + 玻利維亞 + 巴哈馬 + 布威島 + 波札那 + 白俄羅斯 + 貝里斯 + 可可斯群島 + 剛果民主共和國 + 中非共和國 + 剛果 + 科特迪瓦 + 庫克群島 + 喀麥隆 + 中華人民共和國 + 哥倫比亞 + 哥斯大黎加 + 維德角 + 聖誕島 + 賽普勒斯 + 捷克共和國 + 德國 + 吉布地 + 丹麥 + 多明尼加 + 多明尼加共和國 + 阿爾及利亞 + 厄瓜多爾 + 愛沙尼亞 + 厄利垂亞 + 衣索比亞 + 芬蘭 + 斐濟 + 福克蘭群島 + 密克羅尼西亞群島 + 法羅群島 + 法國 + 加彭 + 英國 + 格瑞納達 + 喬治亞共和國 + 法屬圭亞那 + 迦納 + 直布羅陀 + 格陵蘭 + 甘比亞 + 幾內亞 + 哥德普洛 + 赤道幾內亞 + 希臘 + 南喬治亞與南三明治群島 + 瓜地馬拉 + 關島 + 幾內亞比索 + 蓋亞納 + 中華人民共和國香港特別行政區 + 赫德與麥克當諾群島 + 宏都拉斯 + 克羅埃西亞 + 印尼 + 愛爾蘭 + 英屬印度洋領土 + 冰島 + 義大利 + 牙買加 + 約旦 + 肯亞 + 吉爾吉斯 + 高棉 + 吉里巴斯 + 科摩羅群島 + 聖克里斯多福及尼維斯 + 北韓 + 南韓 + 開曼群島 + 哈薩克 + 寮國 + 聖露西亞 + 列支敦斯登 + 斯里蘭卡 + 賴比瑞亞 + 賴索扥 + 盧森堡 + 拉脫維亞 + 利比亞 + 摩納哥 + 摩爾多瓦 + 馬達加斯加 + 馬紹爾群島 + 馬其頓 + 馬利 + 緬甸 + 澳門特別行政區 + 北馬里安納 + 馬丁尼克島 + 茅利塔尼亞 + 蒙特色拉特島 + 馬爾他 + 模里西斯 + 馬爾地夫 + 馬拉威 + 馬來西亞 + 莫三比克 + 納米比亞 + 新喀里多尼亞群島 + 尼日 + 諾福克島 + 奈及利亞 + 荷蘭 + 尼泊爾 + 諾魯 + 紐威島 + 紐西蘭 + 阿曼王國 + 巴拿馬 + 秘魯 + 法屬玻里尼西亞 + 巴布亞紐幾內亞 + 菲律賓 + 波蘭 + 聖彼德與密啟崙 + 皮特康 + 玻多黎克 + 巴勒斯坦 + 帛琉 + 卡達 + 留尼旺 + 羅馬尼亞 + 俄羅斯 + 盧安達 + 沙烏地阿拉伯 + 索羅門群島 + 塞席爾 + 蘇丹 + 聖赫勒拿島 + 斯洛維尼亞 + 冷岸及央麥恩群島 + 獅子山 + 聖馬利諾 + 塞內加爾 + 索馬利亞 + 塞爾維亞 + 蘇利南 + 聖多美及普林西比 + 薩爾瓦多 + 敘利亞 + 史瓦濟蘭 + 土克斯及開科斯群島 + 查德 + 法國南屬地 + 多哥共和國 + 泰國 + 塔吉克 + 托克勞群島 + 東帝文 + 土庫曼 + 突尼西亞 + 東加 + 千里達及托巴哥 + 吐瓦魯 + 臺灣 + 坦尚尼亞 + 烏克蘭 + 烏干達 + 美屬邊疆群島 + 美國 + 烏拉圭 + 烏茲別克 + 梵帝岡 + 聖文森及格瑞那丁 + 委內瑞拉 + 英屬維爾京群島 + 美屬維爾京群島 + 萬那杜 + 瓦利斯和福杜納群島 + 薩摩亞群島 + 葉門 + 馬約特 + 尚比亞 + 辛巴威 + + + 已修訂 + + + 曆法 + 校對 + 貨幣 + + + 佛教曆法 + 農曆 + 公曆 + 希伯來曆法 + 伊斯蘭曆法 + 伊斯蘭城市曆法 + 日本曆法 + 直接順序 + 電話簿順序 + 拼音順序 + 筆劃顺序 + 傳統曆法 + + + + [一-丁七丈-不且世丙丟並中串丸-丹主乃久么之乎-乏乖乘-乙九也乾亂了予事-二于云-互五-井些亞亡交亦亨享-京亮人什-仁仇今-介仍仔他付-仙代-以仰仲件任份企伊伍休伙伯-估伴伸似但佈位-住佔-何余佛-作你佩佳使來例供依侯侵便係-促俊俗保俠-信修俱個倍們-倒候-倚借倫值假偉偏做停健側-偷傑備傢傲-傳傷傻傾僅像僑價儀億儒儘優允元-充兇-光克免兒兔入內-兩八-兮共兵-典兼冊再冒冠冬冰冷准凌凝凡凰-凱出函刀分-切刊列初判-別利-刪到制-刷刺-刻則前剛剩-剪副割創劃劇劉劍力功-加助-劫勁勇勉勒動務勝-勞勢勤勵勸勿包化-北區-十千升-午半卒協南博卡印-危即卷卻厚原厭厲去參又及-友反叔取-受口-另叫-叭可-台史-右司吃-各合-吊同-后吐-向君吝吟否-吧含吳吵吸-吹吾呀呂呆告呢周味呵呼-命和咖咦-咧咪咬咱哀-品哇-哉哎員哥-哦哩-哪哭哲唉唐唬售-唯唱唷-唸商啊問啟啡啥-啦啪喂善喇喊喔喜-喝喬單喵嗎嗚嗨嗯嘆嘉嘗嘛嘴嘻嘿器噴嚇嚴囉四回因困固圈國圍園-圓圖團土在地圾址均坐坡坤坦坪垃型城域執培-基堂堅-堆堪報場塊塔塗塞填塵境增墨墮壁壓壘壞壢士壯壽夏夕-外多夜夠夢夥大天-夫央失夾奇-奉奏契奔套奧奪奮女奶她好如妙妥妨妮妳妹妻姆姊-始姐-姑姓-委姿威娃娘婆婚婦媒媽嫌子孔字-存孝孟季-孤孩孫學它宇-安宋-完宏宗-定宜客-室宮害家容宿寂寄密富寒寞-察寢實-寧審寫-寬寶封射將-專尊-尋對-小少尖尚尤就尺尼尾局-屁居-屆屋屏展屠層屬山岸峰島崇嵐嶺川-州巡工-巨巫差己-已巴巷市-布希帝帥師席帳帶常帽幅幕幣幫干-年幸-幹幻-幾床序底店府度-座庫庭康-庸廉廖廠廢-廣廳延-廷建弄式引弘弟弦弱張強彈彌彎形彥彩彬-彭彰-影役彼往-征待很律-後徐-徒得從復微徵德徹心必忌-忍志-忙忠快念忽怎怒怕-怖思怡急性-怨怪恆恐恢恥恨-恩恭息-恰悅悉悔悟-悠您悲悶情惑惜惠-惡惱想惹愁愈-愉意愚-愛感慈態慕慘慢-慣慧慮慰慶慾憂憐-憑憲憶憾懂應懶-懷懼戀成-戒或截戰戲戴戶房-扁扇手才打托扣扭扯批找-技抄把抓投抗-折披-抬抱抵抹抽拆拉拋拍拒拔拖招-拜括拳拼拾-拿持指按挑挖振挺捐捕捨捲捷掃授-掉掌排掛採-探接控-推措描-提插揚-換握揮援損搖搞搬-搭搶摘摩摸撐撞撥播撿擁擇擊-擋操-擎擔據擠擦擬擴擺擾攝支收改攻放-政故效敏救敗教敝敢-散敦敬整-敵數文斗料斯-新斷方於-施旁旅旋族旗既日-旦早旭昇昌明-昏易星-映春昨昭是時晚晨普-景晴晶智暑暖-暗暫暴曉曰曲更書曼曾-最會月-有朋服朗望朝期木未-本朱朵李材-村杜束杯東松-板析林果-枝架柏-某染-柔查柳校核-根格桃案桌桑梁梅條梯-械棄棋棒棚森椅植椰楊楓楚業極概榜榮構槍樂樓標樞模樣樹橋機橫檔檢欄權次欣欲欺欽-款歉歌歐歡-武歲歷-歸死殊殘段殺殼毀毅母每毒比毛毫氏民氣水永求汝江-污汪決汽沈-沉沒沖沙河油治沿況泉法泡-波泥注泰泳洋洗洛洞洩-洪洲活洽-派流浩-浪浮海消-涉涯液涵涼淑淚淡淨深混淺清減渡測港游湖湯源準溝溪-溫滄-滅滋滑滴滾-滿漂漏演漠漢漫漲漸潔潛潮澤澳激濃濟濤濫灌灣火灰災炎炮炸為烈烏烤無焦然煙煞照煩熊熟熱燃燈燒營爆爐爛爬-爭爵-父爸爺爽-爾牆-版牌牙牛牠牧物牲特牽犧犯狀狂狐狗狠狼猛-猜猶獄-獅獎獨獲獸獻玄率玉王玩玫玲珍珠班現球理琪琴瑜瑞瑪瑰環瓜瓦瓶甘甚甜生產用田-申男界留畢略番-畫異當疏疑疼病痕痛痴瘋療癡登-百的皆-皇皮盃益盛-盜盟盡監-盤目直相盼盾省眉看真-眠眼眾睛睡督瞧瞭矛矣知短石砂砍研砲破硬碎碗碟碧碩碰確碼磁磨礎礙示社祖祝-神祥票禁禍福禪禮秀-私秋科-秒秘租秤秦移稅程稍種稱稿穌-積穩究穹-空穿突窗窩窮立站竟-章童端競竹笑笛符笨第筆等筋答策算管箭箱節範篇築簡簽籃籌-籍米粉粗精糊糕糟系紀約-紅納純紙-紛素索紫累-細紹終組結絕絡給統-絲經綜綠維綱-網緊緒線緣編-緩練縣縮縱總-績繁織繞繪繳繼續缸缺罪置罰署罵罷羅羊美羞群義羽翁習翔翹翻-翼耀-老考者而-耍耐耗耳耶聊聖聚聞聯-聰聲職聽肉肚股肥肩肯育背胎胖胞胡胸能脆脫腦腰腳腿膽臉臥臨自臭至-致臺與-舊舍舒舞-舟航般船艦良色艾芬花芳若-苦英茫茶草荒荷莉-莊莎莫菜菩華菲萊萬落葉著葛蒙蒼蓋蓮蔡蔣蕭薄薦薩-薪藉藍藏藝藤-藥蘇蘭虎處虛號虧蛋蛙蜂蜜蝶融螢蟲蟹蠍蠻血行術街衛衝衡衣表袋被裁-裂裕補-裝裡製複褲西要覆見規視親覺覽觀角解觸言訂計訊討訓託-記訪設許訴註-証評詞詢試詩話-詳誇誌-認誓誕語誠誤說誰課誼調談請諒論諸諾謀謂講謝證識譜警譯-議護譽讀變讓讚谷豆豈豐象豪豬貌貓貝-貞負-貢貨貪-責貴買費-貼賀資賓賜賞賢-賤賦質賭賴賺購-賽贈贊贏赤走起超越趕趙趣趨足跌跑距跟跡路跳踏踢蹟蹤躍身躲車軌-軍軒軟較載輔-輕輛輝輩-輪輯輸轉轟辛辦辨辭辯辱-農迅迎近迪-迫述迴迷追退-送逃逆透-逐途這-逛逝速-造逢-連週-進逸逼遇遊-運遍-過道-違遙遜遠適遭遲遷-選遺避-邁還邊邏那邦邪邱郎部郭郵都鄉鄭鄰配酒酷-酸醉醒醜醫采釋-量金針釣鈴銀銘銳銷鋒鋼錄錢錦錯鍋鍵鍾鎖鎮鏡鐘鐵鑑長門閃閉開閒-間閣閱闆闊關闡防阻阿-陀附降限院-除陪陰陳陵陷-陸陽隆隊階隔際-障隨險隱隻雄-集雖雙雜雞離-難雨雪雲零-雷電需震霧露霸-霹靂靈青靖靜非靠面革鞋韓音韻響頁-頂項-順須預-頑頓頗-領頭頻顆題-額顏願類顧顯風飄飛食飯飲飽-飾餅養餐餘館首香馬駐駕駛騎騙騷驅驗驚骨體高髮鬆鬥鬧鬱鬼魂魅魔魚魯鮮鳥鳳-鳴鴻鵝鷹鹿麗麥麵麻-麼黃黎黑默點黨鼓鼠鼻齊-齋齒齡龍龜] + + + + + + + 民國前 + 民國 + + + + + + + yyyy'年'M'月'd'日'EEEE + + + + + yyyy'年'M'月'd'日' + + + + + yyyy/M/d + + + + + yyyy/M/d + + + + + + + + ahh'時'mm'分'ss'秒' z + + + + + ahh'時'mm'分'ss'秒' + + + + + a h:mm:ss + + + + + a h:mm + + + + + + + {1} {0} + + + + + + + + + 太平洋標準時間 + 太平洋日光節約時間 + + + PST + PDT + + 洛杉磯 + + + + 太平洋標準時間 + 太平洋日光節約時間 + + + PST + PDT + + 洛杉磯 + + + + 山區標準時間 + 山區日光節約時間 + + + MST + MDT + + 丹佛 + + + + 山區標準時間 + 山區日光節約時間 + + + MST + MDT + + 丹佛 + + + + 山區標準時間 + 山區標準時間 + + + MST + MST + + 鳳凰城 + + + + 山區標準時間 + 山區標準時間 + + + MST + MST + + 鳳凰城 + + + + 中部標準時間 + 中部日光節約時間 + + + CST + CDT + + 芝加哥 + + + + 中部標準時間 + 中部日光節約時間 + + + CST + CDT + + 芝加哥 + + + + 東部標準時間 + 東部日光節約時間 + + + EST + EDT + + 紐約 + + + + 東部標準時間 + 東部日光節約時間 + + + EST + EDT + + 紐約 + + + + 東部標準時間 + 東部標準時間 + + + EST + EST + + 印第安那波里斯 + + + + 東部標準時間 + 東部標準時間 + + + EST + EST + + 印第安那波里斯 + + + + 夏威夷標準時間 + 夏威夷標準時間 + + + HST + HST + + 檀香山 + + + + 夏威夷標準時間 + 夏威夷標準時間 + + + HST + HST + + 檀香山 + + + + 阿拉斯加標準時間 + 阿拉斯加日光節約時間 + + + AST + ADT + + 安克里治 + + + + 阿拉斯加標準時間 + 阿拉斯加日光節約時間 + + + AST + ADT + + 安克里治 + + + + 大西洋標準時間 + 大西洋日光節約時間 + + + AST + ADT + + 哈里法克斯 + + + + 紐芬蘭標準時間 + 紐芬蘭日光節約時間 + + + CNT + CDT + + 聖約翰 + + + + 紐芬蘭標準時間 + 紐芬蘭日光節約時間 + + + CNT + CDT + + 聖約翰 + + + + 中歐標準時間 + 中歐日光節約時間 + + + CET + CEST + + 巴黎 + + + + 中歐標準時間 + 中歐日光節約時間 + + + CET + CEST + + 巴黎 + + + + 格林威治標準時間 + 格林威治標準時間 + + + GMT + GMT + + 倫敦 + + + + 格林威治標準時間 + 格林威治標準時間 + + + GMT + GMT + + 卡薩布蘭卡 + + + + 以色列標準時間 + 以色列日光節約時間 + + + IST + IDT + + 耶路撒冷 + + + + 日本標準時間 + 日本標準時間 + + + JST + JST + + 東京 + + + + 日本標準時間 + 日本標準時間 + + + JST + JST + + 東京 + + + + 東歐標準時間 + 東歐日光節約時間 + + + EET + EEST + + 布加勒斯特 + + + + 中國標準時間 + 中國標準時間 + + + CTT + CDT + + 上海 + + + + 中國標準時間 + 中國標準時間 + + + CTT + CDT + + 上海 + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + 安道爾第納爾 + ADD + + + 安道爾陪士特 + ADP + + + 阿拉伯聯合大公國迪爾汗 + AED + + + 阿富汗尼 (1927-2002) + AFA + + + 阿富汗尼 + AFN + + + 阿法爾和伊薩法郎 + AIF + + + 阿爾巴尼亞列克 (1946-1961) + ALK + + + 阿爾巴尼亞列克 + ALL + + + 阿爾巴尼亞列克幣 + ALV + + + 阿爾巴尼亞元外匯券 + ALX + + + 亞美尼亞德拉姆 + AMD + + + 荷蘭 安梯蘭 盾 + ANG + + + 安哥拉寬扎 + AOA + + + 安哥拉寬扎(1977-1990) + AOK + + + 安哥拉新寬扎 (1990-2000) + AON + + + 安哥拉新寬扎 Reajustado (1995-1999) + AOR + + + 安哥拉埃斯庫多 + AOS + + + 阿根廷奧斯特納爾 + ARA + + + 阿根廷披索 Moneda Nacional + ARM + + + 阿根廷披索(1983-1985) + ARP + + + 阿根廷披索 + ARS + + + 奧地利先令 + ATS + + + 澳幣 + AUD + + + 澳大利亞鎊 + AUP + + + 阿魯巴盾 + AWG + + + 阿塞拜彊馬特納 + AZM + + + 波士尼亞-黑塞哥維那第納爾 + BAD + + + 波士尼亞-黑塞哥維那可轉換馬克 + BAM + + + 波士尼亞-黑塞哥維那新第納爾 + BAN + + + 巴貝多元 + BBD + + + 孟加拉塔卡 + BDT + + + 比利時法郎 (可轉換) + BEC + + + 比利時法郎 + BEF + + + 比利時法郎 (金融) + BEL + + + 保加利亞硬列弗 + BGL + + + 保加利亞 社會主義列弗 + BGM + + + 保加利亞新列弗 + BGN + + + 保加利亞列弗 (1879-1952) + BGO + + + 保加利亞列弗外匯券 + BGX + + + 巴林第納爾 + BHD + + + 蒲隆地法郎 + BIF + + + 百慕達幣 + BMD + + + 百慕達鎊 + BMP + + + 汶萊元 + BND + + + 玻利維亞貨幣單位 + BOB + + + 玻利維亞舊貨幣單位 (1863-1962) + BOL + + + 玻利維亞披索 + BOP + + + 玻利維亞 幕多 + BOV + + + 巴西克魯薩多 農瓦 (1967-1986) + BRB + + + 巴西克魯賽羅 + BRC + + + 巴西克魯賽羅 (1990-1993) + BRE + + + 巴西里拉 + BRL + + + 巴西 克如爾達 農瓦 + BRN + + + 巴西克魯賽羅 + BRR + + + 巴西克魯賽羅 (1942-1967) + BRZ + + + 巴哈馬元 + BSD + + + 巴哈馬鎊 + BSP + + + 不丹努扎姆 + BTN + + + 不丹盧布 + BTR + + + 緬甸元 + BUK + + + 緬甸盧布 + BUR + + + 波札那 - 普拉 + BWP + + + 白俄羅斯新盧布 (1994-1999) + BYB + + + 白俄羅斯盧布 (1992-1994) + BYL + + + 白俄羅斯盧布 + BYR + + + 伯利茲元 + BZD + + + 英國的洪都拉斯元r + BZH + + + 加幣 + CAD + + + 剛果法郎 + CDF + + + 剛果共和國法郎 + CDG + + + 剛果扎伊爾 + CDL + + + 中非共和國西非法郎 + CFF + + + 瑞士法郎 + CHF + + + 庫克群島元 + CKD + + + 智利 康導 + CLC + + + 智利埃斯庫多 + CLE + + + 卡林油達佛曼跎 + CLF + + + 智利披索 + CLP + + + 卡麥隆西非法郎 + CMF + + + 中國人民幣元 + CNP + + + 中國美元外匯券 + CNX + + + 人民幣 + CNY + + + 哥倫比亞披索鈔 + COB + + + 剛果西非法郎 + COF + + + 哥倫比亞披索 + COP + + + 哥斯大黎加科郎 + CRC + + + 捷克克朗 + CSC + + + 捷克斯洛伐克硬克朗 + CSK + + + 古巴披索 + CUP + + + 古巴人外匯券 + CUX + + + 維德角埃斯庫多 + CVE + + + 庫拉克 盾 + CWG + + + 賽浦路斯鎊 + CYP + + + 捷克克朗 + CZK + + + 東德東德馬克 + DDM + + + 德國馬克 + DEM + + + 德國 蘇馬克Sperrmark + DES + + + 吉布地法郎 + DJF + + + 丹麥克羅納 + DKK + + + 多明尼加披索 + DOP + + + 阿爾及利亞第納爾 + DZD + + + 阿爾及利亞新法郎 + DZF + + + 阿爾及利亞法郎 Germinal + DZG + + + 厄瓜多蘇克雷 + ECS + + + 厄瓜多爾由里達瓦康斯坦 (UVC) + ECV + + + 愛沙尼亞克朗 + EEK + + + 埃及鎊 + EGP + + + 厄立特里亞納克法 + ERN + + + 西班牙陪士特 + ESP + + + 衣索比亞比爾 + ETB + + + 埃賽俄比亞元 + ETD + + + 歐元 + EUR + + + 芬蘭馬克 + FIM + + + 芬蘭馬克 (1860-1962) + FIN + + + 斐濟元 + FJD + + + 斐濟鎊 + FJP + + + 福克蘭群島鎊 + FKP + + + 法羅島克朗 + FOK + + + 法國法郎 + FRF + + + 法國法郎 捷米那/龐加萊法郎 + FRG + + + 加蓬西非法郎 + GAF + + + 英鎊 + GBP + + + 喬治 庫旁 拉里 + GEK + + + 喬治拉里 + GEL + + + 迦納仙蔕 + GHC + + + 迦納舊仙蔕 + GHO + + + 迦納鎊 + GHP + + + 迦納重新估价後的仙蔕 + GHR + + + 直布羅陀鎊 + GIP + + + 格陵蘭克羅鈉 + GLK + + + 甘比亞達拉西 + GMD + + + 岡比亞鎊 + GMP + + + 幾內亞法郎 + GNF + + + 幾內亞法郎 (1960-1972) + GNI + + + 幾內亞西里 + GNS + + + 瓜德羅普島法郎 + GPF + + + 赤道幾內亞埃奎勒 + GQE + + + 赤道幾內亞佛朗哥 + GQF + + + 赤道幾內亞比塞塔 + GQP + + + 希臘德拉克馬 + GRD + + + 希臘新德拉克馬 + GRN + + + 瓜地馬拉格查爾 + GTQ + + + 法屬圭亞那法郎圭亞那 + GUF + + + 葡屬幾內亞埃斯庫多 + GWE + + + 葡屬幾內亞米爾里斯 + GWM + + + 幾內亞披索披索 + GWP + + + 圭亞那元 + GYD + + + 港元 + HK$ + + + 洪都拉斯倫皮拉 + HNL + + + 克羅地亞第納爾 + HRD + + + 克羅地亞庫納 + HRK + + + 海地古德 + HTG + + + 匈牙利 - 福林 + HUF + + + 北愛爾蘭鎊 + IBP + + + 印度尼西亞尼可盾 + IDG + + + 印度尼西亞爪哇盧布 + IDJ + + + 印度尼西亞新盧布 + IDN + + + 印尼 - 盧布 + IDR + + + 愛爾蘭鎊 + IEP + + + 以色列謝客爾 + ILL + + + 以色列鎊 + ILP + + + 以色列新謝克爾 + ILS + + + 曼城島英鎊 + IMP + + + 印度盧布 + =0#Rs.|1#Re.|1<Rs. + + + 伊拉克第納爾 + IQD + + + 伊朗里亞爾 + IRR + + + 冰島克朗 + ISK + + + 義大利里拉 + ITL + + + 澤西鎊 + JEP + + + 牙買加元 + JMD + + + 牙買加鎊 + JMP + + + 約旦第納爾 + JOD + + + 日圓 + JP¥ + + + 肯尼亞先令 + KES + + + 吉爾吉斯索馬 + KGS + + + 柬埔寨舊瑞爾 + KHO + + + 柬埔寨瑞爾 + KHR + + + 基里巴斯元 + KID + + + 科摩羅法郎 + KMF + + + 北朝鮮人民幣 + KPP + + + 北朝鮮幣 + KPW + + + 韓國 哈瓦 + KRH + + + 南韓舊幣 + KRO + + + 韓國圜 + KRW + + + 科威特第納爾 + KWD + + + 開曼群島美元 + KYD + + + 卡扎克斯坦盧布 + KZR + + + 卡扎克斯坦坦吉 + KZT + + + 老撾 開普 + LAK + + + 黎巴嫩鎊 + LBP + + + 列支敦斯登法郎 + LIF + + + 斯里蘭卡盧布 + LKR + + + 錫蘭盧布 + LNR + + + 賴比瑞亞元 + LRD + + + 賴索托羅蒂 + LSL + + + 立陶宛里塔 + LTL + + + 立陶宛特羅 + LTT + + + 盧森堡法郎 + LUF + + + 拉脫維亞拉特銀幣 + LVL + + + 拉脫維亞盧布 + LVR + + + 利比亞英國的軍事當局里拉 + LYB + + + 利比亞第納爾 + LYD + + + 利比亞鎊 + LYP + + + 摩洛哥迪拉姆 + MAD + + + 摩洛哥法郎 + MAF + + + 摩納哥新法郎 + MCF + + + 摩納哥法郎 傑米那 + MCG + + + 摩杜雲列伊庫旁 + MDC + + + 摩杜雲列伊 + MDL + + + 摩杜雲盧布庫旁 + MDR + + + 馬達加斯加艾瑞爾 + MGA + + + 馬達加斯加法郎 + MGF + + + 馬紹爾群島美元 + MHD + + + 馬其頓第納爾 + MKD + + + 馬其頓第納爾(1992-1993) + MKN + + + 馬里法郎 + MLF + + + 緬甸元 + MMK + + + 緬甸美元外匯券 + MMX + + + 蒙古圖格里克 + MNT + + + 澳門元 + MOP + + + 馬提尼克島法郎 + MQF + + + 茅利塔尼亞烏吉亞 + MRO + + + 馬爾他里拉 + MTL + + + 馬爾他鎊 + MTP + + + 模里西斯盧布 + MUR + + + 馬爾地夫盧布 + MVP + + + 馬爾地夫海島盧非亞 + MVR + + + 馬拉維克瓦查 + MWK + + + 馬拉維鎊 + MWP + + + 墨西哥 - 披索 + MXN + + + 墨西哥銀披索 (1861-1992) + MXP + + + 墨西哥法律反轉(UDI) + MXV + + + 馬來西亞 - 林吉特 + MYR + + + 莫桑比克埃斯庫多 + MZE + + + 莫三比克梅蒂卡爾 + MZM + + + 納米比亞元 + NAD + + + 赫布里底群島 CFP 法郎 + NCF + + + 奈及利亞奈拉 + NGN + + + 奈及利亞鎊 + NGP + + + 新赫布里底群島 CFP 法郎 + NHF + + + 尼加拉瓜科多巴 + NIC + + + 尼加拉瓜金金哥多華 + NIG + + + 尼加拉瓜 金哥多華 + NIO + + + 荷蘭盾 + NLG + + + 挪威克羅納 + NOK + + + 尼泊爾盧布 + NPR + + + 紐西蘭幣 + $NZ + + + 紐西蘭鎊 + NZP + + + 阿曼里奧 + OMR + + + 阿曼里亞爾仙蔕i + OMS + + + 巴拿馬巴波亞 + PAB + + + 車城盧布 Kupon + PDK + + + 車城新盧布 + PDN + + + 車城盧布 + PDR + + + 祕魯因蒂 + PEI + + + 秘魯新太陽幣 + PEN + + + 秘魯太陽幣 + PES + + + 巴布亞紐幾內亞基那 + PGK + + + 菲律賓披索 + PHP + + + 巴基斯坦盧布 + PKR + + + 波蘭茲羅提 + PLN + + + 波蘭美元外匯券 + PLX + + + 波蘭茲羅提 (1950-1995) + PLZ + + + 巴勒斯坦鎊 + PSP + + + 葡萄牙 康拖 + PTC + + + 葡萄牙埃斯庫多 + PTE + + + 巴拉圭瓜拉尼 + PYG + + + 卡達爾里亞爾 + QAR + + + 留尼汪島法郎 + REF + + + 羅馬尼亞列伊 + ROL + + + 羅馬尼亞新列伊 + RON + + + 俄羅斯盧布 + RUB + + + 俄羅斯盧布 (1991-1998) + RUR + + + 盧安達法郎 + RWF + + + 沙烏地里雅 + SRl + + + 沙烏地宗主里雅 + SAS + + + 索羅門群島元 + SBD + + + 塞舌爾群島盧布 + SCR + + + 蘇丹第納爾 + SDD + + + 蘇丹鎊 + SDP + + + 瑞典克羅納 + SEK + + + 新加坡幣 + SGD + + + 聖赫勒拿 鎊 + SHP + + + 斯洛文尼亞 Tolar Bons + SIB + + + 斯洛維尼亞托勒 + SIT + + + 斯洛伐克克朗 + SKK + + + 獅子山利昂 + SLL + + + 聖馬利諾里拉 + SML + + + 索馬利亞先令 + SOS + + + 索馬里蘭先令 + SQS + + + 蘇里南盾 + SRG + + + 蘇格蘭鎊 + SSP + + + 聖多美島和普林西比島多布拉 + STD + + + 聖多美島和普林西比島埃斯庫多 + STE + + + 蘇聯新盧布 + SUN + + + 蘇聯盧布 + SUR + + + 愛爾 薩爾瓦多科郎 + SVC + + + 敘利亞鎊 + SYP + + + 斯威士蘭 里郎 + SZL + + + 土耳其人和凱科斯冠 + TCC + + + 乍得 西非 法郎 + TDF + + + 泰銖 + THB + + + 塔吉克斯坦盧布 + TJR + + + 塔吉克斯坦 索莫尼 + TJS + + + 土庫曼馬納特 + TMM + + + 突尼西亞第納爾 + TND + + + 東加潘加 + TOP + + + 湯加英鎊 + TOS + + + 帝汶 埃斯庫多 + TPE + + + 帝汶元 + TPP + + + 土耳其里拉 + TRL + + + 千里達及托巴哥r + TTD + + + 特立尼達和多巴哥舊元r + TTO + + + 吐瓦魯美元 + TVD + + + 新臺幣 + NT$ + + + 坦桑尼亞 先令 + TZS + + + 烏克蘭格里夫那 + UAH + + + 烏克蘭 卡本瓦那茲 + UAK + + + 烏干達先令 (1966-1987) + UGS + + + 烏干達先令 + UGX + + + 美元 + US$ + + + 美元 (第二天) + USN + + + 美元 (同一天) + USS + + + 烏拉圭披索福厄特 + UYF + + + 烏拉圭披索 (1975-1993) + UYP + + + 烏拉圭披索 + UYU + + + 烏茲別克斯坦 庫邦 索馬 + UZC + + + 烏茲別克斯坦 薩木 + UZS + + + 梵蒂岡城里拉 + VAL + + + 北越南 皮阿斯特越南盾 + VDD + + + 北越南新盾 + VDN + + + 北越南 名 皮阿斯特越南盾 + VDP + + + 委內瑞拉博利瓦 + VEB + + + 英屬維爾斯群島元 + VGD + + + 越南盾 + VND + + + 越南新盾 + VNN + + + 越南共和國 盾 + VNR + + + 越南國家盾 + VNS + + + 萬那杜萬杜 + VUV + + + 西薩摩亞鎊 + WSP + + + 西薩摩亞塔拉 + WST + + + 亞洲第納爾會計單位 + XAD + + + 西非 法郎 BEAC + XAF + + + 亞洲貨幣單位 + XAM + + + 黃金 + XAU + + + 歐洲綜合單位 + XBA + + + 歐洲貨幣單位 + XBB + + + 歐洲會計單位(XBC) + XBC + + + 歐洲會計單位(XBD) + XBD + + + 格瑞那達元 + XCD + + + 西非 新 法郎 + XCF + + + 特殊提款權 + XDR + + + 西非 法郎 BCEAEC + XEF + + + 歐洲貨幣單位 + XEU + + + 法國金法郎 + XFO + + + 法國 UIC 法郎 + XFU + + + 伊斯蘭第納爾 + XID + + + 法國大城市新 法郎 + XMF + + + 法國安的列斯群島 西非 法郎 + XNF + + + 西非 法郎 BCEAO + XOF + + + CFP 法郎 + XPF + + + COMECON 可轉移盧布 + XTR + + + 葉門第納爾 + YDD + + + 也門阿馬迪里亞爾 + YEI + + + 也門里亞爾 + YER + + + 南斯拉夫第納爾硬幣 + YUD + + + 南斯拉夫聯邦第納爾 + YUF + + + 南斯拉夫人1994 第納爾 + YUG + + + 南斯拉夫挪威亞第納爾 + YUM + + + 南斯拉夫 可轉換第納爾 + YUN + + + 南斯拉夫十月 第納爾 + YUO + + + 南斯拉夫改制後的第納爾 + YUR + + + 南非 - 蘭特 (金融) + ZAL + + + 南非鎊 + ZAP + + + 南非蘭特 + ZAR + + + 尚比亞克瓦查 + ZMK + + + 贊比亞鎊 + ZMP + + + 薩伊扎新伊爾 + ZRN + + + 扎伊爾扎伊爾 + ZRZ + + + 辛巴威元 + ZWD + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/zh_HK.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_HK.xml --- zope3-3.4.0/src/zope/i18n/locales/data/zh_HK.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_HK.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,772 @@ + + + + + + + + + + + + 阿布哈西亞文 + 亞齊文 + 阿僑利文 + 阿當莫文 + 阿迪各文 + 阿緯斯陀文 + 南非荷蘭文 + 非閃族及非亞語言 + 阿弗里希利文 + 阿坎文 + 阿卡德文 + 阿留申文 + 阿爾岡昆文 + 阿拉貢文 + 古英文 (ca.450-1100) + 阿帕奇語言 + 阿拉米文 + 阿勞坎文 + 阿拉帕霍文 + 其他人工語言 + 阿拉瓦克文 + 阿薩姆文 + 阿斯圖里亞文 + 阿薩巴斯卡文 + 澳洲英文 + 阿法文 + 艾馬拉文 + 亞塞拜然文 + 巴什客爾文 + 班達文 + 巴米累克文 + 俾路支文 + 巴姆巴拉文 + 巴厘文 + 巴薩文 + 波羅的文(其他) + 白俄羅斯文 + 貝扎文 + 別姆巴文 + 柏柏爾文 + 保加利亞文 + 比哈爾文 + 博傑普爾文 + 比斯拉馬文 + 比科爾文 + 比尼文 + 錫克錫卡文 + 班圖文 + 藏文 + 布拉杰文 + 波士尼亞文 + 巴塔克文 + 布里阿特文 + 布吉斯文 + 加泰羅尼亞文 + 卡多文 + 中美印第安文(其他) + 巴勒比文 + 高加索文(其他) + 車臣文 + 宿務族文 + 克爾特文(其他) + 查莫洛文 + 奇布查文 + 查加文 + 處奇斯文 + 馬里文 + 契奴克文 + 喬克托文 + 奇佩瓦揚文 + 柴羅基文 + 沙伊安文 + 查米克文 + 科普特文 + 歐洲腔調和洋涇濱,源自英文的(其他) + 歐洲腔調和洋涇濱,源自法文的(其他) + 歐洲腔調和洋涇濱,源自葡萄牙文的(其他) + 克裡文 + 克里米亞半島的土耳其文;克里米亞半島的塔塔爾文 + 克里奧爾文和皮欽文 + 卡舒布文 + 庫施特語系(其他) + 楚瓦甚文 + 威爾士文 + 丹麥文 + 達科他文 + 達爾格瓦文 + 迪雅克文 + 德拉瓦 + 斯拉夫 + 多格里布文 + 丁卡文 + 多格來文 + 德拉威文(其他) + 下索布文 + 杜亞拉文 + 荷蘭,中古 (ca. 1050-1350) + 迪維西文 + 迪尤拉文 + 埃緯文 + 埃菲克文 + 古埃及文) + 艾卡朱克文 + 希臘文 + 埃蘭文 + 英文,中世紀 (1100-1500) + 世界語 + 愛沙尼亞文 + 依汪都文 + 芳族文 + 芳蒂文 + 富拉文 + 芬蘭文 + 芬蘭-烏戈爾族文(其他) + 斐濟文 + 法羅文 + 豐文 + 弗留利文 + 弗里斯蘭文 + 愛爾蘭文 + 加族文 + 加約文 + 葛巴亞文 + 蘇格蘭 - 蓋爾文 + 德國的(其他) + 吉茲文 + 吉爾伯特群島文 + 加里西亞文 + 德文, 中古全盛時期 (ca.1050-1500) + 德文,上古全盛時期 (ca.750-1050) + 岡德文 + 科隆達羅文 + 哥特文 + 哥博語 + 古希臘文 (至 1453) + 吉亞拉塔文 + 曼島文 + 圭契文 + 海達文 + 希伯來文 + 北印度文 + 希利蓋農文 + 赫馬查利文 + 赫梯文 + 孟文 + 西里莫圖土文 + 克羅埃西亞文 + 上索布文 + 海地人 + 胡帕文 + 亞美尼亞文 + 赫雷羅文 + 拉丁國際文 + 伊班文 + 印尼文 + 拉丁國際文 + 伊布文 + 四川話 + 伊喬文 + 依奴皮維克文 + 伊洛闊文 + 印度語系(其他) + 印歐語系(其他) + 印古什文 + 伊朗文 + 伊芳朗文 + 易洛魁文 + 冰島文 + 義大利文 + 因紐特文 + 邏輯文 + 猶太教-波斯文 + 猶太教-阿拉伯文 + 喬治亞文 + 卡拉卡爾帕克文 + 卡比爾文 + 卡琴文 + 卡姆巴文 + 克倫文 + 卡威文 + 卡巴爾達文 + 剛果文 + 卡西文 + 其他科伊桑文 + 和闐[與闐]文 + 吉庫尤人 + 廣亞馬文 + 哈薩克文 + 格陵蘭文 + 高棉文 + 金邦杜文 + 坎那達文 + 韓文 + 貢根文 + 科斯雷恩文 + 克佩列文 + 卡努裡文 + 卡拉柴-包爾卡爾文 + 克魯文 + 庫魯科文 + 克什米爾文 + 庫爾德文 + 庫密克文 + 庫特奈文 + 康瓦耳文 + 吉爾吉斯文 + 拉迪諾文 + 拉亨達文 + 蘭巴文 + 盧森堡文 + 立陶宛文 + 干達文 + 林堡文 + 寮國文 + 蒙古文 + 洛齊文 + 魯巴加丹加文 + 魯巴魯魯亞文 + 路易塞諾文 + 盧恩達文 + 盧奧文 + 盧晒文 + 馬都拉文 + 馬加伊文 + 邁蒂利文 + 望加錫文 + 曼丁哥文 + 南島文 + 馬賽文 + 莫克沙文 + 曼達文 + 門德文 + 馬爾加什文 + 愛爾蘭文,中古 (900-1200) + 馬紹爾文 + 米克馬克文 + 米南卡堡文 + 其他語言 + 馬其頓文 + 其他高棉語系 + 馬來亞拉姆文 + 滿族文 + 曼尼普裡文 + 馬諾波文 + 摩爾多瓦文 + 莫霍克文 + 莫西文 + 馬拉地文 + 馬來文 + 馬爾他文 + 多種語言 + 蒙達文 + 克里克文 + 馬爾尼裡文 + 緬甸文 + 馬雅文 + 厄爾茲亞文 + 諾魯文 + 納瓦特文 + 其他北美印地安文 + 拿波里文 + 挪威波克默爾文 + 北地畢列文 + 德國北部的德文; 薩克遜文 + 尼泊爾文 + 尼瓦爾文 + 恩東加文 + 尼亞斯文 + 其他尼日剛果語系 + 紐埃文 + 荷蘭文 + 新挪威文 + 諾蓋文 + 古諾爾斯文 + 南地畢列文 + 北索托文 + 努比亞文 + 納瓦約文 + 尼揚賈文 + 尼揚韋齊文 + 尼揚科萊文 + 尼奧囉文 + 尼茲馬文 + 奧西坦文 + 奧杰布瓦文 + 歐里亞文 + 奧塞提文 + 歐塞奇文 + 鄂圖曼土耳其文 (1500-1928) + 奧托米文 + 其他巴布亞諸語言 + 潘加辛文 + 巴列維文 + 潘帕嘉文 + 帕皮阿門托文 + 帛琉文 + 古波斯文 (ca.600-400 B.C.) + 其他菲律賓文 + 腓尼基文 + 巴利文 + 波蘭文 + 波那貝文 + 印度古代及中世紀之中部及北部方言 + 普羅文斯文 (to 1500) + 普什圖文 + 蓋丘亞文 + 拉賈斯坦諸文 + 復活島文 + 拉羅通加文 + 里托羅曼斯文 + 羅馬尼亞文 + 其他羅曼文 + 吉普賽文 + 盧安達文 + 桑達韋文 + 雅庫特文 + 其他南美印第安文 + 薩利甚文 + 薩瑪利亞阿拉姆文 + 撒撒克文 + 散塔利文 + 撒丁文 + 蘇丹文 + 北方薩米文 + 瑟爾卡普文 + 其他閃族語言 + 古愛爾蘭文(至 900) + 手語 + 塞爾維亞克羅埃西亞文 + 撣文 + 僧伽羅文 + 希達摩文 + 大蘇文 + 其他漢藏文 + 斯洛維尼亞文 + 其他斯拉夫文 + 薩摩亞文 + 南薩米文 + 其他薩米文 + 魯勒薩米文 + 伊納裡薩米文 + 斯科特薩米文 + 塞內加爾文 + 索尼基文 + 索馬利文 + 索格底亞納文 + 桑海文 + 阿爾巴尼亞文 + 塞爾維亞文 + 塞雷爾文 + 非洲撒哈拉沙漠邊緣地帶文 + 蘇丹文 + 蘇庫馬文 + 蘇蘇文 + 蘇美文 + 史瓦希里文 + 古敘利亞文 + 坦米爾文 + 其他泰文 + 泰盧固文 + 提姆文 + 泰雷諾文 + 泰頓文 + 提格利尼亞文 + 蒂格雷文 + 提夫文 + 土庫曼文 + 托克勞文 + 特林基特文 + 塔馬奇克文 + 突尼西亞文 + 東加文 + 湯加文(尼亞薩文) + 托比辛文 + 欽西安文 + 韃靼文 + 圖姆布卡文 + 圖皮文 + 其他阿爾泰諸文 + 吐瓦魯文 + 繁體中文 + 大溪地文 + 土凡文 + 沃蒂艾克文 + 維吾爾文 + 烏加列文 + 烏克蘭文 + 姆本杜文 + 未確定的 + 烏爾都文 + 烏茲別克文 + 越南文 + 溫達文 + 沃提克文 + 瓦隆文 + 夸基武特文 + 瓦拉莫文 + 瓦瑞文 + 瓦紹文 + 文德文 + 沃爾夫文 + 卡爾梅克文 + 班圖文 + 瑤文 + 雅浦文 + 意第緒文 + 約魯巴文 + 愛斯基摩文 + 壯文 + 薩波特克文 + 澤納加文 + 贊德文 + 祖魯文 + 祖尼文 + + + 安道爾 + 阿拉伯聯合大公國 + 安地卡及巴布達 + 安圭拉島 + 阿爾巴尼亞 + 亞美尼亞 + 荷屬安地列斯 + 南極洲 + 美屬薩摩亞群島 + 奧地利 + 澳洲 + 阿路巴 + 亞塞拜然 + 波士尼亞與赫塞格維納 + 巴貝多 + 孟加拉 + 比利時 + 布基納法索 + 保加利亞 + 浦隆地 + 貝南 + 百慕達 + 汶萊 + 玻利維亞 + 巴哈馬 + 布威島 + 波札那 + 白俄羅斯 + 貝里斯 + 可可斯群島 + 剛果民主共和國 + 中非共和國 + 剛果 + 科特迪瓦 + 庫克群島 + 喀麥隆 + 中華人民共和國 + 哥倫比亞 + 哥斯大黎加 + 維德角 + 聖誕島 + 賽普勒斯 + 捷克共和國 + 德國 + 吉布地 + 丹麥 + 多明尼加 + 多明尼加共和國 + 阿爾及利亞 + 厄瓜多爾 + 愛沙尼亞 + 厄利垂亞 + 衣索比亞 + 芬蘭 + 斐濟 + 福克蘭群島 + 密克羅尼西亞群島 + 法羅群島 + 法國 + 加彭 + 英國 + 格瑞納達 + 喬治亞共和國 + 法屬圭亞那 + 迦納 + 直布羅陀 + 格陵蘭 + 甘比亞 + 幾內亞 + 哥德普洛 + 赤道幾內亞 + 希臘 + 南喬治亞與南三明治群島 + 瓜地馬拉 + 關島 + 幾內亞比索 + 蓋亞納 + 中華人民共和國香港特別行政區 + 赫德與麥克當諾群島 + 宏都拉斯 + 克羅埃西亞 + 印尼 + 愛爾蘭 + 英屬印度洋領土 + 冰島 + 義大利 + 牙買加 + 約旦 + 肯亞 + 吉爾吉斯 + 高棉 + 吉里巴斯 + 科摩羅群島 + 聖克里斯多福及尼維斯 + 北韓 + 南韓 + 開曼群島 + 哈薩克 + 寮國 + 聖露西亞 + 列支敦斯登 + 斯里蘭卡 + 賴比瑞亞 + 賴索扥 + 盧森堡 + 拉脫維亞 + 利比亞 + 摩納哥 + 摩爾多瓦 + 馬達加斯加 + 馬紹爾群島 + 馬其頓 + 馬利 + 緬甸 + 澳門特別行政區 + 北馬里安納 + 馬丁尼克島 + 茅利塔尼亞 + 蒙特色拉特島 + 馬爾他 + 模里西斯 + 馬爾地夫 + 馬拉威 + 馬來西亞 + 莫三比克 + 納米比亞 + 新喀里多尼亞群島 + 尼日 + 諾福克島 + 奈及利亞 + 荷蘭 + 尼泊爾 + 諾魯 + 紐威島 + 紐西蘭 + 阿曼王國 + 巴拿馬 + 秘魯 + 法屬玻里尼西亞 + 巴布亞紐幾內亞 + 菲律賓 + 波蘭 + 聖彼德與密啟崙 + 皮特康 + 玻多黎克 + 巴勒斯坦 + 帛琉 + 卡達 + 留尼旺 + 羅馬尼亞 + 俄羅斯 + 盧安達 + 沙烏地阿拉伯 + 索羅門群島 + 塞席爾 + 蘇丹 + 聖赫勒拿島 + 斯洛維尼亞 + 冷岸及央麥恩群島 + 獅子山 + 聖馬利諾 + 塞內加爾 + 索馬利亞 + 塞爾維亞 + 蘇利南 + 聖多美及普林西比 + 薩爾瓦多 + 敘利亞 + 史瓦濟蘭 + 土克斯及開科斯群島 + 查德 + 法國南屬地 + 多哥共和國 + 泰國 + 塔吉克 + 托克勞群島 + 東帝文 + 土庫曼 + 突尼西亞 + 東加 + 千里達及托巴哥 + 吐瓦魯 + 臺灣 + 坦尚尼亞 + 烏克蘭 + 烏干達 + 美屬邊疆群島 + 美國 + 烏拉圭 + 烏茲別克 + 梵帝岡 + 聖文森及格瑞那丁 + 委內瑞拉 + 英屬維爾京群島 + 美屬維爾京群島 + 萬那杜 + 瓦利斯和福杜納群島 + 薩摩亞群島 + 葉門 + 馬約特 + 尚比亞 + 辛巴威 + + + 已修訂 + + + 佛教曆法 + 農曆 + 公曆 + 希伯來曆法 + 伊斯蘭曆法 + 伊斯蘭城市曆法 + 日本曆法 + 直接順序 + 電話簿順序 + 拼音順序 + 筆劃顺序 + 傳統曆法 + + + + [一-丁七丈-不且世丙丟並中串丸-丹主乃久么之乎-乏乖乘-乙九也乾亂了予事-二于云-互五-井些亞亡交亦亨享-京亮人什-仁仇今-介仍仔他付-仙代-以仰仲件任份企伊伍休伙伯-估伴伸似但佈位-住佔-何余佛-作你佩佳使來例供依侯侵便係-促俊俗保俠-信修俱個倍們-倒候-倚借倫值假偉偏做停健側-偷傑備傢傲-傳傷傻傾僅像僑價儀億儒儘優允元-充兇-光克免兒兔入內-兩八-兮共兵-典兼冊再冒冠冬冰冷准凌凝凡凰-凱出函刀分-切刊列初判-別利-刪到制-刷刺-刻則前剛剩-剪副割創劃劇劉劍力功-加助-劫勁勇勉勒動務勝-勞勢勤勵勸勿包化-北區-十千升-午半卒協南博卡印-危即卷卻厚原厭厲去參又及-友反叔取-受口-另叫-叭可-台史-右司吃-各合-吊同-后吐-向君吝吟否-吧含吳吵吸-吹吾呀呂呆告呢周味呵呼-命和咖咦-咧咪咬咱哀-品哇-哉哎員哥-哦哩-哪哭哲唉唐唬售-唯唱唷-唸商啊問啟啡啥-啦啪喂善喇喊喔喜-喝喬單喵嗎嗚嗨嗯嘆嘉嘗嘛嘴嘻嘿器噴嚇嚴囉四回因困固圈國圍園-圓圖團土在地圾址均坐坡坤坦坪垃型城域執培-基堂堅-堆堪報場塊塔塗塞填塵境增墨墮壁壓壘壞壢士壯壽夏夕-外多夜夠夢夥大天-夫央失夾奇-奉奏契奔套奧奪奮女奶她好如妙妥妨妮妳妹妻姆姊-始姐-姑姓-委姿威娃娘婆婚婦媒媽嫌子孔字-存孝孟季-孤孩孫學它宇-安宋-完宏宗-定宜客-室宮害家容宿寂寄密富寒寞-察寢實-寧審寫-寬寶封射將-專尊-尋對-小少尖尚尤就尺尼尾局-屁居-屆屋屏展屠層屬山岸峰島崇嵐嶺川-州巡工-巨巫差己-已巴巷市-布希帝帥師席帳帶常帽幅幕幣幫干-年幸-幹幻-幾床序底店府度-座庫庭康-庸廉廖廠廢-廣廳延-廷建弄式引弘弟弦弱張強彈彌彎形彥彩彬-彭彰-影役彼往-征待很律-後徐-徒得從復微徵德徹心必忌-忍志-忙忠快念忽怎怒怕-怖思怡急性-怨怪恆恐恢恥恨-恩恭息-恰悅悉悔悟-悠您悲悶情惑惜惠-惡惱想惹愁愈-愉意愚-愛感慈態慕慘慢-慣慧慮慰慶慾憂憐-憑憲憶憾懂應懶-懷懼戀成-戒或截戰戲戴戶房-扁扇手才打托扣扭扯批找-技抄把抓投抗-折披-抬抱抵抹抽拆拉拋拍拒拔拖招-拜括拳拼拾-拿持指按挑挖振挺捐捕捨捲捷掃授-掉掌排掛採-探接控-推措描-提插揚-換握揮援損搖搞搬-搭搶摘摩摸撐撞撥播撿擁擇擊-擋操-擎擔據擠擦擬擴擺擾攝支收改攻放-政故效敏救敗教敝敢-散敦敬整-敵數文斗料斯-新斷方於-施旁旅旋族旗既日-旦早旭昇昌明-昏易星-映春昨昭是時晚晨普-景晴晶智暑暖-暗暫暴曉曰曲更書曼曾-最會月-有朋服朗望朝期木未-本朱朵李材-村杜束杯東松-板析林果-枝架柏-某染-柔查柳校核-根格桃案桌桑梁梅條梯-械棄棋棒棚森椅植椰楊楓楚業極概榜榮構槍樂樓標樞模樣樹橋機橫檔檢欄權次欣欲欺欽-款歉歌歐歡-武歲歷-歸死殊殘段殺殼毀毅母每毒比毛毫氏民氣水永求汝江-污汪決汽沈-沉沒沖沙河油治沿況泉法泡-波泥注泰泳洋洗洛洞洩-洪洲活洽-派流浩-浪浮海消-涉涯液涵涼淑淚淡淨深混淺清減渡測港游湖湯源準溝溪-溫滄-滅滋滑滴滾-滿漂漏演漠漢漫漲漸潔潛潮澤澳激濃濟濤濫灌灣火灰災炎炮炸為烈烏烤無焦然煙煞照煩熊熟熱燃燈燒營爆爐爛爬-爭爵-父爸爺爽-爾牆-版牌牙牛牠牧物牲特牽犧犯狀狂狐狗狠狼猛-猜猶獄-獅獎獨獲獸獻玄率玉王玩玫玲珍珠班現球理琪琴瑜瑞瑪瑰環瓜瓦瓶甘甚甜生產用田-申男界留畢略番-畫異當疏疑疼病痕痛痴瘋療癡登-百的皆-皇皮盃益盛-盜盟盡監-盤目直相盼盾省眉看真-眠眼眾睛睡督瞧瞭矛矣知短石砂砍研砲破硬碎碗碟碧碩碰確碼磁磨礎礙示社祖祝-神祥票禁禍福禪禮秀-私秋科-秒秘租秤秦移稅程稍種稱稿穌-積穩究穹-空穿突窗窩窮立站竟-章童端競竹笑笛符笨第筆等筋答策算管箭箱節範篇築簡簽籃籌-籍米粉粗精糊糕糟系紀約-紅納純紙-紛素索紫累-細紹終組結絕絡給統-絲經綜綠維綱-網緊緒線緣編-緩練縣縮縱總-績繁織繞繪繳繼續缸缺罪置罰署罵罷羅羊美羞群義羽翁習翔翹翻-翼耀-老考者而-耍耐耗耳耶聊聖聚聞聯-聰聲職聽肉肚股肥肩肯育背胎胖胞胡胸能脆脫腦腰腳腿膽臉臥臨自臭至-致臺與-舊舍舒舞-舟航般船艦良色艾芬花芳若-苦英茫茶草荒荷莉-莊莎莫菜菩華菲萊萬落葉著葛蒙蒼蓋蓮蔡蔣蕭薄薦薩-薪藉藍藏藝藤-藥蘇蘭虎處虛號虧蛋蛙蜂蜜蝶融螢蟲蟹蠍蠻血行術街衛衝衡衣表袋被裁-裂裕補-裝裡製複褲西要覆見規視親覺覽觀角解觸言訂計訊討訓託-記訪設許訴註-証評詞詢試詩話-詳誇誌-認誓誕語誠誤說誰課誼調談請諒論諸諾謀謂講謝證識譜警譯-議護譽讀變讓讚谷豆豈豐象豪豬貌貓貝-貞負-貢貨貪-責貴買費-貼賀資賓賜賞賢-賤賦質賭賴賺購-賽贈贊贏赤走起超越趕趙趣趨足跌跑距跟跡路跳踏踢蹟蹤躍身躲車軌-軍軒軟較載輔-輕輛輝輩-輪輯輸轉轟辛辦辨辭辯辱-農迅迎近迪-迫述迴迷追退-送逃逆透-逐途這-逛逝速-造逢-連週-進逸逼遇遊-運遍-過道-違遙遜遠適遭遲遷-選遺避-邁還邊邏那邦邪邱郎部郭郵都鄉鄭鄰配酒酷-酸醉醒醜醫采釋-量金針釣鈴銀銘銳銷鋒鋼錄錢錦錯鍋鍵鍾鎖鎮鏡鐘鐵鑑長門閃閉開閒-間閣閱闆闊關闡防阻阿-陀附降限院-除陪陰陳陵陷-陸陽隆隊階隔際-障隨險隱隻雄-集雖雙雜雞離-難雨雪雲零-雷電需震霧露霸-霹靂靈青靖靜非靠面革鞋韓音韻響頁-頂項-順須預-頑頓頗-領頭頻顆題-額顏願類顧顯風飄飛食飯飲飽-飾餅養餐餘館首香馬駐駕駛騎騙騷驅驗驚骨體高髮鬆鬥鬧鬱鬼魂魅魔魚魯鮮鳥鳳-鳴鴻鵝鷹鹿麗麥麵麻-麼黃黎黑默點黨鼓鼠鼻齊-齋齒齡龍龜] + + + + + + + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + + + + + + + + + + + + + + + + + + + + yyyy'年'MM'月'dd'日' EEEE + + + + + yyyy'年'MM'月'dd'日' + + + + + yyyy'年'M'月'd'日' + + + + + yy'年'M'月'd'日' + + + + + + + + ahh'時'mm'分'ss'秒' z + + + + + ahh'時'mm'分'ss'秒' + + + + + ahh:mm:ss + + + + + ah:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/zh_MO.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_MO.xml --- zope3-3.4.0/src/zope/i18n/locales/data/zh_MO.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_MO.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,772 @@ + + + + + + + + + + + + 阿布哈西亞文 + 亞齊文 + 阿僑利文 + 阿當莫文 + 阿迪各文 + 阿緯斯陀文 + 南非荷蘭文 + 非閃族及非亞語言 + 阿弗里希利文 + 阿坎文 + 阿卡德文 + 阿留申文 + 阿爾岡昆文 + 阿拉貢文 + 古英文 (ca.450-1100) + 阿帕奇語言 + 阿拉米文 + 阿勞坎文 + 阿拉帕霍文 + 其他人工語言 + 阿拉瓦克文 + 阿薩姆文 + 阿斯圖里亞文 + 阿薩巴斯卡文 + 澳洲英文 + 阿法文 + 艾馬拉文 + 亞塞拜然文 + 巴什客爾文 + 班達文 + 巴米累克文 + 俾路支文 + 巴姆巴拉文 + 巴厘文 + 巴薩文 + 波羅的文(其他) + 白俄羅斯文 + 貝扎文 + 別姆巴文 + 柏柏爾文 + 保加利亞文 + 比哈爾文 + 博傑普爾文 + 比斯拉馬文 + 比科爾文 + 比尼文 + 錫克錫卡文 + 班圖文 + 藏文 + 布拉杰文 + 波士尼亞文 + 巴塔克文 + 布里阿特文 + 布吉斯文 + 加泰羅尼亞文 + 卡多文 + 中美印第安文(其他) + 巴勒比文 + 高加索文(其他) + 車臣文 + 宿務族文 + 克爾特文(其他) + 查莫洛文 + 奇布查文 + 查加文 + 處奇斯文 + 馬里文 + 契奴克文 + 喬克托文 + 奇佩瓦揚文 + 柴羅基文 + 沙伊安文 + 查米克文 + 科普特文 + 歐洲腔調和洋涇濱,源自英文的(其他) + 歐洲腔調和洋涇濱,源自法文的(其他) + 歐洲腔調和洋涇濱,源自葡萄牙文的(其他) + 克裡文 + 克里米亞半島的土耳其文;克里米亞半島的塔塔爾文 + 克里奧爾文和皮欽文 + 卡舒布文 + 庫施特語系(其他) + 楚瓦甚文 + 威爾士文 + 丹麥文 + 達科他文 + 達爾格瓦文 + 迪雅克文 + 德拉瓦 + 斯拉夫 + 多格里布文 + 丁卡文 + 多格來文 + 德拉威文(其他) + 下索布文 + 杜亞拉文 + 荷蘭,中古 (ca. 1050-1350) + 迪維西文 + 迪尤拉文 + 埃緯文 + 埃菲克文 + 古埃及文) + 艾卡朱克文 + 希臘文 + 埃蘭文 + 英文,中世紀 (1100-1500) + 世界語 + 愛沙尼亞文 + 依汪都文 + 芳族文 + 芳蒂文 + 富拉文 + 芬蘭文 + 芬蘭-烏戈爾族文(其他) + 斐濟文 + 法羅文 + 豐文 + 弗留利文 + 弗里斯蘭文 + 愛爾蘭文 + 加族文 + 加約文 + 葛巴亞文 + 蘇格蘭 - 蓋爾文 + 德國的(其他) + 吉茲文 + 吉爾伯特群島文 + 加里西亞文 + 德文, 中古全盛時期 (ca.1050-1500) + 德文,上古全盛時期 (ca.750-1050) + 岡德文 + 科隆達羅文 + 哥特文 + 哥博語 + 古希臘文 (至 1453) + 吉亞拉塔文 + 曼島文 + 圭契文 + 海達文 + 希伯來文 + 北印度文 + 希利蓋農文 + 赫馬查利文 + 赫梯文 + 孟文 + 西里莫圖土文 + 克羅埃西亞文 + 上索布文 + 海地人 + 胡帕文 + 亞美尼亞文 + 赫雷羅文 + 拉丁國際文 + 伊班文 + 印尼文 + 拉丁國際文 + 伊布文 + 四川話 + 伊喬文 + 依奴皮維克文 + 伊洛闊文 + 印度語系(其他) + 印歐語系(其他) + 印古什文 + 伊朗文 + 伊芳朗文 + 易洛魁文 + 冰島文 + 義大利文 + 因紐特文 + 邏輯文 + 猶太教-波斯文 + 猶太教-阿拉伯文 + 喬治亞文 + 卡拉卡爾帕克文 + 卡比爾文 + 卡琴文 + 卡姆巴文 + 克倫文 + 卡威文 + 卡巴爾達文 + 剛果文 + 卡西文 + 其他科伊桑文 + 和闐[與闐]文 + 吉庫尤人 + 廣亞馬文 + 哈薩克文 + 格陵蘭文 + 高棉文 + 金邦杜文 + 坎那達文 + 韓文 + 貢根文 + 科斯雷恩文 + 克佩列文 + 卡努裡文 + 卡拉柴-包爾卡爾文 + 克魯文 + 庫魯科文 + 克什米爾文 + 庫爾德文 + 庫密克文 + 庫特奈文 + 康瓦耳文 + 吉爾吉斯文 + 拉迪諾文 + 拉亨達文 + 蘭巴文 + 盧森堡文 + 立陶宛文 + 干達文 + 林堡文 + 寮國文 + 蒙古文 + 洛齊文 + 魯巴加丹加文 + 魯巴魯魯亞文 + 路易塞諾文 + 盧恩達文 + 盧奧文 + 盧晒文 + 馬都拉文 + 馬加伊文 + 邁蒂利文 + 望加錫文 + 曼丁哥文 + 南島文 + 馬賽文 + 莫克沙文 + 曼達文 + 門德文 + 馬爾加什文 + 愛爾蘭文,中古 (900-1200) + 馬紹爾文 + 米克馬克文 + 米南卡堡文 + 其他語言 + 馬其頓文 + 其他高棉語系 + 馬來亞拉姆文 + 滿族文 + 曼尼普裡文 + 馬諾波文 + 摩爾多瓦文 + 莫霍克文 + 莫西文 + 馬拉地文 + 馬來文 + 馬爾他文 + 多種語言 + 蒙達文 + 克里克文 + 馬爾尼裡文 + 緬甸文 + 馬雅文 + 厄爾茲亞文 + 諾魯文 + 納瓦特文 + 其他北美印地安文 + 拿波里文 + 挪威波克默爾文 + 北地畢列文 + 德國北部的德文; 薩克遜文 + 尼泊爾文 + 尼瓦爾文 + 恩東加文 + 尼亞斯文 + 其他尼日剛果語系 + 紐埃文 + 荷蘭文 + 新挪威文 + 諾蓋文 + 古諾爾斯文 + 南地畢列文 + 北索托文 + 努比亞文 + 納瓦約文 + 尼揚賈文 + 尼揚韋齊文 + 尼揚科萊文 + 尼奧囉文 + 尼茲馬文 + 奧西坦文 + 奧杰布瓦文 + 歐里亞文 + 奧塞提文 + 歐塞奇文 + 鄂圖曼土耳其文 (1500-1928) + 奧托米文 + 其他巴布亞諸語言 + 潘加辛文 + 巴列維文 + 潘帕嘉文 + 帕皮阿門托文 + 帛琉文 + 古波斯文 (ca.600-400 B.C.) + 其他菲律賓文 + 腓尼基文 + 巴利文 + 波蘭文 + 波那貝文 + 印度古代及中世紀之中部及北部方言 + 普羅文斯文 (to 1500) + 普什圖文 + 蓋丘亞文 + 拉賈斯坦諸文 + 復活島文 + 拉羅通加文 + 里托羅曼斯文 + 羅馬尼亞文 + 其他羅曼文 + 吉普賽文 + 盧安達文 + 桑達韋文 + 雅庫特文 + 其他南美印第安文 + 薩利甚文 + 薩瑪利亞阿拉姆文 + 撒撒克文 + 散塔利文 + 撒丁文 + 蘇丹文 + 北方薩米文 + 瑟爾卡普文 + 其他閃族語言 + 古愛爾蘭文(至 900) + 手語 + 塞爾維亞克羅埃西亞文 + 撣文 + 僧伽羅文 + 希達摩文 + 大蘇文 + 其他漢藏文 + 斯洛維尼亞文 + 其他斯拉夫文 + 薩摩亞文 + 南薩米文 + 其他薩米文 + 魯勒薩米文 + 伊納裡薩米文 + 斯科特薩米文 + 塞內加爾文 + 索尼基文 + 索馬利文 + 索格底亞納文 + 桑海文 + 阿爾巴尼亞文 + 塞爾維亞文 + 塞雷爾文 + 非洲撒哈拉沙漠邊緣地帶文 + 蘇丹文 + 蘇庫馬文 + 蘇蘇文 + 蘇美文 + 史瓦希里文 + 古敘利亞文 + 坦米爾文 + 其他泰文 + 泰盧固文 + 提姆文 + 泰雷諾文 + 泰頓文 + 提格利尼亞文 + 蒂格雷文 + 提夫文 + 土庫曼文 + 托克勞文 + 特林基特文 + 塔馬奇克文 + 突尼西亞文 + 東加文 + 湯加文(尼亞薩文) + 托比辛文 + 欽西安文 + 韃靼文 + 圖姆布卡文 + 圖皮文 + 其他阿爾泰諸文 + 吐瓦魯文 + 繁體中文 + 大溪地文 + 土凡文 + 沃蒂艾克文 + 維吾爾文 + 烏加列文 + 烏克蘭文 + 姆本杜文 + 未確定的 + 烏爾都文 + 烏茲別克文 + 越南文 + 溫達文 + 沃提克文 + 瓦隆文 + 夸基武特文 + 瓦拉莫文 + 瓦瑞文 + 瓦紹文 + 文德文 + 沃爾夫文 + 卡爾梅克文 + 班圖文 + 瑤文 + 雅浦文 + 意第緒文 + 約魯巴文 + 愛斯基摩文 + 壯文 + 薩波特克文 + 澤納加文 + 贊德文 + 祖魯文 + 祖尼文 + + + 安道爾 + 阿拉伯聯合大公國 + 安地卡及巴布達 + 安圭拉島 + 阿爾巴尼亞 + 亞美尼亞 + 荷屬安地列斯 + 南極洲 + 美屬薩摩亞群島 + 奧地利 + 澳洲 + 阿路巴 + 亞塞拜然 + 波士尼亞與赫塞格維納 + 巴貝多 + 孟加拉 + 比利時 + 布基納法索 + 保加利亞 + 浦隆地 + 貝南 + 百慕達 + 汶萊 + 玻利維亞 + 巴哈馬 + 布威島 + 波札那 + 白俄羅斯 + 貝里斯 + 可可斯群島 + 剛果民主共和國 + 中非共和國 + 剛果 + 科特迪瓦 + 庫克群島 + 喀麥隆 + 中華人民共和國 + 哥倫比亞 + 哥斯大黎加 + 維德角 + 聖誕島 + 賽普勒斯 + 捷克共和國 + 德國 + 吉布地 + 丹麥 + 多明尼加 + 多明尼加共和國 + 阿爾及利亞 + 厄瓜多爾 + 愛沙尼亞 + 厄利垂亞 + 衣索比亞 + 芬蘭 + 斐濟 + 福克蘭群島 + 密克羅尼西亞群島 + 法羅群島 + 法國 + 加彭 + 英國 + 格瑞納達 + 喬治亞共和國 + 法屬圭亞那 + 迦納 + 直布羅陀 + 格陵蘭 + 甘比亞 + 幾內亞 + 哥德普洛 + 赤道幾內亞 + 希臘 + 南喬治亞與南三明治群島 + 瓜地馬拉 + 關島 + 幾內亞比索 + 蓋亞納 + 中華人民共和國香港特別行政區 + 赫德與麥克當諾群島 + 宏都拉斯 + 克羅埃西亞 + 印尼 + 愛爾蘭 + 英屬印度洋領土 + 冰島 + 義大利 + 牙買加 + 約旦 + 肯亞 + 吉爾吉斯 + 高棉 + 吉里巴斯 + 科摩羅群島 + 聖克里斯多福及尼維斯 + 北韓 + 南韓 + 開曼群島 + 哈薩克 + 寮國 + 聖露西亞 + 列支敦斯登 + 斯里蘭卡 + 賴比瑞亞 + 賴索扥 + 盧森堡 + 拉脫維亞 + 利比亞 + 摩納哥 + 摩爾多瓦 + 馬達加斯加 + 馬紹爾群島 + 馬其頓 + 馬利 + 緬甸 + 澳門特別行政區 + 北馬里安納 + 馬丁尼克島 + 茅利塔尼亞 + 蒙特色拉特島 + 馬爾他 + 模里西斯 + 馬爾地夫 + 馬拉威 + 馬來西亞 + 莫三比克 + 納米比亞 + 新喀里多尼亞群島 + 尼日 + 諾福克島 + 奈及利亞 + 荷蘭 + 尼泊爾 + 諾魯 + 紐威島 + 紐西蘭 + 阿曼王國 + 巴拿馬 + 秘魯 + 法屬玻里尼西亞 + 巴布亞紐幾內亞 + 菲律賓 + 波蘭 + 聖彼德與密啟崙 + 皮特康 + 玻多黎克 + 巴勒斯坦 + 帛琉 + 卡達 + 留尼旺 + 羅馬尼亞 + 俄羅斯 + 盧安達 + 沙烏地阿拉伯 + 索羅門群島 + 塞席爾 + 蘇丹 + 聖赫勒拿島 + 斯洛維尼亞 + 冷岸及央麥恩群島 + 獅子山 + 聖馬利諾 + 塞內加爾 + 索馬利亞 + 塞爾維亞 + 蘇利南 + 聖多美及普林西比 + 薩爾瓦多 + 敘利亞 + 史瓦濟蘭 + 土克斯及開科斯群島 + 查德 + 法國南屬地 + 多哥共和國 + 泰國 + 塔吉克 + 托克勞群島 + 東帝文 + 土庫曼 + 突尼西亞 + 東加 + 千里達及托巴哥 + 吐瓦魯 + 臺灣 + 坦尚尼亞 + 烏克蘭 + 烏干達 + 美屬邊疆群島 + 美國 + 烏拉圭 + 烏茲別克 + 梵帝岡 + 聖文森及格瑞那丁 + 委內瑞拉 + 英屬維爾京群島 + 美屬維爾京群島 + 萬那杜 + 瓦利斯和福杜納群島 + 薩摩亞群島 + 葉門 + 馬約特 + 尚比亞 + 辛巴威 + + + 已修訂 + + + 佛教曆法 + 農曆 + 公曆 + 希伯來曆法 + 伊斯蘭曆法 + 伊斯蘭城市曆法 + 日本曆法 + 直接順序 + 電話簿順序 + 拼音順序 + 筆劃顺序 + 傳統曆法 + + + + [一-丁七丈-不且世丙丟並中串丸-丹主乃久么之乎-乏乖乘-乙九也乾亂了予事-二于云-互五-井些亞亡交亦亨享-京亮人什-仁仇今-介仍仔他付-仙代-以仰仲件任份企伊伍休伙伯-估伴伸似但佈位-住佔-何余佛-作你佩佳使來例供依侯侵便係-促俊俗保俠-信修俱個倍們-倒候-倚借倫值假偉偏做停健側-偷傑備傢傲-傳傷傻傾僅像僑價儀億儒儘優允元-充兇-光克免兒兔入內-兩八-兮共兵-典兼冊再冒冠冬冰冷准凌凝凡凰-凱出函刀分-切刊列初判-別利-刪到制-刷刺-刻則前剛剩-剪副割創劃劇劉劍力功-加助-劫勁勇勉勒動務勝-勞勢勤勵勸勿包化-北區-十千升-午半卒協南博卡印-危即卷卻厚原厭厲去參又及-友反叔取-受口-另叫-叭可-台史-右司吃-各合-吊同-后吐-向君吝吟否-吧含吳吵吸-吹吾呀呂呆告呢周味呵呼-命和咖咦-咧咪咬咱哀-品哇-哉哎員哥-哦哩-哪哭哲唉唐唬售-唯唱唷-唸商啊問啟啡啥-啦啪喂善喇喊喔喜-喝喬單喵嗎嗚嗨嗯嘆嘉嘗嘛嘴嘻嘿器噴嚇嚴囉四回因困固圈國圍園-圓圖團土在地圾址均坐坡坤坦坪垃型城域執培-基堂堅-堆堪報場塊塔塗塞填塵境增墨墮壁壓壘壞壢士壯壽夏夕-外多夜夠夢夥大天-夫央失夾奇-奉奏契奔套奧奪奮女奶她好如妙妥妨妮妳妹妻姆姊-始姐-姑姓-委姿威娃娘婆婚婦媒媽嫌子孔字-存孝孟季-孤孩孫學它宇-安宋-完宏宗-定宜客-室宮害家容宿寂寄密富寒寞-察寢實-寧審寫-寬寶封射將-專尊-尋對-小少尖尚尤就尺尼尾局-屁居-屆屋屏展屠層屬山岸峰島崇嵐嶺川-州巡工-巨巫差己-已巴巷市-布希帝帥師席帳帶常帽幅幕幣幫干-年幸-幹幻-幾床序底店府度-座庫庭康-庸廉廖廠廢-廣廳延-廷建弄式引弘弟弦弱張強彈彌彎形彥彩彬-彭彰-影役彼往-征待很律-後徐-徒得從復微徵德徹心必忌-忍志-忙忠快念忽怎怒怕-怖思怡急性-怨怪恆恐恢恥恨-恩恭息-恰悅悉悔悟-悠您悲悶情惑惜惠-惡惱想惹愁愈-愉意愚-愛感慈態慕慘慢-慣慧慮慰慶慾憂憐-憑憲憶憾懂應懶-懷懼戀成-戒或截戰戲戴戶房-扁扇手才打托扣扭扯批找-技抄把抓投抗-折披-抬抱抵抹抽拆拉拋拍拒拔拖招-拜括拳拼拾-拿持指按挑挖振挺捐捕捨捲捷掃授-掉掌排掛採-探接控-推措描-提插揚-換握揮援損搖搞搬-搭搶摘摩摸撐撞撥播撿擁擇擊-擋操-擎擔據擠擦擬擴擺擾攝支收改攻放-政故效敏救敗教敝敢-散敦敬整-敵數文斗料斯-新斷方於-施旁旅旋族旗既日-旦早旭昇昌明-昏易星-映春昨昭是時晚晨普-景晴晶智暑暖-暗暫暴曉曰曲更書曼曾-最會月-有朋服朗望朝期木未-本朱朵李材-村杜束杯東松-板析林果-枝架柏-某染-柔查柳校核-根格桃案桌桑梁梅條梯-械棄棋棒棚森椅植椰楊楓楚業極概榜榮構槍樂樓標樞模樣樹橋機橫檔檢欄權次欣欲欺欽-款歉歌歐歡-武歲歷-歸死殊殘段殺殼毀毅母每毒比毛毫氏民氣水永求汝江-污汪決汽沈-沉沒沖沙河油治沿況泉法泡-波泥注泰泳洋洗洛洞洩-洪洲活洽-派流浩-浪浮海消-涉涯液涵涼淑淚淡淨深混淺清減渡測港游湖湯源準溝溪-溫滄-滅滋滑滴滾-滿漂漏演漠漢漫漲漸潔潛潮澤澳激濃濟濤濫灌灣火灰災炎炮炸為烈烏烤無焦然煙煞照煩熊熟熱燃燈燒營爆爐爛爬-爭爵-父爸爺爽-爾牆-版牌牙牛牠牧物牲特牽犧犯狀狂狐狗狠狼猛-猜猶獄-獅獎獨獲獸獻玄率玉王玩玫玲珍珠班現球理琪琴瑜瑞瑪瑰環瓜瓦瓶甘甚甜生產用田-申男界留畢略番-畫異當疏疑疼病痕痛痴瘋療癡登-百的皆-皇皮盃益盛-盜盟盡監-盤目直相盼盾省眉看真-眠眼眾睛睡督瞧瞭矛矣知短石砂砍研砲破硬碎碗碟碧碩碰確碼磁磨礎礙示社祖祝-神祥票禁禍福禪禮秀-私秋科-秒秘租秤秦移稅程稍種稱稿穌-積穩究穹-空穿突窗窩窮立站竟-章童端競竹笑笛符笨第筆等筋答策算管箭箱節範篇築簡簽籃籌-籍米粉粗精糊糕糟系紀約-紅納純紙-紛素索紫累-細紹終組結絕絡給統-絲經綜綠維綱-網緊緒線緣編-緩練縣縮縱總-績繁織繞繪繳繼續缸缺罪置罰署罵罷羅羊美羞群義羽翁習翔翹翻-翼耀-老考者而-耍耐耗耳耶聊聖聚聞聯-聰聲職聽肉肚股肥肩肯育背胎胖胞胡胸能脆脫腦腰腳腿膽臉臥臨自臭至-致臺與-舊舍舒舞-舟航般船艦良色艾芬花芳若-苦英茫茶草荒荷莉-莊莎莫菜菩華菲萊萬落葉著葛蒙蒼蓋蓮蔡蔣蕭薄薦薩-薪藉藍藏藝藤-藥蘇蘭虎處虛號虧蛋蛙蜂蜜蝶融螢蟲蟹蠍蠻血行術街衛衝衡衣表袋被裁-裂裕補-裝裡製複褲西要覆見規視親覺覽觀角解觸言訂計訊討訓託-記訪設許訴註-証評詞詢試詩話-詳誇誌-認誓誕語誠誤說誰課誼調談請諒論諸諾謀謂講謝證識譜警譯-議護譽讀變讓讚谷豆豈豐象豪豬貌貓貝-貞負-貢貨貪-責貴買費-貼賀資賓賜賞賢-賤賦質賭賴賺購-賽贈贊贏赤走起超越趕趙趣趨足跌跑距跟跡路跳踏踢蹟蹤躍身躲車軌-軍軒軟較載輔-輕輛輝輩-輪輯輸轉轟辛辦辨辭辯辱-農迅迎近迪-迫述迴迷追退-送逃逆透-逐途這-逛逝速-造逢-連週-進逸逼遇遊-運遍-過道-違遙遜遠適遭遲遷-選遺避-邁還邊邏那邦邪邱郎部郭郵都鄉鄭鄰配酒酷-酸醉醒醜醫采釋-量金針釣鈴銀銘銳銷鋒鋼錄錢錦錯鍋鍵鍾鎖鎮鏡鐘鐵鑑長門閃閉開閒-間閣閱闆闊關闡防阻阿-陀附降限院-除陪陰陳陵陷-陸陽隆隊階隔際-障隨險隱隻雄-集雖雙雜雞離-難雨雪雲零-雷電需震霧露霸-霹靂靈青靖靜非靠面革鞋韓音韻響頁-頂項-順須預-頑頓頗-領頭頻顆題-額顏願類顧顯風飄飛食飯飲飽-飾餅養餐餘館首香馬駐駕駛騎騙騷驅驗驚骨體高髮鬆鬥鬧鬱鬼魂魅魔魚魯鮮鳥鳳-鳴鴻鵝鷹鹿麗麥麵麻-麼黃黎黑默點黨鼓鼠鼻齊-齋齒齡龍龜] + + + + + + + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + + + + + + + + + + + + + + + + + + + + yyyy'年'MM'月'dd'日' EEEE + + + + + yyyy'年'MM'月'dd'日' + + + + + yyyy'年'M'月'd'日' + + + + + yy'年'M'月'd'日' + + + + + + + + ahh'時'mm'分'ss'秒' z + + + + + ahh'時'mm'分'ss'秒' + + + + + ahh:mm:ss + + + + + ah:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;(¤#,##0.00) + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/zh_SG.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_SG.xml --- zope3-3.4.0/src/zope/i18n/locales/data/zh_SG.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_SG.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,115 @@ + + + + + + + + + + + + 韩文 + 根 源 + 塞尔维亚克罗地亚文 + + + 澳洲 + 捷克 + 印尼 + 南韩 + 马其顿 + 纽西兰 + 沙地阿拉伯 + + + + + + + + + + dd MMMM yyyy + + + + + dd MMM yyyy + + + + + dd-MMM-yy + + + + + dd/MM/yy + + + + + + + + a hh:mm:ss + + + + + a hh:mm:ss + + + + + a hh:mm + + + + + a hh:mm + + + + + + + {1} {0} + + + + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/zh_TW.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_TW.xml --- zope3-3.4.0/src/zope/i18n/locales/data/zh_TW.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/zh_TW.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2645 @@ + + + + + + + + + + + + 阿布哈西亞文 + 亞齊文 + 阿僑利文 + 阿當莫文 + 阿迪各文 + 阿緯斯陀文 + 南非荷蘭文 + 非閃族及非亞語言 + 阿弗里希利文 + 阿坎文 + 阿卡德文 + 阿留申文 + 阿爾岡昆文 + 阿拉貢文 + 古英文 (ca.450-1100) + 阿帕奇語言 + 阿拉米文 + 阿勞坎文 + 阿拉帕霍文 + 其他人工語言 + 阿拉瓦克文 + 阿薩姆文 + 阿斯圖里亞文 + 阿薩巴斯卡文 + 澳洲英文 + 阿法文 + 艾馬拉文 + 亞塞拜然文 + 巴什客爾文 + 班達文 + 巴米累克文 + 俾路支文 + 巴姆巴拉文 + 巴厘文 + 巴薩文 + 波羅的文(其他) + 白俄羅斯文 + 貝扎文 + 別姆巴文 + 柏柏爾文 + 保加利亞文 + 比哈爾文 + 博傑普爾文 + 比斯拉馬文 + 比科爾文 + 比尼文 + 錫克錫卡文 + 班圖文 + 藏文 + 布拉杰文 + 波士尼亞文 + 巴塔克文 + 布里阿特文 + 布吉斯文 + 加泰羅尼亞文 + 卡多文 + 中美印第安文(其他) + 巴勒比文 + 高加索文(其他) + 車臣文 + 宿務族文 + 克爾特文(其他) + 查莫洛文 + 奇布查文 + 查加文 + 處奇斯文 + 馬里文 + 契奴克文 + 喬克托文 + 奇佩瓦揚文 + 柴羅基文 + 沙伊安文 + 查米克文 + 科普特文 + 歐洲腔調和洋涇濱,源自英文的(其他) + 歐洲腔調和洋涇濱,源自法文的(其他) + 歐洲腔調和洋涇濱,源自葡萄牙文的(其他) + 克裡文 + 克里米亞半島的土耳其文;克里米亞半島的塔塔爾文 + 克里奧爾文和皮欽文 + 卡舒布文 + 庫施特語系(其他) + 楚瓦甚文 + 威爾士文 + 丹麥文 + 達科他文 + 達爾格瓦文 + 迪雅克文 + 德拉瓦 + 斯拉夫 + 多格里布文 + 丁卡文 + 多格來文 + 德拉威文(其他) + 下索布文 + 杜亞拉文 + 荷蘭,中古 (ca. 1050-1350) + 迪維西文 + 迪尤拉文 + 埃緯文 + 埃菲克文 + 古埃及文) + 艾卡朱克文 + 希臘文 + 埃蘭文 + 英文,中世紀 (1100-1500) + 世界語 + 愛沙尼亞文 + 依汪都文 + 芳族文 + 芳蒂文 + 富拉文 + 芬蘭文 + 芬蘭-烏戈爾族文(其他) + 斐濟文 + 法羅文 + 豐文 + 弗留利文 + 弗里斯蘭文 + 愛爾蘭文 + 加族文 + 加約文 + 葛巴亞文 + 蘇格蘭 - 蓋爾文 + 德國的(其他) + 吉茲文 + 吉爾伯特群島文 + 加里西亞文 + 德文, 中古全盛時期 (ca.1050-1500) + 德文,上古全盛時期 (ca.750-1050) + 岡德文 + 科隆達羅文 + 哥特文 + 哥博語 + 古希臘文 (至 1453) + 吉亞拉塔文 + 曼島文 + 圭契文 + 海達文 + 希伯來文 + 北印度文 + 希利蓋農文 + 赫馬查利文 + 赫梯文 + 孟文 + 西里莫圖土文 + 克羅埃西亞文 + 上索布文 + 海地人 + 胡帕文 + 亞美尼亞文 + 赫雷羅文 + 拉丁國際文 + 伊班文 + 印尼文 + 拉丁國際文 + 伊布文 + 四川話 + 伊喬文 + 依奴皮維克文 + 伊洛闊文 + 印度語系(其他) + 印歐語系(其他) + 印古什文 + 伊朗文 + 伊芳朗文 + 易洛魁文 + 冰島文 + 義大利文 + 因紐特文 + 邏輯文 + 猶太教-波斯文 + 猶太教-阿拉伯文 + 喬治亞文 + 卡拉卡爾帕克文 + 卡比爾文 + 卡琴文 + 卡姆巴文 + 克倫文 + 卡威文 + 卡巴爾達文 + 剛果文 + 卡西文 + 其他科伊桑文 + 和闐[與闐]文 + 吉庫尤人 + 廣亞馬文 + 哈薩克文 + 格陵蘭文 + 高棉文 + 金邦杜文 + 坎那達文 + 韓文 + 貢根文 + 科斯雷恩文 + 克佩列文 + 卡努裡文 + 卡拉柴-包爾卡爾文 + 克魯文 + 庫魯科文 + 克什米爾文 + 庫爾德文 + 庫密克文 + 庫特奈文 + 康瓦耳文 + 吉爾吉斯文 + 拉迪諾文 + 拉亨達文 + 蘭巴文 + 盧森堡文 + 立陶宛文 + 干達文 + 林堡文 + 寮國文 + 蒙古文 + 洛齊文 + 魯巴加丹加文 + 魯巴魯魯亞文 + 路易塞諾文 + 盧恩達文 + 盧奧文 + 盧晒文 + 馬都拉文 + 馬加伊文 + 邁蒂利文 + 望加錫文 + 曼丁哥文 + 南島文 + 馬賽文 + 莫克沙文 + 曼達文 + 門德文 + 馬爾加什文 + 愛爾蘭文,中古 (900-1200) + 馬紹爾文 + 米克馬克文 + 米南卡堡文 + 其他語言 + 馬其頓文 + 其他高棉語系 + 馬來亞拉姆文 + 滿族文 + 曼尼普裡文 + 馬諾波文 + 摩爾多瓦文 + 莫霍克文 + 莫西文 + 馬拉地文 + 馬來文 + 馬爾他文 + 多種語言 + 蒙達文 + 克里克文 + 馬爾尼裡文 + 緬甸文 + 馬雅文 + 厄爾茲亞文 + 諾魯文 + 納瓦特文 + 其他北美印地安文 + 拿波里文 + 挪威波克默爾文 + 北地畢列文 + 德國北部的德文; 薩克遜文 + 尼泊爾文 + 尼瓦爾文 + 恩東加文 + 尼亞斯文 + 其他尼日剛果語系 + 紐埃文 + 荷蘭文 + 新挪威文 + 諾蓋文 + 古諾爾斯文 + 南地畢列文 + 北索托文 + 努比亞文 + 納瓦約文 + 尼揚賈文 + 尼揚韋齊文 + 尼揚科萊文 + 尼奧囉文 + 尼茲馬文 + 奧西坦文 + 奧杰布瓦文 + 歐里亞文 + 奧塞提文 + 歐塞奇文 + 鄂圖曼土耳其文 (1500-1928) + 奧托米文 + 其他巴布亞諸語言 + 潘加辛文 + 巴列維文 + 潘帕嘉文 + 帕皮阿門托文 + 帛琉文 + 古波斯文 (ca.600-400 B.C.) + 其他菲律賓文 + 腓尼基文 + 巴利文 + 波蘭文 + 波那貝文 + 印度古代及中世紀之中部及北部方言 + 普羅文斯文 (to 1500) + 普什圖文 + 蓋丘亞文 + 拉賈斯坦諸文 + 復活島文 + 拉羅通加文 + 里托羅曼斯文 + 羅馬尼亞文 + 其他羅曼文 + 吉普賽文 + 盧安達文 + 桑達韋文 + 雅庫特文 + 其他南美印第安文 + 薩利甚文 + 薩瑪利亞阿拉姆文 + 撒撒克文 + 散塔利文 + 撒丁文 + 蘇丹文 + 北方薩米文 + 瑟爾卡普文 + 其他閃族語言 + 古愛爾蘭文(至 900) + 手語 + 塞爾維亞克羅埃西亞文 + 撣文 + 僧伽羅文 + 希達摩文 + 大蘇文 + 其他漢藏文 + 斯洛維尼亞文 + 其他斯拉夫文 + 薩摩亞文 + 南薩米文 + 其他薩米文 + 魯勒薩米文 + 伊納裡薩米文 + 斯科特薩米文 + 塞內加爾文 + 索尼基文 + 索馬利文 + 索格底亞納文 + 桑海文 + 阿爾巴尼亞文 + 塞爾維亞文 + 塞雷爾文 + 非洲撒哈拉沙漠邊緣地帶文 + 蘇丹文 + 蘇庫馬文 + 蘇蘇文 + 蘇美文 + 史瓦希里文 + 古敘利亞文 + 坦米爾文 + 其他泰文 + 泰盧固文 + 提姆文 + 泰雷諾文 + 泰頓文 + 提格利尼亞文 + 蒂格雷文 + 提夫文 + 土庫曼文 + 托克勞文 + 特林基特文 + 塔馬奇克文 + 突尼西亞文 + 東加文 + 湯加文(尼亞薩文) + 托比辛文 + 欽西安文 + 韃靼文 + 圖姆布卡文 + 圖皮文 + 其他阿爾泰諸文 + 吐瓦魯文 + 繁體中文 + 大溪地文 + 土凡文 + 沃蒂艾克文 + 維吾爾文 + 烏加列文 + 烏克蘭文 + 姆本杜文 + 未確定的 + 烏爾都文 + 烏茲別克文 + 越南文 + 溫達文 + 沃提克文 + 瓦隆文 + 夸基武特文 + 瓦拉莫文 + 瓦瑞文 + 瓦紹文 + 文德文 + 沃爾夫文 + 卡爾梅克文 + 班圖文 + 瑤文 + 雅浦文 + 意第緒文 + 約魯巴文 + 愛斯基摩文 + 壯文 + 薩波特克文 + 澤納加文 + 贊德文 + 祖魯文 + 祖尼文 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 安道爾 + 阿拉伯聯合大公國 + 安地卡及巴布達 + 安圭拉島 + 阿爾巴尼亞 + 亞美尼亞 + 荷屬安地列斯 + 南極洲 + 美屬薩摩亞群島 + 奧地利 + 澳洲 + 阿路巴 + 亞塞拜然 + 波士尼亞與赫塞格維納 + 巴貝多 + 孟加拉 + 比利時 + 布基納法索 + 保加利亞 + 浦隆地 + 貝南 + 百慕達 + 汶萊 + 玻利維亞 + 巴哈馬 + 布威島 + 波札那 + 白俄羅斯 + 貝里斯 + 可可斯群島 + 剛果民主共和國 + 中非共和國 + 剛果 + 科特迪瓦 + 庫克群島 + 喀麥隆 + 中華人民共和國 + 哥倫比亞 + 哥斯大黎加 + 維德角 + 聖誕島 + 賽普勒斯 + 捷克共和國 + 德國 + 吉布地 + 丹麥 + 多明尼加 + 多明尼加共和國 + 阿爾及利亞 + 厄瓜多爾 + 愛沙尼亞 + 厄利垂亞 + 衣索比亞 + 芬蘭 + 斐濟 + 福克蘭群島 + 密克羅尼西亞群島 + 法羅群島 + 法國 + 加彭 + 英國 + 格瑞納達 + 喬治亞共和國 + 法屬圭亞那 + 迦納 + 直布羅陀 + 格陵蘭 + 甘比亞 + 幾內亞 + 哥德普洛 + 赤道幾內亞 + 希臘 + 南喬治亞與南三明治群島 + 瓜地馬拉 + 關島 + 幾內亞比索 + 蓋亞納 + 中華人民共和國香港特別行政區 + 赫德與麥克當諾群島 + 宏都拉斯 + 克羅埃西亞 + 印尼 + 愛爾蘭 + 英屬印度洋領土 + 冰島 + 義大利 + 牙買加 + 約旦 + 肯亞 + 吉爾吉斯 + 高棉 + 吉里巴斯 + 科摩羅群島 + 聖克里斯多福及尼維斯 + 北韓 + 南韓 + 開曼群島 + 哈薩克 + 寮國 + 聖露西亞 + 列支敦斯登 + 斯里蘭卡 + 賴比瑞亞 + 賴索扥 + 盧森堡 + 拉脫維亞 + 利比亞 + 摩納哥 + 摩爾多瓦 + 馬達加斯加 + 馬紹爾群島 + 馬其頓 + 馬利 + 緬甸 + 澳門特別行政區 + 北馬里安納 + 馬丁尼克島 + 茅利塔尼亞 + 蒙特色拉特島 + 馬爾他 + 模里西斯 + 馬爾地夫 + 馬拉威 + 馬來西亞 + 莫三比克 + 納米比亞 + 新喀里多尼亞群島 + 尼日 + 諾福克島 + 奈及利亞 + 荷蘭 + 尼泊爾 + 諾魯 + 紐威島 + 紐西蘭 + 阿曼王國 + 巴拿馬 + 秘魯 + 法屬玻里尼西亞 + 巴布亞紐幾內亞 + 菲律賓 + 波蘭 + 聖彼德與密啟崙 + 皮特康 + 玻多黎克 + 巴勒斯坦 + 帛琉 + 卡達 + 留尼旺 + 羅馬尼亞 + 俄羅斯 + 盧安達 + 沙烏地阿拉伯 + 索羅門群島 + 塞席爾 + 蘇丹 + 聖赫勒拿島 + 斯洛維尼亞 + 冷岸及央麥恩群島 + 獅子山 + 聖馬利諾 + 塞內加爾 + 索馬利亞 + 塞爾維亞 + 蘇利南 + 聖多美及普林西比 + 薩爾瓦多 + 敘利亞 + 史瓦濟蘭 + 土克斯及開科斯群島 + 查德 + 法國南屬地 + 多哥共和國 + 泰國 + 塔吉克 + 托克勞群島 + 東帝文 + 土庫曼 + 突尼西亞 + 東加 + 千里達及托巴哥 + 吐瓦魯 + 臺灣 + 坦尚尼亞 + 烏克蘭 + 烏干達 + 美屬邊疆群島 + 美國 + 烏拉圭 + 烏茲別克 + 梵帝岡 + 聖文森及格瑞那丁 + 委內瑞拉 + 英屬維爾京群島 + 美屬維爾京群島 + 萬那杜 + 瓦利斯和福杜納群島 + 薩摩亞群島 + 葉門 + 馬約特 + 尚比亞 + 辛巴威 + + + 已修訂 + + + 曆法 + 校對 + 貨幣 + + + 佛教曆法 + 農曆 + 公曆 + 希伯來曆法 + 伊斯蘭曆法 + 伊斯蘭城市曆法 + 日本曆法 + 直接順序 + 電話簿順序 + 拼音順序 + 筆劃顺序 + 傳統曆法 + + + + [一-丁七丈-不且世丙丟並中串丸-丹主乃久么之乎-乏乖乘-乙九也乾亂了予事-二于云-互五-井些亞亡交亦亨享-京亮人什-仁仇今-介仍仔他付-仙代-以仰仲件任份企伊伍休伙伯-估伴伸似但佈位-住佔-何余佛-作你佩佳使來例供依侯侵便係-促俊俗保俠-信修俱個倍們-倒候-倚借倫值假偉偏做停健側-偷傑備傢傲-傳傷傻傾僅像僑價儀億儒儘優允元-充兇-光克免兒兔入內-兩八-兮共兵-典兼冊再冒冠冬冰冷准凌凝凡凰-凱出函刀分-切刊列初判-別利-刪到制-刷刺-刻則前剛剩-剪副割創劃劇劉劍力功-加助-劫勁勇勉勒動務勝-勞勢勤勵勸勿包化-北區-十千升-午半卒協南博卡印-危即卷卻厚原厭厲去參又及-友反叔取-受口-另叫-叭可-台史-右司吃-各合-吊同-后吐-向君吝吟否-吧含吳吵吸-吹吾呀呂呆告呢周味呵呼-命和咖咦-咧咪咬咱哀-品哇-哉哎員哥-哦哩-哪哭哲唉唐唬售-唯唱唷-唸商啊問啟啡啥-啦啪喂善喇喊喔喜-喝喬單喵嗎嗚嗨嗯嘆嘉嘗嘛嘴嘻嘿器噴嚇嚴囉四回因困固圈國圍園-圓圖團土在地圾址均坐坡坤坦坪垃型城域執培-基堂堅-堆堪報場塊塔塗塞填塵境增墨墮壁壓壘壞壢士壯壽夏夕-外多夜夠夢夥大天-夫央失夾奇-奉奏契奔套奧奪奮女奶她好如妙妥妨妮妳妹妻姆姊-始姐-姑姓-委姿威娃娘婆婚婦媒媽嫌子孔字-存孝孟季-孤孩孫學它宇-安宋-完宏宗-定宜客-室宮害家容宿寂寄密富寒寞-察寢實-寧審寫-寬寶封射將-專尊-尋對-小少尖尚尤就尺尼尾局-屁居-屆屋屏展屠層屬山岸峰島崇嵐嶺川-州巡工-巨巫差己-已巴巷市-布希帝帥師席帳帶常帽幅幕幣幫干-年幸-幹幻-幾床序底店府度-座庫庭康-庸廉廖廠廢-廣廳延-廷建弄式引弘弟弦弱張強彈彌彎形彥彩彬-彭彰-影役彼往-征待很律-後徐-徒得從復微徵德徹心必忌-忍志-忙忠快念忽怎怒怕-怖思怡急性-怨怪恆恐恢恥恨-恩恭息-恰悅悉悔悟-悠您悲悶情惑惜惠-惡惱想惹愁愈-愉意愚-愛感慈態慕慘慢-慣慧慮慰慶慾憂憐-憑憲憶憾懂應懶-懷懼戀成-戒或截戰戲戴戶房-扁扇手才打托扣扭扯批找-技抄把抓投抗-折披-抬抱抵抹抽拆拉拋拍拒拔拖招-拜括拳拼拾-拿持指按挑挖振挺捐捕捨捲捷掃授-掉掌排掛採-探接控-推措描-提插揚-換握揮援損搖搞搬-搭搶摘摩摸撐撞撥播撿擁擇擊-擋操-擎擔據擠擦擬擴擺擾攝支收改攻放-政故效敏救敗教敝敢-散敦敬整-敵數文斗料斯-新斷方於-施旁旅旋族旗既日-旦早旭昇昌明-昏易星-映春昨昭是時晚晨普-景晴晶智暑暖-暗暫暴曉曰曲更書曼曾-最會月-有朋服朗望朝期木未-本朱朵李材-村杜束杯東松-板析林果-枝架柏-某染-柔查柳校核-根格桃案桌桑梁梅條梯-械棄棋棒棚森椅植椰楊楓楚業極概榜榮構槍樂樓標樞模樣樹橋機橫檔檢欄權次欣欲欺欽-款歉歌歐歡-武歲歷-歸死殊殘段殺殼毀毅母每毒比毛毫氏民氣水永求汝江-污汪決汽沈-沉沒沖沙河油治沿況泉法泡-波泥注泰泳洋洗洛洞洩-洪洲活洽-派流浩-浪浮海消-涉涯液涵涼淑淚淡淨深混淺清減渡測港游湖湯源準溝溪-溫滄-滅滋滑滴滾-滿漂漏演漠漢漫漲漸潔潛潮澤澳激濃濟濤濫灌灣火灰災炎炮炸為烈烏烤無焦然煙煞照煩熊熟熱燃燈燒營爆爐爛爬-爭爵-父爸爺爽-爾牆-版牌牙牛牠牧物牲特牽犧犯狀狂狐狗狠狼猛-猜猶獄-獅獎獨獲獸獻玄率玉王玩玫玲珍珠班現球理琪琴瑜瑞瑪瑰環瓜瓦瓶甘甚甜生產用田-申男界留畢略番-畫異當疏疑疼病痕痛痴瘋療癡登-百的皆-皇皮盃益盛-盜盟盡監-盤目直相盼盾省眉看真-眠眼眾睛睡督瞧瞭矛矣知短石砂砍研砲破硬碎碗碟碧碩碰確碼磁磨礎礙示社祖祝-神祥票禁禍福禪禮秀-私秋科-秒秘租秤秦移稅程稍種稱稿穌-積穩究穹-空穿突窗窩窮立站竟-章童端競竹笑笛符笨第筆等筋答策算管箭箱節範篇築簡簽籃籌-籍米粉粗精糊糕糟系紀約-紅納純紙-紛素索紫累-細紹終組結絕絡給統-絲經綜綠維綱-網緊緒線緣編-緩練縣縮縱總-績繁織繞繪繳繼續缸缺罪置罰署罵罷羅羊美羞群義羽翁習翔翹翻-翼耀-老考者而-耍耐耗耳耶聊聖聚聞聯-聰聲職聽肉肚股肥肩肯育背胎胖胞胡胸能脆脫腦腰腳腿膽臉臥臨自臭至-致臺與-舊舍舒舞-舟航般船艦良色艾芬花芳若-苦英茫茶草荒荷莉-莊莎莫菜菩華菲萊萬落葉著葛蒙蒼蓋蓮蔡蔣蕭薄薦薩-薪藉藍藏藝藤-藥蘇蘭虎處虛號虧蛋蛙蜂蜜蝶融螢蟲蟹蠍蠻血行術街衛衝衡衣表袋被裁-裂裕補-裝裡製複褲西要覆見規視親覺覽觀角解觸言訂計訊討訓託-記訪設許訴註-証評詞詢試詩話-詳誇誌-認誓誕語誠誤說誰課誼調談請諒論諸諾謀謂講謝證識譜警譯-議護譽讀變讓讚谷豆豈豐象豪豬貌貓貝-貞負-貢貨貪-責貴買費-貼賀資賓賜賞賢-賤賦質賭賴賺購-賽贈贊贏赤走起超越趕趙趣趨足跌跑距跟跡路跳踏踢蹟蹤躍身躲車軌-軍軒軟較載輔-輕輛輝輩-輪輯輸轉轟辛辦辨辭辯辱-農迅迎近迪-迫述迴迷追退-送逃逆透-逐途這-逛逝速-造逢-連週-進逸逼遇遊-運遍-過道-違遙遜遠適遭遲遷-選遺避-邁還邊邏那邦邪邱郎部郭郵都鄉鄭鄰配酒酷-酸醉醒醜醫采釋-量金針釣鈴銀銘銳銷鋒鋼錄錢錦錯鍋鍵鍾鎖鎮鏡鐘鐵鑑長門閃閉開閒-間閣閱闆闊關闡防阻阿-陀附降限院-除陪陰陳陵陷-陸陽隆隊階隔際-障隨險隱隻雄-集雖雙雜雞離-難雨雪雲零-雷電需震霧露霸-霹靂靈青靖靜非靠面革鞋韓音韻響頁-頂項-順須預-頑頓頗-領頭頻顆題-額顏願類顧顯風飄飛食飯飲飽-飾餅養餐餘館首香馬駐駕駛騎騙騷驅驗驚骨體高髮鬆鬥鬧鬱鬼魂魅魔魚魯鮮鳥鳳-鳴鴻鵝鷹鹿麗麥麵麻-麼黃黎黑默點黨鼓鼠鼻齊-齋齒齡龍龜] + + + + + + + 民國前 + 民國 + + + + + + + yyyy'年'M'月'd'日'EEEE + + + + + yyyy'年'M'月'd'日' + + + + + yyyy/M/d + + + + + yyyy/M/d + + + + + + + + ahh'時'mm'分'ss'秒' z + + + + + ahh'時'mm'分'ss'秒' + + + + + a h:mm:ss + + + + + a h:mm + + + + + + + {1} {0} + + + + + + + + + 太平洋標準時間 + 太平洋日光節約時間 + + + PST + PDT + + 洛杉磯 + + + + 太平洋標準時間 + 太平洋日光節約時間 + + + PST + PDT + + 洛杉磯 + + + + 山區標準時間 + 山區日光節約時間 + + + MST + MDT + + 丹佛 + + + + 山區標準時間 + 山區日光節約時間 + + + MST + MDT + + 丹佛 + + + + 山區標準時間 + 山區標準時間 + + + MST + MST + + 鳳凰城 + + + + 山區標準時間 + 山區標準時間 + + + MST + MST + + 鳳凰城 + + + + 中部標準時間 + 中部日光節約時間 + + + CST + CDT + + 芝加哥 + + + + 中部標準時間 + 中部日光節約時間 + + + CST + CDT + + 芝加哥 + + + + 東部標準時間 + 東部日光節約時間 + + + EST + EDT + + 紐約 + + + + 東部標準時間 + 東部日光節約時間 + + + EST + EDT + + 紐約 + + + + 東部標準時間 + 東部標準時間 + + + EST + EST + + 印第安那波里斯 + + + + 東部標準時間 + 東部標準時間 + + + EST + EST + + 印第安那波里斯 + + + + 夏威夷標準時間 + 夏威夷標準時間 + + + HST + HST + + 檀香山 + + + + 夏威夷標準時間 + 夏威夷標準時間 + + + HST + HST + + 檀香山 + + + + 阿拉斯加標準時間 + 阿拉斯加日光節約時間 + + + AST + ADT + + 安克里治 + + + + 阿拉斯加標準時間 + 阿拉斯加日光節約時間 + + + AST + ADT + + 安克里治 + + + + 大西洋標準時間 + 大西洋日光節約時間 + + + AST + ADT + + 哈里法克斯 + + + + 紐芬蘭標準時間 + 紐芬蘭日光節約時間 + + + CNT + CDT + + 聖約翰 + + + + 紐芬蘭標準時間 + 紐芬蘭日光節約時間 + + + CNT + CDT + + 聖約翰 + + + + 中歐標準時間 + 中歐日光節約時間 + + + CET + CEST + + 巴黎 + + + + 中歐標準時間 + 中歐日光節約時間 + + + CET + CEST + + 巴黎 + + + + 格林威治標準時間 + 格林威治標準時間 + + + GMT + GMT + + 倫敦 + + + + 格林威治標準時間 + 格林威治標準時間 + + + GMT + GMT + + 卡薩布蘭卡 + + + + 以色列標準時間 + 以色列日光節約時間 + + + IST + IDT + + 耶路撒冷 + + + + 日本標準時間 + 日本標準時間 + + + JST + JST + + 東京 + + + + 日本標準時間 + 日本標準時間 + + + JST + JST + + 東京 + + + + 東歐標準時間 + 東歐日光節約時間 + + + EET + EEST + + 布加勒斯特 + + + + 中國標準時間 + 中國標準時間 + + + CTT + CDT + + 上海 + + + + 中國標準時間 + 中國標準時間 + + + CTT + CDT + + 上海 + + + + + + + + #,##0.###;-#,##0.### + + + + + + + #E0 + + + + + + + #,##0% + + + + + + + ¤#,##0.00;-¤#,##0.00 + + + + + + 安道爾第納爾 + ADD + + + 安道爾陪士特 + ADP + + + 阿拉伯聯合大公國迪爾汗 + AED + + + 阿富汗尼 (1927-2002) + AFA + + + 阿富汗尼 + AFN + + + 阿法爾和伊薩法郎 + AIF + + + 阿爾巴尼亞列克 (1946-1961) + ALK + + + 阿爾巴尼亞列克 + ALL + + + 阿爾巴尼亞列克幣 + ALV + + + 阿爾巴尼亞元外匯券 + ALX + + + 亞美尼亞德拉姆 + AMD + + + 荷蘭 安梯蘭 盾 + ANG + + + 安哥拉寬扎 + AOA + + + 安哥拉寬扎(1977-1990) + AOK + + + 安哥拉新寬扎 (1990-2000) + AON + + + 安哥拉新寬扎 Reajustado (1995-1999) + AOR + + + 安哥拉埃斯庫多 + AOS + + + 阿根廷奧斯特納爾 + ARA + + + 阿根廷披索 Moneda Nacional + ARM + + + 阿根廷披索(1983-1985) + ARP + + + 阿根廷披索 + ARS + + + 奧地利先令 + ATS + + + 澳幣 + AUD + + + 澳大利亞鎊 + AUP + + + 阿魯巴盾 + AWG + + + 阿塞拜彊馬特納 + AZM + + + 波士尼亞-黑塞哥維那第納爾 + BAD + + + 波士尼亞-黑塞哥維那可轉換馬克 + BAM + + + 波士尼亞-黑塞哥維那新第納爾 + BAN + + + 巴貝多元 + BBD + + + 孟加拉塔卡 + BDT + + + 比利時法郎 (可轉換) + BEC + + + 比利時法郎 + BEF + + + 比利時法郎 (金融) + BEL + + + 保加利亞硬列弗 + BGL + + + 保加利亞 社會主義列弗 + BGM + + + 保加利亞新列弗 + BGN + + + 保加利亞列弗 (1879-1952) + BGO + + + 保加利亞列弗外匯券 + BGX + + + 巴林第納爾 + BHD + + + 蒲隆地法郎 + BIF + + + 百慕達幣 + BMD + + + 百慕達鎊 + BMP + + + 汶萊元 + BND + + + 玻利維亞貨幣單位 + BOB + + + 玻利維亞舊貨幣單位 (1863-1962) + BOL + + + 玻利維亞披索 + BOP + + + 玻利維亞 幕多 + BOV + + + 巴西克魯薩多 農瓦 (1967-1986) + BRB + + + 巴西克魯賽羅 + BRC + + + 巴西克魯賽羅 (1990-1993) + BRE + + + 巴西里拉 + BRL + + + 巴西 克如爾達 農瓦 + BRN + + + 巴西克魯賽羅 + BRR + + + 巴西克魯賽羅 (1942-1967) + BRZ + + + 巴哈馬元 + BSD + + + 巴哈馬鎊 + BSP + + + 不丹努扎姆 + BTN + + + 不丹盧布 + BTR + + + 緬甸元 + BUK + + + 緬甸盧布 + BUR + + + 波札那 - 普拉 + BWP + + + 白俄羅斯新盧布 (1994-1999) + BYB + + + 白俄羅斯盧布 (1992-1994) + BYL + + + 白俄羅斯盧布 + BYR + + + 伯利茲元 + BZD + + + 英國的洪都拉斯元r + BZH + + + 加幣 + CAD + + + 剛果法郎 + CDF + + + 剛果共和國法郎 + CDG + + + 剛果扎伊爾 + CDL + + + 中非共和國西非法郎 + CFF + + + 瑞士法郎 + CHF + + + 庫克群島元 + CKD + + + 智利 康導 + CLC + + + 智利埃斯庫多 + CLE + + + 卡林油達佛曼跎 + CLF + + + 智利披索 + CLP + + + 卡麥隆西非法郎 + CMF + + + 中國人民幣元 + CNP + + + 中國美元外匯券 + CNX + + + 人民幣 + CNY + + + 哥倫比亞披索鈔 + COB + + + 剛果西非法郎 + COF + + + 哥倫比亞披索 + COP + + + 哥斯大黎加科郎 + CRC + + + 捷克克朗 + CSC + + + 捷克斯洛伐克硬克朗 + CSK + + + 古巴披索 + CUP + + + 古巴人外匯券 + CUX + + + 維德角埃斯庫多 + CVE + + + 庫拉克 盾 + CWG + + + 賽浦路斯鎊 + CYP + + + 捷克克朗 + CZK + + + 東德東德馬克 + DDM + + + 德國馬克 + DEM + + + 德國 蘇馬克Sperrmark + DES + + + 吉布地法郎 + DJF + + + 丹麥克羅納 + DKK + + + 多明尼加披索 + DOP + + + 阿爾及利亞第納爾 + DZD + + + 阿爾及利亞新法郎 + DZF + + + 阿爾及利亞法郎 Germinal + DZG + + + 厄瓜多蘇克雷 + ECS + + + 厄瓜多爾由里達瓦康斯坦 (UVC) + ECV + + + 愛沙尼亞克朗 + EEK + + + 埃及鎊 + EGP + + + 厄立特里亞納克法 + ERN + + + 西班牙陪士特 + ESP + + + 衣索比亞比爾 + ETB + + + 埃賽俄比亞元 + ETD + + + 歐元 + EUR + + + 芬蘭馬克 + FIM + + + 芬蘭馬克 (1860-1962) + FIN + + + 斐濟元 + FJD + + + 斐濟鎊 + FJP + + + 福克蘭群島鎊 + FKP + + + 法羅島克朗 + FOK + + + 法國法郎 + FRF + + + 法國法郎 捷米那/龐加萊法郎 + FRG + + + 加蓬西非法郎 + GAF + + + 英鎊 + GBP + + + 喬治 庫旁 拉里 + GEK + + + 喬治拉里 + GEL + + + 迦納仙蔕 + GHC + + + 迦納舊仙蔕 + GHO + + + 迦納鎊 + GHP + + + 迦納重新估价後的仙蔕 + GHR + + + 直布羅陀鎊 + GIP + + + 格陵蘭克羅鈉 + GLK + + + 甘比亞達拉西 + GMD + + + 岡比亞鎊 + GMP + + + 幾內亞法郎 + GNF + + + 幾內亞法郎 (1960-1972) + GNI + + + 幾內亞西里 + GNS + + + 瓜德羅普島法郎 + GPF + + + 赤道幾內亞埃奎勒 + GQE + + + 赤道幾內亞佛朗哥 + GQF + + + 赤道幾內亞比塞塔 + GQP + + + 希臘德拉克馬 + GRD + + + 希臘新德拉克馬 + GRN + + + 瓜地馬拉格查爾 + GTQ + + + 法屬圭亞那法郎圭亞那 + GUF + + + 葡屬幾內亞埃斯庫多 + GWE + + + 葡屬幾內亞米爾里斯 + GWM + + + 幾內亞披索披索 + GWP + + + 圭亞那元 + GYD + + + 港元 + HK$ + + + 洪都拉斯倫皮拉 + HNL + + + 克羅地亞第納爾 + HRD + + + 克羅地亞庫納 + HRK + + + 海地古德 + HTG + + + 匈牙利 - 福林 + HUF + + + 北愛爾蘭鎊 + IBP + + + 印度尼西亞尼可盾 + IDG + + + 印度尼西亞爪哇盧布 + IDJ + + + 印度尼西亞新盧布 + IDN + + + 印尼 - 盧布 + IDR + + + 愛爾蘭鎊 + IEP + + + 以色列謝客爾 + ILL + + + 以色列鎊 + ILP + + + 以色列新謝克爾 + ILS + + + 曼城島英鎊 + IMP + + + 印度盧布 + =0#Rs.|1#Re.|1<Rs. + + + 伊拉克第納爾 + IQD + + + 伊朗里亞爾 + IRR + + + 冰島克朗 + ISK + + + 義大利里拉 + ITL + + + 澤西鎊 + JEP + + + 牙買加元 + JMD + + + 牙買加鎊 + JMP + + + 約旦第納爾 + JOD + + + 日圓 + JP¥ + + + 肯尼亞先令 + KES + + + 吉爾吉斯索馬 + KGS + + + 柬埔寨舊瑞爾 + KHO + + + 柬埔寨瑞爾 + KHR + + + 基里巴斯元 + KID + + + 科摩羅法郎 + KMF + + + 北朝鮮人民幣 + KPP + + + 北朝鮮幣 + KPW + + + 韓國 哈瓦 + KRH + + + 南韓舊幣 + KRO + + + 韓國圜 + KRW + + + 科威特第納爾 + KWD + + + 開曼群島美元 + KYD + + + 卡扎克斯坦盧布 + KZR + + + 卡扎克斯坦坦吉 + KZT + + + 老撾 開普 + LAK + + + 黎巴嫩鎊 + LBP + + + 列支敦斯登法郎 + LIF + + + 斯里蘭卡盧布 + LKR + + + 錫蘭盧布 + LNR + + + 賴比瑞亞元 + LRD + + + 賴索托羅蒂 + LSL + + + 立陶宛里塔 + LTL + + + 立陶宛特羅 + LTT + + + 盧森堡法郎 + LUF + + + 拉脫維亞拉特銀幣 + LVL + + + 拉脫維亞盧布 + LVR + + + 利比亞英國的軍事當局里拉 + LYB + + + 利比亞第納爾 + LYD + + + 利比亞鎊 + LYP + + + 摩洛哥迪拉姆 + MAD + + + 摩洛哥法郎 + MAF + + + 摩納哥新法郎 + MCF + + + 摩納哥法郎 傑米那 + MCG + + + 摩杜雲列伊庫旁 + MDC + + + 摩杜雲列伊 + MDL + + + 摩杜雲盧布庫旁 + MDR + + + 馬達加斯加艾瑞爾 + MGA + + + 馬達加斯加法郎 + MGF + + + 馬紹爾群島美元 + MHD + + + 馬其頓第納爾 + MKD + + + 馬其頓第納爾(1992-1993) + MKN + + + 馬里法郎 + MLF + + + 緬甸元 + MMK + + + 緬甸美元外匯券 + MMX + + + 蒙古圖格里克 + MNT + + + 澳門元 + MOP + + + 馬提尼克島法郎 + MQF + + + 茅利塔尼亞烏吉亞 + MRO + + + 馬爾他里拉 + MTL + + + 馬爾他鎊 + MTP + + + 模里西斯盧布 + MUR + + + 馬爾地夫盧布 + MVP + + + 馬爾地夫海島盧非亞 + MVR + + + 馬拉維克瓦查 + MWK + + + 馬拉維鎊 + MWP + + + 墨西哥 - 披索 + MXN + + + 墨西哥銀披索 (1861-1992) + MXP + + + 墨西哥法律反轉(UDI) + MXV + + + 馬來西亞 - 林吉特 + MYR + + + 莫桑比克埃斯庫多 + MZE + + + 莫三比克梅蒂卡爾 + MZM + + + 納米比亞元 + NAD + + + 赫布里底群島 CFP 法郎 + NCF + + + 奈及利亞奈拉 + NGN + + + 奈及利亞鎊 + NGP + + + 新赫布里底群島 CFP 法郎 + NHF + + + 尼加拉瓜科多巴 + NIC + + + 尼加拉瓜金金哥多華 + NIG + + + 尼加拉瓜 金哥多華 + NIO + + + 荷蘭盾 + NLG + + + 挪威克羅納 + NOK + + + 尼泊爾盧布 + NPR + + + 紐西蘭幣 + $NZ + + + 紐西蘭鎊 + NZP + + + 阿曼里奧 + OMR + + + 阿曼里亞爾仙蔕i + OMS + + + 巴拿馬巴波亞 + PAB + + + 車城盧布 Kupon + PDK + + + 車城新盧布 + PDN + + + 車城盧布 + PDR + + + 祕魯因蒂 + PEI + + + 秘魯新太陽幣 + PEN + + + 秘魯太陽幣 + PES + + + 巴布亞紐幾內亞基那 + PGK + + + 菲律賓披索 + PHP + + + 巴基斯坦盧布 + PKR + + + 波蘭茲羅提 + PLN + + + 波蘭美元外匯券 + PLX + + + 波蘭茲羅提 (1950-1995) + PLZ + + + 巴勒斯坦鎊 + PSP + + + 葡萄牙 康拖 + PTC + + + 葡萄牙埃斯庫多 + PTE + + + 巴拉圭瓜拉尼 + PYG + + + 卡達爾里亞爾 + QAR + + + 留尼汪島法郎 + REF + + + 羅馬尼亞列伊 + ROL + + + 羅馬尼亞新列伊 + RON + + + 俄羅斯盧布 + RUB + + + 俄羅斯盧布 (1991-1998) + RUR + + + 盧安達法郎 + RWF + + + 沙烏地里雅 + SRl + + + 沙烏地宗主里雅 + SAS + + + 索羅門群島元 + SBD + + + 塞舌爾群島盧布 + SCR + + + 蘇丹第納爾 + SDD + + + 蘇丹鎊 + SDP + + + 瑞典克羅納 + SEK + + + 新加坡幣 + SGD + + + 聖赫勒拿 鎊 + SHP + + + 斯洛文尼亞 Tolar Bons + SIB + + + 斯洛維尼亞托勒 + SIT + + + 斯洛伐克克朗 + SKK + + + 獅子山利昂 + SLL + + + 聖馬利諾里拉 + SML + + + 索馬利亞先令 + SOS + + + 索馬里蘭先令 + SQS + + + 蘇里南盾 + SRG + + + 蘇格蘭鎊 + SSP + + + 聖多美島和普林西比島多布拉 + STD + + + 聖多美島和普林西比島埃斯庫多 + STE + + + 蘇聯新盧布 + SUN + + + 蘇聯盧布 + SUR + + + 愛爾 薩爾瓦多科郎 + SVC + + + 敘利亞鎊 + SYP + + + 斯威士蘭 里郎 + SZL + + + 土耳其人和凱科斯冠 + TCC + + + 乍得 西非 法郎 + TDF + + + 泰銖 + THB + + + 塔吉克斯坦盧布 + TJR + + + 塔吉克斯坦 索莫尼 + TJS + + + 土庫曼馬納特 + TMM + + + 突尼西亞第納爾 + TND + + + 東加潘加 + TOP + + + 湯加英鎊 + TOS + + + 帝汶 埃斯庫多 + TPE + + + 帝汶元 + TPP + + + 土耳其里拉 + TRL + + + 千里達及托巴哥r + TTD + + + 特立尼達和多巴哥舊元r + TTO + + + 吐瓦魯美元 + TVD + + + 新臺幣 + NT$ + + + 坦桑尼亞 先令 + TZS + + + 烏克蘭格里夫那 + UAH + + + 烏克蘭 卡本瓦那茲 + UAK + + + 烏干達先令 (1966-1987) + UGS + + + 烏干達先令 + UGX + + + 美元 + US$ + + + 美元 (第二天) + USN + + + 美元 (同一天) + USS + + + 烏拉圭披索福厄特 + UYF + + + 烏拉圭披索 (1975-1993) + UYP + + + 烏拉圭披索 + UYU + + + 烏茲別克斯坦 庫邦 索馬 + UZC + + + 烏茲別克斯坦 薩木 + UZS + + + 梵蒂岡城里拉 + VAL + + + 北越南 皮阿斯特越南盾 + VDD + + + 北越南新盾 + VDN + + + 北越南 名 皮阿斯特越南盾 + VDP + + + 委內瑞拉博利瓦 + VEB + + + 英屬維爾斯群島元 + VGD + + + 越南盾 + VND + + + 越南新盾 + VNN + + + 越南共和國 盾 + VNR + + + 越南國家盾 + VNS + + + 萬那杜萬杜 + VUV + + + 西薩摩亞鎊 + WSP + + + 西薩摩亞塔拉 + WST + + + 亞洲第納爾會計單位 + XAD + + + 西非 法郎 BEAC + XAF + + + 亞洲貨幣單位 + XAM + + + 黃金 + XAU + + + 歐洲綜合單位 + XBA + + + 歐洲貨幣單位 + XBB + + + 歐洲會計單位(XBC) + XBC + + + 歐洲會計單位(XBD) + XBD + + + 格瑞那達元 + XCD + + + 西非 新 法郎 + XCF + + + 特殊提款權 + XDR + + + 西非 法郎 BCEAEC + XEF + + + 歐洲貨幣單位 + XEU + + + 法國金法郎 + XFO + + + 法國 UIC 法郎 + XFU + + + 伊斯蘭第納爾 + XID + + + 法國大城市新 法郎 + XMF + + + 法國安的列斯群島 西非 法郎 + XNF + + + 西非 法郎 BCEAO + XOF + + + CFP 法郎 + XPF + + + COMECON 可轉移盧布 + XTR + + + 葉門第納爾 + YDD + + + 也門阿馬迪里亞爾 + YEI + + + 也門里亞爾 + YER + + + 南斯拉夫第納爾硬幣 + YUD + + + 南斯拉夫聯邦第納爾 + YUF + + + 南斯拉夫人1994 第納爾 + YUG + + + 南斯拉夫挪威亞第納爾 + YUM + + + 南斯拉夫 可轉換第納爾 + YUN + + + 南斯拉夫十月 第納爾 + YUO + + + 南斯拉夫改制後的第納爾 + YUR + + + 南非 - 蘭特 (金融) + ZAL + + + 南非鎊 + ZAP + + + 南非蘭特 + ZAR + + + 尚比亞克瓦查 + ZMK + + + 贊比亞鎊 + ZMP + + + 薩伊扎新伊爾 + ZRN + + + 扎伊爾扎伊爾 + ZRZ + + + 辛巴威元 + ZWD + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/data/zh.xml zope3-3.5~bzr18/src/zope/i18n/locales/data/zh.xml --- zope3-3.4.0/src/zope/i18n/locales/data/zh.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/data/zh.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2674 @@ + + + + + + + + + + + 阿法文 + 阿布哈西亚文 + 亚齐文 + 阿乔利文 + 阿当梅文 + 阿迪何文 + 阿维斯塔文 + 南非荷兰文 + 其他亚非语系 + 阿弗里希利文 + 阿肯文 + 阿卡德文 + 阿留申群岛之土语 + 其他阿尔贡语系 + 阿姆哈拉文 + 中古英语 + 阿帕切文 + 阿拉伯文 + 阿拉米文 + 阿劳坎文 + 阿拉帕霍文 + 其他人工语言 + 阿拉瓦克文 + 阿萨姆文 + 阿斯图里亚思特语 + 其他阿撒巴斯卡语系 + 澳大利亚语系 + 阿瓦尔文 + 阿瓦乔文 + 艾马拉文 + 阿塞拜疆文 + 巴什客尔文 + 班达文 + 巴米累克文 + 俾路支文 + 班巴拉文 + 巴里文 + 巴萨文 + 波罗的海地区之语言 + 白俄罗斯文 + 别札文 + 别姆巴文 + 北非回教土族之语言 + 保加利亚文 + 比哈尔文 + 博杰普尔文 + 比斯拉马文 + 毕库尔文 + 比尼文 + 司克司卡文 + 班巴拉文 + 孟加拉文 + 班图文 + 西藏文 + 布里多尼文 + 布拉杰文 + 波斯尼亚文 + 巴塔克文 + 布里亚特文 + 布吉文 + 布林文 + 加泰罗尼亚文 + 卡多文 + 其他中美印第安语系 + 巴勒比文 + 其他高加索语系 + 车臣文 + 宿务文 + 其他凯尔特语系 + 查莫罗文 + 契布卡文 + 查加文 + 楚吾克文 + 马里文 + 契努克文 + 乔克托文 + 佩瓦扬文 + 彻罗基文 + 夏延文 + 查米克文 + 科西嘉文 + 科普特文 + 不纯粹之英国方言 + 不纯粹之法国方言 + 不纯粹之葡国方言 + 克里族文 + 克里米亚土耳其文;克里米亚塔塔文 + 克里奥尔语和皮钦文 + 捷克文 + 卡舒文 + 宗教斯拉夫文 + 其他库施特语系 + 楚瓦什文 + 威尔士文 + 丹麦文 + 达科他文 + 达尔格瓦文 + 达雅克文 + 德文 + 特拉瓦印第安人文 + 司雷夫文 + 多格来文 + 丁卡文 + 多格来文 + 其他德拉维语系 + 下塞尔维亚文 + 都阿拉文 + 中古荷兰文 + 迪维希文 + 迪尤拉文 + 不丹文 + 幽文 + 希腊文 + 艾拉米特文 + 英文 + 中古英文 + 世界文 + 西班牙文 + 爱沙尼亚文 + 巴斯克文 + 旺杜文 + 波斯文 + 芳格文 + 芳蒂文 + 夫拉文 + 芬兰文 + 芬匈文(其他) + 斐济文 + 法罗文 + 丰文 + 法文 + 中古法文 + 古法文 + 弗留利文 + 弗里斯兰文 + 爱尔兰文 + 加文 + 迦约文 + 葛巴亚文 + 苏格兰- 盖尔文 + 吉兹文 + 吉尔伯特斯文 + 加利西亚文 + 中古高地德文 + 瓜拉尼文 + 古代高地德文 + 岗德文 + 科洛涅达罗文 + 哥达文 + 格列博文 + 古希腊文 + 古加拉提文 + 马恩岛文 + 吉维克琴文 + 豪撒文 + 海达文 + 夏威夷文 + 希伯来文 + 印地文 + 希利盖农文 + 赫马查利文 + 西台文 + 赫蒙文 + 新里木托文 + 克罗地亚文 + 上索布文 + 匈牙利文 + 胡帕文 + 亚美尼亚文 + 赫雷罗文 + 拉丁国际文 + 伊班文 + 印度尼西亚文 + 拉丁国际文 + 伊格博文 + 四川话 + 伊乔文 + 依奴皮维克文 + 伊洛干诺文 + 印度文(其他) + 其他印欧语系 + 印古什文 + 爱德莪文(人工语言) + 伊朗文 + 伊洛郭伊费文 + 冰岛文 + 意大利文 + 爱斯基摩文 + 日文 + 洛吉般(人工语言) + 犹太波斯语系 + 犹太阿拉伯语系 + 爪哇文 + 格鲁吉亚文 + 卡拉卡尔帕克文 + 卡比尔文 + 卡琴文 + 卡姆巴文 + 喀伦文 + 卡威文 + 卡巴尔达文 + 刚果文 + 卡西文 + 其他科伊桑文 + 和田文 + 吉库尤文 + 关琊玛文 + 哈萨克文 + 格陵兰文 + 柬埔寨文 + 金邦杜文 + 埃纳德文 + 韩文 + 刚卡尼文 + 柯司瑞恩文 + 克佩列文 + 卡努里文 + 卡拉卡尔帕克文 + 克鲁文 + 库鲁克文 + 克什米尔文 + 库尔德文 + 库梅克文 + 库特内文 + 科米文 + 凯尔特文 + 吉尔吉斯文 + 拉丁文 + 拉迪诺文 + 拉亨达文 + 兰巴文 + 卢森堡文 + 莱兹依昂文 + 卢干达文 + 淋布尔吉文 + 林加拉文 + 老挝文 + 蒙古文 + 洛兹文 + 立陶宛文 + 鲁巴加丹加文 + 鲁巴鲁瓦文 + 路易塞诺文 + 隆达文 + 卢奥文 + 卢晒文 + 拉脫維亞文 + 马都拉文 + 马加伊文 + 迈蒂利文 + 望加锡文 + 曼丁哥文 + 马来亚玻里尼西亚语系 + 萨伊语 + 莫克沙文 + 曼达尔 + 门迪文 + 马尔加什文 + 中古爱尔兰文 + 马绍尔文 + 毛利文 + 米克马克文 + 米南卡保文 + 各种不同语言 + 马其顿文 + 其他蒙吉蔑文 + 马来亚拉姆文 + 蒙古文 + 满文 + 曼尼普里文 + 马诺博污文 + 摩尔多瓦文 + 摩霍克文 + 莫西文 + 马拉地文 + 马来文 + 马耳他文 + 多种语言 + 蒙达文 + 摩斯科格文 + 马尔尼里文 + 缅甸文 + 玛雅文 + 俄日亚文 + 瑙鲁文 + 纳瓦特尔文 + 其他北美印第安语系 + 拿波里文 + 挪威博克马尔文 + 北恩德贝勒文 + 德国北部的德文;低地萨克逊文 + 尼泊尔文 + 尼瓦尔文 + 恩东加文 + 尼尔司文 + 其他尼日尔刚果语系 + 纽埃文 + 荷兰文 + 挪威尼诺斯克文 + 挪威文 + 诺盖文 + 古诺尔斯文 + 南部恩德贝勒文 + 北索托文 + 努比亚文 + 纳瓦霍文 + 尼昂加文;切瓦文;切瓦文 + 尼亚姆韦齐文 + 尼昂科勒文 + 尼约罗语族 + 尼兹玛文 + 奥西坦文 + 奥季布瓦文 + 阿曼文 + 欧里亚文 + 奥塞提文 + 奥萨哲文 + 奥托曼土耳其文 + 奥托米语系 + 旁遮普文 + 其他巴布亚文 + 邦阿西南文 + 帕拉维文 + 邦板牙文 + 帕皮亚内托文 + 帕劳文 + 古老波斯语 + 其他菲律宾语系 + 腓利基文 + 帕利文 + 波兰文 + 波那贝文 + 印度古代及中世纪之中部及北部方言 + 普罗文斯文 + 普什图文 + 葡萄牙文 + 盖丘亚文 + 拉贾斯坦文 + 拉帕努文 + 拉罗汤加文 + 里托罗曼斯文 + 基隆迪文 + 罗马尼亚文 + 其他拉丁语系 + 吉普赛文 + 俄文 + 卢旺达文 + 梵文 + 散达维文 + 雅库特文 + 其他南美印第安文 + 萨利什文 + 萨玛利亚文 + 塞塞卡文 + 桑嗒利文 + 萨丁文 + 苏格兰文 + 苏丹文 + 北萨迷文 + 塞尔库普文 + 其他闪族语系 + 桑戈文 + 古爱尔兰文 + 手语 + 塞波尼斯-克罗地亚文 + 掸文 + 僧伽罗文 + 悉达摩文 + 苏语诸语言 + 其他汉藏语系 + 斯洛伐克文 + 斯洛文尼亚文 + 其他斯拉夫语系 + 萨摩亚文 + 南萨迷文 + 其他萨迷文 + 卢乐萨迷文 + 依纳日萨迷文 + 司寇特萨迷文 + 塞内加尔文 + 索尼基文 + 索马里文 + 索格迪亚文 + 桑海文 + 阿尔巴尼亚文 + 塞尔维亚文 + 谢列尔文 + 辛辛那提文 + 其他尼罗萨哈兰文 + 塞索托文 + 苏丹文 + 苏库马文 + 苏苏文 + 苏马文 + 瑞典文 + 斯瓦希里文 + 叙利亚文 + 泰米尔文 + 其他泰文 + 泰卢固文 + 体姆呐文 + 特喏诺文 + 特图们文 + 塔吉克文 + 泰文 + 提格里尼亚文 + 提格雷文 + 蒂夫文 + 土库曼文 + 陀克娄文 + 塔加路族文 + 特林吉特文 + 塔玛厍克文 + 突尼斯文 + 汤加文 + 汤加文(尼亚萨地区) + 托克皮辛文 + 土耳其文 + 特松加文 + 蒂姆西亚文 + 鞑靼文 + 通布卡文 + 图匹文 + 其他阿尔泰语系 + 图瓦卢文 + 台湾文 + 塔西提文 + 图瓦文 + 乌德穆尔特文 + 维吾尔文 + 乌加里特文 + 乌克兰文 + 姆崩杜文 + 未定语种 + 乌尔都文 + 乌兹别克文 + 瓦伊文 + 文达文 + 越南文 + 沃拉普克文 + 沃提克文 + 華隆文 + 瓦喀山文 + 瓦拉莫文 + 佤瑞文 + 瓦绍文 + 索布诸语言 + 沃尔夫文 + 卡啦迷克文 + 班图文 + 瑶族文 + 雅浦文 + 依地文 + 约鲁巴文 + 喻皮克文 + 藏文 + 萨波蒂克文 + 泽纳加文 + 中文 + 赞德文 + 祖鲁文 + 祖尼语 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 安道尔 + 阿拉伯联合酋长国 + 阿富汗 + 安提瓜和巴布达 + 安圭拉 + 阿尔巴尼亚 + 亚美尼亚 + 荷属安的列斯群岛 + 安哥拉 + 南极洲 + 阿根廷 + 美属萨摩亚 + 奥地利 + 澳大利亚 + 阿鲁巴 + 阿塞拜疆 + 波斯尼亚和黑山共和国 + 巴巴多斯 + 孟加拉国 + 比利时 + 布基纳法索 + 保加利亚 + 巴林 + 布隆迪 + 贝宁 + 百慕大 + 文莱 + 玻利维亚 + 巴西 + 巴哈马 + 不丹 + 布维特岛 + 博茨瓦纳 + 白俄罗斯 + 伯利兹 + 加拿大 + 科科斯群岛 + 刚果民主共和国 + 中非共和国 + 刚果 + 瑞士 + 象牙海岸 + 库克群岛 + 智利 + 喀麦隆 + 中国 + 哥伦比亚 + 哥斯达黎加 + 古巴 + 佛得角 + 圣诞岛 + 塞浦路斯 + 捷克共和国 + 德国 + 吉布提 + 丹麦 + 多米尼加岘 + 多米尼加共和国 + 阿尔及利亚 + 厄瓜多尔 + 爱沙尼亚 + 埃及 + 西撒哈拉 + 厄立特里亚 + 西班牙 + 埃塞俄比亚 + 芬兰 + 斐济 + 福克兰群岛 + 密克罗尼西亚联邦 + 法罗群岛 + 法国 + 加蓬 + 英国 + 格林纳达 + 格鲁吉亚 + 法属圭亚那 + 加纳 + 直布罗陀 + 格陵兰 + 冈比亚 + 几内亚 + 瓜德罗普岛 + 赤道几内亚 + 希腊 + 南佐治亚和南三明治群岛 + 危地马拉 + 关岛 + 几内亚比绍 + 圭亚那 + 中国香港特别行政区 + 赫德与麦克唐纳群岛 + 洪都拉斯 + 克罗地亚 + 海地 + 匈牙利 + 印度尼西亚 + 爱尔兰 + 以色列 + 印度 + 英属印度洋领地 + 伊拉克 + 伊朗 + 冰岛 + 意大利 + 牙买加 + 约旦 + 日本 + 肯尼亚 + 吉尔吉克斯坦 + 柬埔寨 + 基里巴斯 + 科摩罗 + 圣基茨和尼维斯 + 北朝鲜 + 韩国 + 科威特 + 开曼群岛 + 哈萨克斯坦 + 老挝人民民主共和国 + 黎巴嫩 + 圣卢西亚 + 列支敦士登 + 斯里兰卡 + 利比里亚 + 莱索托 + 立陶宛 + 卢森堡 + 拉脱维亚 + 利比亚 + 摩洛哥 + 摩纳哥 + 摩尔多瓦共和国 + 马达加斯加 + 马绍尔群岛 + 马其顿王国 + 马里 + 缅甸 + 蒙古 + 中国澳门特别行政区 + 北马里亚纳群岛 + 马提尼克岛 + 毛里塔尼亚 + 蒙特塞拉群岛 + 马耳他 + 毛里求斯 + 马尔代夫 + 马拉维 + 墨西哥 + 马来西亚 + 莫桑比克 + 纳米比亚 + 新喀里多尼亚 + 尼日尔 + 诺福克岛 + 尼日利亚 + 尼加拉瓜 + 荷兰 + 挪威 + 尼泊尔 + 瑙鲁 + 纽埃 + 新西兰 + 阿曼 + 巴拿马 + 秘鲁 + 法属波利尼西亚 + 巴布亚新几内亚 + 菲律宾 + 巴基斯坦 + 波兰 + 圣皮埃尔和密克隆 + 皮特凯恩 + 波多黎各 + 巴勒斯坦领土 + 葡萄牙 + 帕劳 + 巴拉圭 + 卡塔尔 + 留尼汪 + 罗马尼亚 + 俄罗斯联邦 + 卢旺达 + 沙特阿拉伯 + 所罗门群岛 + 塞舌尔 + 苏丹 + 瑞典 + 新加坡 + 圣赫勒拿 + 斯洛文尼亚 + 斯瓦尔巴特和扬马延 + 斯洛伐克 + 塞拉利昂 + 圣马力诺 + 塞内加尔 + 索马里 + 塞尔维亚 + 苏里南 + 圣多美和普林西比 + 萨尔瓦多 + 叙利亚 + 斯威士兰 + 特克斯和凯科斯群岛 + 乍得 + 法属南半球领地 + 多哥 + 泰国 + 塔吉克斯坦 + 托克劳 + 东帝汶 + 土库曼斯坦 + 突尼斯 + 汤加 + 土耳其 + 特立尼达和多巴哥 + 图瓦卢 + 台湾 + 坦桑尼亚 + 乌克兰 + 乌干达 + 美国边远小岛 + 美国 + 乌拉圭 + 乌兹别克斯坦 + 梵蒂冈 + 圣文森特和格林纳丁斯 + 委内瑞拉 + 英属维京群岛 + 美属维京群岛 + 越南 + 瓦努阿图 + 瓦利斯和富图纳 + 萨摩亚 + 也门 + 马约特 + 南斯拉夫 + 南非 + 赞比亚 + 津巴布韦 + + + 已修订 + + + 日历 + 对照 + 货币 + + + 佛教日历 + 农历 + 公历 + 希伯来日历 + 伊斯兰日历 + 伊斯兰希吉来历 + 日本日历 + 顺序 + 电话簿顺序 + 拼音顺序 + 笔划顺序 + 传统历法 + + + + [一-丁七丈-不专-且世丙-业东-丝丢两-严丧个中串临丸-主丽-举乃久么-义之-乌乎-乐乔乖乘-乙九也-乡书买-乱乾了予-争事-二于-亏云-互五-井亚-些亡交亦亨享-京亮亲人亿-仁仅仇今-介仍-从仔他付-仙代-以仪们仰仲件任份仿企伊伍伏休众伙-会伟-传伤伦伯-估伴伸似但位-住佑体何余佛-作你佩佳使例供依侠侦-侨侬侯侵便促俊俗保信俩修俱倍倒候-倚借倦值倾假偏做停健偶-偷储催傲傻像儒允元-充先-光克免兔入全八-兮兰-共关-典养-兽再冒写军-农冠冬冰冲冷准凌凝凡凤凭凯-凰出-击函刀分-切刊刑列-创初判利到制-刷刺-刻剂前剑剧剩-剪副割力劝-务劣动-劫励-劳势勇勉勒勤勾-勿包-匆化-北匙区-医十千升-午半华-协卒单-南博占-卡卫印-危即卷厅-历厉压-厌厚原去县参又-反发叔取-变口-另叫-叭可-台史-右叶-叹吃-各合-吊同-后吐-向吓吗君吝吟否-吧含吵吸-吹吻吾呀呆呈告员呜呢呦周味呵呼-命和咖咦-咧咪咬咱哀-品哇-哉响-哎哟哥-哦哩-哪哭哲唉唐唤唬售-唯唱唷商啊啡啥-啦啪喂善喇喊喔喜-喝喵喷喻嗨嗯嘉嘛嘴嘻嘿器四回因团园困围固国-图圆圈土在地场圾址均坐-坑块坚-坜坡坤坦坪垂-垃型垒埋城域培-基堂堆堕堡堪塑塔塞填境增墨壁士壮声处备夏夕-外多夜夥大天-夫央失头夹-夺奇-奉奋奏契奔套女奶她好如妇-妈妖妙妥妨妮妹妻姆姊-始姐-姑姓-委姿威娃娘娟婆婚媒嫁嫌子孔-孕字-孙孝孟季-孤学孩它宇-安宋-完宏宗-定宜-实审-室宪害家容宽-宿寂寄密富寒寝-察寡寸-对寻-导寿封射将尊小少尔尖尘尚尝尤就尺尼-尾局-层居屋屏展属屠山岁-岂岚-岛岳岸峰崇川-州巡工-巨巫差己-已巴巷币-布帅师希帐帝带席-帮常帽幅幕干-年幸幻-幽广庆床序库-底店庙府废度-座庭康-庸廉廖延-廷建开弃-弄弊式引弘弟-张弥-弦弯弱弹归-当形彩彬-彭彰-影彷役彻-彼往-征径-待很律-後徐徒得循微徵德心必-忆忌-忍志-忙忠忧快念忽态怎怒怕-怖思怡急性-怨怪总恋恐恢恨-恩恭息-恰恶恼悄悉悔悟-悠患您悲情惑惜惠惧-惨惯想惹愁愈-愉意愚感愧慈慎慕慢慧慰憾懂懒戏-戒或战截戴房-扁扇手才打托扣执扩扫-扯批找-技抄把抑抓投抗-折抢护-报披-抬抱抵抹抽担-拆拉拍拒拔拖拘招-拜拟拥-拦拨-择括拳拷拼拾-拿持指按挑挖挡挤-挥振挺捉捐捕损捡-换捷授-掉掌排探接控-措描-提插握援搜搞搬-搭摄摆摊摔摘摩摸撒撞播操-擎擦支收改攻放-政故效敌敏救教敝敢-散敦敬数敲整文斋斗料斜斥断斯-新方於-施旁旅旋族旗无既日-早旭时旺昆昌明-昏易星-映春昨昭是显晃晋晓晚晨普-景晴晶智暂暑暖-暗暮暴曰曲更曹曼曾-最月-有朋服朗望朝期木未-札术朱朵杀杂-权杉李材-村杜束条来杨杯-杰松-板析林果-枝枢枪-枫架柏-某染-柔查柯柳-柴标栋栏树校样-根格桃框案桌桑档桥梁梅梦梯-械检棋棒棚森椅植椰楚楼概榜模樱欠-欣欧欲欺款歉歌止-武歪死殊-残段毅母每毒比-毕毛毫氏民氛水永求汉汗汝江-污汤汪汽沈-沉沙沟沧河油治沿泉-泊法泛泡-泣泥注泰泳泽洋洗洛洞津洪洲活洽-派流浅测济浑浓浩-浪浮浴海消-涉涛涨涯液涵淑淡深混添清渐渡港渴游湖湾源溜溪滋滑满滥滴漂漏演漠漫潘潜潮澎激灌火灭灯-灰灵灿炉炎炮炸-点烂烈烤烦-烧热焦然煌煞照煮熊熟燃燕爆爬爱爵-爸爽片-版牌牙牛牡-牢牧物牲牵特-牺犯状犹狂狐狗狠独狮狱狼猛-猜献玄率玉王玛玩玫环-现玲玻珊珍珠班球理琪琳-琴瑜瑞瑰璃瓜瓦瓶甘甚甜生用田-申电男画畅界留略番疏疑疗疯疲疼疾病痕痛痴登白-百的皆-皇皮盈益监-盒盖盘盛盟目直相盼盾省眉看真-眠眼睛睡督瞧矛矣知短石码-砂砍研破础硕硬碍-碎碗碟碧碰磁磨示礼社祖祝-神祥票祸禁禅福秀-私秋科-秒秘租秤秦秩积-称移稀程稍稣稳稿究-穷穹-空穿突窗窝立站竞-章童端竹笑笔笛符笨第等筋答策筹签简算管箭箱篇篮籍米类粉粒粗精糊糕-糖糟系素索紧紫累繁红约-级纪纯纲-纳纵纷-纸纽练-组细-终绍经结绕绘-给络统继绩-绪续维-绵综缓编缘缠缩缴缸缺罐罗罚罢罪置署羊美羞群羯羽翁翅翔翘翠翻-翼耀-老考者而-耍耐耗耳耶聊职联聚聪肉肚股肤-肥肩肯育胁胆背胎胖胞胡胶胸能脆脑脸腐腰腹腾-腿臂臣自臭至-致舍舒舞-舟航般舰船良色艺艾节芒芬-芭花芳苍苏苗若-苦英茂茫茶草荒荣药荷莉莎莫莱-莲获菜菩菲萍萤-营萧-萨落著葛蒋蒙蓉蓝蔡薄薪藉藏藤虎虑虫虹虽-虾蚁蛇蛋蛙蛮蜂蜜蝶融蟹蠢血行街衡衣补表袋被袭裁-裂装裕裤西要覆见-观规视览-觉角解言誉誓警计-订认讨-让训-记讲许论设-访证评识诉词译试诗诚话-诞询该-详语误说请-诸诺-读课谁调谅谈谊-谋谓谜谢谨谱谷豆象豪貌贝-负贡-败货-贪购贯贱贴-贵费-贺贼资赋-赌赏-赐赔赖赚-赛赞赠赢赤走赵起趁超越-趋趣足跃跌跑距跟路跳踏踢踩身躲车轨-轩转轮-轰轻载较辅-辆辈-辉辑输辛辞辨-辩辱边达迁迅过-迈迎运-近返还-这进-迟迪-迫述迷追退-送逃逆选-逊透-逐递途通-逛逝速-造逢逸逻-逼遇遍道遗遭遵避-邀邓那邦邪邮邱邻郎郑部郭都配酒酷-酸醉醒采释里-量金针钓钟钢钦钱钻铁铃铭银销-锁锅锋错锦键锺镇镜长门闪闭-问间闷闹闻阁阐阔队阮防-阶阻阿-陀附-陆陈降限院除险-陪陵-陷隆随-隐隔障难雄-集雨雪雯雳零-雷雾需震霖露霸-霹青靖静非靠面革鞋韩音页-顶项-须顽-顿预领-颇频颗-题额风飘-飙飞-食餐饭-饮饰-饱饼馆首香馨马驱驶驻驾验骑骗骚骤骨高鬼魂魅魔鱼鲁鲜鸟鸣鸭鸿鹅鹤鹰鹿麦麻黎黑默鼓鼠鼻齐齿龄龙龟] + + + GanjkHmsSEDFwWxhKzAeugXZ + + + + + + 一月 + 二月 + 三月 + 四月 + 五月 + 六月 + 七月 + 八月 + 九月 + 十月 + 十一月 + 十二月 + + + 1月 + 2月 + 3月 + 4月 + 5月 + 6月 + 7月 + 8月 + 9月 + 10月 + 11月 + 12月 + + + 一月 + 二月 + 三月 + 四月 + 五月 + 六月 + 七月 + 八月 + 九月 + 十月 + 十一月 + 十二月 + + + + + + + + + + + + + + + + + + + + + + + + + 星期日 + 星期一 + 星期二 + 星期三 + 星期四 + 星期五 + 星期六 + + + + 上午 + 下午 + + + 公元前 + 公元 + + + + + + + + 太平洋标准时间 + 太平洋夏令时间 + + + PST + PDT + + 洛杉矶 + + + + 太平洋标准时间 + 太平洋夏令时间 + + + PST + PDT + + 洛杉矶 + + + + 山区标准时间 + 山区夏令时间 + + + MST + MDT + + 丹佛 + + + + 山区标准时间 + 山区夏令时间 + + + MST + MDT + + 丹佛 + + + + 山区标准时间 + 山区标准时间 + + + MST + MST + + 凤凰城 + + + + 山区标准时间 + 山区标准时间 + + + MST + MST + + 凤凰城 + + + + 中央标准时间 + 中央夏令时间 + + + CST + CDT + + 芝加哥 + + + + 中央标准时间 + 中央夏令时间 + + + CST + CDT + + 芝加哥 + + + + 东部标准时间 + 东部夏令时间 + + + EST + EDT + + 纽约 + + + + 东部标准时间 + 东部夏令时间 + + + EST + EDT + + 纽约 + + + + 东部标准时间 + 东部标准时间 + + + EST + EST + + 印地安纳波利斯 + + + + 东部标准时间 + 东部标准时间 + + + EST + EST + + 印地安纳波利斯 + + + + 夏威夷标准时间 + 夏威夷标准时间 + + + HST + HST + + 檀香山 + + + + 夏威夷标准时间 + 夏威夷标准时间 + + + HST + HST + + 檀香山 + + + + 阿拉斯加标准时间 + 阿拉斯加夏令时间 + + + AST + ADT + + 安克雷奇 + + + + 阿拉斯加标准时间 + 阿拉斯加夏令时间 + + + AST + ADT + + 安克雷奇 + + + + 大西洋标准时间 + 大西洋夏令时间 + + + AST + ADT + + 哈利法克斯 + + + + 纽芬兰标准时间 + 纽芬兰夏令时间 + + + CNT + CDT + + 圣约翰 + + + + 纽芬兰标准时间 + 纽芬兰夏令时间 + + + CNT + CDT + + 圣约翰 + + + + 中欧标准时间 + 中欧夏令时间 + + + CET + CEST + + 巴黎 + + + + 中欧标准时间 + 中欧夏令时间 + + + CET + CEST + + 巴黎 + + + + 格林威治标准时间 + 格林威治标准时间 + + + GMT + GMT + + 伦敦 + + + + 格林威治标准时间 + 格林威治标准时间 + + + GMT + GMT + + 卡萨布兰卡 + + + + 以色列标准时间 + 以色列夏令时间 + + + IST + IDT + + 耶路撒冷 + + + + 日本标准时间 + 日本标准时间 + + + JST + JST + + 东京 + + + + 日本标准时间 + 日本标准时间 + + + JST + JST + + 东京 + + + + 东欧标准时间 + 东欧夏令时间 + + + EET + EEST + + 布加勒斯特 + + + + 中国标准时间 + 中国标准时间 + + + CTT + CDT + + 上海 + + + + 中国标准时间 + 中国标准时间 + + + CTT + CDT + + 上海 + + + + + + + 安道尔第纳尔元 + ADD + + + 安道尔比塞塔元 + ADP + + + 阿联酋迪拉姆 + AED + + + 阿富汗尼 (1927-2002) + AFA + + + 阿富汗尼 + AFN + + + 阿发和伊萨法郎 + AIF + + + 阿尔巴尼亚列克 (1946-1961) + ALK + + + 阿尔巴尼亚列克 + ALL + + + 阿尔巴尼亚列克币 + ALV + + + 阿尔巴尼亚元外汇券 + ALX + + + 亚美尼亚德拉姆 + AMD + + + 荷兰安替兰盾 + ANG + + + 安戈拉宽扎 + AOA + + + 安戈拉宽扎 (1977-1990) + AOK + + + 安戈拉新宽扎 (1990-2000) + AON + + + 安戈拉宽扎 Reajustado (1995-1999) + AOR + + + 安哥拉埃斯库多 + AOS + + + 阿根廷奥斯特 + ARA + + + 阿根廷比索标准局 + ARM + + + 阿根廷比索 (1983-1985) + ARP + + + 阿根廷比索 + ARS + + + 奥地利西令 + ATS + + + 澳大利亚元 + AUD + + + 澳大利亚磅 + AUP + + + 阿鲁巴基尔德元 + AWG + + + 波士尼亚-赫塞哥维纳第纳尔元 + BAD + + + 波士尼亚-赫塞哥维纳兑换券 + BAM + + + 波士尼亚-赫塞哥维纳新第纳尔元 + BAN + + + 巴巴多斯元 + BBD + + + 孟加拉达卡 + BDT + + + 比利时法郎兑换券 + BEC + + + 比利时法郎 + BEF + + + 比利时法郎(金融) + BEL + + + 保加利亚硬列克 + BGL + + + 保加利亚社会主义列克 + BGM + + + 保加利亚新列克 + BGN + + + 保加利亚列克 (1879-1952) + BGO + + + 保加利亚列克外汇券 + BGX + + + 巴林第纳尔元 + BHD + + + 布隆迪法郎 + BIF + + + 百慕大元 + BMD + + + 百慕大磅 + BMP + + + 汶莱元 + BND + + + 玻利维亚 + BOB + + + 玻利维亚 (1863-1962) + BOL + + + 玻利维亚比索 + BOP + + + 巴西克鲁赛罗 (1967-1986) + BRB + + + 巴西克鲁塞罗 + BRC + + + 巴西克鲁塞罗 (1990-1993) + BRE + + + 巴西里尔 + BRL + + + 巴西克鲁塞罗 Cruzado Novo + BRN + + + 巴西克鲁塞罗 + BRR + + + 巴西克鲁塞罗 (1942-1967) + BRZ + + + 巴哈马元 + BSD + + + 巴哈马磅 + BSP + + + 不丹努扎姆 + BTN + + + 不丹卢比 + BTR + + + 缅元 + BUK + + + 缅甸卢比 + BUR + + + 波渣那扑拉 + BWP + + + 白俄罗斯新卢布 (1994-1999) + BYB + + + 白俄罗斯卢布 (1992-1994) + BYL + + + 白俄罗斯卢布 + BYR + + + 伯利兹元 + BZD + + + 属洪都拉斯元 + BZH + + + 加拿大元 + CAD + + + 刚果法郎 + CDF + + + 刚果共和国法郎 + CDG + + + 刚果扎伊尔 + CDL + + + 中非共和国 CFA 法郎 + CFF + + + 瑞士法郎 + CHF + + + 库克群岛元 + CKD + + + 智利肯杜 + CLC + + + 智利埃斯库多 + CLE + + + 智利 Unidades de Fomento + CLF + + + 智利 比索 + CLP + + + 喀麦隆 CFA 法郎 + CMF + + + 中国人民票元 + CNP + + + 中国美元外汇券 + CNX + + + 人民币 + + + + 哥伦比亚纸比索 + COB + + + 刚果 CFA 法郎 + COF + + + 哥伦比亚比索 + COP + + + 哥斯达黎加科隆 + CRC + + + 捷克克郎 + CSC + + + 捷克硬克郎 + CSK + + + 古巴比索 + CUP + + + 古巴外汇券 + CUX + + + 佛得角埃斯库多 + CVE + + + 库拉盾 + CWG + + + 塞浦路斯磅 + CYP + + + 捷克克郎 + CZK + + + 东德奥斯特马克 + DDM + + + 德国马克 + DEM + + + 德国司萡马克 + DES + + + 吉布提法郎 + DJF + + + 丹麦克朗 + DKK + + + 多米尼加比索 + DOP + + + 阿尔及利亚第纳尔元 + DZD + + + 阿尔及利亚新法郎 + DZF + + + 阿尔及利亚法郎比斯查 + DZG + + + 厄瓜多尔苏克雷 + ECS + + + 爱沙尼亚克朗 + EEK + + + 埃及磅 + EGP + + + 厄立特里亚纳福卡 + ERN + + + 西班牙马赛塔 + ESP + + + 埃塞俄比亚比尔 + ETB + + + 埃塞俄比亚元 + ETD + + + 欧元 + + + + 芬兰玛卡 + FIM + + + 芬兰玛卡 (1860-1962) + FIN + + + 斐济元 + FJD + + + 斐济磅 + FJP + + + 福克兰群岛磅 + FKP + + + 法罗群岛克朗 + FOK + + + 法国法郎 + FRF + + + 法国法郎比斯查/法郎庞加莱 + FRG + + + 英磅 + £ + + + 乔治亚库蓬拉瑞特 + GEK + + + 乔治亚库蓬拉瑞 + GEL + + + 加纳塞第 + GHC + + + 加纳旧塞第 + GHO + + + 加纳磅 + GHP + + + 加纳重评估塞第 + GHR + + + 直布罗陀磅 + GIP + + + 格陵兰克朗 + GLK + + + 冈比亚达拉西 + GMD + + + 冈比亚磅 + GMP + + + 几内亚法郎 + GNF + + + 几内亚法郎 (1960-1972) + GNI + + + 几内亚Syli + GNS + + + 瓜德罗普岛法郎 + GPF + + + 赤道几内亚爱克威乐 + GQE + + + 赤道几内亚法郎 + GQF + + + 赤道几内亚匹塞塔 + GQP + + + 希腊德拉克马 + GRD + + + 希腊新德拉克马 + GRN + + + 危地马拉 + GTQ + + + 法国属圭亚那法郎 + GUF + + + 葡萄牙几内亚埃斯库多 + GWE + + + 葡萄牙几内亚迷洱瑞 + GWM + + + 几内亚比索 + GWP + + + 圭亚那元 + GYD + + + 港元 + HK$ + + + 洪都拉斯勒皮拉 + HNL + + + 克罗地亚第纳尔元 + HRD + + + 克罗地亚库娜元 + HRK + + + 海地古德 + HTG + + + 匈牙利缶瑞特 + HUF + + + 北爱尔兰磅 + IBP + + + 印度尼西亚尼卡盾 + IDG + + + 印度尼西亚爪哇盾 + IDJ + + + 印度尼西亚新盾 + IDN + + + 印度尼西亚盾 + IDR + + + 爱尔兰磅 + IEP + + + 以色列谢客尔 + ILL + + + 以色列磅 + ILP + + + 以色列新谢客尔 + ILS + + + 曼岛磅 + IMP + + + 印度卢比 + =0#Rs.|1#Re.|1<Rs. + + + 伊拉克第纳尔元 + IQD + + + 伊朗里亚 尔 + IRR + + + 冰岛克朗 + ISK + + + 意大利里拉 + ITL + + + 泽西磅 + JEP + + + 牙买加元 + JMD + + + 牙买加磅 + JMP + + + 约旦第纳尔元 + JOD + + + 日元 + JP¥ + + + 肯尼亚先令 + KES + + + 吉尔吉斯坦萨姆 + KGS + + + 柬埔寨旧里尔 + KHO + + + 柬埔寨里尔 + KHR + + + 基里巴斯元 + KID + + + 科摩罗法郎 + KMF + + + 北朝鲜人民币 + KPP + + + 北朝鲜币 + KPW + + + 韩国元 + KRH + + + 韩国旧币 + KRO + + + 韩国币 + + + + 科威特第纳尔元 + KWD + + + 开曼岛元 + KYD + + + 哈萨克卢布 + KZR + + + 哈萨克腾额 + KZT + + + 老挝基普 + LAK + + + 黎巴嫩磅 + LBP + + + 列支敦士登法郎 + LIF + + + 斯里兰卡卢比 + LKR + + + 锡兰卢比 + LNR + + + 利比亚元 + LRD + + + 莱索托 + LSL + + + 立陶宛利塔 + LTL + + + 立陶宛塔咯呐司 + LTT + + + 卢森堡法郎 + LUF + + + 拉脱维亚拉特 + LVL + + + 拉脱维亚卢布 + LVR + + + 利比亚英国军队军方里拉 + LYB + + + 利比亚第纳尔元 + LYD + + + 利比亚磅 + LYP + + + 摩洛哥迪拉姆 + MAD + + + 摩洛哥法郎 + MAF + + + 摩洛哥新法郎 + MCF + + + 摩洛哥革命时期货币 + MCG + + + 南特市列伊币 + MDC + + + 南特市列伊 + MDL + + + 南特市卢布 + MDR + + + 马达加斯加阿日瑞 + MGA + + + 马达加斯加法郎 + MGF + + + 马绍尔群岛元 + MHD + + + 马其顿戴纳 + MKD + + + 马其顿戴纳 (1992-1993) + MKN + + + 马里法郎 + MLF + + + 缅甸开亚特 + MMK + + + 缅甸元外汇券 + MMX + + + 蒙古图格里克 + MNT + + + 澳门元 + P + + + 马蒂尼法郎 + MQF + + + 里塔尼亚乌吉亚 + MRO + + + 马尔他里拉 + MTL + + + 马尔他磅 + MTP + + + 毛里求斯卢比 + MUR + + + 马尔代夫群岛卢比 + MVP + + + 马尔代夫群岛芦菲亚 + MVR + + + 马拉维夸恰 + MWK + + + 马拉维磅 + MWP + + + 墨西哥比索 + MXN + + + 墨西哥银比索 (1861-1992) + MXP + + + 马来西亚币 + MYR + + + 莫桑比克埃斯库多 + MZE + + + 莫桑比克币 + MZM + + + 纳米比亚元 + NAD + + + 新卡里多尼亚新法郎 + NCF + + + 尼日利亚奈拉. + NGN + + + 尼日利亚磅 + NGP + + + 新赫布里底 CFP 法郎 + NHF + + + 尼加拉瓜科多巴 + NIC + + + 尼加拉瓜金科多巴 + NIG + + + 尼加拉瓜金哥多华 + NIO + + + 荷兰盾 + NLG + + + 挪威克朗 + NOK + + + 尼泊尔卢比 + NPR + + + 新西兰元 + NZD + + + 新西兰磅 + NZP + + + 阿曼里尔 + OMR + + + 阿曼里尔塞迪 + OMS + + + 巴拿马巴波亚 + PAB + + + 车城卢布券 + PDK + + + 车城新卢布 + PDN + + + 车城卢布 + PDR + + + 秘鲁因蒂 + PEI + + + 秘鲁索额奴艾挝 + PEN + + + 秘鲁索额 + PES + + + 巴布亚新几内亚基那 + PGK + + + 菲律宾比索 + PHP + + + 巴基斯坦卢比 + PKR + + + 波兰兹罗提 + PLN + + + 波兰美元 外汇券 + PLX + + + 波兰兹罗提 (1950-1995) + PLZ + + + 巴勒斯坦磅 + PSP + + + 葡萄牙倥涂 + PTC + + + 葡萄牙铃木 + PTE + + + 巴拉圭币 + PYG + + + 卡塔尔里亚尔 + QAR + + + 留尼汪联合会法郎 + REF + + + 罗马尼亚镭 + ROL + + + 罗马尼亚新镭 + RON + + + 俄国卢布 + RUB + + + 俄国卢布 (1991-1998) + RUR + + + 卢旺达法郎 + RWF + + + 沙特里亚尔 + SRl + + + 沙特特权里亚尔 + SAS + + + 所罗门群岛元 + SBD + + + 塞舌尔卢比 + SCR + + + 苏丹第纳尔元 + SDD + + + 苏丹磅 + SDP + + + 瑞士克朗 + SEK + + + 新加坡元 + S$ + + + 圣赫勒拿磅 + SHP + + + 斯洛文尼亚淘拉磅 + SIB + + + 斯洛文尼亚淘拉 + SIT + + + 斯洛伐克科路那 + SKK + + + 赛拉里昂币 + SLL + + + 圣马利诺里拉 + SML + + + 索马里先令 + SOS + + + 索马里大陆先令 + SQS + + + 苏里南基尔 + SRG + + + 苏格兰磅 + SSP + + + 圣多美普林西比都比拉 + STD + + + 圣多美普林西比铃木 + STE + + + 苏联新卢布 + SUN + + + 苏联卢布 + SUR + + + 萨尔瓦多科洛涅 + SVC + + + 叙利亚磅 + SYP + + + 斯威士兰币 + SZL + + + 特克斯和凯科斯群岛克朗 + TCC + + + 泰铢 + THB + + + 塔吉克斯坦卢布 + TJR + + + 塔吉克斯坦索莫尼 + TJS + + + 土库曼斯坦币 + TMM + + + 突尼斯第纳尔元 + TND + + + 汤加币 + TOP + + + 汤加磅 + TOS + + + 帝汶埃斯库多 + TPE + + + 帝汶帕塔卡 + TPP + + + 土耳其里拉 + TRL + + + 特立尼达和多巴哥元 + TTD + + + 特立尼达和多巴哥旧元 + TTO + + + 图瓦卢元 + TVD + + + 新台币 + NT$ + + + 坦桑尼亚先令 + TZS + + + 乌克兰赫里纳 + UAH + + + 乌克兰币 + UAK + + + 乌干达先令 (1966-1987) + UGS + + + 乌干达先令 + UGX + + + 美元 + US$ + + + 美元 (下一天) + USN + + + 美元 (同一天) + USS + + + 乌拉圭比索伏尔特 + UYF + + + 乌拉圭比索 (1975-1993) + UYP + + + 乌拉圭比索 + UYU + + + 乌兹别克斯坦货币券 + UZC + + + 乌兹别克斯坦币 + UZS + + + 梵谛冈里拉 + VAL + + + 北越皮艾斯特盾 + VDD + + + 北越皮艾斯特新盾 + VDN + + + 北越皮艾斯特明盾 + VDP + + + 内瑞拉博利瓦 + VEB + + + 英国维京群岛币 + VGD + + + 越南盾 + VND + + + 越南明盾 + VNN + + + 越南共和国盾 + VNR + + + 越南国家盾 + VNS + + + 瓦努阿图 + VUV + + + 西萨摩亚磅 + WSP + + + 西萨摩亚塔拉 + WST + + + 亚洲第纳尔元帐户单位 + XAD + + + 亚洲货币单位 + XAM + + + 黄金 + XAU + + + 欧洲复合单位 + XBA + + + 欧洲金融单位 + XBB + + + 东加勒比元 + XCD + + + 特别提款权 + XDR + + + 欧洲货币单位 + XEU + + + 法国金法郎 + XFO + + + 伊斯兰第纳尔元 + XID + + + 法国城市偌佛法郎 + XMF + + + 法国安的列斯 CFA 法郎 + XNF + + + 也门第纳尔元 + YDD + + + 也门阿马迪里尔 + YEI + + + 也门里尔 + YER + + + 南斯拉夫硬第纳尔元 + YUD + + + 南斯拉夫联邦第纳尔元 + YUF + + + 南斯拉夫 1994 第纳尔元 + YUG + + + 南斯拉夫偌威第纳尔元 + YUM + + + 南斯拉夫兑换第纳尔元 + YUN + + + 南斯拉夫十月第纳尔元 + YUO + + + 南斯拉夫改革第纳尔元 + YUR + + + 南非兰特 (金融) + ZAL + + + 南非磅 + ZAP + + + 南非兰特 + ZAR + + + 赞比亚马拉维 + ZMK + + + 赞比亚磅 + ZMP + + + 新扎伊尔元 + ZRN + + + 扎伊尔元 + ZRZ + + + 津巴布韦元 + ZWD + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/fallbackcollator.py zope3-3.5~bzr18/src/zope/i18n/locales/fallbackcollator.py --- zope3-3.4.0/src/zope/i18n/locales/fallbackcollator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/fallbackcollator.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,33 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Fallback collator + +$Id: fallbackcollator.py 41114 2006-01-03 19:38:42Z jim $ +""" + +from unicodedata import normalize + +class FallbackCollator: + + def __init__(self, locale): + pass + + def key(self, s): + s = normalize('NFKC', s) + return s.lower(), s + + def cmp(self, s1, s2): + return cmp(self.key(s1), self.key(s2)) + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/fallbackcollator.txt zope3-3.5~bzr18/src/zope/i18n/locales/fallbackcollator.txt --- zope3-3.4.0/src/zope/i18n/locales/fallbackcollator.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/fallbackcollator.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,63 @@ +Fallback Collator +================= + +The zope.i18n.interfaces.locales.ICollator interface defines an API +for collating text. Why is this important? Simply sorting unicode +strings doesn't provide an ordering that users in a given locale will +fine useful. Various languages have text sorting conventions that +don't agree with the ordering of unicode code points. (This is even +true for English. :) + +Text collation is a fairly involved process. Systems that need this, +will likely use something like ICU +(http://www-306.ibm.com/software/globalization/icu, +http://pyicu.osafoundation.org/). We don't want to introduce a +dependency on ICU and this time, so we are providing a fallback +collator that: + +- Provides an implementation of the ICollator interface that can be + used for development, and + +- Provides a small amount of value, at least for English speakers. :) + +Application code should obtain a collator by adapting a locale to +ICollator. Here we just call the collator factory with None. The +fallback collator doesn't actually use the locale, although +application code should certainly *not* count on this. + + >>> import zope.i18n.locales.fallbackcollator + >>> collator = zope.i18n.locales.fallbackcollator.FallbackCollator(None) + +Now, we can pass the collator's key method to sort functions to sort +strings in a slightly friendly way: + + >>> sorted([u'Sam', u'sally', u'Abe', u'alice', u'Terry', u'tim'], + ... key=collator.key) + [u'Abe', u'alice', u'sally', u'Sam', u'Terry', u'tim'] + + +The collator has a very simple algorithm. It normalizes strings and +then returns a tuple with the result of lower-casing the normalized +string and the normalized string. We can see this by calling the key +method, which converts unicode strings to collation keys: + + >>> collator.key(u'Sam') + (u'sam', u'Sam') + + >>> collator.key(u'\xc6\xf8a\u030a') + (u'\xe6\xf8\xe5', u'\xc6\xf8\xe5') + +There is also a cmp function for comparing strings: + + >>> collator.cmp(u'Terry', u'sally') + 1 + + + >>> collator.cmp(u'sally', u'Terry') + -1 + + >>> collator.cmp(u'terry', u'Terry') + 1 + + >>> collator.cmp(u'terry', u'terry') + 0 diff -Nru zope3-3.4.0/src/zope/i18n/locales/inheritance.py zope3-3.5~bzr18/src/zope/i18n/locales/inheritance.py --- zope3-3.4.0/src/zope/i18n/locales/inheritance.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/inheritance.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,225 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Locale Inheritance Support + +This module provides support for locale inheritance. + +Note: In many respects this is similar to Zope 2's acquisition model, since +locale inheritance is not inheritance in the programming sense. + +$Id: inheritance.py 70897 2006-10-24 08:21:55Z flox $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import implements +from zope.i18n.interfaces.locales import \ + ILocaleInheritance, IAttributeInheritance, IDictionaryInheritance + +class NoParentException(AttributeError): + pass + +class Inheritance(object): + """A simple base version of locale inheritance. + + This object contains some shared code amongst the various + 'ILocaleInheritance' implementations. + """ + + implements(ILocaleInheritance) + + # See zope.i18n.interfaces.locales.ILocaleInheritance + __parent__ = None + + # See zope.i18n.interfaces.locales.ILocaleInheritance + __name__ = None + + def getInheritedSelf(self): + """See zope.i18n.interfaces.locales.ILocaleInheritance""" + if self.__parent__ is None: + raise NoParentException('No parent was specified.') + parent = self.__parent__.getInheritedSelf() + if isinstance(parent, dict): + return parent[self.__name__] + return getattr(parent, self.__name__) + + +class AttributeInheritance(Inheritance): + r"""Implementation of locale inheritance for attributes. + + Example:: + + >>> from zope.i18n.locales.tests.test_docstrings import \ + ... LocaleInheritanceStub + + >>> root = LocaleInheritanceStub() + >>> root.data = 'value' + >>> root.attr = 'bar value' + >>> root.data2 = AttributeInheritance() + >>> root.data2.attr = 'value2' + + >>> locale = LocaleInheritanceStub(root) + >>> locale.attr = 'foo value' + >>> locale.data2 = AttributeInheritance() + + Here is an attribute lookup directly from the locale:: + + >>> locale.data + 'value' + >>> locale.attr + 'foo value' + + ... however, we can also have any amount of nesting:: + + >>> locale.data2.attr + 'value2' + + Once we have looked up a particular attribute, it should be cached, + i.e. exist in the dictionary of this inheritance object:: + + >>> 'attr' in locale.data2.__dict__ + True + >>> locale.data2.__dict__['attr'] + 'value2' + + Make sure that None can be assigned as value as well:: + + >>> locale.data2.attr = None + >>> locale.data2.attr is None + True + """ + + implements(IAttributeInheritance) + + def __setattr__(self, name, value): + """See zope.i18n.interfaces.locales.ILocaleInheritance""" + # If we have a value that can also inherit data from other locales, we + # set its parent and name, so that we know how to get to it. + if (ILocaleInheritance.providedBy(value) and + not name.startswith('__')): + value.__parent__ = self + value.__name__ = name + super(AttributeInheritance, self).__setattr__(name, value) + + + def __getattr__(self, name): + """See zope.i18n.interfaces.locales.ILocaleInheritance""" + try: + selfUp = self.getInheritedSelf() + except NoParentException: + # There was simply no parent anymore, so let's raise an error + # for good + raise AttributeError("'%s' object (or any of its parents) has no " + "attribute '%s'" % (self.__class__.__name__, + name)) + else: + value = getattr(selfUp, name) + # Since a locale hierarchy never changes after startup, we can + # cache the value locally, saving the time to ever look it up + # again. + # Note that we cannot use the normal setattr function, since + # __setattr__ of this class tries to assign a parent and name, + # which we do not want to override. + super(AttributeInheritance, self).__setattr__(name, value) + return value + + + +class InheritingDictionary(Inheritance, dict): + """Implementation of a dictionary that can also inherit values. + + Example:: + + >>> from zope.i18n.locales.tests.test_docstrings import \\ + ... LocaleInheritanceStub + + >>> root = LocaleInheritanceStub() + >>> root.data = InheritingDictionary({1: 'one', 2: 'two', 3: 'three'}) + >>> root.data2 = AttributeInheritance() + >>> root.data2.dict = InheritingDictionary({1: 'i', 2: 'ii', 3: 'iii'}) + + >>> locale = LocaleInheritanceStub(root) + >>> locale.data = InheritingDictionary({1: 'eins'}) + >>> locale.data2 = AttributeInheritance() + >>> locale.data2.dict = InheritingDictionary({1: 'I'}) + + Here is a dictionary lookup directly from the locale:: + + >>> locale.data[1] + 'eins' + >>> locale.data[2] + 'two' + + ... however, we can also have any amount of nesting:: + + >>> locale.data2.dict[1] + 'I' + >>> locale.data2.dict[2] + 'ii' + + We also have to overwrite 'get()', 'keys()' and 'items()' since we want + to make sure that all upper locales are consulted before returning the + default or to construct the list of elements, respectively:: + + >>> locale.data2.dict.get(2) + 'ii' + >>> locale.data2.dict.get(4) is None + True + >>> locale.data.keys() + [1, 2, 3] + >>> locale.data.items() + [(1, 'eins'), (2, 'two'), (3, 'three')] + """ + + implements(IDictionaryInheritance) + + def __setitem__(self, name, value): + """See zope.i18n.interfaces.locales.ILocaleInheritance""" + if ILocaleInheritance.providedBy(value): + value.__parent__ = self + value.__name__ = name + super(InheritingDictionary, self).__setitem__(name, value) + + def __getitem__(self, name): + """See zope.i18n.interfaces.locales.ILocaleInheritance""" + if not self.has_key(name): + try: + selfUp = self.getInheritedSelf() + except NoParentException: + pass + else: + return selfUp.__getitem__(name) + return super(InheritingDictionary, self).__getitem__(name) + + def get(self, name, default=None): + """See zope.i18n.interfaces.locales.ILocaleInheritance""" + try: + return self[name] + except KeyError: + return default + + def items(self): + try: + d = dict(self.getInheritedSelf()) + except NoParentException: + d = {} + d.update(self) + return d.items() + + def keys(self): + return [item[0] for item in self.items()] + + def value(self): + return [item[1] for item in self.items()] + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/__init__.py zope3-3.5~bzr18/src/zope/i18n/locales/__init__.py --- zope3-3.4.0/src/zope/i18n/locales/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,683 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Locale and LocaleProvider Implementation. + +$Id: __init__.py 97333 2009-02-27 09:11:16Z nadako $ +""" +__docformat__ = 'restructuredtext' + +import os +from datetime import datetime, date +from time import strptime + +from zope.interface import implements +from zope.i18n.interfaces.locales import ILocale +from zope.i18n.interfaces.locales import ILocaleDisplayNames, ILocaleDates +from zope.i18n.interfaces.locales import ILocaleVersion, ILocaleIdentity +from zope.i18n.interfaces.locales import ILocaleTimeZone, ILocaleCalendar +from zope.i18n.interfaces.locales import ILocaleCurrency, ILocaleNumbers +from zope.i18n.interfaces.locales import ILocaleFormat, ILocaleFormatLength +from zope.i18n.interfaces.locales import ILocaleOrientation +from zope.i18n.interfaces.locales import ILocaleDayContext, ILocaleMonthContext +from zope.i18n.format import NumberFormat, DateTimeFormat +from zope.i18n.locales.inheritance import \ + AttributeInheritance, InheritingDictionary, NoParentException +from zope.i18n.locales.provider import LocaleProvider, LoadLocaleError + +# Setup the locale directory +from zope import i18n +LOCALEDIR = os.path.join(os.path.dirname(i18n.__file__), "locales", "data") + +# Global LocaleProvider. We really just need this single one. +locales = LocaleProvider(LOCALEDIR) + +# Define some constants that can be used + +JANUARY = 1 +FEBRUARY = 2 +MARCH = 3 +APRIL = 4 +MAY = 5 +JUNE = 6 +JULY = 7 +AUGUST = 8 +SEPTEMBER = 9 +OCTOBER = 10 +NOVEMBER = 11 +DECEMBER = 12 + +MONDAY = 1 +TUESDAY = 2 +WEDNESDAY = 3 +THURSDAY = 4 +FRIDAY = 5 +SATURDAY = 6 +SUNDAY = 7 + +dayMapping = {'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, + 'fri': 5, 'sat': 6, 'sun': 7} + +BC = 1 +AD = 2 + +calendarAliases = {'islamic': ('arabic',), + 'islamic-civil': ('civil-arabic',), + 'buddhist': ('thai-buddhist', )} + +class LocaleIdentity(object): + """Represents a unique identification of the locale + + This class does not have to deal with inheritance. + + Examples:: + + >>> id = LocaleIdentity('en') + >>> id + + + >>> id = LocaleIdentity('en', 'latin') + >>> id + + + >>> id = LocaleIdentity('en', 'latin', 'US') + >>> id + + + >>> id = LocaleIdentity('en', 'latin', 'US', 'POSIX') + >>> id + + + >>> id = LocaleIdentity('en', None, 'US', 'POSIX') + >>> id + + """ + implements(ILocaleIdentity) + + def __init__(self, language=None, script=None, territory=None, variant=None): + """Initialize object.""" + self.language = language + self.script = script + self.territory = territory + self.variant = variant + + def __repr__(self): + """See zope.i18n.interfaces.ILocaleIdentity + """ + return "" %( + self.language, self.script, self.territory, self.variant) + + +class LocaleVersion(object): + """Represents a particular version of a locale + + This class does not have to deal with inheritance. + + Examples:: + + >>> cmp(LocaleVersion('1.0', datetime(2004, 1, 1), 'no notes'), + ... LocaleVersion('1.0', datetime(2004, 1, 1), 'no notes again')) + 0 + + >>> cmp(LocaleVersion('1.0', datetime(2004, 1, 1), 'no notes'), + ... LocaleVersion('1.0', datetime(2004, 1, 2), 'no notes again')) + -1 + + >>> cmp(LocaleVersion('1.0', datetime(2004, 1, 1), 'no notes'), + ... LocaleVersion('0.9', datetime(2004, 1, 2), 'no notes again')) + -1 + + >>> cmp(LocaleVersion('1.0', datetime(2004, 1, 1), 'no notes'), + ... LocaleVersion('0.9', datetime(2004, 1, 1), 'no notes again')) + 1 + + """ + implements(ILocaleVersion) + + def __init__(self, number, generationDate, notes): + """Initialize object.""" + self.number = number + assert(isinstance(generationDate, (date, type(None)))) + self.generationDate = generationDate + self.notes = notes + + def __cmp__(self, other): + "See zope.i18n.interfaces.ILocaleVersion" + return cmp((self.generationDate, self.number), + (other.generationDate, other.number)) + + +class LocaleDisplayNames(AttributeInheritance): + """Locale display names with inheritable data. + + Examples:: + + >>> from zope.i18n.locales.tests.test_docstrings import \\ + ... LocaleInheritanceStub + >>> root = LocaleInheritanceStub() + >>> root.displayNames = LocaleDisplayNames() + >>> root.displayNames.languages = ['en', 'de'] + >>> root.displayNames.keys = ['foo', 'bar'] + + >>> locale = LocaleInheritanceStub(nextLocale=root) + >>> locale.displayNames = LocaleDisplayNames() + >>> locale.displayNames.keys = ['fu', 'bahr'] + + Here you can see the inheritance in action:: + + >>> locale.displayNames.languages + ['en', 'de'] + >>> locale.displayNames.keys + ['fu', 'bahr'] + """ + implements(ILocaleDisplayNames) + + +class LocaleTimeZone(object): + """Specifies one of the timezones of a specific locale. + + The attributes of this class are not inherited, since all timezone + information is always provided together. + + Example:: + + >>> tz = LocaleTimeZone('Europe/Berlin') + >>> tz.cities = ['Berlin'] + >>> tz.names = {'standard': ('Mitteleuropaeische Zeit', 'MEZ'), + ... 'daylight': ('Mitteleuropaeische Sommerzeit', 'MESZ')} + + >>> tz.type + 'Europe/Berlin' + >>> tz.cities + ['Berlin'] + """ + implements(ILocaleTimeZone) + + def __init__(self, type): + """Initialize the object.""" + self.type = type + self.cities = [] + self.names = {} + + +class LocaleFormat(object): + """Specifies one of the format of a specific format length. + + The attributes of this class are not inherited, since all format + information is always provided together. Note that this information by + itself is often not useful, since other calendar data is required to use + the specified pattern for formatting and parsing. + """ + implements(ILocaleFormat) + + def __init__(self, type=None): + """Initialize the object.""" + self.type = type + self.displayName = u'' + self.pattern = u'' + + +class LocaleFormatLength(AttributeInheritance): + """Specifies one of the format lengths of a specific quantity, like + numbers, dates, times and datetimes.""" + + implements(ILocaleFormatLength) + + def __init__(self, type=None): + """Initialize the object.""" + self.type = type + self.default = None + + +class LocaleMonthContext(AttributeInheritance): + + implements(ILocaleMonthContext) + + def __init__(self, type=None): + """Initialize the object.""" + self.type = type + self.default = u'wide' + + +class LocaleDayContext(AttributeInheritance): + + implements(ILocaleDayContext) + + def __init__(self, type=None): + """Initialize the object.""" + self.type = type + self.default = u'wide' + + +class LocaleCalendar(AttributeInheritance): + """Represents locale data for a calendar, like 'gregorian'. + + This object is particular tricky, since the calendar not only inherits + from higher-up locales, but also from the specified gregorian calendar + available for this locale. This was done, since most other calendars have + different year and era data, but everything else remains the same. + + Example: + + Even though the 'Locale' object has no 'calendar' attribute for real, it + helps us here to make the example simpler. + + >>> from zope.i18n.locales.tests.test_docstrings import \\ + ... LocaleInheritanceStub + >>> root = LocaleInheritanceStub() + >>> root.calendar = LocaleCalendar('gregorian') + >>> locale = LocaleInheritanceStub(nextLocale=root) + >>> locale.calendar = LocaleCalendar('gregorian') + + >>> root.calendar.months = InheritingDictionary( + ... {1: (u'January', u'Jan'), 2: (u'February', u'Feb')}) + >>> locale.calendar.months = InheritingDictionary( + ... {2: (u'Februar', u'Feb'), 3: (u'Maerz', u'Mrz')}) + >>> locale.calendar.getMonthNames()[:4] + [u'January', u'Februar', u'Maerz', None] + >>> locale.calendar.getMonthTypeFromName(u'January') + 1 + >>> locale.calendar.getMonthTypeFromName(u'Februar') + 2 + >>> locale.calendar.getMonthAbbreviations()[:4] + [u'Jan', u'Feb', u'Mrz', None] + >>> locale.calendar.getMonthTypeFromAbbreviation(u'Jan') + 1 + >>> locale.calendar.getMonthTypeFromAbbreviation(u'Mrz') + 3 + + >>> root.calendar.days = InheritingDictionary( + ... {1: (u'Monday', u'Mon'), 2: (u'Tuesday', u'Tue')}) + >>> locale.calendar.days = InheritingDictionary( + ... {2: (u'Dienstag', u'Die'), 3: (u'Mittwoch', u'Mit')}) + >>> locale.calendar.getDayNames()[:4] + [u'Monday', u'Dienstag', u'Mittwoch', None] + >>> locale.calendar.getDayTypeFromName(u'Monday') + 1 + >>> locale.calendar.getDayTypeFromName(u'Dienstag') + 2 + >>> locale.calendar.getDayAbbreviations()[:4] + [u'Mon', u'Die', u'Mit', None] + >>> locale.calendar.getDayTypeFromAbbreviation(u'Mon') + 1 + >>> locale.calendar.getDayTypeFromAbbreviation(u'Die') + 2 + + Let's test the direct attribute access as well. + + >>> root.am = u'AM' + >>> root.pm = u'PM' + >>> locale.pm = u'nachm.' + >>> locale.pm + u'nachm.' + >>> locale.am + u'AM' + """ + implements(ILocaleCalendar) + + def __init__(self, type): + """Initialize the object.""" + self.type = type + + def getMonthNames(self): + """See zope.i18n.interfaces.ILocaleCalendar""" + return [self.months.get(type, (None, None))[0] for type in range(1, 13)] + + def getMonthTypeFromName(self, name): + """See zope.i18n.interfaces.ILocaleCalendar""" + for item in self.months.items(): + if item[1][0] == name: + return item[0] + + def getMonthAbbreviations(self): + """See zope.i18n.interfaces.ILocaleCalendar""" + return [self.months.get(type, (None, None))[1] for type in range(1, 13)] + + def getMonthTypeFromAbbreviation(self, abbr): + """See zope.i18n.interfaces.ILocaleCalendar""" + for item in self.months.items(): + if item[1][1] == abbr: + return item[0] + + def getDayNames(self): + """See zope.i18n.interfaces.ILocaleCalendar""" + return [self.days.get(type, (None, None))[0] for type in range(1, 8)] + + def getDayTypeFromName(self, name): + """See zope.i18n.interfaces.ILocaleCalendar""" + for item in self.days.items(): + if item[1][0] == name: + return item[0] + + def getDayAbbreviations(self): + """See zope.i18n.interfaces.ILocaleCalendar""" + return [self.days.get(type, (None, None))[1] for type in range(1, 8)] + + def getDayTypeFromAbbreviation(self, abbr): + """See zope.i18n.interfaces.ILocaleCalendar""" + for item in self.days.items(): + if item[1][1] == abbr: + return item[0] + + def isWeekend(self, datetime): + """See zope.i18n.interfaces.ILocaleCalendar""" + day = datetime.weekday() + time = datetime.time() + # TODO: Implement this method + return False + + def getFirstWeekDayName(self): + """See zope.i18n.interfaces.ILocaleCalendar""" + return self.days[dayMapping[self.week['firstDay']]][0] + + +class LocaleDates(AttributeInheritance): + """Simple ILocaleDates implementation that can inherit data from other + locales. + + Examples:: + + >>> from zope.i18n.tests.test_formats import LocaleCalendarStub as Stub + >>> from datetime import datetime, date, time + >>> dates = LocaleDates() + >>> cal = LocaleCalendar('gregorian') + >>> cal.months = Stub.months + >>> cal.days = Stub.days + >>> cal.am = Stub.am + >>> cal.pm = Stub.pm + >>> cal.eras = Stub.eras + >>> cal.week = {'firstDay': 1, 'minDays': 1} + >>> dates.calendars = {'gregorian': cal} + + Setting up and accessing date format through a specific length + (very common scenario):: + + >>> fulllength = LocaleFormatLength() + >>> format = LocaleFormat() + >>> format.pattern = u'EEEE, d. MMMM yyyy' + >>> fulllength.formats = {None: format} + + >>> mediumlength = LocaleFormatLength() + >>> format = LocaleFormat() + >>> format.pattern = u'dd.MM.yyyy' + >>> mediumlength.formats = {None: format} + + >>> cal.dateFormats = {'full': fulllength, 'medium': mediumlength} + >>> cal.defaultDateFormat = 'medium' + + >>> formatter = dates.getFormatter('date') + >>> formatter.format(date(2004, 02, 04)) + u'04.02.2004' + + >>> formatter = dates.getFormatter('date', length='full') + >>> formatter.format(date(2004, 02, 04)) + u'Mittwoch, 4. Februar 2004' + + Let's also test the time formatter:: + + >>> fulllength = LocaleFormatLength() + >>> format = LocaleFormat() + >>> format.pattern = u"H:mm' Uhr 'z" + >>> fulllength.formats = {None: format} + + >>> mediumlength = LocaleFormatLength() + >>> format = LocaleFormat() + >>> format.pattern = u'HH:mm:ss' + >>> mediumlength.formats = {None: format} + + >>> cal.timeFormats = {'full': fulllength, 'medium': mediumlength} + >>> cal.defaultTimeFormat = 'medium' + + >>> formatter = dates.getFormatter('time') + >>> formatter.format(time(12, 15, 00)) + u'12:15:00' + + >>> formatter = dates.getFormatter('time', length='full') + >>> formatter.format(time(12, 15, 00)) + u'12:15 Uhr +000' + + The datetime formatter is a bit special, since it is constructed from + the other two:: + + >>> length = LocaleFormatLength() + >>> format = LocaleFormat() + >>> format.pattern = u'{1} {0}' + >>> length.formats = {None: format} + >>> cal.dateTimeFormats = {None: length} + + >>> formatter = dates.getFormatter('dateTime') + >>> formatter.format(datetime(2004, 02, 04, 12, 15, 00)) + u'04.02.2004 12:15:00' + + >>> formatter = dates.getFormatter('dateTime', length='full') + >>> formatter.format(datetime(2004, 02, 04, 12, 15, 00)) + u'Mittwoch, 4. Februar 2004 12:15 Uhr +000' + + Finally, we'll test some invalid input:: + + >>> dates.getFormatter('timeDate') + Traceback (most recent call last): + ValueError: Invalid category: timeDate + + >>> dates.getFormatter('date', length='superlong') + Traceback (most recent call last): + ValueError: Invalid format length: superlong + + >>> dates.getFormatter('date', calendar='irish-catholic') + Traceback (most recent call last): + ValueError: Invalid calendar: irish-catholic + + """ + implements(ILocaleDates) + + def getFormatter(self, category, length=None, name=None, + calendar=u'gregorian'): + """See zope.i18n.interfaces.locales.ILocaleDates""" + if category not in (u'date', u'time', u'dateTime'): + raise ValueError('Invalid category: %s' % category) + if calendar not in (u'gregorian', u'arabic', u'chinese', + u'civil-arabic', u'hebrew', u'japanese', + u'thai-buddhist'): + raise ValueError('Invalid calendar: %s' % calendar) + if length not in (u'short', u'medium', u'long', u'full', None): + raise ValueError('Invalid format length: %s' % length) + + cal = self.calendars[calendar] + + formats = getattr(cal, category+'Formats') + if length is None: + length = getattr( + cal, + 'default'+category[0].upper()+category[1:]+'Format', + formats.keys()[0]) + + # 'datetime' is always a bit special; we often do not have a length + # specification, but we need it for looking up the date and time + # formatters + if category == 'dateTime': + formatLength = formats.get(length, formats[None]) + else: + formatLength = formats[length] + + if name is None: + name = formatLength.default + + format = formatLength.formats[name] + pattern = format.pattern + + if category == 'dateTime': + date_pat = self.getFormatter( + 'date', length, name, calendar).getPattern() + time_pat = self.getFormatter( + 'time', length, name, calendar).getPattern() + pattern = pattern.replace('{1}', date_pat) + pattern = pattern.replace('{0}', time_pat) + + return DateTimeFormat(pattern, cal) + + +class LocaleCurrency(object): + """Simple implementation of ILocaleCurrency without inheritance support, + since it is not needed for a single currency.""" + implements(ILocaleCurrency) + + def __init__(self, type): + """Initialize object.""" + self.type = type + self.symbol = None + self.symbolChoice = False + self.displayName = None + + +class LocaleNumbers(AttributeInheritance): + """Implementation of ILocaleCurrency including inheritance support. + + Examples:: + + >>> numbers = LocaleNumbers() + >>> numbers.symbols = { + ... 'decimal': ',', 'group': '.', 'list': ';', 'percentSign': '%', + ... 'nativeZeroDigit': '0', 'patternDigit': '#', 'plusSign': '+', + ... 'minusSign': '-', 'exponential': 'E', 'perMille': 'o/oo', + ... 'infinity': 'oo', 'nan': 'N/A'} + + Setting up and accessing totally unnamed decimal format + (very common scenario):: + + >>> length = LocaleFormatLength() + >>> format = LocaleFormat() + >>> format.pattern = u'#,##0.###;-#,##0.###' + >>> length.formats = {None: format} + >>> numbers.decimalFormats = {None: length} + >>> formatter = numbers.getFormatter('decimal') + >>> formatter.format(3.4) + u'3,4' + >>> formatter.format(-3.4567) + u'-3,457' + >>> formatter.format(3210.4) + u'3.210,4' + + Setting up and accessing scientific formats with named format lengths:: + + >>> longlength = LocaleFormatLength('long') + >>> format = LocaleFormat() + >>> format.pattern = u'0.000###E+00' + >>> longlength.formats = {None: format} + >>> mediumlength = LocaleFormatLength('long') + >>> format = LocaleFormat() + >>> format.pattern = u'0.00##E+00' + >>> mediumlength.formats = {None: format} + >>> numbers.scientificFormats = {'long': longlength, + ... 'medium': mediumlength} + >>> numbers.defaultScientificFormat = 'long' + >>> formatter = numbers.getFormatter('scientific') + >>> formatter.format(1234.5678) + u'1,234568E+03' + >>> formatter = numbers.getFormatter('scientific', 'medium') + >>> formatter.format(1234.5678) + u'1,2346E+03' + + Setting up and accessing percent formats with named format lengths + and format names:: + + >>> longlength = LocaleFormatLength('long') + >>> fooformat = LocaleFormat() + >>> fooformat.pattern = u'0.##0%' + >>> barformat = LocaleFormat() + >>> barformat.pattern = u'0%' + >>> longlength.formats = {None: fooformat, 'bar': barformat} + >>> numbers.percentFormats = {'long': longlength} + >>> numbers.defaultPercentFormat = 'long' + >>> formatter = numbers.getFormatter('percent') + >>> formatter.format(123.45678) + u'123,457%' + >>> formatter = numbers.getFormatter('percent', name='bar') + >>> formatter.format(123.45678) + u'123%' + + ...using a default name:: + + >>> numbers.percentFormats['long'].default = 'bar' + >>> formatter = numbers.getFormatter('percent') + >>> formatter.format(123.45678) + u'123%' + + """ + implements(ILocaleNumbers) + + def getFormatter(self, category, length=None, name=None): + """See zope.i18n.interfaces.locales.ILocaleNumbers""" + assert category in (u'decimal', u'percent', u'scientific', u'currency') + assert length in (u'short', u'medium', u'long', u'full', None) + + formats = getattr(self, category+'Formats') + if length is None: + length = getattr( + self, + 'default'+category[0].upper()+category[1:]+'Format', + formats.keys()[0]) + formatLength = formats[length] + + if name is None: + name = formatLength.default + + format = formatLength.formats[name] + + return NumberFormat(format.pattern, self.symbols) + + +class LocaleOrientation(AttributeInheritance): + """Implementation of ILocaleOrientation + """ + implements(ILocaleOrientation) + +class Locale(AttributeInheritance): + """Implementation of the ILocale interface.""" + implements(ILocale) + + def __init__(self, id): + self.id = id + + def getLocaleID(self): + """Return the locale id.""" + id = self.id + pieces = filter(None, + (id.language, id.script, id.territory, id.variant)) + id_string = '_'.join(pieces) + # TODO: What about keys??? Where do I get this info from? + pieces = [key+'='+type for key, type in ()] + if pieces: + id_string += '@' + ','.join(pieces) + return id_string + + def getInheritedSelf(self): + """See zope.i18n.interfaces.locales.ILocaleInheritance + + This is the really interesting method that looks up the next (more + general) Locale object. This is used in case this locale object does + not have the required information. + + This method works closely with with LocaleProvider. + """ + language = self.id.language + territory = self.id.territory + variant = self.id.variant + if variant is not None: + return locales.getLocale(language, territory, None) + elif territory is not None: + return locales.getLocale(language, None, None) + elif language is not None: + return locales.getLocale(None, None, None) + else: + # Well, this is bad; we are already at the root locale + raise NoParentException('Cannot find a more general locale.') diff -Nru zope3-3.4.0/src/zope/i18n/locales/LocaleElements.dtd zope3-3.5~bzr18/src/zope/i18n/locales/LocaleElements.dtd --- zope3-3.4.0/src/zope/i18n/locales/LocaleElements.dtd 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/LocaleElements.dtd 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,549 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/locales/provider.py zope3-3.5~bzr18/src/zope/i18n/locales/provider.py --- zope3-3.4.0/src/zope/i18n/locales/provider.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/provider.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,79 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Locale Provider + +The Locale Provider looks up locales and loads them from the XML data, if +necessary. + +$Id: provider.py 38178 2005-08-30 21:50:19Z mj $ +""" +import os +from zope.interface import implements +from zope.i18n.interfaces.locales import ILocaleProvider + +class LoadLocaleError(Exception): + """This error is raised if a locale cannot be loaded.""" + + +class LocaleProvider(object): + """A locale provider that get's its data from the XML data.""" + + implements(ILocaleProvider) + + def __init__(self, locale_dir): + self._locales = {} + self._locale_dir = locale_dir + + def loadLocale(self, language=None, country=None, variant=None): + """See zope.i18n.interfaces.locales.ILocaleProvider""" + # Creating the filename + if language == None and country == None and variant == None: + filename = 'root.xml' + else: + filename = language + if country is not None: + filename += '_'+country + if variant is not None: + if '_' not in filename: + filename += '_' + filename += '_'+variant + filename += '.xml' + + # Making sure we have this locale + path = os.path.join(self._locale_dir, filename) + if not os.path.exists(path): + raise LoadLocaleError( + 'The desired locale is not available.\nPath: %s' % path) + + # Import here to avoid circular imports + from zope.i18n.locales.xmlfactory import LocaleFactory + + # Let's get it! + locale = LocaleFactory(path)() + self._locales[(language, country, variant)] = locale + + def getLocale(self, language=None, country=None, variant=None): + """See zope.i18n.interfaces.locales.ILocaleProvider""" + # We want to be liberal in what we accept, but the standard is lower + # case language codes, upper case country codes, and upper case + # variants, so coerce case here. + if language: + language = language.lower() + if country: + country = country.upper() + if variant: + variant = variant.upper() + if not self._locales.has_key((language, country, variant)): + self.loadLocale(language, country, variant) + return self._locales[(language, country, variant)] diff -Nru zope3-3.4.0/src/zope/i18n/locales/README.txt zope3-3.5~bzr18/src/zope/i18n/locales/README.txt --- zope3-3.4.0/src/zope/i18n/locales/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,32 @@ +================= +Locales XML Files +================= + +The XML locale files were produced as part of the Unicode Common Locale +Data Repository (CLDR). They are provided here under the Unicode Terms of +Use (see http://unicode.org/copyright.html). + + +CLDR Web site +------------- + + http://www.unicode.org/cldr/ + + +Locale Data Markup Language +--------------------------- + +The XML files follow the now public and completed LDML format. + +The DTD can be found at + + http://www.unicode.org/cldr/dtd/1.1/ldml.dtd + +The specification is at + + http://www.unicode.org/reports/tr35/tr35-2.html + + +Download:: + + http://www.unicode.org/cldr/repository_access.html diff -Nru zope3-3.4.0/src/zope/i18n/locales/tests/__init__.py zope3-3.5~bzr18/src/zope/i18n/locales/tests/__init__.py --- zope3-3.4.0/src/zope/i18n/locales/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Test package for locales diff -Nru zope3-3.4.0/src/zope/i18n/locales/tests/test_docstrings.py zope3-3.5~bzr18/src/zope/i18n/locales/tests/test_docstrings.py --- zope3-3.4.0/src/zope/i18n/locales/tests/test_docstrings.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/tests/test_docstrings.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,42 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 ZCML Documentation Module + +$Id: test_docstrings.py 111000 2010-04-16 19:43:57Z tseaver $ +""" +import unittest +from doctest import DocTestSuite +from zope.i18n.locales.inheritance import AttributeInheritance +from zope.i18n.locales.inheritance import NoParentException + +class LocaleInheritanceStub(AttributeInheritance): + + def __init__(self, nextLocale=None): + self.__nextLocale__ = nextLocale + + def getInheritedSelf(self): + if self.__nextLocale__ is None: + raise NoParentException('No parent was specified.') + return self.__nextLocale__ + + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite('zope.i18n.locales'), + DocTestSuite('zope.i18n.locales.inheritance'), + DocTestSuite('zope.i18n.locales.xmlfactory'), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/i18n/locales/tests/test_fallbackcollator.py zope3-3.5~bzr18/src/zope/i18n/locales/tests/test_fallbackcollator.py --- zope3-3.4.0/src/zope/i18n/locales/tests/test_fallbackcollator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/tests/test_fallbackcollator.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 unittest +import doctest + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('../fallbackcollator.txt'), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') + diff -Nru zope3-3.4.0/src/zope/i18n/locales/tests/test_locales.py zope3-3.5~bzr18/src/zope/i18n/locales/tests/test_locales.py --- zope3-3.4.0/src/zope/i18n/locales/tests/test_locales.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/tests/test_locales.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,171 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 module tests the LocaleProvider and everything that goes with it. + +$Id: test_locales.py 40085 2005-11-13 18:56:35Z srichter $ +""" +import os +import datetime +from unittest import TestCase, TestSuite, makeSuite, main + +from zope.i18n.interfaces.locales import ILocaleProvider +from zope.i18n.locales import locales +from zope.i18n.locales.provider import LocaleProvider, LoadLocaleError + +import zope.i18n +datadir = os.path.join(os.path.dirname(zope.i18n.__file__), 'locales', 'data') + +class TestILocaleProvider(TestCase): + """Test the functionality of an implmentation of the ILocaleProvider + interface.""" + + def _makeNewProvider(self): + raise NotImplemented + + def setUp(self): + self.locales = self._makeNewProvider() + + def testInterfaceConformity(self): + self.assert_(ILocaleProvider.providedBy(self.locales)) + + def test_getLocale(self): + locale = self.locales.getLocale(None, None, None) + self.assertEqual(locale.id.language, None) + self.assertEqual(locale.id.territory, None) + self.assertEqual(locale.id.variant, None) + + locale = self.locales.getLocale('en', None, None) + self.assertEqual(locale.id.language, 'en') + self.assertEqual(locale.id.territory, None) + self.assertEqual(locale.id.variant, None) + + locale = self.locales.getLocale('en', 'US', None) + self.assertEqual(locale.id.language, 'en') + self.assertEqual(locale.id.territory, 'US') + self.assertEqual(locale.id.variant, None) + + locale = self.locales.getLocale('en', 'US', 'POSIX') + self.assertEqual(locale.id.language, 'en') + self.assertEqual(locale.id.territory, 'US') + self.assertEqual(locale.id.variant, 'POSIX') + + +class TestLocaleProvider(TestILocaleProvider): + + def _makeNewProvider(self): + return LocaleProvider(datadir) + + def test_loadLocale(self): + self.locales.loadLocale(None, None, None) + self.assertEqual(self.locales._locales.keys(), [(None, None, None)]) + + self.locales.loadLocale('en', None, None) + self.assert_(('en', None, None) in self.locales._locales.keys()) + + def test_loadLocaleFailure(self): + self.assertRaises(LoadLocaleError, self.locales.loadLocale, 'zzz') + + +class TestLocaleAndProvider(TestCase): + + # Set the locale on the class so that test cases don't have + # to pay to construct a new one each time. + + locales.loadLocale(None, None, None) + locales.loadLocale('en', None, None) + locales.loadLocale('en', 'US', None) + locales.loadLocale('en', 'US', 'POSIX') + locale = locales.getLocale('en', 'US', 'POSIX') + + def test_getTimeFormatter(self): + formatter = self.locale.dates.getFormatter('time', 'medium') + self.assertEqual(formatter.getPattern(), 'h:mm:ss a') + self.assertEqual(formatter.format(datetime.time(12, 30, 10)), + '12:30:10 PM') + self.assertEqual(formatter.parse('12:30:10 PM'), + datetime.time(12, 30, 10)) + + def test_getDateFormatter(self): + formatter = self.locale.dates.getFormatter('date', 'medium') + self.assertEqual(formatter.getPattern(), 'MMM d, yyyy') + self.assertEqual(formatter.format(datetime.date(2003, 01, 02)), + 'Jan 2, 2003') + self.assertEqual(formatter.parse('Jan 2, 2003'), + datetime.date(2003, 01, 02)) + + def test_getDateTimeFormatter(self): + formatter = self.locale.dates.getFormatter('dateTime', 'medium') + self.assertEqual(formatter.getPattern(), 'MMM d, yyyy h:mm:ss a') + self.assertEqual( + formatter.format(datetime.datetime(2003, 01, 02, 12, 30)), + 'Jan 2, 2003 12:30:00 PM') + self.assertEqual(formatter.parse('Jan 2, 2003 12:30:00 PM'), + datetime.datetime(2003, 01, 02, 12, 30)) + + def test_getNumberFormatter(self): + formatter = self.locale.numbers.getFormatter('decimal') + self.assertEqual(formatter.getPattern(), '###0.###;-###0.###') + self.assertEqual(formatter.format(1234.5678), '1234.568') + self.assertEqual(formatter.format(-1234.5678), '-1234.568') + self.assertEqual(formatter.parse('1234.567'), 1234.567) + self.assertEqual(formatter.parse('-1234.567'), -1234.567) + + +class TestGlobalLocaleProvider(TestCase): + + def testLoading(self): + locales.loadLocale(None, None, None) + self.assert_(locales._locales.has_key((None, None, None))) + locales.loadLocale('en', None, None) + self.assert_(locales._locales.has_key(('en', None, None))) + locales.loadLocale('en', 'US', None) + self.assert_(locales._locales.has_key(('en', 'US', None))) + locales.loadLocale('en', 'US', 'POSIX') + self.assert_(locales._locales.has_key(('en', 'US', 'POSIX'))) + + def test_getLocale(self): + locale = locales.getLocale('en', 'GB') + self.assertEqual(locale.id.language, 'en') + self.assertEqual(locale.id.territory, 'GB') + self.assertEqual(locale.id.variant, None) + +class TestRootLocale(TestCase): + """There were some complaints that the root locale does not work + correctly, so make sure it does.""" + + locales.loadLocale(None, None, None) + locale = locales.getLocale(None, None, None) + + def test_dateFormatter(self): + formatter = self.locale.dates.getFormatter('date') + self.assertEqual( + formatter.format(datetime.date(2004, 10, 31), 'E'), '1') + self.assertEqual( + formatter.format(datetime.date(2004, 10, 31), 'EE'), '01') + self.assertEqual( + formatter.format(datetime.date(2004, 10, 31), 'EEE'), '1') + self.assertEqual( + formatter.format(datetime.date(2004, 10, 31), 'EEEE'), '1') + + +def test_suite(): + return TestSuite(( + makeSuite(TestLocaleProvider), + makeSuite(TestLocaleAndProvider), + makeSuite(TestGlobalLocaleProvider), + makeSuite(TestRootLocale), + )) + +if __name__ == "__main__": + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/i18n/locales/tests/test_xmlfactory.py zope3-3.5~bzr18/src/zope/i18n/locales/tests/test_xmlfactory.py --- zope3-3.4.0/src/zope/i18n/locales/tests/test_xmlfactory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/tests/test_xmlfactory.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,71 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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 PARTLAR PURPOSE. +# +############################################################################## +"""Testing all XML Locale functionality. + +$Id: test_xmlfactory.py 97333 2009-02-27 09:11:16Z nadako $ +""" +import os +from unittest import TestCase, TestSuite, makeSuite, main + +from zope.i18n.locales.xmlfactory import LocaleFactory +from zope.i18n.format import parseDateTimePattern, parseNumberPattern +import zope.i18n + +class LocaleXMLFileTestCase(TestCase): + """This test verifies that every locale XML file can be loaded.""" + + # only run when running tests of level 2 + level = 2 + + def __init__(self, path): + self.__path = path + TestCase.__init__(self) + + def runTest(self): + # Loading Locale object + locale = LocaleFactory(self.__path)() + + # XXX: The tests below are commented out because it's not + # necessary for the xml files to have all format definitions. + + ## Making sure all number format patterns parse + #for category in (u'decimal', u'scientific', u'percent', u'currency'): + # for length in getattr(locale.numbers, category+'Formats').values(): + # for format in length.formats.values(): + # self.assert_(parseNumberPattern(format.pattern) is not None) + + ## Making sure all datetime patterns parse + #for calendar in locale.dates.calendars.values(): + # for category in ('date', 'time', 'dateTime'): + # for length in getattr(calendar, category+'Formats').values(): + # for format in length.formats.values(): + # self.assert_( + # parseDateTimePattern(format.pattern) is not None) + + + +def test_suite(): + suite = TestSuite() + locale_dir = os.path.join(os.path.dirname(zope.i18n.__file__), + 'locales', 'data') + for path in os.listdir(locale_dir): + if not path.endswith(".xml"): + continue + path = os.path.join(locale_dir, path) + case = LocaleXMLFileTestCase(path) + suite.addTest(case) + return suite + +if __name__ == '__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/i18n/locales/xmlfactory.py zope3-3.5~bzr18/src/zope/i18n/locales/xmlfactory.py --- zope3-3.4.0/src/zope/i18n/locales/xmlfactory.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/locales/xmlfactory.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1346 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""XML Locale-related objects and functions + +$Id: xmlfactory.py 97333 2009-02-27 09:11:16Z nadako $ +""" +from datetime import datetime, date, time +from xml.dom.minidom import parse as parseXML +from zope.i18n.locales import Locale, LocaleDisplayNames, LocaleDates +from zope.i18n.locales import LocaleVersion, LocaleIdentity, LocaleTimeZone +from zope.i18n.locales import LocaleCalendar, LocaleCurrency, LocaleNumbers +from zope.i18n.locales import LocaleFormat, LocaleFormatLength, dayMapping +from zope.i18n.locales import LocaleOrientation, LocaleDayContext +from zope.i18n.locales import LocaleMonthContext, calendarAliases +from zope.i18n.locales.inheritance import InheritingDictionary + +class LocaleFactory(object): + """This class creates a Locale object from an ICU XML file.""" + + def __init__(self, path): + """Initialize factory.""" + self._path = path + # Mainly for testing + if path: + self._data = parseXML(path).documentElement + + def _getText(self, nodelist): + rc = u'' + for node in nodelist: + if node.nodeType == node.TEXT_NODE: + rc = rc + node.data + return rc + + + def _extractVersion(self, identity_node): + """Extract the Locale's version info based on data from the DOM + tree. + + Example:: + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... Some notes + ... + ... + ... + ... ''' + >>> dom = parseString(xml) + + >>> version = factory._extractVersion(dom.documentElement) + >>> version.number + u'1.0' + >>> version.generationDate + datetime.date(2003, 12, 19) + >>> version.notes + u'Some notes' + """ + number = generationDate = notes = None + # Retrieve the version number and notes of the locale + nodes = identity_node.getElementsByTagName('version') + if nodes: + number = nodes[0].getAttribute('number') + notes = self._getText(nodes[0].childNodes) + # Retrieve the generationDate of the locale + nodes = identity_node.getElementsByTagName('generation') + if nodes: + year, month, day = nodes[0].getAttribute('date').split('-') + generationDate = date(int(year), int(month), int(day)) + + return LocaleVersion(number, generationDate, notes) + + + def _extractIdentity(self): + """Extract the Locale's identity object based on info from the DOM + tree. + + Example:: + + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... + ... + ... + ... + ... ''' + >>> factory = LocaleFactory(None) + >>> factory._data = parseString(xml).documentElement + + >>> id = factory._extractIdentity() + >>> id.language + u'en' + >>> id.script is None + True + >>> id.territory + u'US' + >>> id.variant + u'POSIX' + >>> id.version.number + u'1.0' + """ + id = LocaleIdentity() + identity = self._data.getElementsByTagName('identity')[0] + # Retrieve the language of the locale + nodes = identity.getElementsByTagName('language') + if nodes != []: + id.language = nodes[0].getAttribute('type') or None + # Retrieve the territory of the locale + nodes = identity.getElementsByTagName('territory') + if nodes != []: + id.territory = nodes[0].getAttribute('type') or None + # Retrieve the varriant of the locale + nodes = identity.getElementsByTagName('variant') + if nodes != []: + id.variant = nodes[0].getAttribute('type') or None + + id.version = self._extractVersion(identity) + return id + + + def _extractTypes(self, names_node): + """Extract all types from the names_node. + + Example:: + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... BUDDHIST + ... CHINESE + ... GREGORIAN + ... STROKE + ... TRADITIONAL + ... + ... ''' + >>> dom = parseString(xml) + + >>> types = factory._extractTypes(dom.documentElement) + >>> keys = types.keys() + >>> keys.sort() + >>> keys[:2] + [(u'Fallback', u'calendar'), (u'buddhist', u'calendar')] + >>> keys[2:4] + [(u'chinese', u'calendar'), (u'gregorian', u'calendar')] + >>> keys[4:] + [(u'stroke', u'collation'), (u'traditional', u'collation')] + >>> types[(u'chinese', u'calendar')] + u'CHINESE' + >>> types[(u'stroke', u'collation')] + u'STROKE' + """ + # 'types' node has not to exist + types_nodes = names_node.getElementsByTagName('types') + if types_nodes == []: + return + # Retrieve all types + types = InheritingDictionary() + for type_node in types_nodes[0].getElementsByTagName('type'): + type = type_node.getAttribute('type') + key = type_node.getAttribute('key') + types[(type, key)] = self._getText(type_node.childNodes) + return types + + + def _extractDisplayNames(self): + """Extract all display names from the DOM tree. + + Example:: + + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... aa + ... ab + ... + ... + ... + ... + ... + ... + ... AD + ... AE + ... + ... + ... + ... POSIX + ... + ... + ... CALENDAR + ... COLLATION + ... + ... + ... BUDDHIST + ... STROKE + ... + ... + ... ''' + >>> factory = LocaleFactory(None) + >>> factory._data = parseString(xml).documentElement + + >>> names = factory._extractDisplayNames() + + >>> keys = names.languages.keys() + >>> keys.sort() + >>> keys + [u'Fallback', u'aa', u'ab'] + >>> names.languages[u'aa'] + u'aa' + + >>> keys = names.scripts.keys() + >>> keys.sort() + >>> keys + [u'Arab', u'Armn'] + >>> names.scripts[u'Arab'] + u'Arab' + + >>> keys = names.territories.keys() + >>> keys.sort() + >>> keys + [u'AD', u'AE'] + >>> names.territories[u'AD'] + u'AD' + + >>> keys = names.variants.keys() + >>> keys.sort() + >>> keys + [u'Fallback', u'POSIX'] + >>> names.variants[u'Fallback'] + u'' + + >>> keys = names.keys.keys() + >>> keys.sort() + >>> keys + [u'calendar', u'collation'] + >>> names.keys[u'calendar'] + u'CALENDAR' + + >>> names.types[(u'stroke', u'collation')] + u'STROKE' + """ + displayNames = LocaleDisplayNames() + # Neither the 'localeDisplayNames' or 'scripts' node has to exist + names_nodes = self._data.getElementsByTagName('localeDisplayNames') + if names_nodes == []: + return displayNames + + for group_tag, single_tag in (('languages', 'language'), + ('scripts', 'script'), + ('territories', 'territory'), + ('variants', 'variant'), + ('keys', 'key')): + group_nodes = names_nodes[0].getElementsByTagName(group_tag) + if group_nodes == []: + continue + # Retrieve all children + elements = InheritingDictionary() + for element in group_nodes[0].getElementsByTagName(single_tag): + type = element.getAttribute('type') + elements[type] = self._getText(element.childNodes) + setattr(displayNames, group_tag, elements) + + types = self._extractTypes(names_nodes[0]) + if types is not None: + displayNames.types = types + return displayNames + + + def _extractMonths(self, months_node, calendar): + """Extract all month entries from cal_node and store them in calendar. + + Example:: + + >>> class CalendarStub(object): + ... months = None + >>> calendar = CalendarStub() + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... + ... Januar + ... Februar + ... Maerz + ... April + ... Mai + ... Juni + ... Juli + ... August + ... September + ... Oktober + ... November + ... Dezember + ... + ... + ... Jan + ... Feb + ... Mrz + ... Apr + ... Mai + ... Jun + ... Jul + ... Aug + ... Sep + ... Okt + ... Nov + ... Dez + ... + ... + ... ''' + >>> dom = parseString(xml) + >>> factory._extractMonths(dom.documentElement, calendar) + + The contexts and widths were introduced in CLDR 1.1, the way + of getting month names is like this:: + + >>> calendar.defaultMonthContext + u'format' + + >>> ctx = calendar.monthContexts[u'format'] + >>> ctx.defaultWidth + u'wide' + + >>> names = [ctx.months[u'wide'][type] for type in xrange(1,13)] + >>> names[:7] + [u'Januar', u'Februar', u'Maerz', u'April', u'Mai', u'Juni', u'Juli'] + >>> names[7:] + [u'August', u'September', u'Oktober', u'November', u'Dezember'] + + >>> abbrs = [ctx.months[u'abbreviated'][type] for type in xrange(1,13)] + >>> abbrs[:6] + [u'Jan', u'Feb', u'Mrz', u'Apr', u'Mai', u'Jun'] + >>> abbrs[6:] + [u'Jul', u'Aug', u'Sep', u'Okt', u'Nov', u'Dez'] + + The old, CLDR 1.0 way of getting month names and abbreviations:: + + >>> names = [calendar.months.get(type, (None, None))[0] + ... for type in range(1, 13)] + >>> names[:7] + [u'Januar', u'Februar', u'Maerz', u'April', u'Mai', u'Juni', u'Juli'] + >>> names[7:] + [u'August', u'September', u'Oktober', u'November', u'Dezember'] + + >>> abbrs = [calendar.months.get(type, (None, None))[1] + ... for type in range(1, 13)] + >>> abbrs[:6] + [u'Jan', u'Feb', u'Mrz', u'Apr', u'Mai', u'Jun'] + >>> abbrs[6:] + [u'Jul', u'Aug', u'Sep', u'Okt', u'Nov', u'Dez'] + + + """ + + defaultMonthContext_node = months_node.getElementsByTagName('default') + if defaultMonthContext_node: + calendar.defaultMonthContext = defaultMonthContext_node[0].getAttribute('type') + + monthContext_nodes = months_node.getElementsByTagName('monthContext') + if not monthContext_nodes: + return + + calendar.monthContexts = InheritingDictionary() + names_node = abbrs_node = None # BBB + + for node in monthContext_nodes: + context_type = node.getAttribute('type') + mctx = LocaleMonthContext(context_type) + calendar.monthContexts[context_type] = mctx + + defaultWidth_node = node.getElementsByTagName('default') + if defaultWidth_node: + mctx.defaultWidth = defaultWidth_node[0].getAttribute('type') + + widths = InheritingDictionary() + mctx.months = widths + for width_node in node.getElementsByTagName('monthWidth'): + width_type = width_node.getAttribute('type') + width = InheritingDictionary() + widths[width_type] = width + + for month_node in width_node.getElementsByTagName('month'): + mtype = int(month_node.getAttribute('type')) + width[mtype] = self._getText(month_node.childNodes) + + if context_type == 'format': + if width_type == 'abbreviated': + abbrs_node = width_node + elif width_type == 'wide': + names_node = width_node + + if not (names_node and abbrs_node): + return + + # Get all month names + names = {} + for name_node in names_node.getElementsByTagName('month'): + type = int(name_node.getAttribute('type')) + names[type] = self._getText(name_node.childNodes) + + # Get all month abbrs + abbrs = {} + for abbr_node in abbrs_node.getElementsByTagName('month'): + type = int(abbr_node.getAttribute('type')) + abbrs[type] = self._getText(abbr_node.childNodes) + + # Put the info together + calendar.months = InheritingDictionary() + for type in xrange(1, 13): + calendar.months[type] = (names.get(type, None), + abbrs.get(type, None)) + + + def _extractDays(self, days_node, calendar): + """Extract all day entries from cal_node and store them in + calendar. + + Example:: + + >>> class CalendarStub(object): + ... days = None + >>> calendar = CalendarStub() + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... + ... Sonntag + ... Montag + ... Dienstag + ... Mittwoch + ... Donnerstag + ... Freitag + ... Samstag + ... + ... + ... So + ... Mo + ... Di + ... Mi + ... Do + ... Fr + ... Sa + ... + ... + ... ''' + >>> dom = parseString(xml) + >>> factory._extractDays(dom.documentElement, calendar) + + Day contexts and widths were introduced in CLDR 1.1, here's + how to use them:: + + >>> calendar.defaultDayContext + u'format' + + >>> ctx = calendar.dayContexts[u'format'] + >>> ctx.defaultWidth + u'wide' + + >>> names = [ctx.days[u'wide'][type] for type in xrange(1,8)] + >>> names[:4] + [u'Montag', u'Dienstag', u'Mittwoch', u'Donnerstag'] + >>> names[4:] + [u'Freitag', u'Samstag', u'Sonntag'] + + >>> abbrs = [ctx.days[u'abbreviated'][type] for type in xrange(1,8)] + >>> abbrs + [u'Mo', u'Di', u'Mi', u'Do', u'Fr', u'Sa', u'So'] + + And here's the old CLDR 1.0 way of getting day names and + abbreviations:: + + >>> names = [calendar.days.get(type, (None, None))[0] + ... for type in xrange(1, 8)] + >>> names[:4] + [u'Montag', u'Dienstag', u'Mittwoch', u'Donnerstag'] + >>> names[4:] + [u'Freitag', u'Samstag', u'Sonntag'] + + >>> abbrs = [calendar.days.get(type, (None, None))[1] + ... for type in xrange(1, 8)] + >>> abbrs + [u'Mo', u'Di', u'Mi', u'Do', u'Fr', u'Sa', u'So'] + """ + + defaultDayContext_node = days_node.getElementsByTagName('default') + if defaultDayContext_node: + calendar.defaultDayContext = defaultDayContext_node[0].getAttribute('type') + + dayContext_nodes = days_node.getElementsByTagName('dayContext') + if not dayContext_nodes: + return + + calendar.dayContexts = InheritingDictionary() + names_node = abbrs_node = None # BBB + + for node in dayContext_nodes: + context_type = node.getAttribute('type') + dctx = LocaleDayContext(context_type) + calendar.dayContexts[context_type] = dctx + + defaultWidth_node = node.getElementsByTagName('default') + if defaultWidth_node: + dctx.defaultWidth = defaultWidth_node[0].getAttribute('type') + + widths = InheritingDictionary() + dctx.days = widths + for width_node in node.getElementsByTagName('dayWidth'): + width_type = width_node.getAttribute('type') + width = InheritingDictionary() + widths[width_type] = width + + for day_node in width_node.getElementsByTagName('day'): + dtype = dayMapping[day_node.getAttribute('type')] + width[dtype] = self._getText(day_node.childNodes) + + if context_type == 'format': + if width_type == 'abbreviated': + abbrs_node = width_node + elif width_type == 'wide': + names_node = width_node + + if not (names_node and abbrs_node): + return + + # Get all weekday names + names = {} + for name_node in names_node.getElementsByTagName('day'): + type = dayMapping[name_node.getAttribute('type')] + names[type] = self._getText(name_node.childNodes) + # Get all weekday abbreviations + abbrs = {} + for abbr_node in abbrs_node.getElementsByTagName('day'): + type = dayMapping[abbr_node.getAttribute('type')] + abbrs[type] = self._getText(abbr_node.childNodes) + + # Put the info together + calendar.days = InheritingDictionary() + for type in xrange(1, 13): + calendar.days[type] = (names.get(type, None), + abbrs.get(type, None)) + + + def _extractWeek(self, cal_node, calendar): + """Extract all week entries from cal_node and store them in + calendar. + + Example:: + + >>> class CalendarStub(object): + ... week = None + >>> calendar = CalendarStub() + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... + ... + ... + ... ''' + >>> dom = parseString(xml) + >>> factory._extractWeek(dom.documentElement, calendar) + + >>> calendar.week['minDays'] + 1 + >>> calendar.week['firstDay'] + 7 + >>> calendar.week['weekendStart'] + (5, datetime.time(18, 0)) + >>> calendar.week['weekendEnd'] + (7, datetime.time(18, 0)) + """ + # See whether we have week entries + week_nodes = cal_node.getElementsByTagName('week') + if not week_nodes: + return + + calendar.week = InheritingDictionary() + + # Get the 'minDays' value if available + for node in week_nodes[0].getElementsByTagName('minDays'): + calendar.week['minDays'] = int(node.getAttribute('count')) + + # Get the 'firstDay' value if available + for node in week_nodes[0].getElementsByTagName('firstDay'): + calendar.week['firstDay'] = dayMapping[node.getAttribute('day')] + + # Get the 'weekendStart' value if available + for node in week_nodes[0].getElementsByTagName('weekendStart'): + day = dayMapping[node.getAttribute('day')] + time_args = map(int, node.getAttribute('time').split(':')) + calendar.week['weekendStart'] = (day, time(*time_args)) + + # Get the 'weekendEnd' value if available + for node in week_nodes[0].getElementsByTagName('weekendEnd'): + day = dayMapping[node.getAttribute('day')] + time_args = map(int, node.getAttribute('time').split(':')) + calendar.week['weekendEnd'] = (day, time(*time_args)) + + + def _extractEras(self, cal_node, calendar): + """Extract all era entries from cal_node and store them in + calendar. + + Example:: + + >>> class CalendarStub(object): + ... days = None + >>> calendar = CalendarStub() + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... BC + ... AD + ... + ... + ... Before Christ + ... + ... + ... ''' + >>> dom = parseString(xml) + >>> factory._extractEras(dom.documentElement, calendar) + + >>> names = [calendar.eras.get(type, (None, None))[0] + ... for type in range(2)] + >>> names + [u'Before Christ', None] + + >>> abbrs = [calendar.eras.get(type, (None, None))[1] + ... for type in range(2)] + >>> abbrs + [u'BC', u'AD'] + """ + # See whether we have era names and abbreviations + eras_nodes = cal_node.getElementsByTagName('eras') + if not eras_nodes: + return + names_nodes = eras_nodes[0].getElementsByTagName('eraName') + abbrs_nodes = eras_nodes[0].getElementsByTagName('eraAbbr') + + # Get all era names + names = {} + if names_nodes: + for name_node in names_nodes[0].getElementsByTagName('era'): + type = int(name_node.getAttribute('type')) + names[type] = self._getText(name_node.childNodes) + # Get all era abbreviations + abbrs = {} + if abbrs_nodes: + for abbr_node in abbrs_nodes[0].getElementsByTagName('era'): + type = int(abbr_node.getAttribute('type')) + abbrs[type] = self._getText(abbr_node.childNodes) + + calendar.eras = InheritingDictionary() + for type in abbrs.keys(): + calendar.eras[type] = (names.get(type, None), abbrs.get(type, None)) + + + def _extractFormats(self, formats_node, lengthNodeName, formatNodeName): + """Extract all format entries from formats_node and return a + tuple of the form (defaultFormatType, [LocaleFormatLength, ...]). + + Example:: + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... EEEE, MMMM d, yyyy + ... + ... + ... + ... + ... + ... Standard Date + ... MMM d, yyyy + ... + ... + ... MMM dd, yyyy + ... + ... + ... ''' + >>> dom = parseString(xml) + + >>> default, lengths = factory._extractFormats( + ... dom.documentElement, 'dateFormatLength', 'dateFormat') + >>> default + u'medium' + >>> lengths[u'full'].formats[None].pattern + u'EEEE, MMMM d, yyyy' + >>> lengths[u'medium'].default + u'DateFormatsKey2' + >>> lengths[u'medium'].formats['DateFormatsKey3'].pattern + u'MMM dd, yyyy' + >>> lengths[u'medium'].formats['DateFormatsKey2'].displayName + u'Standard Date' + """ + formats_default = None + default_nodes = formats_node.getElementsByTagName('default') + if default_nodes: + formats_default = default_nodes[0].getAttribute('type') + + lengths = InheritingDictionary() + for length_node in formats_node.getElementsByTagName(lengthNodeName): + type = length_node.getAttribute('type') or None + length = LocaleFormatLength(type) + + default_nodes = length_node.getElementsByTagName('default') + if default_nodes: + length.default = default_nodes[0].getAttribute('type') + + if length_node.getElementsByTagName(formatNodeName): + length.formats = InheritingDictionary() + + for format_node in length_node.getElementsByTagName(formatNodeName): + format = LocaleFormat() + format.type = format_node.getAttribute('type') or None + pattern_node = format_node.getElementsByTagName('pattern')[0] + format.pattern = self._getText(pattern_node.childNodes) + name_nodes = format_node.getElementsByTagName('displayName') + if name_nodes: + format.displayName = self._getText(name_nodes[0].childNodes) + length.formats[format.type] = format + + lengths[length.type] = length + + return (formats_default, lengths) + + def _extractCalendars(self, dates_node): + """Extract all calendars and their specific information from the + Locale's DOM tree. + + Example:: + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... January + ... December + ... + ... + ... Jan + ... Dec + ... + ... + ... Sunday + ... Saturday + ... + ... + ... Sun + ... Sat + ... + ... + ... + ... + ... + ... AM + ... PM + ... + ... + ... BC + ... AD + ... + ... + ... + ... + ... + ... EEEE, MMMM d, yyyy + ... + ... + ... + ... + ... + ... + ... + ... h:mm:ss a + ... + ... + ... + ... + ... + ... + ... {0} {1} + ... + ... + ... + ... + ... + ... + ... BE + ... + ... + ... + ... ''' + >>> dom = parseString(xml) + + >>> calendars = factory._extractCalendars(dom.documentElement) + >>> keys = calendars.keys() + >>> keys.sort() + >>> keys + [u'buddhist', u'gregorian', 'thai-buddhist'] + + Note that "thai-buddhist" are added as an alias to "buddhist". + + >>> calendars['buddhist'] is calendars['thai-buddhist'] + True + + """ + cals_nodes = dates_node.getElementsByTagName('calendars') + # no calendar node + if cals_nodes == []: + return + + calendars = InheritingDictionary() + for cal_node in cals_nodes[0].getElementsByTagName('calendar'): + # get the calendar type + type = cal_node.getAttribute('type') + calendar = LocaleCalendar(type) + + # get month names and abbreviations + months_nodes = cal_node.getElementsByTagName('months') + if months_nodes: + self._extractMonths(months_nodes[0], calendar) + + # get weekday names and abbreviations + days_nodes = cal_node.getElementsByTagName('days') + if days_nodes: + self._extractDays(days_nodes[0], calendar) + + # get week information + self._extractWeek(cal_node, calendar) + + # get am/pm designation values + nodes = cal_node.getElementsByTagName('am') + if nodes: + calendar.am = self._getText(nodes[0].childNodes) + nodes = cal_node.getElementsByTagName('pm') + if nodes: + calendar.pm = self._getText(nodes[0].childNodes) + + # get era names and abbreviations + self._extractEras(cal_node, calendar) + + for formatsName, lengthName, formatName in ( + ('dateFormats', 'dateFormatLength', 'dateFormat'), + ('timeFormats', 'timeFormatLength', 'timeFormat'), + ('dateTimeFormats', 'dateTimeFormatLength', 'dateTimeFormat')): + + formats_nodes = cal_node.getElementsByTagName(formatsName) + if formats_nodes: + default, formats = self._extractFormats( + formats_nodes[0], lengthName, formatName) + setattr(calendar, + 'default'+formatName[0].upper()+formatName[1:], + default) + setattr(calendar, formatsName, formats) + + calendars[calendar.type] = calendar + if calendar.type in calendarAliases: + for alias in calendarAliases[calendar.type]: + calendars[alias] = calendar + + return calendars + + + def _extractTimeZones(self, dates_node): + """Extract all timezone information for the locale from the DOM + tree. + + Example:: + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... Pacific Time + ... Pacific Standard Time + ... Pacific Daylight Time + ... + ... + ... PT + ... PST + ... PDT + ... + ... San Francisco + ... + ... + ... + ... British Time + ... British Standard Time + ... British Daylight Time + ... + ... York + ... + ... + ... ''' + >>> dom = parseString(xml) + >>> zones = factory._extractTimeZones(dom.documentElement) + + >>> keys = zones.keys() + >>> keys.sort() + >>> keys + [u'America/Los_Angeles', u'Europe/London'] + >>> zones[u'Europe/London'].names[u'generic'] + (u'British Time', None) + >>> zones[u'Europe/London'].cities + [u'York'] + >>> zones[u'America/Los_Angeles'].names[u'generic'] + (u'Pacific Time', u'PT') + """ + tz_names = dates_node.getElementsByTagName('timeZoneNames') + if not tz_names: + return + + zones = InheritingDictionary() + for node in tz_names[0].getElementsByTagName('zone'): + type = node.getAttribute('type') + zone = LocaleTimeZone(type) + + # get the short and long name node + long = node.getElementsByTagName('long') + short = node.getElementsByTagName('short') + for type in (u'generic', u'standard', u'daylight'): + # get long name + long_desc = None + if long: + long_nodes = long[0].getElementsByTagName(type) + if long_nodes: + long_desc = self._getText(long_nodes[0].childNodes) + # get short name + short_desc = None + if short: + short_nodes = short[0].getElementsByTagName(type) + if short_nodes: + short_desc = self._getText(short_nodes[0].childNodes) + if long_desc is not None or short_desc is not None: + zone.names[type] = (long_desc, short_desc) + + for city in node.getElementsByTagName('exemplarCity'): + zone.cities.append(self._getText(city.childNodes)) + + zones[zone.type] = zone + + return zones + + + def _extractDates(self): + """Extract all date information from the DOM tree""" + dates_nodes = self._data.getElementsByTagName('dates') + if dates_nodes == []: + return + + dates = LocaleDates() + calendars = self._extractCalendars(dates_nodes[0]) + if calendars is not None: + dates.calendars = calendars + timezones = self._extractTimeZones(dates_nodes[0]) + if timezones is not None: + dates.timezones = timezones + return dates + + + def _extractSymbols(self, numbers_node): + """Extract all week entries from cal_node and store them in + calendar. + + Example:: + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... . + ... , + ... ; + ... % + ... 0 + ... # + ... + + ... - + ... E + ... o/oo + ... oo + ... NaN + ... + ... ''' + >>> dom = parseString(xml) + >>> symbols = factory._extractSymbols(dom.documentElement) + + >>> symbols['list'] + u';' + >>> keys = symbols.keys() + >>> keys.sort() + >>> keys[:5] + [u'decimal', u'exponential', u'group', u'infinity', u'list'] + >>> keys[5:9] + [u'minusSign', u'nan', u'nativeZeroDigit', u'patternDigit'] + >>> keys[9:] + [u'perMille', u'percentSign', u'plusSign'] + """ + # See whether we have symbols entries + symbols_nodes = numbers_node.getElementsByTagName('symbols') + if not symbols_nodes: + return + + symbols = InheritingDictionary() + for name in (u'decimal', u'group', u'list', u'percentSign', + u'nativeZeroDigit', u'patternDigit', u'plusSign', + u'minusSign', u'exponential', u'perMille', + u'infinity', u'nan'): + nodes = symbols_nodes[0].getElementsByTagName(name) + if nodes: + symbols[name] = self._getText(nodes[0].childNodes) + + return symbols + + + def _extractNumberFormats(self, numbers_node, numbers): + """Extract all number formats from the numbers_node and save the data + in numbers. + + Example:: + + >>> class Numbers(object): + ... defaultDecimalFormat = None + ... decimalFormats = None + ... defaultScientificFormat = None + ... scientificFormats = None + ... defaultPercentFormat = None + ... percentFormats = None + ... defaultCurrencyFormat = None + ... currencyFormats = None + >>> numbers = Numbers() + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... #,##0.### + ... + ... + ... + ... + ... + ... + ... + ... 0.000###E+00 + ... + ... + ... + ... + ... 0.00##E+00 + ... + ... + ... + ... + ... + ... + ... #,##0% + ... + ... + ... + ... + ... + ... + ... $ #,##0.00;($ #,##0.00) + ... + ... + ... + ... ''' + >>> dom = parseString(xml) + >>> factory._extractNumberFormats(dom.documentElement, numbers) + + >>> numbers.decimalFormats[u'long'].formats[None].pattern + u'#,##0.###' + + >>> numbers.defaultScientificFormat + u'long' + >>> numbers.scientificFormats[u'long'].formats[None].pattern + u'0.000###E+00' + >>> numbers.scientificFormats[u'medium'].formats[None].pattern + u'0.00##E+00' + + >>> numbers.percentFormats[u'long'].formats[None].pattern + u'#,##0%' + >>> numbers.percentFormats.get(u'medium', None) is None + True + + >>> numbers.currencyFormats[u'long'].formats[None].pattern + u'$ #,##0.00;($ #,##0.00)' + >>> numbers.currencyFormats.get(u'medium', None) is None + True + """ + + for category in ('decimal', 'scientific', 'percent', 'currency'): + formatsName = category+'Formats' + lengthName = category+'FormatLength' + formatName = category+'Format' + defaultName = 'default'+formatName[0].upper()+formatName[1:] + + formats_nodes = numbers_node.getElementsByTagName(formatsName) + if formats_nodes: + default, formats = self._extractFormats( + formats_nodes[0], lengthName, formatName) + setattr(numbers, defaultName, default) + setattr(numbers, formatsName, formats) + + + def _extractCurrencies(self, numbers_node): + """Extract all currency definitions and their information from the + Locale's DOM tree. + + Example:: + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... Dollar + ... $ + ... + ... + ... Yen + ... Y + ... + ... + ... Rupee + ... 0<=Rf|1<=Ru|1<Rf + ... + ... + ... Escudo + ... $ + ... + ... + ... ''' + >>> dom = parseString(xml) + >>> currencies = factory._extractCurrencies(dom.documentElement) + + >>> keys = currencies.keys() + >>> keys.sort() + >>> keys + [u'INR', u'JPY', u'PTE', u'USD'] + + >>> currencies['USD'].symbol + u'$' + >>> currencies['USD'].displayName + u'Dollar' + >>> currencies['USD'].symbolChoice + False + """ + currs_nodes = numbers_node.getElementsByTagName('currencies') + if not currs_nodes: + return + + currencies = InheritingDictionary() + for curr_node in currs_nodes[0].getElementsByTagName('currency'): + type = curr_node.getAttribute('type') + currency = LocaleCurrency(type) + + nodes = curr_node.getElementsByTagName('symbol') + if nodes: + currency.symbol = self._getText(nodes[0].childNodes) + currency.symbolChoice = \ + nodes[0].getAttribute('choice') == u'true' + + nodes = curr_node.getElementsByTagName('displayName') + if nodes: + currency.displayName = self._getText(nodes[0].childNodes) + + currencies[type] = currency + + return currencies + + + def _extractNumbers(self): + """Extract all number information from the DOM tree""" + numbers_nodes = self._data.getElementsByTagName('numbers') + if not numbers_nodes: + return + + numbers = LocaleNumbers() + symbols = self._extractSymbols(numbers_nodes[0]) + if symbols is not None: + numbers.symbols = symbols + self._extractNumberFormats(numbers_nodes[0], numbers) + currencies = self._extractCurrencies(numbers_nodes[0]) + if currencies is not None: + numbers.currencies = currencies + return numbers + + + def _extractDelimiters(self): + """Extract all delimiter entries from the DOM tree. + + Example:: + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... `` + ... '' + ... ` + ... ' + ... + ... ''' + >>> dom = parseString(xml) + >>> factory._data = parseString(xml).documentElement + >>> delimiters = factory._extractDelimiters() + + >>> delimiters[u'quotationStart'] + u'``' + >>> delimiters[u'quotationEnd'] + u"''" + >>> delimiters[u'alternateQuotationStart'] + u'`' + >>> delimiters[u'alternateQuotationEnd'] + u"'" + + Escape: "'" + """ + # See whether we have symbols entries + delimiters_nodes = self._data.getElementsByTagName('delimiters') + if not delimiters_nodes: + return + + delimiters = InheritingDictionary() + for name in (u'quotationStart', u'quotationEnd', + u'alternateQuotationStart', u'alternateQuotationEnd'): + nodes = delimiters_nodes[0].getElementsByTagName(name) + if nodes: + delimiters[name] = self._getText(nodes[0].childNodes) + + return delimiters + + + def _extractOrientation(self): + """Extract orientation information. + + >>> factory = LocaleFactory(None) + >>> from xml.dom.minidom import parseString + >>> xml = u''' + ... + ... + ... + ... + ... ''' + >>> dom = parseString(xml) + >>> factory._data = parseString(xml).documentElement + >>> orientation = factory._extractOrientation() + >>> orientation.lines + u'bottom-to-top' + >>> orientation.characters + u'right-to-left' + """ + orientation_nodes = self._data.getElementsByTagName('orientation') + if not orientation_nodes: + return + orientation = LocaleOrientation() + for name in (u'characters', u'lines'): + value = orientation_nodes[0].getAttribute(name) + if value: + setattr(orientation, name, value) + return orientation + + + def __call__(self): + """Create the Locale.""" + locale = Locale(self._extractIdentity()) + + names = self._extractDisplayNames() + if names is not None: + locale.displayNames = names + + dates = self._extractDates() + if dates is not None: + locale.dates = dates + + numbers = self._extractNumbers() + if numbers is not None: + locale.numbers = numbers + + delimiters = self._extractDelimiters() + if delimiters is not None: + locale.delimiters = delimiters + + orientation = self._extractOrientation() + if orientation is not None: + locale.orientation = orientation + + # Unmapped: + # + # - + # - + # - , + + return locale diff -Nru zope3-3.4.0/src/zope/i18n/meta.zcml zope3-3.5~bzr18/src/zope/i18n/meta.zcml --- zope3-3.4.0/src/zope/i18n/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,16 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/i18n/negotiator.py zope3-3.5~bzr18/src/zope/i18n/negotiator.py --- zope3-3.4.0/src/zope/i18n/negotiator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/negotiator.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,60 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Language Negotiator + +$Id: negotiator.py 88073 2008-07-06 17:28:53Z hannosch $ +""" +from zope.interface import implements + +from zope.i18n.interfaces import INegotiator +from zope.i18n.interfaces import IUserPreferredLanguages + +def normalize_lang(lang): + lang = lang.strip().lower() + lang = lang.replace('_', '-') + lang = lang.replace(' ', '') + return lang + +def normalize_langs(langs): + # Make a mapping from normalized->original so we keep can match + # the normalized lang and return the original string. + n_langs = {} + for l in langs: + n_langs[normalize_lang(l)] = l + return n_langs + +class Negotiator(object): + implements(INegotiator) + + def getLanguage(self, langs, env): + envadapter = IUserPreferredLanguages(env) + userlangs = envadapter.getPreferredLanguages() + # Prioritize on the user preferred languages. Return the + # first user preferred language that the object has available. + langs = normalize_langs(langs) + for lang in userlangs: + if lang in langs: + return langs.get(lang) + # If the user asked for a specific variation, but we don't + # have it available we may serve the most generic one, + # according to the spec (eg: user asks for ('en-us', + # 'de'), but we don't have 'en-us', then 'en' is preferred + # to 'de'). + parts = lang.split('-') + if len(parts) > 1 and parts[0] in langs: + return langs.get(parts[0]) + return None + + +negotiator = Negotiator() diff -Nru zope3-3.4.0/src/zope/i18n/simpletranslationdomain.py zope3-3.5~bzr18/src/zope/i18n/simpletranslationdomain.py --- zope3-3.4.0/src/zope/i18n/simpletranslationdomain.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/simpletranslationdomain.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,68 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 is a simple implementation of the ITranslationDomain interface. + +$Id: simpletranslationdomain.py 68771 2006-06-20 11:24:31Z hdima $ +""" +from zope.interface import implements +from zope.component import getUtility +from zope.i18n.interfaces import ITranslationDomain, INegotiator +from zope.i18n import interpolate + + +class SimpleTranslationDomain(object): + """This is the simplest implementation of the ITranslationDomain I + could come up with. + + The constructor takes one optional argument 'messages', which will be + used to do the translation. The 'messages' attribute has to have the + following structure: + + {('language', 'msg_id'): 'message', ...} + + Note: This Translation Domain does not use message catalogs. + """ + implements(ITranslationDomain) + + # See zope.i18n.interfaces.ITranslationDomain + domain = None + + def __init__(self, domain, messages=None): + """Initializes the object. No arguments are needed.""" + self.domain = domain + if messages is None: + self.messages = {} + else: + assert isinstance(messages, dict) + self.messages = messages + + + def translate(self, msgid, mapping=None, context=None, + target_language=None, default=None): + '''See interface ITranslationDomain''' + # Find out what the target language should be + if target_language is None and context is not None: + langs = [m[0] for m in self.messages.keys()] + # Let's negotiate the language to translate to. :) + negotiator = getUtility(INegotiator) + target_language = negotiator.getLanguage(langs, context) + + # Find a translation; if nothing is found, use the default + # value + if default is None: + default = unicode(msgid) + text = self.messages.get((target_language, msgid)) + if text is None: + text = default + return interpolate(text, mapping) diff -Nru zope3-3.4.0/src/zope/i18n/testing.py zope3-3.5~bzr18/src/zope/i18n/testing.py --- zope3-3.4.0/src/zope/i18n/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Unit test logic for setting up and tearing down basic infrastructure + +$Id: testing.py 67630 2006-04-27 00:54:03Z jim $ +""" +import zope.component +from zope.publisher.browser import BrowserLanguages +from zope.publisher.http import HTTPCharsets + +def setUp(test=None): + zope.component.provideAdapter(HTTPCharsets) + zope.component.provideAdapter(BrowserLanguages) + +class PlacelessSetup(object): + + def setUp(self): + setUp() diff -Nru zope3-3.4.0/src/zope/i18n/testmessagecatalog.py zope3-3.5~bzr18/src/zope/i18n/testmessagecatalog.py --- zope3-3.4.0/src/zope/i18n/testmessagecatalog.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/testmessagecatalog.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,57 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 message catalog + +$Id: testmessagecatalog.py 40011 2005-11-09 21:30:39Z jim $ +""" + +from zope import component, interface +import zope.i18n.interfaces +from zope.i18n.translationdomain import TranslationDomain + +class TestMessageCatalog: + interface.implements(zope.i18n.interfaces.IGlobalMessageCatalog) + + language = 'test' + + def __init__(self, domain): + self.domain = domain + + def queryMessage(self, msgid, default=None): + default = getattr(msgid, 'default', default) + if default != None and default != msgid: + msg = u"%s (%s)" % (msgid, default) + else: + msg = msgid + + return u'[[%s][%s]]' % (self.domain, msg) + + getMessage = queryMessage + + def getIdentifier(self): + return 'test' + + def reload(self): + pass + +@interface.implementer(zope.i18n.interfaces.ITranslationDomain) +def TestMessageFallbackDomain(domain_id=u''): + domain = TranslationDomain(domain_id) + domain.addCatalog(TestMessageCatalog(domain_id)) + return domain + +interface.directlyProvides( + TestMessageFallbackDomain, + zope.i18n.interfaces.IFallbackTranslationDomainFactory, + ) diff -Nru zope3-3.4.0/src/zope/i18n/testmessagecatalog.txt zope3-3.5~bzr18/src/zope/i18n/testmessagecatalog.txt --- zope3-3.4.0/src/zope/i18n/testmessagecatalog.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/testmessagecatalog.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,66 @@ +Test Message Catalog +==================== + +The test message catalog "translates" test by simply outputing the +domain and message id in square-bracket markers: + + >>> import zope.i18n.testmessagecatalog + >>> cat = zope.i18n.testmessagecatalog.TestMessageCatalog('foo.bar') + + >>> cat.language, cat.domain + ('test', 'foo.bar') + + >>> cat.queryMessage('eek') + u'[[foo.bar][eek]]' + + >>> cat.getMessage('eek') + u'[[foo.bar][eek]]' + + >>> cat.getIdentifier() + 'test' + + >>> cat.reload() + +If a message id has a default, it will be included in the output: + + >>> id = zope.i18nmessageid.MessageFactory('foo.bar')('eek', default='Eek') + + >>> cat.queryMessage(id) + u'[[foo.bar][eek (Eek)]]' + + >>> cat.getMessage(id) + u'[[foo.bar][eek (Eek)]]' + +If a message doesn't have a default, but a default is passed in to +queryMessage, the default will be used used: + + >>> cat.queryMessage('eek', default='Eek') + u'[[foo.bar][eek (Eek)]]' + + >>> cat.getMessage(id, default='Waaa') + u'[[foo.bar][eek (Eek)]]' + +Fallback domains +---------------- + +The testmessagecatalog module also provide a fallback domain factory +that has the test catalog as it's only catalog: + + >>> factory = zope.i18n.testmessagecatalog.TestMessageFallbackDomain + >>> import zope.i18n.interfaces + >>> zope.i18n.interfaces.IFallbackTranslationDomainFactory.providedBy( + ... factory) + True + + >>> domain = factory('foo.bar') + >>> domain.translate(u'eek') + u'eek' + + >>> domain.translate(u'eek', target_language='test') + u'[[foo.bar][eek]]' + +Note that if a default is padded in, it will be included in test +output: + + >>> domain.translate(u'eek', target_language='test', default=u'Eek') + u'[[foo.bar][eek (Eek)]]' Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/i18n/tests/de-default.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/i18n/tests/de-default.mo differ diff -Nru zope3-3.4.0/src/zope/i18n/tests/de-default.po zope3-3.5~bzr18/src/zope/i18n/tests/de-default.po --- zope3-3.4.0/src/zope/i18n/tests/de-default.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/de-default.po 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ +msgid "" +msgstr "" +"Project-Id-Version: Zope 3\n" +"PO-Revision-Date: 2002/06/13\n" +"Last-Translator: Zope 3 contributors\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "short_greeting" +msgstr "Hallo!" + +msgid "greeting" +msgstr "Hallo $name, wie geht es Dir?" Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/i18n/tests/en-alt.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/i18n/tests/en-alt.mo differ diff -Nru zope3-3.4.0/src/zope/i18n/tests/en-alt.po zope3-3.5~bzr18/src/zope/i18n/tests/en-alt.po --- zope3-3.4.0/src/zope/i18n/tests/en-alt.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/en-alt.po 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ +msgid "" +msgstr "" +"Project-Id-Version: Zope 3\n" +"PO-Revision-Date: 2002/06/13\n" +"Last-Translator: Zope 3 contributors\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "short_greeting" +msgstr "Hey!" + +msgid "special" +msgstr "Wow" Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/i18n/tests/en-default.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/i18n/tests/en-default.mo differ diff -Nru zope3-3.4.0/src/zope/i18n/tests/en-default.po zope3-3.5~bzr18/src/zope/i18n/tests/en-default.po --- zope3-3.4.0/src/zope/i18n/tests/en-default.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/en-default.po 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ +msgid "" +msgstr "" +"Project-Id-Version: Zope 3\n" +"PO-Revision-Date: 2002/06/13\n" +"Last-Translator: Zope 3 contributors\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "short_greeting" +msgstr "Hello!" + +msgid "greeting" +msgstr "Hello $name, how are you?" diff -Nru zope3-3.4.0/src/zope/i18n/tests/__init__.py zope3-3.5~bzr18/src/zope/i18n/tests/__init__.py --- zope3-3.4.0/src/zope/i18n/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/i18n/tests/locale/de/LC_MESSAGES/zope-i18n.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/i18n/tests/locale/de/LC_MESSAGES/zope-i18n.mo differ diff -Nru zope3-3.4.0/src/zope/i18n/tests/locale/de/LC_MESSAGES/zope-i18n.po zope3-3.5~bzr18/src/zope/i18n/tests/locale/de/LC_MESSAGES/zope-i18n.po --- zope3-3.4.0/src/zope/i18n/tests/locale/de/LC_MESSAGES/zope-i18n.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/locale/de/LC_MESSAGES/zope-i18n.po 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ +msgid "" +msgstr "" +"Project-Id-Version: Zope 3\n" +"PO-Revision-Date: 2002/06/13\n" +"Last-Translator: Zope 3 contributors\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Message" +msgstr "Message translated" diff -Nru zope3-3.4.0/src/zope/i18n/tests/locale/en/LC_MESSAGES/__init__.py zope3-3.5~bzr18/src/zope/i18n/tests/locale/en/LC_MESSAGES/__init__.py --- zope3-3.4.0/src/zope/i18n/tests/locale/en/LC_MESSAGES/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/locale/en/LC_MESSAGES/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/i18n/tests/locale/en/LC_MESSAGES/zope-i18n.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/i18n/tests/locale/en/LC_MESSAGES/zope-i18n.mo differ diff -Nru zope3-3.4.0/src/zope/i18n/tests/locale/en/LC_MESSAGES/zope-i18n.po zope3-3.5~bzr18/src/zope/i18n/tests/locale/en/LC_MESSAGES/zope-i18n.po --- zope3-3.4.0/src/zope/i18n/tests/locale/en/LC_MESSAGES/zope-i18n.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/locale/en/LC_MESSAGES/zope-i18n.po 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,14 @@ +msgid "" +msgstr "" +"Project-Id-Version: Zope 3\n" +"PO-Revision-Date: 2002/06/13\n" +"Last-Translator: Zope 3 contributors\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "New Domain" +msgstr "New Domain translated" + +msgid "New Language" +msgstr "New Language translated" Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/i18n/tests/locale2/en/LC_MESSAGES/zope-i18n.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/i18n/tests/locale2/en/LC_MESSAGES/zope-i18n.mo differ diff -Nru zope3-3.4.0/src/zope/i18n/tests/locale2/en/LC_MESSAGES/zope-i18n.po zope3-3.5~bzr18/src/zope/i18n/tests/locale2/en/LC_MESSAGES/zope-i18n.po --- zope3-3.4.0/src/zope/i18n/tests/locale2/en/LC_MESSAGES/zope-i18n.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/locale2/en/LC_MESSAGES/zope-i18n.po 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,14 @@ +msgid "" +msgstr "" +"Project-Id-Version: Zope 3\n" +"PO-Revision-Date: 2008/04/26\n" +"Last-Translator: Zope 3 contributors\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Additional message" +msgstr "Additional message translated" + +msgid "New Language" +msgstr "New Language translated differently" diff -Nru zope3-3.4.0/src/zope/i18n/tests/locale2/__init__.py zope3-3.5~bzr18/src/zope/i18n/tests/locale2/__init__.py --- zope3-3.4.0/src/zope/i18n/tests/locale2/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/locale2/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/i18n/tests/locale3/en/LC_MESSAGES/__init__.py zope3-3.5~bzr18/src/zope/i18n/tests/locale3/en/LC_MESSAGES/__init__.py --- zope3-3.4.0/src/zope/i18n/tests/locale3/en/LC_MESSAGES/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/locale3/en/LC_MESSAGES/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n2.po zope3-3.5~bzr18/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n2.po --- zope3-3.4.0/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n2.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n2.po 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ +msgid "" +msgstr "" +"Project-Id-Version: Zope 3\n" +"PO-Revision-Date: 2008/04/26\n" +"Last-Translator: Zope 3 contributors\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "I'm a new file" +msgstr "I'm a new file translated" Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.in and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.in differ Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mo and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mo differ diff -Nru zope3-3.4.0/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.po zope3-3.5~bzr18/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.po --- zope3-3.4.0/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.po 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.po 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ +msgid "" +msgstr "" +"Project-Id-Version: Zope 3\n" +"PO-Revision-Date: 2008/04/26\n" +"Last-Translator: Zope 3 contributors\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "I'm a newer file" +msgstr "I'm a newer file translated" diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_formats.py zope3-3.5~bzr18/src/zope/i18n/tests/test_formats.py --- zope3-3.4.0/src/zope/i18n/tests/test_formats.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_formats.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1188 @@ +############################################################################## +# +# Copyright (c) 2002, 2003 Zope Corporation 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 module tests the Formats and everything that goes with it. + +$Id: test_formats.py 88151 2008-07-10 05:57:18Z srichter $ +""" +import decimal +import os +import datetime +import pytz +import pickle +from unittest import TestCase, TestSuite, makeSuite + +from zope.i18n.interfaces import IDateTimeFormat +from zope.i18n.format import DateTimeFormat +from zope.i18n.format import parseDateTimePattern, buildDateTimeParseInfo +from zope.i18n.format import DateTimePatternParseError, DateTimeParseError + +from zope.i18n.interfaces import INumberFormat +from zope.i18n.format import NumberFormat, NumberParseError +from zope.i18n.format import parseNumberPattern + +class LocaleStub(object): + pass + +class LocaleCalendarStub(object): + + type = u'gregorian' + + months = { 1: ('Januar', 'Jan'), 2: ('Februar', 'Feb'), + 3: ('Maerz', 'Mrz'), 4: ('April', 'Apr'), + 5: ('Mai', 'Mai'), 6: ('Juni', 'Jun'), + 7: ('Juli', 'Jul'), 8: ('August', 'Aug'), + 9: ('September', 'Sep'), 10: ('Oktober', 'Okt'), + 11: ('November', 'Nov'), 12: ('Dezember', 'Dez')} + + days = {1: ('Montag', 'Mo'), 2: ('Dienstag', 'Di'), + 3: ('Mittwoch', 'Mi'), 4: ('Donnerstag', 'Do'), + 5: ('Freitag', 'Fr'), 6: ('Samstag', 'Sa'), + 7: ('Sonntag', 'So')} + + am = 'vorm.' + pm = 'nachm.' + + eras = {1: (None, 'v. Chr.'), 2: (None, 'n. Chr.')} + + week = {'firstDay': 1, 'minDays': 1} + + def getMonthNames(self): + return [self.months.get(type, (None, None))[0] for type in range(1, 13)] + + def getMonthTypeFromName(self, name): + for item in self.months.items(): + if item[1][0] == name: + return item[0] + + def getMonthAbbreviations(self): + return [self.months.get(type, (None, None))[1] for type in range(1, 13)] + + def getMonthTypeFromAbbreviation(self, abbr): + for item in self.months.items(): + if item[1][1] == abbr: + return item[0] + + def getDayNames(self): + return [self.days.get(type, (None, None))[0] for type in range(1, 8)] + + def getDayTypeFromName(self, name): + for item in self.days.items(): + if item[1][0] == name: + return item[0] + + def getDayAbbreviations(self): + return [self.days.get(type, (None, None))[1] for type in range(1, 8)] + + def getDayTypeFromAbbreviation(self, abbr): + for item in self.days.items(): + if item[1][1] == abbr: + return item[0] + + +class TestDateTimePatternParser(TestCase): + """Extensive tests for the ICU-based-syntax datetime pattern parser.""" + + def testParseSimpleTimePattern(self): + self.assertEqual(parseDateTimePattern('HH'), + [('H', 2)]) + self.assertEqual(parseDateTimePattern('HH:mm'), + [('H', 2), ':', ('m', 2)]) + self.assertEqual(parseDateTimePattern('HH:mm:ss'), + [('H', 2), ':', ('m', 2), ':', ('s', 2)]) + self.assertEqual(parseDateTimePattern('mm:ss'), + [('m', 2), ':', ('s', 2)]) + self.assertEqual(parseDateTimePattern('H:m:s'), + [('H', 1), ':', ('m', 1), ':', ('s', 1)]) + self.assertEqual(parseDateTimePattern('HHH:mmmm:sssss'), + [('H', 3), ':', ('m', 4), ':', ('s', 5)]) + + def testParseGermanTimePattern(self): + # German full + self.assertEqual(parseDateTimePattern("H:mm' Uhr 'z"), + [('H', 1), ':', ('m', 2), ' Uhr ', ('z', 1)]) + # German long + self.assertEqual(parseDateTimePattern("HH:mm:ss z"), + [('H', 2), ':', ('m', 2), ':', ('s', 2), ' ', + ('z', 1)]) + # German medium + self.assertEqual(parseDateTimePattern("HH:mm:ss"), + [('H', 2), ':', ('m', 2), ':', ('s', 2)]) + # German short + self.assertEqual(parseDateTimePattern("HH:mm"), + [('H', 2), ':', ('m', 2)]) + + def testParseRealDate(self): + # German full + self.assertEqual(parseDateTimePattern("EEEE, d. MMMM yyyy"), + [('E', 4), ', ', ('d', 1), '. ', ('M', 4), + ' ', ('y', 4)]) + # German long + self.assertEqual(parseDateTimePattern("d. MMMM yyyy"), + [('d', 1), '. ', ('M', 4), ' ', ('y', 4)]) + # German medium + self.assertEqual(parseDateTimePattern("dd.MM.yyyy"), + [('d', 2), '.', ('M', 2), '.', ('y', 4)]) + # German short + self.assertEqual(parseDateTimePattern("dd.MM.yy"), + [('d', 2), '.', ('M', 2), '.', ('y', 2)]) + + def testParseRealDateTime(self): + # German full + self.assertEqual( + parseDateTimePattern("EEEE, d. MMMM yyyy H:mm' Uhr 'z"), + [('E', 4), ', ', ('d', 1), '. ', ('M', 4), ' ', ('y', 4), + ' ', ('H', 1), ':', ('m', 2), ' Uhr ', ('z', 1)]) + # German long + self.assertEqual( + parseDateTimePattern("d. MMMM yyyy HH:mm:ss z"), + [('d', 1), '. ', ('M', 4), ' ', ('y', 4), + ' ', ('H', 2), ':', ('m', 2), ':', ('s', 2), ' ', ('z', 1)]) + # German medium + self.assertEqual( + parseDateTimePattern("dd.MM.yyyy HH:mm:ss"), + [('d', 2), '.', ('M', 2), '.', ('y', 4), + ' ', ('H', 2), ':', ('m', 2), ':', ('s', 2)]) + # German short + self.assertEqual( + parseDateTimePattern("dd.MM.yy HH:mm"), + [('d', 2), '.', ('M', 2), '.', ('y', 2), + ' ', ('H', 2), ':', ('m', 2)]) + + def testParseQuotesInPattern(self): + self.assertEqual(parseDateTimePattern("HH''mm"), + [('H', 2), "'", ('m', 2)]) + self.assertEqual(parseDateTimePattern("HH'HHmm'mm"), + [('H', 2), 'HHmm', ('m', 2)]) + self.assertEqual(parseDateTimePattern("HH':'''':'mm"), + [('H', 2), ":':", ('m', 2)]) + self.assertEqual(parseDateTimePattern("HH':' ':'mm"), + [('H', 2), ": :", ('m', 2)]) + + def testParseDateTimePatternError(self): + # Quote not closed + try: + parseDateTimePattern("HH' Uhr") + except DateTimePatternParseError, err: + self.assertEqual( + str(err), 'The quote starting at character 2 is not closed.') + # Test correct length of characters in datetime fields + try: + parseDateTimePattern("HHHHH") + except DateTimePatternParseError, err: + self.assert_(str(err).endswith('You have: 5')) + + +class TestBuildDateTimeParseInfo(TestCase): + """This class tests the functionality of the buildDateTimeParseInfo() + method with the German locale. + """ + + def info(self, entry): + info = buildDateTimeParseInfo(LocaleCalendarStub(), [entry]) + return info[entry] + + def testGenericNumbers(self): + for char in 'dDFkKhHmsSwW': + for length in range(1, 6): + self.assertEqual(self.info((char, length)), + '([0-9]{%i,1000})' %length) + def testYear(self): + self.assertEqual(self.info(('y', 2)), '([0-9]{2})') + self.assertEqual(self.info(('y', 4)), '([0-9]{4})') + self.assertRaises(DateTimePatternParseError, self.info, ('y', 1)) + self.assertRaises(DateTimePatternParseError, self.info, ('y', 3)) + self.assertRaises(DateTimePatternParseError, self.info, ('y', 5)) + + def testAMPMMarker(self): + names = ['vorm.', 'nachm.'] + for length in range(1, 6): + self.assertEqual(self.info(('a', length)), '('+'|'.join(names)+')') + + def testEra(self): + self.assertEqual(self.info(('G', 1)), '(v. Chr.|n. Chr.)') + + def testTimeZone(self): + self.assertEqual(self.info(('z', 1)), r'([\+-][0-9]{3,4})') + self.assertEqual(self.info(('z', 2)), r'([\+-][0-9]{2}:[0-9]{2})') + self.assertEqual(self.info(('z', 3)), r'([a-zA-Z]{3})') + self.assertEqual(self.info(('z', 4)), r'([a-zA-Z /\.]*)') + self.assertEqual(self.info(('z', 5)), r'([a-zA-Z /\.]*)') + + def testMonthNumber(self): + self.assertEqual(self.info(('M', 1)), '([0-9]{1,2})') + self.assertEqual(self.info(('M', 2)), '([0-9]{2})') + + def testMonthNames(self): + names = [u'Januar', u'Februar', u'Maerz', u'April', + u'Mai', u'Juni', u'Juli', u'August', u'September', u'Oktober', + u'November', u'Dezember'] + self.assertEqual(self.info(('M', 4)), '('+'|'.join(names)+')') + + def testMonthAbbr(self): + names = ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', + 'Sep', 'Okt', 'Nov', 'Dez'] + self.assertEqual(self.info(('M', 3)), '('+'|'.join(names)+')') + + def testWeekdayNumber(self): + self.assertEqual(self.info(('E', 1)), '([0-9])') + self.assertEqual(self.info(('E', 2)), '([0-9]{2})') + + def testWeekdayNames(self): + names = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', + 'Freitag', 'Samstag', 'Sonntag'] + self.assertEqual(self.info(('E', 4)), '('+'|'.join(names)+')') + self.assertEqual(self.info(('E', 5)), '('+'|'.join(names)+')') + self.assertEqual(self.info(('E', 10)), '('+'|'.join(names)+')') + + def testWeekdayAbbr(self): + names = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'] + self.assertEqual(self.info(('E', 3)), '('+'|'.join(names)+')') + + +class TestDateTimeFormat(TestCase): + """Test the functionality of an implmentation of the ILocaleProvider + interface.""" + + format = DateTimeFormat(calendar=LocaleCalendarStub()) + + def testInterfaceConformity(self): + self.assert_(IDateTimeFormat.providedBy(self.format)) + + def testParseSimpleDateTime(self): + # German short + self.assertEqual( + self.format.parse('02.01.03 21:48', 'dd.MM.yy HH:mm'), + datetime.datetime(2003, 01, 02, 21, 48)) + + def testParseRealDateTime(self): + # German medium + self.assertEqual( + self.format.parse('02.01.2003 21:48:01', 'dd.MM.yyyy HH:mm:ss'), + datetime.datetime(2003, 01, 02, 21, 48, 01)) + + # German long + # TODO: The parser does not support timezones yet. + self.assertEqual(self.format.parse( + '2. Januar 2003 21:48:01 +100', + 'd. MMMM yyyy HH:mm:ss z'), + datetime.datetime(2003, 01, 02, 21, 48, 01, + tzinfo=pytz.timezone('Europe/Berlin'))) + + # German full + # TODO: The parser does not support timezones yet. + self.assertEqual(self.format.parse( + 'Donnerstag, 2. Januar 2003 21:48 Uhr +100', + "EEEE, d. MMMM yyyy H:mm' Uhr 'z"), + datetime.datetime(2003, 01, 02, 21, 48, + tzinfo=pytz.timezone('Europe/Berlin'))) + + def testParseAMPMDateTime(self): + self.assertEqual( + self.format.parse('02.01.03 09:48 nachm.', 'dd.MM.yy hh:mm a'), + datetime.datetime(2003, 01, 02, 21, 48)) + + def testParseTimeZone(self): + dt = self.format.parse('09:48 -600', 'HH:mm z') + self.assertEqual(pickle.loads(pickle.dumps(dt)), dt) + self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-6)) + self.assertEqual(dt.tzinfo.zone, None) + self.assertEqual(dt.tzinfo.tzname(dt), None) + + dt = self.format.parse('09:48 -06:00', 'HH:mm zz') + self.assertEqual(pickle.loads(pickle.dumps(dt)), dt) + self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-6)) + self.assertEqual(dt.tzinfo.zone, None) + self.assertEqual(dt.tzinfo.tzname(dt), None) + + def testParseTimeZoneNames(self): + # Note that EST is a deprecated timezone name since it is a US + # interpretation (other countries also use the EST timezone + # abbreviation) + dt = self.format.parse('01.01.2003 09:48 EST', 'dd.MM.yyyy HH:mm zzz') + self.assertEqual(pickle.loads(pickle.dumps(dt)), dt) + self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-5)) + self.assertEqual(dt.tzinfo.zone, 'EST') + self.assertEqual(dt.tzinfo.tzname(dt), 'EST') + + dt = self.format.parse('01.01.2003 09:48 US/Eastern', + 'dd.MM.yyyy HH:mm zzzz') + self.assertEqual(pickle.loads(pickle.dumps(dt)), dt) + self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-5)) + self.assertEqual(dt.tzinfo.zone, 'US/Eastern') + self.assertEqual(dt.tzinfo.tzname(dt), 'EST') + + dt = self.format.parse('01.01.2003 09:48 Australia/Sydney', + 'dd.MM.yyyy HH:mm zzzz') + self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=11)) + self.assertEqual(dt.tzinfo.zone, 'Australia/Sydney') + self.assertEqual(dt.tzinfo.tzname(dt), 'EST') + + # Note that historical and future (as far as known) + # timezones are handled happily using the pytz timezone database + # US DST transition points are changing in 2007 + dt = self.format.parse('01.04.2006 09:48 US/Eastern', + 'dd.MM.yyyy HH:mm zzzz') + self.assertEqual(dt.tzinfo.zone, 'US/Eastern') + self.assertEqual(dt.tzinfo.tzname(dt), 'EST') + self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-5)) + dt = self.format.parse('01.04.2007 09:48 US/Eastern', + 'dd.MM.yyyy HH:mm zzzz') + self.assertEqual(dt.tzinfo.zone, 'US/Eastern') + self.assertEqual(dt.tzinfo.tzname(dt), 'EDT') + self.assertEqual(dt.tzinfo.utcoffset(dt), datetime.timedelta(hours=-4)) + + + def testDateTimeParseError(self): + self.assertRaises(DateTimeParseError, + self.format.parse, '02.01.03 21:48', 'dd.MM.yyyy HH:mm') + self.assertRaises(DateTimeParseError, + self.format.parse, '02.01.2003', 'dd.MM.yy') + self.assertRaises(DateTimeParseError, + self.format.parse, 'ff02.01.03', 'dd.MM.yy') + + def testParse12PM(self): + self.assertEqual( + self.format.parse('01.01.03 12:00 nachm.', 'dd.MM.yy hh:mm a'), + datetime.datetime(2003, 01, 01, 12, 00, 00, 00)) + + def testParseUnusualFormats(self): + self.assertEqual( + self.format.parse('001. Januar 03 0012:00', + 'ddd. MMMMM yy HHHH:mm'), + datetime.datetime(2003, 01, 01, 12, 00, 00, 00)) + self.assertEqual( + self.format.parse('0001. Jan 2003 0012:00 vorm.', + 'dddd. MMM yyyy hhhh:mm a'), + datetime.datetime(2003, 01, 01, 00, 00, 00, 00)) + + def testFormatSimpleDateTime(self): + # German short + self.assertEqual( + self.format.format(datetime.datetime(2003, 01, 02, 21, 48), + 'dd.MM.yy HH:mm'), + '02.01.03 21:48') + + def testFormatRealDateTime(self): + tz = pytz.timezone('Europe/Berlin') + dt = datetime.datetime(2003, 01, 02, 21, 48, 01, tzinfo=tz) + # German medium + self.assertEqual( + self.format.format(dt, 'dd.MM.yyyy HH:mm:ss'), + '02.01.2003 21:48:01') + + # German long + self.assertEqual( + self.format.format(dt, 'd. MMMM yyyy HH:mm:ss z'), + '2. Januar 2003 21:48:01 +100') + + # German full + self.assertEqual(self.format.format( + dt, "EEEE, d. MMMM yyyy H:mm' Uhr 'z"), + 'Donnerstag, 2. Januar 2003 21:48 Uhr +100') + + def testFormatAMPMDateTime(self): + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 21, 48), + 'dd.MM.yy hh:mm a'), + '02.01.03 09:48 nachm.') + + def testFormatAllWeekdays(self): + for day in range(1, 8): + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, day+5, 21, 48), + "EEEE, d. MMMM yyyy H:mm' Uhr 'z"), + '%s, %i. Januar 2003 21:48 Uhr +000' %( + self.format.calendar.days[day][0], day+5)) + + def testFormatTimeZone(self): + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 12, 00), 'z'), + '+000') + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 12, 00), 'zz'), + '+00:00') + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 12, 00), 'zzz'), + 'UTC') + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 12, 00), 'zzzz'), + 'UTC') + tz = pytz.timezone('US/Eastern') + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 12, tzinfo=tz), 'z'), + '-500') + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 12, tzinfo=tz), 'zz'), + '-05:00') + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 12, tzinfo=tz), 'zzz'), + 'EST') + self.assertEqual(self.format.format( + datetime.datetime(2003, 01, 02, 12, tzinfo=tz), 'zzzz'), + 'US/Eastern') + + def testFormatWeekDay(self): + date = datetime.date(2003, 01, 02) + self.assertEqual(self.format.format(date, "E"), + '4') + self.assertEqual(self.format.format(date, "EE"), + '04') + self.assertEqual(self.format.format(date, "EEE"), + 'Do') + self.assertEqual(self.format.format(date, "EEEE"), + 'Donnerstag') + + # Create custom calendar, which has Sunday as the first day of the + # week. I am assigning a totally new dict here, since dicts are + # mutable and the value would be changed for the class and all its + # instances. + calendar = LocaleCalendarStub() + calendar.week = {'firstDay': 7, 'minDays': 1} + format = DateTimeFormat(calendar=calendar) + + self.assertEqual(format.format(date, "E"), + '5') + self.assertEqual(format.format(date, "EE"), + '05') + + def testFormatDayOfWeekInMonth(self): + date = datetime.date(2003, 01, 02) + self.assertEqual(self.format.format(date, "F"), + '1') + self.assertEqual(self.format.format(date, "FF"), + '01') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 9), "F"), + '2') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 16), "F"), + '3') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 23), "F"), + '4') + + def testFormatWeekInMonth(self): + self.assertEqual( + self.format.format(datetime.date(2003, 1, 3), "W"), + '1') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 3), "WW"), + '01') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 8), "W"), + '2') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 19), "W"), + '3') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 20), "W"), + '4') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 31), "W"), + '5') + + def testFormatHourInDayOneTo24(self): + self.assertEqual( + self.format.format(datetime.time(5, 0), "k"), + '5') + self.assertEqual( + self.format.format(datetime.time(5, 0), "kk"), + '05') + self.assertEqual( + self.format.format(datetime.time(0, 0), "k"), + '24') + self.assertEqual( + self.format.format(datetime.time(1, 0), "k"), + '1') + + def testFormatHourInDayZeroToEleven(self): + self.assertEqual( + self.format.format(datetime.time(5, 0), "K"), + '5') + self.assertEqual( + self.format.format(datetime.time(5, 0), "KK"), + '05') + self.assertEqual( + self.format.format(datetime.time(0, 0), "K"), + '0') + self.assertEqual( + self.format.format(datetime.time(12, 0), "K"), + '0') + self.assertEqual( + self.format.format(datetime.time(11, 0), "K"), + '11') + self.assertEqual( + self.format.format(datetime.time(23, 0), "K"), + '11') + + def testFormatSimpleHourRepresentation(self): + self.assertEqual( + self.format.format(datetime.datetime(2003, 01, 02, 23, 00), + 'dd.MM.yy h:mm:ss a'), + '02.01.03 11:00:00 nachm.') + self.assertEqual( + self.format.format(datetime.datetime(2003, 01, 02, 02, 00), + 'dd.MM.yy h:mm:ss a'), + '02.01.03 2:00:00 vorm.') + self.assertEqual( + self.format.format(datetime.time(0, 15), 'h:mm a'), + '12:15 vorm.') + self.assertEqual( + self.format.format(datetime.time(1, 15), 'h:mm a'), + '1:15 vorm.') + self.assertEqual( + self.format.format(datetime.time(12, 15), 'h:mm a'), + '12:15 nachm.') + self.assertEqual( + self.format.format(datetime.time(13, 15), 'h:mm a'), + '1:15 nachm.') + + def testFormatDayInYear(self): + self.assertEqual( + self.format.format(datetime.date(2003, 1, 3), 'D'), + u'3') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 3), 'DD'), + u'03') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 3), 'DDD'), + u'003') + self.assertEqual( + self.format.format(datetime.date(2003, 12, 31), 'D'), + u'365') + self.assertEqual( + self.format.format(datetime.date(2003, 12, 31), 'DD'), + u'365') + self.assertEqual( + self.format.format(datetime.date(2003, 12, 31), 'DDD'), + u'365') + self.assertEqual( + self.format.format(datetime.date(2004, 12, 31), 'DDD'), + u'366') + + def testFormatDayOfWeekInMOnth(self): + self.assertEqual( + self.format.format(datetime.date(2003, 1, 3), 'F'), + u'1') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 10), 'F'), + u'2') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 17), 'F'), + u'3') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 24), 'F'), + u'4') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 31), 'F'), + u'5') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 6), 'F'), + u'1') + + def testFormatUnusualFormats(self): + self.assertEqual( + self.format.format(datetime.date(2003, 1, 3), 'DDD-yyyy'), + u'003-2003') + self.assertEqual( + self.format.format(datetime.date(2003, 1, 10), + "F. EEEE 'im' MMMM, yyyy"), + u'2. Freitag im Januar, 2003') + + + +class TestNumberPatternParser(TestCase): + """Extensive tests for the ICU-based-syntax number pattern parser.""" + + def testParseSimpleIntegerPattern(self): + self.assertEqual( + parseNumberPattern('###0'), + ((None, '', None, '###0', '', '', None, '', None, 0), + (None, '', None, '###0', '', '', None, '', None, 0))) + + def testParseScientificIntegerPattern(self): + self.assertEqual( + parseNumberPattern('###0E#0'), + ((None, '', None, '###0', '', '#0', None, '', None, 0), + (None, '', None, '###0', '', '#0', None, '', None, 0))) + self.assertEqual( + parseNumberPattern('###0E+#0'), + ((None, '', None, '###0', '', '+#0', None, '', None, 0), + (None, '', None, '###0', '', '+#0', None, '', None, 0))) + + def testParsePosNegAlternativeIntegerPattern(self): + self.assertEqual( + parseNumberPattern('###0;#0'), + ((None, '', None, '###0', '', '', None, '', None, 0), + (None, '', None, '#0', '', '', None, '', None, 0))) + + def testParsePrefixedIntegerPattern(self): + self.assertEqual( + parseNumberPattern('+###0'), + ((None, '+', None, '###0', '', '', None, '', None, 0), + (None, '+', None, '###0', '', '', None, '', None, 0))) + + def testParsePosNegIntegerPattern(self): + self.assertEqual( + parseNumberPattern('+###0;-###0'), + ((None, '+', None, '###0', '', '', None, '', None, 0), + (None, '-', None, '###0', '', '', None, '', None, 0))) + + def testParseScientificPosNegIntegerPattern(self): + self.assertEqual( + parseNumberPattern('+###0E0;-###0E#0'), + ((None, '+', None, '###0', '', '0', None, '', None, 0), + (None, '-', None, '###0', '', '#0', None, '', None, 0))) + + def testParseThousandSeparatorIntegerPattern(self): + self.assertEqual( + parseNumberPattern('#,##0'), + ((None, '', None, '###0', '', '', None, '', None, 1), + (None, '', None, '###0', '', '', None, '', None, 1))) + + def testParseSimpleDecimalPattern(self): + self.assertEqual( + parseNumberPattern('###0.00#'), + ((None, '', None, '###0', '00#', '', None, '', None, 0), + (None, '', None, '###0', '00#', '', None, '', None, 0))) + + def testParseScientificDecimalPattern(self): + self.assertEqual( + parseNumberPattern('###0.00#E#0'), + ((None, '', None, '###0', '00#', '#0', None, '', None, 0), + (None, '', None, '###0', '00#', '#0', None, '', None, 0))) + + def testParsePosNegAlternativeFractionPattern(self): + self.assertEqual( + parseNumberPattern('###0.00#;#0.0#'), + ((None, '', None, '###0', '00#', '', None, '', None, 0), + (None, '', None, '#0', '0#', '', None, '', None, 0))) + + def testParsePosNegFractionPattern(self): + self.assertEqual( + parseNumberPattern('+###0.0##;-###0.0##'), + ((None, '+', None, '###0', '0##', '', None, '', None, 0), + (None, '-', None, '###0', '0##', '', None, '', None, 0))) + + def testParseScientificPosNegFractionPattern(self): + self.assertEqual( + parseNumberPattern('+###0.0##E#0;-###0.0##E0'), + ((None, '+', None, '###0', '0##', '#0', None, '', None, 0), + (None, '-', None, '###0', '0##', '0', None, '', None, 0))) + + def testParseThousandSeparatorFractionPattern(self): + self.assertEqual( + parseNumberPattern('#,##0.0#'), + ((None, '', None, '###0', '0#', '', None, '', None, 1), + (None, '', None, '###0', '0#', '', None, '', None, 1))) + + def testParsePadding1WithoutPrefixPattern(self): + self.assertEqual( + parseNumberPattern('* ###0'), + ((' ', '', None, '###0', '', '', None, '', None, 0), + (' ', '', None, '###0', '', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern('* ###0.0##'), + ((' ', '', None, '###0', '0##', '', None, '', None, 0), + (' ', '', None, '###0', '0##', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern('* ###0.0##;*_###0.0##'), + ((' ', '', None, '###0', '0##', '', None, '', None, 0), + ('_', '', None, '###0', '0##', '', None, '', None, 0))) + + def testParsePadding1WithPrefixPattern(self): + self.assertEqual( + parseNumberPattern('* +###0'), + ((' ', '+', None, '###0', '', '', None, '', None, 0), + (' ', '+', None, '###0', '', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern('* +###0.0##'), + ((' ', '+', None, '###0', '0##', '', None, '', None, 0), + (' ', '+', None, '###0', '0##', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern('* +###0.0##;*_-###0.0##'), + ((' ', '+', None, '###0', '0##', '', None, '', None, 0), + ('_', '-', None, '###0', '0##', '', None, '', None, 0))) + + def testParsePadding1Padding2WithPrefixPattern(self): + self.assertEqual( + parseNumberPattern('* +* ###0'), + ((' ', '+', ' ', '###0', '', '', None, '', None, 0), + (' ', '+', ' ', '###0', '', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern('* +* ###0.0##'), + ((' ', '+', ' ', '###0', '0##', '', None, '', None, 0), + (' ', '+', ' ', '###0', '0##', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern('* +* ###0.0##;*_-*_###0.0##'), + ((' ', '+', ' ', '###0', '0##', '', None, '', None, 0), + ('_', '-', '_', '###0', '0##', '', None, '', None, 0))) + + def testParsePadding3WithoutSufffixPattern(self): + self.assertEqual( + parseNumberPattern('###0* '), + ((None, '', None, '###0', '', '', ' ', '', None, 0), + (None, '', None, '###0', '', '', ' ', '', None, 0))) + self.assertEqual( + parseNumberPattern('###0.0##* '), + ((None, '', None, '###0', '0##', '', ' ', '', None, 0), + (None, '', None, '###0', '0##', '', ' ', '', None, 0))) + self.assertEqual( + parseNumberPattern('###0.0##* ;###0.0##*_'), + ((None, '', None, '###0', '0##', '', ' ', '', None, 0), + (None, '', None, '###0', '0##', '', '_', '', None, 0))) + + def testParsePadding3InScientificPattern(self): + self.assertEqual( + parseNumberPattern('###0E#0* '), + ((None, '', None, '###0', '', '#0', ' ', '', None, 0), + (None, '', None, '###0', '', '#0', ' ', '', None, 0))) + self.assertEqual( + parseNumberPattern('###0.0##E0* '), + ((None, '', None, '###0', '0##', '0', ' ', '', None, 0), + (None, '', None, '###0', '0##', '0', ' ', '', None, 0))) + self.assertEqual( + parseNumberPattern('###0.0##E#0* ;###0.0##E0*_'), + ((None, '', None, '###0', '0##', '#0', ' ', '', None, 0), + (None, '', None, '###0', '0##', '0', '_', '', None, 0))) + + def testParsePadding3WithSufffixPattern(self): + self.assertEqual( + parseNumberPattern('###0* /'), + ((None, '', None, '###0', '', '', ' ', '/', None, 0), + (None, '', None, '###0', '', '', ' ', '/', None, 0))) + self.assertEqual( + parseNumberPattern('###0.0#* /'), + ((None, '', None, '###0', '0#', '', ' ', '/', None, 0), + (None, '', None, '###0', '0#', '', ' ', '/', None, 0))) + self.assertEqual( + parseNumberPattern('###0.0#* /;###0.0#*_/'), + ((None, '', None, '###0', '0#', '', ' ', '/', None, 0), + (None, '', None, '###0', '0#', '', '_', '/', None, 0))) + + def testParsePadding3And4WithSuffixPattern(self): + self.assertEqual( + parseNumberPattern('###0* /* '), + ((None, '', None, '###0', '', '', ' ', '/', ' ', 0), + (None, '', None, '###0', '', '', ' ', '/', ' ', 0))) + self.assertEqual( + parseNumberPattern('###0* /* ;###0*_/*_'), + ((None, '', None, '###0', '', '', ' ', '/', ' ', 0), + (None, '', None, '###0', '', '', '_', '/', '_', 0))) + + def testParseMultipleCharacterPrefix(self): + self.assertEqual( + parseNumberPattern('DM###0'), + ((None, 'DM', None, '###0', '', '', None, '', None, 0), + (None, 'DM', None, '###0', '', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern('DM* ###0'), + ((None, 'DM', ' ', '###0', '', '', None, '', None, 0), + (None, 'DM', ' ', '###0', '', '', None, '', None, 0))) + + def testParseStringEscapedPrefix(self): + self.assertEqual( + parseNumberPattern("'DEM'###0"), + ((None, 'DEM', None, '###0', '', '', None, '', None, 0), + (None, 'DEM', None, '###0', '', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern("D'EM'###0"), + ((None, 'DEM', None, '###0', '', '', None, '', None, 0), + (None, 'DEM', None, '###0', '', '', None, '', None, 0))) + self.assertEqual( + parseNumberPattern("D'E'M###0"), + ((None, 'DEM', None, '###0', '', '', None, '', None, 0), + (None, 'DEM', None, '###0', '', '', None, '', None, 0))) + + def testParseStringEscapedSuffix(self): + self.assertEqual( + parseNumberPattern("###0'DEM'"), + ((None, '', None, '###0', '', '', None, 'DEM', None, 0), + (None, '', None, '###0', '', '', None, 'DEM', None, 0))) + self.assertEqual( + parseNumberPattern("###0D'EM'"), + ((None, '', None, '###0', '', '', None, 'DEM', None, 0), + (None, '', None, '###0', '', '', None, 'DEM', None, 0))) + self.assertEqual( + parseNumberPattern("###0D'E'M"), + ((None, '', None, '###0', '', '', None, 'DEM', None, 0), + (None, '', None, '###0', '', '', None, 'DEM', None, 0))) + + +class TestNumberFormat(TestCase): + """Test the functionality of an implmentation of the NumberFormat.""" + + format = NumberFormat(symbols={ + 'decimal': '.', 'group': ',', 'list': ';', 'percentSign': '%', + 'nativeZeroDigit': '0', 'patternDigit': '#', 'plusSign': '+', + 'minusSign': '-', 'exponential': 'E', 'perMille': 'o/oo', + 'infinity': 'oo', 'nan': 'N/A'}) + + def testInterfaceConformity(self): + self.assert_(INumberFormat.providedBy(self.format)) + + def testParseSimpleInteger(self): + self.assertEqual(self.format.parse('23341', '###0'), + 23341) + self.assertEqual(self.format.parse('041', '#000'), + 41) + + def testParseScientificInteger(self): + self.assertEqual(self.format.parse('2.3341E4', '0.0###E0'), + 23341) + self.assertEqual(self.format.parse('4.100E01', '0.000##E00'), + 41) + self.assertEqual(self.format.parse('1E0', '0E0'), + 1) + self.assertEqual(self.format.parse('0E0', '0E0'), + 0) + # This is a special case I found not working, but is used frequently + # in the new LDML Locale files. + self.assertEqual(self.format.parse('2.3341E+04', '0.000###E+00'), + 23341) + + def testParsePosNegAlternativeInteger(self): + self.assertEqual(self.format.parse('23341', '#000;#00'), + 23341) + self.assertEqual(self.format.parse('041', '#000;#00'), + 41) + self.assertEqual(self.format.parse('41', '#000;#00'), + -41) + self.assertEqual(self.format.parse('01', '#000;#00'), + -1) + + def testParsePrefixedInteger(self): + self.assertEqual(self.format.parse('+23341', '+###0'), + 23341) + self.assertEqual(self.format.parse('+041', '+#000'), + 41) + + def testParsePosNegInteger(self): + self.assertEqual(self.format.parse('+23341', '+###0;-###0'), + 23341) + self.assertEqual(self.format.parse('+041', '+#000;-#000'), + 41) + self.assertEqual(self.format.parse('-23341', '+###0;-###0'), + -23341) + self.assertEqual(self.format.parse('-041', '+#000;-#000'), + -41) + + def testParseThousandSeparatorInteger(self): + self.assertEqual(self.format.parse('+23,341', '+#,##0;-#,##0'), + 23341) + self.assertEqual(self.format.parse('-23,341', '+#,##0;-#,##0'), + -23341) + self.assertEqual(self.format.parse('+0,041', '+#0,000;-#0,000'), + 41) + self.assertEqual(self.format.parse('-0,041', '+#0,000;-#0,000'), + -41) + + def testParseDecimal(self): + self.assertEqual(self.format.parse('23341.02', '###0.0#'), + 23341.02) + self.assertEqual(self.format.parse('23341.1', '###0.0#'), + 23341.1) + self.assertEqual(self.format.parse('23341.020', '###0.000#'), + 23341.02) + + def testParseDecimalWithOptionalDecimalDigits(self): + self.assertEqual(self.format.parse('23341.02', '###0.##'), + 23341.02) + self.assertEqual(self.format.parse('23341', '###0.#'), + 23341.0) + self.assertEqual(self.format.parse('23341.', '###0.#'), + 23341.0) + + def testParseScientificDecimal(self): + self.assertEqual(self.format.parse('2.334102E04', '0.00####E00'), + 23341.02) + self.assertEqual(self.format.parse('2.3341020E004', '0.0000000E000'), + 23341.02) + self.assertEqual(self.format.parse('0.0E0', '0.0#E0'), + 0.0) + + def testParseScientificDecimalSmallerOne(self): + self.assertEqual(self.format.parse('2.357E-02', '0.00####E00'), + 0.02357) + self.assertEqual(self.format.parse('2.0000E-02', '0.0000E00'), + 0.02) + + def testParsePadding1WithoutPrefix(self): + self.assertEqual(self.format.parse(' 41', '* ##0;*_##0'), + 41) + self.assertEqual(self.format.parse('_41', '* ##0;*_##0'), + -41) + + def testParsePadding1WithPrefix(self): + self.assertEqual(self.format.parse(' +41', '* +##0;*_-##0'), + 41) + self.assertEqual(self.format.parse('_-41', '* +##0;*_-##0'), + -41) + + def testParsePadding1Padding2WithPrefix(self): + self.assertEqual(self.format.parse(' + 41', '* +* ###0;*_-*_###0'), + +41) + self.assertEqual(self.format.parse('__-_41', '* +* ###0;*_-*_###0'), + -41) + + def testParsePadding1Scientific(self): + self.assertEqual(self.format.parse(' 4.102E1', + '* 0.0####E0;*_0.0####E0'), + 41.02) + self.assertEqual(self.format.parse('__4.102E1', + '* 0.0####E0;*_0.0####E0'), + -41.02) + self.assertEqual(self.format.parse(' +4.102E1', + '* +0.0###E0;*_-0.0###E0'), + 41.02) + self.assertEqual(self.format.parse('_-4.102E1', + '* +0.0###E0;*_-0.0###E0'), + -41.02) + + def testParsePadding3WithoutSufffix(self): + self.assertEqual(self.format.parse('41.02 ', '#0.0###* ;#0.0###*_'), + 41.02) + self.assertEqual(self.format.parse('41.02__', '#0.0###* ;#0.0###*_'), + -41.02) + + def testParsePadding3WithSufffix(self): + self.assertEqual( + self.format.parse('[41.02 ]', '[#0.0###* ];(#0.0###*_)'), + 41.02) + self.assertEqual( + self.format.parse('(41.02__)', '[#0.0###* ];(#0.0###*_)'), + -41.02) + + def testParsePadding3Scientific(self): + self.assertEqual(self.format.parse('4.102E1 ', + '0.0##E0##* ;0.0##E0##*_'), + 41.02) + self.assertEqual(self.format.parse('4.102E1__', + '0.0##E0##* ;0.0##E0##*_'), + -41.02) + self.assertEqual(self.format.parse('(4.102E1 )', + '(0.0##E0##* );0.0E0'), + 41.02) + self.assertEqual(self.format.parse('[4.102E1__]', + '0.0E0;[0.0##E0##*_]'), + -41.02) + + def testParsePadding3Padding4WithSuffix(self): + self.assertEqual(self.format.parse('(41.02 ) ', '(#0.0###* )* '), + 41.02) + self.assertEqual(self.format.parse('(4.102E1 ) ', '(0.0##E0##* )* '), + 41.02) + + def testParseDecimalWithGermanDecimalSeparator(self): + format = NumberFormat(symbols={'decimal': ',', 'group': '.'}) + self.assertEqual(format.parse('1.234,567', '#,##0.000'), 1234.567) + + def testParseWithAlternativeExponentialSymbol(self): + format = NumberFormat( + symbols={'decimal': '.', 'group': ',', 'exponential': 'X'}) + self.assertEqual(format.parse('1.2X11', '#.#E0'), 1.2e11) + + def testParseFailWithInvalidCharacters(self): + self.assertRaises( + NumberParseError,self.format.parse, '123xx', '###0.0#') + self.assertRaises( + NumberParseError, self.format.parse, 'xx123', '###0.0#') + self.assertRaises( + NumberParseError, self.format.parse, '1xx23', '###0.0#') + + def testParseFailWithInvalidGroupCharacterPosition(self): + self.assertRaises( + NumberParseError,self.format.parse, '123,00', '###0.0#') + self.assertRaises( + NumberParseError, self.format.parse, ',123', '###0.0#') + self.assertRaises( + NumberParseError, self.format.parse, '1,23.00', '###0.0#') + + def testChangeOutputType(self): + format = NumberFormat() + format.type = decimal.Decimal + self.assertEqual(format.parse('23341', '###0'), + decimal.Decimal('23341')) + self.assertEqual(format.parse('233.41', '###0.00'), + decimal.Decimal('233.41')) + + def testFormatSimpleInteger(self): + self.assertEqual(self.format.format(23341, '###0'), + '23341') + self.assertEqual(self.format.format(41, '#000'), + '041') + + def testFormatScientificInteger(self): + self.assertEqual(self.format.format(23341, '0.000#E0'), + '2.3341E4') + self.assertEqual(self.format.format(23341, '0.000#E00'), + '2.3341E04') + self.assertEqual(self.format.format(1, '0.##E0'), + '1E0') + self.assertEqual(self.format.format(1, '0.00E00'), + '1.00E00') + # This is a special case I found not working, but is used frequently + # in the new LDML Locale files. + self.assertEqual(self.format.format(23341, '0.000###E+00'), + '2.3341E+04') + + def testFormatScientificZero(self): + self.assertEqual(self.format.format(0, '0.00E00'), + '0.00E00') + self.assertEqual(self.format.format(0, '0E0'), + '0E0') + + def testFormatPosNegAlternativeInteger(self): + self.assertEqual(self.format.format(23341, '#000;#00'), + '23341') + self.assertEqual(self.format.format(41, '#000;#00'), + '041') + self.assertEqual(self.format.format(-23341, '#000;#00'), + '23341') + self.assertEqual(self.format.format(-41, '#000;#00'), + '41') + self.assertEqual(self.format.format(-1, '#000;#00'), + '01') + + def testFormatPrefixedInteger(self): + self.assertEqual(self.format.format(23341, '+###0'), + '+23341') + self.assertEqual(self.format.format(41, '+#000'), + '+041') + self.assertEqual(self.format.format(-23341, '+###0'), + '+23341') + self.assertEqual(self.format.format(-41, '+#000'), + '+041') + + def testFormatPosNegInteger(self): + self.assertEqual(self.format.format(23341, '+###0;-###0'), + '+23341') + self.assertEqual(self.format.format(41, '+#000;-#000'), + '+041') + self.assertEqual(self.format.format(-23341, '+###0;-###0'), + '-23341') + self.assertEqual(self.format.format(-41, '+#000;-#000'), + '-041') + + def testFormatPosNegScientificInteger(self): + self.assertEqual(self.format.format(23341, '+0.00###E00;-0.00###E00'), + '+2.3341E04') + self.assertEqual(self.format.format(23341, '-0.00###E00;-0.00###E00'), + '-2.3341E04') + + def testFormatThousandSeparatorInteger(self): + self.assertEqual(self.format.format(23341, '+#,##0;-#,##0'), + '+23,341') + self.assertEqual(self.format.format(-23341, '+#,##0;-#,##0'), + '-23,341') + self.assertEqual(self.format.format(41, '+#0,000;-#0,000'), + '+0,041') + self.assertEqual(self.format.format(-41, '+#0,000;-#0,000'), + '-0,041') + + def testFormatDecimal(self): + self.assertEqual(self.format.format(23341.02357, '###0.0#'), + '23341.02') + self.assertEqual(self.format.format(23341.02357, '###0.000#'), + '23341.0236') + self.assertEqual(self.format.format(23341.02, '###0.000#'), + '23341.020') + + def testRounding(self): + self.assertEqual(self.format.format(0.5, '#'), '1') + self.assertEqual(self.format.format(0.49, '#'), '0') + self.assertEqual(self.format.format(0.45, '0.0'), '0.5') + self.assertEqual(self.format.format(150, '0E0'), '2E2') + self.assertEqual(self.format.format(149, '0E0'), '1E2') + self.assertEqual(self.format.format(1.9999, '0.000'), '2.000') + self.assertEqual(self.format.format(1.9999, '0.0000'), '1.9999') + + + def testFormatScientificDecimal(self): + self.assertEqual(self.format.format(23341.02357, '0.00####E00'), + '2.334102E04') + self.assertEqual(self.format.format(23341.02, '0.0000000E000'), + '2.3341020E004') + + def testFormatScientificDecimalSmallerOne(self): + self.assertEqual(self.format.format(0.02357, '0.00####E00'), + '2.357E-02') + self.assertEqual(self.format.format(0.02, '0.0000E00'), + '2.0000E-02') + + def testFormatPadding1WithoutPrefix(self): + self.assertEqual(self.format.format(41, '* ##0;*_##0'), + ' 41') + self.assertEqual(self.format.format(-41, '* ##0;*_##0'), + '_41') + + def testFormatPadding1WithPrefix(self): + self.assertEqual(self.format.format(41, '* +##0;*_-##0'), + ' +41') + self.assertEqual(self.format.format(-41, '* +##0;*_-##0'), + '_-41') + + def testFormatPadding1Scientific(self): + self.assertEqual(self.format.format(41.02, '* 0.0####E0;*_0.0####E0'), + ' 4.102E1') + self.assertEqual(self.format.format(-41.02, '* 0.0####E0;*_0.0####E0'), + '__4.102E1') + self.assertEqual(self.format.format(41.02, '* +0.0###E0;*_-0.0###E0'), + ' +4.102E1') + self.assertEqual(self.format.format(-41.02, '* +0.0###E0;*_-0.0###E0'), + '_-4.102E1') + + def testFormatPadding1Padding2WithPrefix(self): + self.assertEqual(self.format.format(41, '* +* ###0;*_-*_###0'), + ' + 41') + self.assertEqual(self.format.format(-41, '* +* ###0;*_-*_###0'), + '__-_41') + + def testFormatPadding3WithoutSufffix(self): + self.assertEqual(self.format.format(41.02, '#0.0###* ;#0.0###*_'), + '41.02 ') + self.assertEqual(self.format.format(-41.02, '#0.0###* ;#0.0###*_'), + '41.02__') + + def testFormatPadding3WithSufffix(self): + self.assertEqual(self.format.format(41.02, '[#0.0###* ];(#0.0###*_)'), + '[41.02 ]') + self.assertEqual(self.format.format(-41.02, '[#0.0###* ];(#0.0###*_)'), + '(41.02__)') + + def testFormatPadding3Scientific(self): + self.assertEqual(self.format.format(41.02, '0.0##E0##* ;0.0##E0##*_'), + '4.102E1 ') + self.assertEqual(self.format.format(-41.02, '0.0##E0##* ;0.0##E0##*_'), + '4.102E1__') + self.assertEqual(self.format.format(41.02, '(0.0##E0##* );0.0E0'), + '(4.102E1 )') + self.assertEqual(self.format.format(-41.02, '0.0E0;[0.0##E0##*_]'), + '[4.102E1__]') + + def testFormatPadding3Padding4WithSuffix(self): + self.assertEqual(self.format.format(41.02, '(#0.0###* )* '), + '(41.02 ) ') + self.assertEqual(self.format.format(41.02, '(0.0##E0##* )* '), + '(4.102E1 ) ') + + +def test_suite(): + return TestSuite(( + makeSuite(TestDateTimePatternParser), + makeSuite(TestBuildDateTimeParseInfo), + makeSuite(TestDateTimeFormat), + makeSuite(TestNumberPatternParser), + makeSuite(TestNumberFormat), + )) diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_gettextmessagecatalog.py zope3-3.5~bzr18/src/zope/i18n/tests/test_gettextmessagecatalog.py --- zope3-3.4.0/src/zope/i18n/tests/test_gettextmessagecatalog.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_gettextmessagecatalog.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,45 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 a gettext implementation of a Message Catalog. + +$Id: test_gettextmessagecatalog.py 88399 2008-07-16 13:17:24Z regebro $ +""" +import unittest, os +from zope.i18n.gettextmessagecatalog import GettextMessageCatalog +from zope.i18n.tests.test_imessagecatalog import TestIMessageCatalog + + +class GettextMessageCatalogTest(TestIMessageCatalog): + + def _getMessageCatalog(self): + from zope.i18n import tests + path = os.path.dirname(tests.__file__) + self._path = os.path.join(path, 'en-default.mo') + catalog = GettextMessageCatalog('en', 'default', self._path) + return catalog + + + def _getUniqueIndentifier(self): + return self._path + + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(GettextMessageCatalogTest)) + return suite + + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/i18n/tests/testi18nawareobject.py zope3-3.5~bzr18/src/zope/i18n/tests/testi18nawareobject.py --- zope3-3.4.0/src/zope/i18n/tests/testi18nawareobject.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/testi18nawareobject.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,90 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 is a test for the II18nAware interface. + +$Id: testi18nawareobject.py 26559 2004-07-15 21:22:32Z srichter $ +""" +import unittest + +from zope.i18n.interfaces import II18nAware +from zope.i18n.tests.testii18naware import TestII18nAware +from zope.interface import implements + + +class I18nAwareContentObject(object): + + implements(II18nAware) + + def __init__(self): + self.content = {} + self.defaultLanguage = 'en' + + def getContent(self, language): + return self.content[language] + + def queryContent(self, language, default=None): + return self.content.get(language, default) + + def setContent(self, content, language): + self.content[language] = content + + ############################################################ + # Implementation methods for interface + # II18nAware.py + + def getDefaultLanguage(self): + 'See II18nAware' + return self.defaultLanguage + + def setDefaultLanguage(self, language): + 'See II18nAware' + self.defaultLanguage = language + + def getAvailableLanguages(self): + 'See II18nAware' + return self.content.keys() + + # + ############################################################ + + +class TestI18nAwareObject(TestII18nAware): + + def _createObject(self): + object = I18nAwareContentObject() + object.setContent('English', 'en') + object.setContent('Lithuanian', 'lt') + object.setContent('French', 'fr') + return object + + def testSetContent(self): + self.object.setContent('German', 'de') + self.assertEqual(self.object.content['de'], 'German') + + def testGetContent(self): + self.assertEqual(self.object.getContent('en'), 'English') + self.assertRaises(KeyError, self.object.getContent, 'es') + + def testQueryContent(self): + self.assertEqual(self.object.queryContent('en'), 'English') + self.assertEqual(self.object.queryContent('es', 'N/A'), 'N/A') + + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(TestI18nAwareObject) + + +if __name__ == '__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/i18n/tests/testii18naware.py zope3-3.5~bzr18/src/zope/i18n/tests/testii18naware.py --- zope3-3.4.0/src/zope/i18n/tests/testii18naware.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/testii18naware.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 is a test for the II18nAware interface. + +$Id: testii18naware.py 25177 2004-06-02 13:17:31Z jim $ +""" +import unittest + +def sorted(list): + list.sort() + return list + +class TestII18nAware(unittest.TestCase): + + def setUp(self): + self.object = self._createObject() + self.object.setDefaultLanguage('fr') + + def _createObject(self): + # Should create an object that has lt, en and fr as available + # languages + pass + + def testGetDefaultLanguage(self): + self.assertEqual(self.object.getDefaultLanguage(), 'fr') + + def testSetDefaultLanguage(self): + self.object.setDefaultLanguage('lt') + self.assertEqual(self.object.getDefaultLanguage(), 'lt') + + def testGetAvailableLanguages(self): + self.assertEqual(sorted(self.object.getAvailableLanguages()), ['en', 'fr', 'lt']) + + +def test_suite(): + return unittest.TestSuite() # Deliberatly empty diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_imessagecatalog.py zope3-3.5~bzr18/src/zope/i18n/tests/test_imessagecatalog.py --- zope3-3.4.0/src/zope/i18n/tests/test_imessagecatalog.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_imessagecatalog.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,69 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 is an 'abstract' test for the IMessageCatalog interface. + +$Id: test_imessagecatalog.py 88399 2008-07-16 13:17:24Z regebro $ +""" +import unittest +from zope.interface.verify import verifyObject +from zope.i18n.interfaces import IMessageCatalog + + +class TestIMessageCatalog(unittest.TestCase): + + + # This should be overwritten by every class that inherits this test + def _getMessageCatalog(self): + pass + + def _getUniqueIndentifier(self): + pass + + + def setUp(self): + self._catalog = self._getMessageCatalog() + + def testInterface(self): + verifyObject(IMessageCatalog, self._catalog) + + def testGetMessage(self): + catalog = self._catalog + self.assertEqual(catalog.getMessage('short_greeting'), 'Hello!') + self.assertRaises(KeyError, catalog.getMessage, 'foo') + + + def testQueryMessage(self): + catalog = self._catalog + self.assertEqual(catalog.queryMessage('short_greeting'), 'Hello!') + self.assertEqual(catalog.queryMessage('foo'), None) + self.assertEqual(catalog.queryMessage('foo', 'bar'), 'bar') + + + def testGetLanguage(self): + catalog = self._catalog + self.assertEqual(catalog.language, 'en') + + + def testGetDomain(self): + catalog = self._catalog + self.assertEqual(catalog.domain, 'default') + + + def testGetIdentifier(self): + catalog = self._catalog + self.assertEqual(catalog.getIdentifier(), self._getUniqueIndentifier()) + + +def test_suite(): + return unittest.TestSuite() # Deliberatly empty diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_itranslationdomain.py zope3-3.5~bzr18/src/zope/i18n/tests/test_itranslationdomain.py --- zope3-3.4.0/src/zope/i18n/tests/test_itranslationdomain.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_itranslationdomain.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,108 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 is an 'abstract' test for the ITranslationDomain interface. + +$Id: test_itranslationdomain.py 68771 2006-06-20 11:24:31Z hdima $ +""" +import unittest +from zope.interface.verify import verifyObject +from zope.interface import implements + +import zope.component +from zope.component.testing import PlacelessSetup + +from zope.i18n.negotiator import negotiator +from zope.i18n.interfaces import INegotiator, IUserPreferredLanguages +from zope.i18n.interfaces import ITranslationDomain + + +class Environment(object): + + implements(IUserPreferredLanguages) + + def __init__(self, langs=()): + self.langs = langs + + def getPreferredLanguages(self): + return self.langs + +class TestITranslationDomain(PlacelessSetup): + + # This should be overwritten by every class that inherits this test + def _getTranslationDomain(self): + pass + + def setUp(self): + super(TestITranslationDomain, self).setUp() + self._domain = self._getTranslationDomain() + + # Setup the negotiator utility + zope.component.provideUtility(negotiator, INegotiator) + + def testInterface(self): + verifyObject(ITranslationDomain, self._domain) + + def testSimpleTranslate(self): + translate = self._domain.translate + eq = self.assertEqual + # Test that a given message id is properly translated in a supported + # language + eq(translate('short_greeting', target_language='de'), 'Hallo!') + # Same test, but use the context argument + context = Environment(('de', 'en')) + eq(translate('short_greeting', context=context), 'Hallo!') + + def testDynamicTranslate(self): + translate = self._domain.translate + eq = self.assertEqual + # Testing both translation and interpolation + eq(translate('greeting', mapping={'name': 'Stephan'}, + target_language='de'), + 'Hallo Stephan, wie geht es Dir?') + # Testing default value interpolation + eq(translate('greeting', mapping={'name': 'Philipp'}, + target_language='fr', + default="Hello $name, how are you?"), + 'Hello Philipp, how are you?') + + def testNoTranslation(self): + translate = self._domain.translate + eq = self.assertEqual + # Verify that an unknown message id will end up not being translated + eq(translate('glorp_smurf_hmpf', target_language='en'), + 'glorp_smurf_hmpf') + # Test default value behaviour + eq(translate('glorp_smurf_hmpf', target_language='en', + default='Glorp Smurf Hmpf'), + 'Glorp Smurf Hmpf') + + def testUnicodeDefaultValue(self): + translate = self._domain.translate + translated = translate('no way', target_language='en') + self.assertEqual(translated, "no way") + self.assert_(type(translated) is unicode) + + def testNoTargetLanguage(self): + translate = self._domain.translate + eq = self.assertEqual + # Test that default is returned when no language can be negotiated + context = Environment(('xx', )) + eq(translate('short_greeting', context=context, default=42), 42) + + # Test that default is returned when there's no destination language + eq(translate('short_greeting', default=42), 42) + + +def test_suite(): + return unittest.TestSuite() # Deliberately empty diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_negotiator.py zope3-3.5~bzr18/src/zope/i18n/tests/test_negotiator.py --- zope3-3.4.0/src/zope/i18n/tests/test_negotiator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_negotiator.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,63 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Language Negotiator tests. + +$Id: test_negotiator.py 67630 2006-04-27 00:54:03Z jim $ +""" +import unittest + +from zope.i18n.negotiator import Negotiator +from zope.i18n.interfaces import IUserPreferredLanguages +from zope.component.testing import PlacelessSetup +from zope.interface import implements + +class Env(object): + implements(IUserPreferredLanguages) + + def __init__(self, langs=()): + self.langs = langs + + def getPreferredLanguages(self): + return self.langs + + +class NegotiatorTest(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(NegotiatorTest, self).setUp() + self.negotiator = Negotiator() + + def test_findLanguages(self): + + _cases = ( + (('en','de'), ('en','de','fr'), 'en'), + (('en'), ('it','de','fr'), None), + (('pt-br','de'), ('pt_BR','de','fr'), 'pt_BR'), + (('pt-br','en'), ('pt', 'en', 'fr'), 'pt'), + (('pt-br','en-us', 'de'), ('de', 'en', 'fr'), 'en'), + ) + + for user_pref_langs, obj_langs, expected in _cases: + env = Env(user_pref_langs) + self.assertEqual(self.negotiator.getLanguage(obj_langs, env), + expected) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(NegotiatorTest), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/i18n/tests/test.py zope3-3.5~bzr18/src/zope/i18n/tests/test.py --- zope3-3.4.0/src/zope/i18n/tests/test.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""Misc tests + +$Id: test.py 111000 2010-04-16 19:43:57Z tseaver $ +""" +import unittest + +import doctest +from zope.component.testing import setUp, tearDown + + +def test_suite(): + return doctest.DocTestSuite("zope.i18n", setUp=setUp, tearDown=tearDown) + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_simpletranslationdomain.py zope3-3.5~bzr18/src/zope/i18n/tests/test_simpletranslationdomain.py --- zope3-3.4.0/src/zope/i18n/tests/test_simpletranslationdomain.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_simpletranslationdomain.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,50 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 module tests the regular persistent Translation Domain. + +$Id: test_simpletranslationdomain.py 72133 2007-01-20 13:01:58Z mgedmin $ +""" +import unittest +from zope.i18n.simpletranslationdomain import SimpleTranslationDomain +from zope.i18n.tests.test_itranslationdomain import TestITranslationDomain + + +data = { + ('en', 'short_greeting'): 'Hello!', + ('de', 'short_greeting'): 'Hallo!', + ('en', 'greeting'): 'Hello $name, how are you?', + ('de', 'greeting'): 'Hallo $name, wie geht es Dir?'} + + +class TestSimpleTranslationDomain(unittest.TestCase, TestITranslationDomain): + + def setUp(self): + TestITranslationDomain.setUp(self) + + def tearDown(self): + TestITranslationDomain.tearDown(self) + + def _getTranslationDomain(self): + domain = SimpleTranslationDomain('default', data) + return domain + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestSimpleTranslationDomain)) + return suite + + +if __name__ == '__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_testmessagecatalog.py zope3-3.5~bzr18/src/zope/i18n/tests/test_testmessagecatalog.py --- zope3-3.4.0/src/zope/i18n/tests/test_testmessagecatalog.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_testmessagecatalog.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 unittest +import doctest + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('../testmessagecatalog.txt'), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') + diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_translationdomain.py zope3-3.5~bzr18/src/zope/i18n/tests/test_translationdomain.py --- zope3-3.4.0/src/zope/i18n/tests/test_translationdomain.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_translationdomain.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,198 @@ +############################################################################## +# +# Copyright (c) 2001-2008 Zope Corporation 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 module tests the regular persistent Translation Domain. + +$Id: test_translationdomain.py 99163 2009-04-14 12:20:56Z jinty $ +""" +import unittest, os +from zope.i18n.translationdomain import TranslationDomain +from zope.i18n.gettextmessagecatalog import GettextMessageCatalog +from zope.i18n.tests.test_itranslationdomain import \ + TestITranslationDomain, Environment +from zope.i18nmessageid import MessageFactory +from zope.i18n.interfaces import ITranslationDomain +import zope.component + +def testdir(): + from zope.i18n import tests + return os.path.dirname(tests.__file__) + + +class TestGlobalTranslationDomain(unittest.TestCase, TestITranslationDomain): + + def setUp(self): + TestITranslationDomain.setUp(self) + + def tearDown(self): + TestITranslationDomain.tearDown(self) + + def _getTranslationDomain(self): + domain = TranslationDomain('default') + path = testdir() + en_catalog = GettextMessageCatalog('en', 'default', + os.path.join(path, 'en-default.mo')) + de_catalog = GettextMessageCatalog('de', 'default', + os.path.join(path, 'de-default.mo')) + + domain.addCatalog(en_catalog) + domain.addCatalog(de_catalog) + return domain + + def testNoTargetLanguage(self): + # Having a fallback would interfere with this test + self._domain.setLanguageFallbacks([]) + TestITranslationDomain.testNoTargetLanguage(self) + + def testSimpleNoTranslate(self): + translate = self._domain.translate + eq = self.assertEqual + # Unset fallback translation languages + self._domain.setLanguageFallbacks([]) + + # Test that a translation in an unsupported language returns the + # default, if there is no fallback language + eq(translate('short_greeting', target_language='es'), 'short_greeting') + eq(translate('short_greeting', target_language='es', + default='short_greeting'), 'short_greeting') + + # Same test, but use the context argument instead of target_language + context = Environment() + eq(translate('short_greeting', context=context), 'short_greeting') + eq(translate('short_greeting', context=context, + default='short_greeting'), 'short_greeting') + + def testEmptyStringTranslate(self): + translate = self._domain.translate + self.assertEqual(translate(u'', target_language='en'), u'') + self.assertEqual(translate(u'', target_language='foo'), u'') + + def testStringTranslate(self): + self.assertEqual( + self._domain.translate(u'short_greeting', target_language='en'), + u'Hello!') + + def testMessageIDTranslate(self): + factory = MessageFactory('default') + translate = self._domain.translate + msgid = factory(u'short_greeting', 'default') + self.assertEqual(translate(msgid, target_language='en'), u'Hello!') + # MessageID attributes override arguments + msgid = factory('43-not-there', 'this ${that} the other', + mapping={'that': 'THAT'}) + self.assertEqual( + translate(msgid, target_language='en', default="default", + mapping={"that": "that"}), "this THAT the other") + + def testMessageIDRecursiveTranslate(self): + factory = MessageFactory('default') + translate = self._domain.translate + msgid_sub1 = factory(u'44-not-there', '${blue}', + mapping = {'blue': 'BLUE'}) + msgid_sub2 = factory(u'45-not-there', '${yellow}', + mapping = {'yellow': 'YELLOW'}) + mapping = {'color1': msgid_sub1, + 'color2': msgid_sub2} + msgid = factory(u'46-not-there', 'Color: ${color1}/${color2}', + mapping=mapping) + self.assertEqual( + translate(msgid, target_language='en', default="default"), + "Color: BLUE/YELLOW") + # The recursive translation must not change the mappings + self.assertEqual(msgid.mapping, {'color1': msgid_sub1, + 'color2': msgid_sub2}) + # A circular reference should not lead to crashes + msgid1 = factory(u'47-not-there', 'Message 1 and $msg2', + mapping = {}) + msgid2 = factory(u'48-not-there', 'Message 2 and $msg1', + mapping = {}) + msgid1.mapping['msg2'] = msgid2 + msgid2.mapping['msg1'] = msgid1 + self.assertRaises(ValueError, + translate, msgid1, None, None, 'en',"default") + # Recusrive translations also work if the original message id wasn't a + # message id but a unicode with a directly passed mapping + self.assertEqual("Color: BLUE/YELLOW", + translate(u'Color: ${color1}/${color2}', mapping=mapping, + target_language='en')) + + # If we have mapping with a message id from a different domain, make sure + # we use that domain, not ours. If the message domain is not registered yet, + # we should return a defualt translation. + alt_factory = MessageFactory('alt') + msgid_sub = alt_factory(u'special', default=u'oohhh') + mapping = {'message': msgid_sub} + msgid = factory(u'46-not-there', 'Message: ${message}', + mapping=mapping) + # test we get a default with no domain registerd + self.assertEqual( + translate(msgid, target_language='en', default="default"), + "Message: oohhh") + # provide the domain + domain = TranslationDomain('alt') + path = testdir() + en_catalog = GettextMessageCatalog('en', 'alt', + os.path.join(path, 'en-alt.mo')) + domain.addCatalog(en_catalog) + # test that we get the right translation + zope.component.provideUtility(domain, ITranslationDomain, 'alt') + self.assertEqual( + translate(msgid, target_language='en', default="default"), + "Message: Wow") + + + + + def testMessageIDTranslateForDifferentDomain(self): + domain = TranslationDomain('alt') + path = testdir() + en_catalog = GettextMessageCatalog('en', 'alt', + os.path.join(path, 'en-alt.mo')) + domain.addCatalog(en_catalog) + + zope.component.provideUtility(domain, ITranslationDomain, 'alt') + + factory = MessageFactory('alt') + msgid = factory(u'special', 'default') + self.assertEqual( + self._domain.translate(msgid, target_language='en'), u'Wow') + + def testSimpleFallbackTranslation(self): + translate = self._domain.translate + eq = self.assertEqual + # Test that a translation in an unsupported language returns a + # translation in the fallback language (by default, English) + eq(translate('short_greeting', target_language='es'), + u'Hello!') + # Same test, but use the context argument instead of target_language + context = Environment() + eq(translate('short_greeting', context=context), + u'Hello!') + + def testInterpolationWithoutTranslation(self): + translate = self._domain.translate + self.assertEqual( + translate('42-not-there', target_language="en", + default="this ${that} the other", + mapping={"that": "THAT"}), + "this THAT the other") + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestGlobalTranslationDomain)) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') + diff -Nru zope3-3.4.0/src/zope/i18n/tests/test_zcml.py zope3-3.5~bzr18/src/zope/i18n/tests/test_zcml.py --- zope3-3.4.0/src/zope/i18n/tests/test_zcml.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/tests/test_zcml.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,159 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 the gts ZCML namespace directives. + +$Id: test_zcml.py 102560 2009-08-07 12:22:51Z hannosch $ +""" +import os +import shutil +import stat +import unittest + +from zope.component import getUtility +from zope.component import queryUtility +from zope.component.testing import PlacelessSetup + +import zope.i18n.tests +from zope.i18n.interfaces import ITranslationDomain +from zope.i18n import config + +template = """\ + + %s +""" + +class DirectivesTest(PlacelessSetup, unittest.TestCase): + + # This test suite needs the [zcml] and [compile] extra dependencies + level = 2 + + def setUp(self): + from zope.configuration import xmlconfig + super(DirectivesTest, self).setUp() + self.context = xmlconfig.file('meta.zcml', zope.i18n) + self.allowed = config.ALLOWED_LANGUAGES + config.ALLOWED_LANGUAGES = None + + def tearDown(self): + super(DirectivesTest, self).tearDown() + config.ALLOWED_LANGUAGES = self.allowed + + def testRegisterTranslations(self): + from zope.configuration import xmlconfig + self.assert_(queryUtility(ITranslationDomain) is None) + xmlconfig.string( + template % ''' + + + + ''', self.context) + path = os.path.join(os.path.dirname(zope.i18n.tests.__file__), + 'locale', 'en', 'LC_MESSAGES', 'zope-i18n.mo') + util = getUtility(ITranslationDomain, 'zope-i18n') + self.assertEquals(util._catalogs.get('test'), ['test']) + self.assertEquals(util._catalogs.get('en'), [unicode(path)]) + + def testAllowedTranslations(self): + from zope.configuration import xmlconfig + self.assert_(queryUtility(ITranslationDomain) is None) + config.ALLOWED_LANGUAGES = ('de', 'fr') + xmlconfig.string( + template % ''' + + + + ''', self.context) + path = os.path.join(os.path.dirname(zope.i18n.tests.__file__), + 'locale', 'de', 'LC_MESSAGES', 'zope-i18n.mo') + util = getUtility(ITranslationDomain, 'zope-i18n') + self.assertEquals(util._catalogs, + {'test': ['test'], 'de': [unicode(path)]}) + + def testRegisterDistributedTranslations(self): + from zope.configuration import xmlconfig + self.assert_(queryUtility(ITranslationDomain, 'zope-i18n') is None) + xmlconfig.string( + template % ''' + + + + ''', self.context) + xmlconfig.string( + template % ''' + + + + ''', self.context) + path1 = os.path.join(os.path.dirname(zope.i18n.tests.__file__), + 'locale', 'en', 'LC_MESSAGES', 'zope-i18n.mo') + path2 = os.path.join(os.path.dirname(zope.i18n.tests.__file__), + 'locale2', 'en', 'LC_MESSAGES', 'zope-i18n.mo') + util = getUtility(ITranslationDomain, 'zope-i18n') + self.assertEquals(util._catalogs.get('test'), ['test', 'test']) + self.assertEquals(util._catalogs.get('en'), + [unicode(path1), unicode(path2)]) + + msg = util.translate(u'Additional message', target_language='en') + self.assertEquals(msg, u'Additional message translated') + + msg = util.translate(u'New Domain', target_language='en') + self.assertEquals(msg, u'New Domain translated') + + msg = util.translate(u'New Language', target_language='en') + self.assertEquals(msg, u'New Language translated') + + def testRegisterAndCompileTranslations(self): + from zope.configuration import xmlconfig + config.COMPILE_MO_FILES = True + self.assert_(queryUtility(ITranslationDomain) is None) + + # Copy an old and outdated file over, so we can test if the + # newer file check works + testpath = os.path.join(os.path.dirname(zope.i18n.tests.__file__)) + basepath = os.path.join(testpath, 'locale3', 'en', 'LC_MESSAGES') + in_ = os.path.join(basepath, 'zope-i18n.in') + path = os.path.join(basepath, 'zope-i18n.mo') + shutil.copy2(in_, path) + + # Make sure the older mo file always has an older time stamp + # than the po file + path_atime = os.stat(path)[stat.ST_ATIME] + path_mtime = os.stat(path)[stat.ST_MTIME] + os.utime(path, (path_atime, path_mtime - 6000)) + + xmlconfig.string( + template % ''' + + + + ''', self.context) + util = getUtility(ITranslationDomain, 'zope-i18n') + self.assertEquals(util._catalogs, + {'test': ['test'], 'en': [unicode(path)]}) + + msg = util.translate(u"I'm a newer file", target_language='en') + self.assertEquals(msg, u"I'm a newer file translated") + + util = getUtility(ITranslationDomain, 'zope-i18n2') + msg = util.translate(u"I'm a new file", target_language='en') + self.assertEquals(msg, u"I'm a new file translated") + + # Reset the mtime of the mo file + os.utime(path, (path_atime, path_mtime)) + + +def test_suite(): + return unittest.makeSuite(DirectivesTest) diff -Nru zope3-3.4.0/src/zope/i18n/translationdomain.py zope3-3.5~bzr18/src/zope/i18n/translationdomain.py --- zope3-3.4.0/src/zope/i18n/translationdomain.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/translationdomain.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,150 @@ +############################################################################## +# +# Copyright (c) 2001-2008 Zope Corporation 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. +# +############################################################################## +"""Global Translation Service for providing I18n to file-based code. + +$Id: translationdomain.py 99163 2009-04-14 12:20:56Z jinty $ +""" +import zope.component +from zope.i18nmessageid import Message +from zope.i18n import translate, interpolate +from zope.i18n.simpletranslationdomain import SimpleTranslationDomain +from zope.i18n.interfaces import ITranslationDomain, INegotiator + +# The configuration should specify a list of fallback languages for the +# site. If a particular catalog for a negotiated language is not available, +# then the zcml specified order should be tried. If that fails, then as a +# last resort the languages in the following list are tried. If these fail +# too, then the msgid is returned. +# +# Note that these fallbacks are used only to find a catalog. If a particular +# message in a catalog is not translated, tough luck, you get the msgid. +LANGUAGE_FALLBACKS = ['en'] + + +class TranslationDomain(SimpleTranslationDomain): + + def __init__(self, domain, fallbacks=None): + self.domain = domain + # _catalogs maps (language, domain) to IMessageCatalog instances + self._catalogs = {} + # _data maps IMessageCatalog.getIdentifier() to IMessageCatalog + self._data = {} + # What languages to fallback to, if there is no catalog for the + # requested language (no fallback on individual messages) + if fallbacks is None: + fallbacks = LANGUAGE_FALLBACKS + self._fallbacks = fallbacks + + def _registerMessageCatalog(self, language, catalog_name): + key = language + mc = self._catalogs.setdefault(key, []) + mc.append(catalog_name) + + def addCatalog(self, catalog): + self._data[catalog.getIdentifier()] = catalog + self._registerMessageCatalog(catalog.language, + catalog.getIdentifier()) + + def setLanguageFallbacks(self, fallbacks=None): + if fallbacks is None: + fallbacks = LANGUAGE_FALLBACKS + self._fallbacks = fallbacks + + def translate(self, msgid, mapping=None, context=None, + target_language=None, default=None): + """See zope.i18n.interfaces.ITranslationDomain""" + # if the msgid is empty, let's save a lot of calculations and return + # an empty string. + if msgid == u'': + return u'' + + if target_language is None and context is not None: + langs = self._catalogs.keys() + # invoke local or global unnamed 'INegotiator' utilities + negotiator = zope.component.getUtility(INegotiator) + # try to determine target language from negotiator utility + target_language = negotiator.getLanguage(langs, context) + + return self._recursive_translate( + msgid, mapping, target_language, default, context) + + def _recursive_translate(self, msgid, mapping, target_language, default, + context, seen=None): + """Recursively translate msg.""" + # MessageID attributes override arguments + if isinstance(msgid, Message): + if msgid.domain != self.domain: + return translate(msgid, msgid.domain, mapping, context, + target_language, default) + default = msgid.default + mapping = msgid.mapping + + # Recursively translate mappings, if they are translatable + if (mapping is not None + and Message in (type(m) for m in mapping.values())): + if seen is None: + seen = set() + seen.add(msgid) + mapping = mapping.copy() + for key, value in mapping.items(): + if isinstance(value, Message): + # TODO Why isn't there an IMessage interface? + # https://bugs.launchpad.net/zope3/+bug/220122 + if value in seen: + raise ValueError( + "Circular reference in mappings detected: %s" % + value) + mapping[key] = self._recursive_translate( + value, mapping, target_language, + default, context, seen) + + if default is None: + default = unicode(msgid) + + # Get the translation. Use the specified fallbacks if this fails + catalog_names = self._catalogs.get(target_language) + if catalog_names is None: + for language in self._fallbacks: + catalog_names = self._catalogs.get(language) + if catalog_names is not None: + break + + text = default + if catalog_names: + if len(catalog_names) == 1: + # this is a slight optimization for the case when there is a + # single catalog. More importantly, it is extremely helpful + # when testing and the test language is used, because it + # allows the test language to get the default. + text = self._data[catalog_names[0]].queryMessage( + msgid, default) + else: + for name in catalog_names: + catalog = self._data[name] + s = catalog.queryMessage(msgid) + if s is not None: + text = s + break + + # Now we need to do the interpolation + if text and mapping: + text = interpolate(text, mapping) + return text + + def getCatalogsInfo(self): + return self._catalogs + + def reloadCatalogs(self, catalogNames): + for catalogName in catalogNames: + self._data[catalogName].reload() diff -Nru zope3-3.4.0/src/zope/i18n/zcml.py zope3-3.5~bzr18/src/zope/i18n/zcml.py --- zope3-3.4.0/src/zope/i18n/zcml.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18n/zcml.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,111 @@ + +# ############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 module handles the 'i18n' namespace directives. + +$Id: zcml.py 106482 2009-12-14 12:22:58Z chrism $ +""" +__docformat__ = 'restructuredtext' + +import os + +from zope.component import getSiteManager +from zope.component import queryUtility +from zope.component.interface import provideInterface +from zope.configuration.fields import Path +from zope.interface import Interface + +from zope.i18n import config +from zope.i18n.compile import compile_mo_file +from zope.i18n.gettextmessagecatalog import GettextMessageCatalog +from zope.i18n.testmessagecatalog import TestMessageCatalog +from zope.i18n.translationdomain import TranslationDomain +from zope.i18n.interfaces import ITranslationDomain + + +class IRegisterTranslationsDirective(Interface): + """Register translations with the global site manager.""" + + directory = Path( + title=u"Directory", + description=u"Directory containing the translations", + required=True + ) + + +def allow_language(lang): + if config.ALLOWED_LANGUAGES is None: + return True + return lang in config.ALLOWED_LANGUAGES + + +def handler(catalogs, name): + """ special handler handling the merging of two message catalogs """ + gsm = getSiteManager() + # Try to get an existing domain and add the given catalogs to it + domain = queryUtility(ITranslationDomain, name) + if domain is None: + domain = TranslationDomain(name) + gsm.registerUtility(domain, ITranslationDomain, name=name) + for catalog in catalogs: + domain.addCatalog(catalog) + # make sure we have a TEST catalog for each domain: + domain.addCatalog(TestMessageCatalog(name)) + + +def registerTranslations(_context, directory): + path = os.path.normpath(directory) + domains = {} + + # Gettext has the domain-specific catalogs inside the language directory, + # which is exactly the opposite as we need it. So create a dictionary that + # reverses the nesting. + for language in os.listdir(path): + if not allow_language(language): + continue + lc_messages_path = os.path.join(path, language, 'LC_MESSAGES') + if os.path.isdir(lc_messages_path): + # Preprocess files and update or compile the mo files + if config.COMPILE_MO_FILES: + for domain_file in os.listdir(lc_messages_path): + if domain_file.endswith('.po'): + domain = domain_file[:-3] + compile_mo_file(domain, lc_messages_path) + for domain_file in os.listdir(lc_messages_path): + if domain_file.endswith('.mo'): + domain_path = os.path.join(lc_messages_path, domain_file) + domain = domain_file[:-3] + if not domain in domains: + domains[domain] = {} + domains[domain][language] = domain_path + + # Now create TranslationDomain objects and add them as utilities + for name, langs in domains.items(): + catalogs = [] + for lang, file in langs.items(): + catalogs.append(GettextMessageCatalog(lang, name, file)) + # register the necessary actions directly (as opposed to using + # `zope.component.zcml.utility`) since we need the actual utilities + # in place before the merging can be done... + _context.action( + discriminator = None, + callable = handler, + args = (catalogs, name)) + + # also register the interface for the translation utilities + provides = ITranslationDomain + _context.action( + discriminator = None, + callable = provideInterface, + args = (provides.__module__ + '.' + provides.getName(), provides)) diff -Nru zope3-3.4.0/src/zope/i18nmessageid/__init__.py zope3-3.5~bzr18/src/zope/i18nmessageid/__init__.py --- zope3-3.4.0/src/zope/i18nmessageid/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18nmessageid/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""I18n Messages + +$Id: __init__.py 110713 2010-04-10 18:35:28Z tseaver $ +""" +from zope.i18nmessageid.message import Message, MessageFactory + +# import this as _ to create i18n messages in the zope domain +ZopeMessageFactory = MessageFactory('zope') diff -Nru zope3-3.4.0/src/zope/i18nmessageid/message.py zope3-3.5~bzr18/src/zope/i18nmessageid/message.py --- zope3-3.4.0/src/zope/i18nmessageid/message.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18nmessageid/message.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,188 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""I18n Messages + +$Id: message.py 110713 2010-04-10 18:35:28Z tseaver $ +""" +__docformat__ = "reStructuredText" + +class Message(unicode): + """Message (Python implementation) + + This is a string used as a message. It has a domain attribute that is + its source domain, and a default attribute that is its default text to + display when there is no translation. domain may be None meaning there is + no translation domain. default may also be None, in which case the + message id itself implicitly serves as the default text. + + These are the doc tests from message.txt. Note that we have to create the + message manually since MessageFactory would return the C implementation. + + >>> from zope.i18nmessageid.message import pyMessage as Message + >>> robot = Message(u"robot-message", 'futurama', u"${name} is a robot.") + + >>> robot + u'robot-message' + >>> isinstance(robot, unicode) + True + + >>> robot.default + u'${name} is a robot.' + >>> robot.mapping + + Only the python implementation has a _readonly attribute + >>> robot._readonly + True + + >>> robot.domain = "planetexpress" + Traceback (most recent call last): + ... + TypeError: readonly attribute + + >>> robot.default = u"${name} is not a robot." + Traceback (most recent call last): + ... + TypeError: readonly attribute + + >>> robot.mapping = {u'name': u'Bender'} + Traceback (most recent call last): + ... + TypeError: readonly attribute + + >>> new_robot = Message(robot, mapping={u'name': u'Bender'}) + >>> new_robot + u'robot-message' + >>> new_robot.domain + 'futurama' + >>> new_robot.default + u'${name} is a robot.' + >>> new_robot.mapping + {u'name': u'Bender'} + + >>> callable, args = new_robot.__reduce__() + >>> callable is Message + True + >>> args + (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) + + >>> fembot = Message(u'fembot') + >>> callable, args = fembot.__reduce__() + >>> callable is Message + True + >>> args + (u'fembot', None, None, None) + + Change classes for pickle tests + >>> import zope.i18nmessageid.message + >>> oldMessage = zope.i18nmessageid.message.Message + >>> zope.i18nmessageid.message.Message = Message + + At first check if pickling and unpicklung from pyMessage to pyMessage works + >>> from pickle import dumps, loads + >>> pystate = dumps(new_robot) + >>> pickle_bot = loads(pystate) + >>> pickle_bot, pickle_bot.domain, pickle_bot.default, pickle_bot.mapping + (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) + >>> pickle_bot._readonly + True + >>> from zope.i18nmessageid.message import pyMessage + >>> pickle_bot.__reduce__()[0] is pyMessage + True + >>> del pickle_bot + + At second check if cMessage is able to load the state of a pyMessage + >>> from _zope_i18nmessageid_message import Message + >>> zope.i18nmessageid.message.Message = Message + >>> c_bot = loads(pystate) + >>> c_bot, c_bot.domain, c_bot.default, c_bot.mapping + (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) + >>> c_bot._readonly + Traceback (most recent call last): + AttributeError: 'zope.i18nmessageid.message.Message' object has no attribute '_readonly' + >>> from _zope_i18nmessageid_message import Message as cMessage + >>> c_bot.__reduce__()[0] is cMessage + True + + At last check if pyMessage can load a state of cMessage + >>> cstate = dumps(c_bot) + >>> del c_bot + >>> from zope.i18nmessageid.message import pyMessage as Message + >>> zope.i18nmessageid.message.Message = Message + >>> py_bot = loads(cstate) + >>> py_bot, py_bot.domain, py_bot.default, py_bot.mapping + (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) + >>> py_bot._readonly + True + >>> py_bot.__reduce__()[0] is pyMessage + True + + Both pickle states should be equal + >>> pystate == cstate + True + + Finally restore classes for other unit tests + >>> zope.i18nmessageid.message.Message = oldMessage + """ + + __slots__ = ('domain', 'default', 'mapping', '_readonly') + + def __new__(cls, ustr, domain=None, default=None, mapping=None): + self = unicode.__new__(cls, ustr) + if isinstance(ustr, self.__class__): + domain = ustr.domain and ustr.domain[:] or domain + default = ustr.default and ustr.default[:] or default + mapping = ustr.mapping and ustr.mapping.copy() or mapping + ustr = unicode(ustr) + self.domain = domain + if default is None: + # MessageID does: self.default = ustr + self.default = default + else: + self.default = unicode(default) + self.mapping = mapping + self._readonly = True + return self + + def __setattr__(self, key, value): + """Message is immutable + + It cannot be changed once the message id is created. + """ + if getattr(self, '_readonly', False): + raise TypeError('readonly attribute') + else: + return unicode.__setattr__(self, key, value) + + def __reduce__(self): + return self.__class__, self.__getstate__() + + def __getstate__(self): + return unicode(self), self.domain, self.default, self.mapping + +# save a copy for the unit tests +pyMessage = Message + +try: + from _zope_i18nmessageid_message import Message +except ImportError: + pass + +class MessageFactory(object): + """Factory for creating i18n messages.""" + + def __init__(self, domain): + self._domain = domain + + def __call__(self, ustr, default=None, mapping=None): + return Message(ustr, self._domain, default, mapping) diff -Nru zope3-3.4.0/src/zope/i18nmessageid/messages.txt zope3-3.5~bzr18/src/zope/i18nmessageid/messages.txt --- zope3-3.4.0/src/zope/i18nmessageid/messages.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18nmessageid/messages.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,134 @@ +============= +I18n Messages +============= + +Rationale +--------- + +To translate any text, we must be able to discover the source domain +of the text. A source domain is an identifier that identifies a +project that produces program source strings. Source strings occur as +literals in python programs, text in templates, and some text in XML +data. The project implies a source language and an application +context. + +We can think of a source domain as a collection of messages and +associated translation strings. + +We often need to create unicode strings that will be displayed by +separate views. The view cannot translate the string without knowing +its source domain. A string or unicode literal carries no domain +information, therefore we use messages. Messages are unicode strings +which carry a translation source domain and possibly a default +translation. They are created by a message factory. The message +factory is created by calling ``MessageFactory`` with the source +domain. + +ZopeMessageFactory +------------------ + + >>> from zope.i18nmessageid import ZopeMessageFactory as _z_ + >>> foo = _z_('foo') + >>> foo.domain + 'zope' + + + +Example +------- + +In this example, we create a message factory and assign it to _. By +convention, we use _ as the name of our factory to be compatible with +translatable string extraction tools such as xgettext. We then call _ +with a string that needs to be translatable: + + >>> from zope.i18nmessageid import MessageFactory, Message + >>> _ = MessageFactory("futurama") + >>> robot = _(u"robot-message", u"${name} is a robot.") + +Messages at first seem like they are unicode strings: + + >>> robot + u'robot-message' + >>> isinstance(robot, unicode) + True + +The additional domain, default and mapping information is available +through attributes: + + >>> robot.default + u'${name} is a robot.' + >>> robot.mapping + >>> robot.domain + 'futurama' + +The message's attributes are considered part of the immutable message +object. They cannot be changed once the message id is created: + + >>> robot.domain = "planetexpress" + Traceback (most recent call last): + ... + TypeError: readonly attribute + + >>> robot.default = u"${name} is not a robot." + Traceback (most recent call last): + ... + TypeError: readonly attribute + + >>> robot.mapping = {u'name': u'Bender'} + Traceback (most recent call last): + ... + TypeError: readonly attribute + +If you need to change their information, you'll have to make a new +message id object: + + >>> new_robot = Message(robot, mapping={u'name': u'Bender'}) + >>> new_robot + u'robot-message' + >>> new_robot.domain + 'futurama' + >>> new_robot.default + u'${name} is a robot.' + >>> new_robot.mapping + {u'name': u'Bender'} + +Last but not least, messages are reduceable for pickling: + + >>> callable, args = new_robot.__reduce__() + >>> callable is Message + True + >>> args + (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'}) + + >>> fembot = Message(u'fembot') + >>> callable, args = fembot.__reduce__() + >>> callable is Message + True + >>> args + (u'fembot', None, None, None) + + +Message IDs and backward compatability +-------------------------------------- + +The change to immutability is not a simple refactoring that can be +coped with backward compatible APIs--it is a change in semantics. +Because immutability is one of those "you either have it or you don't" +things (like pregnancy or death), we will not be able to support both +in one implementation. + +The proposed solution for backward compatability is to support both +implementations in parallel, deprecating the mutable one. A separate +factory, ``MessageFactory``, instantiates immutable messages, while +the deprecated old one continues to work like before. + +The roadmap to immutable-only message ids is proposed as follows: + + Zope 3.1: Immutable message ids are introduced. Security + declarations for mutable message ids are provided to make the + stripping of security proxies unnecessary. + + Zope 3.2: Mutable message ids are deprecated. + + Zope 3.3: Mutable message ids are removed. diff -Nru zope3-3.4.0/src/zope/i18nmessageid/tests.py zope3-3.5~bzr18/src/zope/i18nmessageid/tests.py --- zope3-3.4.0/src/zope/i18nmessageid/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18nmessageid/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Message ID tests. + +$Id: tests.py 110993 2010-04-16 18:53:41Z tseaver $ +""" +import unittest +from doctest import DocFileSuite +from doctest import DocTestSuite + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite('zope.i18nmessageid.message'), + DocFileSuite('messages.txt', package='zope.i18nmessageid'), + )) + +if __name__ == '__main__': + unittest.main(defaultTest="test_suite") diff -Nru zope3-3.4.0/src/zope/i18nmessageid/_zope_i18nmessageid_message.c zope3-3.5~bzr18/src/zope/i18nmessageid/_zope_i18nmessageid_message.c --- zope3-3.4.0/src/zope/i18nmessageid/_zope_i18nmessageid_message.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18nmessageid/_zope_i18nmessageid_message.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,266 @@ +/*############################################################################ +# +# 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. +# +############################################################################*/ + +/* $Id: _zope_i18nmessageid_message.c 110713 2010-04-10 18:35:28Z tseaver $ */ + +#include "Python.h" + +/* these macros make gc support easier; they are only available in + Python 2.4 and borrowed from there */ + +#ifndef Py_CLEAR +#define Py_CLEAR(op) \ + do { \ + if (op) { \ + PyObject *tmp = (op); \ + (op) = NULL; \ + Py_DECREF(tmp); \ + } \ + } while (0) +#endif + +#ifndef Py_VISIT +#define Py_VISIT(op) \ + do { \ + if (op) { \ + int vret = visit((op), arg); \ + if (vret) \ + return vret; \ + } \ + } while (0) +#endif + +/* ----------------------------------------------------- */ + +typedef struct { + PyUnicodeObject base; + PyObject *domain; + PyObject *default_; + PyObject *mapping; +} Message; + +static PyTypeObject MessageType; + +static PyObject * +Message_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"value", "domain", "default", "mapping", NULL}; + PyObject *value, *domain=NULL, *default_=NULL, *mapping=NULL, *s; + Message *self; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist, + &value, &domain, &default_, &mapping)) + return NULL; + + args = Py_BuildValue("(O)", value); + if (args == NULL) + return NULL; + + s = PyUnicode_Type.tp_new(type, args, NULL); + Py_DECREF(args); + if (s == NULL) + return NULL; + + if (! PyObject_TypeCheck(s, &MessageType)) + { + PyErr_SetString(PyExc_TypeError, + "unicode.__new__ didn't return a Message"); + Py_DECREF(s); + return NULL; + } + + self = (Message*)s; + + if (PyObject_TypeCheck(value, &MessageType)) + { + self->domain = ((Message *)value)->domain; + self->default_ = ((Message *)value)->default_; + self->mapping = ((Message *)value)->mapping; + } + else + { + self->domain = self->default_ = self->mapping = NULL; + } + + if (domain != NULL) + self->domain = domain; + + if (default_ != NULL) + self->default_ = default_; + + if (mapping != NULL) + self->mapping = mapping; + + Py_XINCREF(self->mapping); + Py_XINCREF(self->default_); + Py_XINCREF(self->domain); + + return (PyObject *)self; +} + +/* Code to access structure members by accessing attributes */ + +#include "structmember.h" + +static PyMemberDef Message_members[] = { + { "domain", T_OBJECT, offsetof(Message, domain), RO }, + { "default", T_OBJECT, offsetof(Message, default_), RO }, + { "mapping", T_OBJECT, offsetof(Message, mapping), RO }, + {NULL} /* Sentinel */ +}; + +static int +Message_traverse(Message *self, visitproc visit, void *arg) +{ + Py_VISIT(self->domain); + Py_VISIT(self->default_); + Py_VISIT(self->mapping); + return 0; +} + +static int +Message_clear(Message *self) +{ + Py_CLEAR(self->domain); + Py_CLEAR(self->default_); + Py_CLEAR(self->mapping); + return 0; +} + +static void +Message_dealloc(Message *self) +{ + Message_clear(self); + PyUnicode_Type.tp_dealloc((PyObject*)self); +} + +static PyObject * +Message_reduce(Message *self) +{ + PyObject *value, *result; + value = PyObject_CallFunctionObjArgs((PyObject *)&PyUnicode_Type, self, NULL); + if (value == NULL) + return NULL; + result = Py_BuildValue("(O(OOOO))", self->base.ob_type, + value, + self->domain ? self->domain : Py_None, + self->default_ ? self->default_ : Py_None, + self->mapping ? self->mapping : Py_None); + Py_DECREF(value); + return result; +} + +static PyMethodDef Message_methods[] = { + {"__reduce__", (PyCFunction)Message_reduce, METH_NOARGS, + "Reduce messages to a serializable form."}, + {NULL} /* Sentinel */ +}; + + +static char MessageType__doc__[] = +"Message\n" +"\n" +"This is a string used as a message. It has a domain attribute that is\n" +"its source domain, and a default attribute that is its default text to\n" +"display when there is no translation. domain may be None meaning there is\n" +"no translation domain. default may also be None, in which case the\n" +"message id itself implicitly serves as the default text.\n"; + +statichere PyTypeObject +MessageType = { + PyObject_HEAD_INIT(NULL) + /* ob_size */ 0, + /* tp_name */ "zope.i18nmessageid.message." + "Message", + /* tp_basicsize */ sizeof(Message), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)&Message_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ (cmpfunc)0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ MessageType__doc__, + /* tp_traverse */ (traverseproc)Message_traverse, + /* tp_clear */ (inquiry)Message_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ Message_methods, + /* tp_members */ Message_members, + /* tp_getset */ 0, + /* tp_base */ 0, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)0, + /* tp_descr_set */ (descrsetfunc)0, + /* tp_dictoffset */ 0, + /* tp_init */ (initproc)0, + /* tp_alloc */ (allocfunc)0, + /* tp_new */ (newfunc)Message_new, + /* tp_free */ 0, /* Low-level free-mem routine */ + /* tp_is_gc */ (inquiry)0, /* For PyObject_IS_GC */ +}; + +/* End of code for Message objects */ +/* -------------------------------------------------------- */ + + +/* List of methods defined in the module */ + +static struct PyMethodDef _zope_i18nmessageid_message_methods[] = { + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + + +static char _zope_i18nmessageid_message_module_documentation[] = +"I18n Messages" +; + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +init_zope_i18nmessageid_message(void) +{ + PyObject *m; + /* Initialize types: */ + MessageType.tp_base = &PyUnicode_Type; + if (PyType_Ready(&MessageType) < 0) + return; + + /* Create the module and add the functions */ + m = Py_InitModule3("_zope_i18nmessageid_message", + _zope_i18nmessageid_message_methods, + _zope_i18nmessageid_message_module_documentation); + + if (m == NULL) + return; + + /* Add types: */ + if (PyModule_AddObject(m, "Message", (PyObject *)&MessageType) < 0) + return; +} diff -Nru zope3-3.4.0/src/zope/i18nmessageid/_zope_i18nmessageid_message.py zope3-3.5~bzr18/src/zope/i18nmessageid/_zope_i18nmessageid_message.py --- zope3-3.4.0/src/zope/i18nmessageid/_zope_i18nmessageid_message.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/i18nmessageid/_zope_i18nmessageid_message.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_zope_i18nmessageid_message.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/zope/__init__.py zope3-3.5~bzr18/src/zope/__init__.py --- zope3-3.4.0/src/zope/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff -Nru zope3-3.4.0/src/zope/interface/adapter.py zope3-3.5~bzr18/src/zope/interface/adapter.py --- zope3-3.4.0/src/zope/interface/adapter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/adapter.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,694 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""Adapter management + +$Id: adapter.py 110699 2010-04-09 08:16:17Z regebro $ +""" + +import weakref +from zope.interface import providedBy, Interface, ro + +_marker = object +class BaseAdapterRegistry(object): + + # List of methods copied from lookup sub-objects: + _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter', + 'adapter_hook', 'lookupAll', 'names', + 'subscriptions', 'subscribers') + + # All registries maintain a generation that can be used by verifying + # registries + _generation = 0 + + def __init__(self, bases=()): + + # The comments here could be improved. Possibly this bit needs + # explaining in a separate document, as the comments here can + # be quite confusing. /regebro + + # {order -> {required -> {provided -> {name -> value}}}} + # Here "order" is actually an index in a list, "required" and + # "provided" are interfaces, and "required" is really a nested + # key. So, for example: + # for order == 0 (that is, self._adapters[0]), we have: + # {provided -> {name -> value}} + # but for order == 2 (that is, self._adapters[2]), we have: + # {r1 -> {r2 -> {provided -> {name -> value}}}} + # + self._adapters = [] + + # {order -> {required -> {provided -> {name -> [value]}}}} + # where the remarks about adapters above apply + self._subscribers = [] + + # Set, with a reference count, keeping track of the interfaces + # for which we have provided components: + self._provided = {} + + # Create ``_v_lookup`` object to perform lookup. We make this a + # separate object to to make it easier to implement just the + # lookup functionality in C. This object keeps track of cache + # invalidation data in two kinds of registries. + + # Invalidating registries have caches that are invalidated + # when they or their base registies change. An invalidating + # registry can only have invalidating registries as bases. + # See LookupBasePy below for the pertinent logic. + + # Verifying registies can't rely on getting invalidation messages, + # so have to check the generations of base registries to determine + # if their cache data are current. See VerifyingBasePy below + # for the pertinent object. + self._createLookup() + + # Setting the bases causes the registries described above + # to be initialized (self._setBases -> self.changed -> + # self._v_lookup.changed). + + self.__bases__ = bases + + def _setBases(self, bases): + self.__dict__['__bases__'] = bases + self.ro = ro.ro(self) + self.changed(self) + + __bases__ = property(lambda self: self.__dict__['__bases__'], + lambda self, bases: self._setBases(bases), + ) + + def _createLookup(self): + self._v_lookup = self.LookupClass(self) + for name in self._delegated: + self.__dict__[name] = getattr(self._v_lookup, name) + + def changed(self, originally_changed): + self._generation += 1 + self._v_lookup.changed(originally_changed) + + def register(self, required, provided, name, value): + if value is None: + self.unregister(required, provided, name, value) + return + + required = tuple(map(_convert_None_to_Interface, required)) + name = _normalize_name(name) + order = len(required) + byorder = self._adapters + while len(byorder) <= order: + byorder.append({}) + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + d = {} + components[k] = d + components = d + + if components.get(name) is value: + return + + components[name] = value + + n = self._provided.get(provided, 0) + 1 + self._provided[provided] = n + if n == 1: + self._v_lookup.add_extendor(provided) + + self.changed(self) + + def registered(self, required, provided, name=u''): + required = tuple(map(_convert_None_to_Interface, required)) + name = _normalize_name(name) + order = len(required) + byorder = self._adapters + if len(byorder) <= order: + return None + + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + return None + components = d + + return components.get(name) + + def unregister(self, required, provided, name, value=None): + required = tuple(map(_convert_None_to_Interface, required)) + order = len(required) + byorder = self._adapters + if order >= len(byorder): + return False + components = byorder[order] + key = required + (provided,) + + # Keep track of how we got to `components`: + lookups = [] + # Keep track of how we got to `components`: + lookups = [] + for k in key: + d = components.get(k) + if d is None: + return + lookups.append((components, k)) + components = d + + old = components.get(name) + if old is None: + return + if (value is not None) and (old is not value): + return + + del components[name] + if not components: + # Clean out empty containers, since we don't want our keys + # to reference global objects (interfaces) unnecessarily. + # This is often a problem when an interface is slated for + # removal; a hold-over entry in the registry can make it + # difficult to remove such interfaces. + for comp, k in reversed(lookups): + d = comp[k] + if d: + break + else: + del comp[k] + while byorder and not byorder[-1]: + del byorder[-1] + n = self._provided[provided] - 1 + if n == 0: + del self._provided[provided] + self._v_lookup.remove_extendor(provided) + else: + self._provided[provided] = n + + self.changed(self) + + def subscribe(self, required, provided, value): + required = tuple(map(_convert_None_to_Interface, required)) + name = u'' + order = len(required) + byorder = self._subscribers + while len(byorder) <= order: + byorder.append({}) + components = byorder[order] + key = required + (provided,) + + for k in key: + d = components.get(k) + if d is None: + d = {} + components[k] = d + components = d + + components[name] = components.get(name, ()) + (value, ) + + if provided is not None: + n = self._provided.get(provided, 0) + 1 + self._provided[provided] = n + if n == 1: + self._v_lookup.add_extendor(provided) + + self.changed(self) + + def unsubscribe(self, required, provided, value=None): + required = tuple(map(_convert_None_to_Interface, required)) + order = len(required) + byorder = self._subscribers + if order >= len(byorder): + return + components = byorder[order] + key = required + (provided,) + + # Keep track of how we got to `components`: + lookups = [] + # Keep track of how we got to `components`: + lookups = [] + for k in key: + d = components.get(k) + if d is None: + return + lookups.append((components, k)) + components = d + + old = components.get(u'') + if not old: + return + + if value is None: + new = () + else: + new = tuple([v for v in old if v is not value]) + + if new == old: + return + + if new: + components[u''] = new + else: + # Instead of setting components[u''] = new, we clean out + # empty containers, since we don't want our keys to + # reference global objects (interfaces) unnecessarily. This + # is often a problem when an interface is slated for + # removal; a hold-over entry in the registry can make it + # difficult to remove such interfaces. + if u'' in components: + del components[u''] + for comp, k in reversed(lookups): + d = comp[k] + if d: + break + else: + del comp[k] + while byorder and not byorder[-1]: + del byorder[-1] + + if provided is not None: + n = self._provided[provided] + len(new) - len(old) + if n == 0: + del self._provided[provided] + self._v_lookup.remove_extendor(provided) + + self.changed(self) + + # XXX hack to fake out twisted's use of a private api. We need to get them + # to use the new registed method. + def get(self, _): + class XXXTwistedFakeOut: + selfImplied = {} + return XXXTwistedFakeOut + + +_not_in_mapping = object() +class LookupBasePy(object): + + def __init__(self): + self._cache = {} + self._mcache = {} + self._scache = {} + + def changed(self, ignored=None): + self._cache.clear() + self._mcache.clear() + self._scache.clear() + + def _getcache(self, provided, name): + cache = self._cache.get(provided) + if cache is None: + cache = {} + self._cache[provided] = cache + if name: + c = cache.get(name) + if c is None: + c = {} + cache[name] = c + cache = c + return cache + + def lookup(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + if len(required) == 1: + result = cache.get(required[0], _not_in_mapping) + else: + result = cache.get(tuple(required), _not_in_mapping) + + if result is _not_in_mapping: + result = self._uncached_lookup(required, provided, name) + if len(required) == 1: + cache[required[0]] = result + else: + cache[tuple(required)] = result + + if result is None: + return default + + return result + + def lookup1(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + return self.lookup((required, ), provided, name, default) + + if result is None: + return default + + return result + + def queryAdapter(self, object, provided, name=u'', default=None): + return self.adapter_hook(provided, object, name, default) + + def adapter_hook(self, provided, object, name=u'', default=None): + required = providedBy(object) + cache = self._getcache(provided, name) + factory = cache.get(required, _not_in_mapping) + if factory is _not_in_mapping: + factory = self.lookup((required, ), provided, name) + + if factory is not None: + result = factory(object) + if result is not None: + return result + + return default + + def lookupAll(self, required, provided): + cache = self._mcache.get(provided) + if cache is None: + cache = {} + self._mcache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_lookupAll(required, provided) + cache[required] = result + + return result + + + def subscriptions(self, required, provided): + cache = self._scache.get(provided) + if cache is None: + cache = {} + self._scache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_subscriptions(required, provided) + cache[required] = result + + return result + +LookupBase = LookupBasePy + +class VerifyingBasePy(LookupBasePy): + + def changed(self, originally_changed): + LookupBasePy.changed(self, originally_changed) + self._verify_ro = self._registry.ro[1:] + self._verify_generations = [r._generation for r in self._verify_ro] + + def _verify(self): + if ([r._generation for r in self._verify_ro] + != self._verify_generations): + self.changed(None) + + def _getcache(self, provided, name): + self._verify() + return LookupBasePy._getcache(self, provided, name) + + def lookupAll(self, required, provided): + self._verify() + return LookupBasePy.lookupAll(self, required, provided) + + def subscriptions(self, required, provided): + self._verify() + return LookupBasePy.subscriptions(self, required, provided) + +VerifyingBase = VerifyingBasePy + + +try: + import _zope_interface_coptimizations +except ImportError: + pass +else: + from _zope_interface_coptimizations import LookupBase, VerifyingBase + +class AdapterLookupBase(object): + + def __init__(self, registry): + self._registry = registry + self._required = {} + self.init_extendors() + super(AdapterLookupBase, self).__init__() + + def changed(self, ignored=None): + super(AdapterLookupBase, self).changed(None) + for r in self._required.keys(): + r = r() + if r is not None: + r.unsubscribe(self) + self._required.clear() + + + # Extendors + # --------- + + # When given an target interface for an adapter lookup, we need to consider + # adapters for interfaces that extend the target interface. This is + # what the extendors dictionary is about. It tells us all of the + # interfaces that extend an interface for which there are adapters + # registered. + + # We could separate this by order and name, thus reducing the + # number of provided interfaces to search at run time. The tradeoff, + # however, is that we have to store more information. For example, + # is the same interface is provided for multiple names and if the + # interface extends many interfaces, we'll have to keep track of + # a fair bit of information for each name. It's better to + # be space efficient here and be time efficient in the cache + # implementation. + + # TODO: add invalidation when a provided interface changes, in case + # the interface's __iro__ has changed. This is unlikely enough that + # we'll take our chances for now. + + def init_extendors(self): + self._extendors = {} + for p in self._registry._provided: + self.add_extendor(p) + + def add_extendor(self, provided): + _extendors = self._extendors + for i in provided.__iro__: + extendors = _extendors.get(i, ()) + _extendors[i] = ( + [e for e in extendors if provided.isOrExtends(e)] + + + [provided] + + + [e for e in extendors if not provided.isOrExtends(e)] + ) + + def remove_extendor(self, provided): + _extendors = self._extendors + for i in provided.__iro__: + _extendors[i] = [e for e in _extendors.get(i, ()) + if e != provided] + + + def _subscribe(self, *required): + _refs = self._required + for r in required: + ref = r.weakref() + if ref not in _refs: + r.subscribe(self) + _refs[ref] = 1 + + def _uncached_lookup(self, required, provided, name=u''): + result = None + order = len(required) + for registry in self._registry.ro: + byorder = registry._adapters + if order >= len(byorder): + continue + + extendors = registry._v_lookup._extendors.get(provided) + if not extendors: + continue + + components = byorder[order] + result = _lookup(components, required, extendors, name, 0, + order) + if result is not None: + break + + self._subscribe(*required) + + return result + + def queryMultiAdapter(self, objects, provided, name=u'', default=None): + factory = self.lookup(map(providedBy, objects), provided, name) + if factory is None: + return default + + result = factory(*objects) + if result is None: + return default + + return result + + def _uncached_lookupAll(self, required, provided): + order = len(required) + result = {} + for registry in reversed(self._registry.ro): + byorder = registry._adapters + if order >= len(byorder): + continue + extendors = registry._v_lookup._extendors.get(provided) + if not extendors: + continue + components = byorder[order] + _lookupAll(components, required, extendors, result, 0, order) + + self._subscribe(*required) + + return tuple(result.iteritems()) + + def names(self, required, provided): + return [c[0] for c in self.lookupAll(required, provided)] + + def _uncached_subscriptions(self, required, provided): + order = len(required) + result = [] + for registry in reversed(self._registry.ro): + byorder = registry._subscribers + if order >= len(byorder): + continue + + if provided is None: + extendors = (provided, ) + else: + extendors = registry._v_lookup._extendors.get(provided) + if extendors is None: + continue + + _subscriptions(byorder[order], required, extendors, u'', + result, 0, order) + + self._subscribe(*required) + + return result + + def subscribers(self, objects, provided): + subscriptions = self.subscriptions(map(providedBy, objects), provided) + if provided is None: + result = () + for subscription in subscriptions: + subscription(*objects) + else: + result = [] + for subscription in subscriptions: + subscriber = subscription(*objects) + if subscriber is not None: + result.append(subscriber) + return result + +class AdapterLookup(AdapterLookupBase, LookupBase): + pass + +class AdapterRegistry(BaseAdapterRegistry): + + LookupClass = AdapterLookup + + def __init__(self, bases=()): + # AdapterRegisties are invalidating registries, so + # we need to keep track of out invalidating subregistries. + self._v_subregistries = weakref.WeakKeyDictionary() + + super(AdapterRegistry, self).__init__(bases) + + def _addSubregistry(self, r): + self._v_subregistries[r] = 1 + + def _removeSubregistry(self, r): + if r in self._v_subregistries: + del self._v_subregistries[r] + + def _setBases(self, bases): + old = self.__dict__.get('__bases__', ()) + for r in old: + if r not in bases: + r._removeSubregistry(self) + for r in bases: + if r not in old: + r._addSubregistry(self) + + super(AdapterRegistry, self)._setBases(bases) + + def changed(self, originally_changed): + super(AdapterRegistry, self).changed(originally_changed) + + for sub in self._v_subregistries.keys(): + sub.changed(originally_changed) + + +class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase): + pass + +class VerifyingAdapterRegistry(BaseAdapterRegistry): + + LookupClass = VerifyingAdapterLookup + +def _convert_None_to_Interface(x): + if x is None: + return Interface + else: + return x + +def _normalize_name(name): + if isinstance(name, basestring): + return unicode(name) + + raise TypeError("name must be a regular or unicode string") + +def _lookup(components, specs, provided, name, i, l): + if i < l: + for spec in specs[i].__sro__: + comps = components.get(spec) + if comps: + r = _lookup(comps, specs, provided, name, i+1, l) + if r is not None: + return r + else: + for iface in provided: + comps = components.get(iface) + if comps: + r = comps.get(name) + if r is not None: + return r + + return None + +def _lookupAll(components, specs, provided, result, i, l): + if i < l: + for spec in reversed(specs[i].__sro__): + comps = components.get(spec) + if comps: + _lookupAll(comps, specs, provided, result, i+1, l) + else: + for iface in reversed(provided): + comps = components.get(iface) + if comps: + result.update(comps) + +def _subscriptions(components, specs, provided, name, result, i, l): + if i < l: + for spec in reversed(specs[i].__sro__): + comps = components.get(spec) + if comps: + _subscriptions(comps, specs, provided, name, result, i+1, l) + else: + for iface in reversed(provided): + comps = components.get(iface) + if comps: + comps = comps.get(name) + if comps: + result.extend(comps) diff -Nru zope3-3.4.0/src/zope/interface/adapter.ru.txt zope3-3.5~bzr18/src/zope/interface/adapter.ru.txt --- zope3-3.4.0/src/zope/interface/adapter.ru.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/adapter.ru.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,540 @@ +================ +Реестр адаптеров +================ + +.. contents:: + +Реестры адаптеров предоставляют возможность для регистрации объектов которые +зависят от одной, или нескольких спецификаций интерфейсов и предоставляют +(возможно не напрямую) какой-либо интерфейс. В дополнение, регистрации имеют +имена. (Можно думать об именах как о спецификаторах предоставляемого +интерфейса.) + +Термин "спецификация интерфейса" ссылается и на интерфейсы и на определения +интерфейсов, такие как определения интерфейсов реализованных некоторым классом. + +Одиночные адаптеры +================== + +Давайте рассмотрим простой пример использующий единственную требуемую +спецификацию:: + + >>> from zope.interface.adapter import AdapterRegistry + >>> import zope.interface + + >>> class IR1(zope.interface.Interface): + ... pass + >>> class IP1(zope.interface.Interface): + ... pass + >>> class IP2(IP1): + ... pass + + >>> registry = AdapterRegistry() + +Мы зарегистрируем объект который зависит от IR1 и "предоставляет" IP2:: + + >>> registry.register([IR1], IP2, '', 12) + +После регистрации мы можем запросить объект снова:: + + >>> registry.lookup([IR1], IP2, '') + 12 + +Заметьте, что мы используем целое в этом примере. В реальных приложениях вы +можете использовать объекты которые на самом деле зависят или предоставляют +интерфейсы. Реестр не заботиться о том, что регистрируется и таким образом мы +можем использовать целые, или строки что бы упростить наши примеры. Здесь есть +одно исключение. Регистрация значения None удаляет регистрацию для любого +зарегистрированного прежде значения. + +Если объект зависит от спецификации он может быть запрошен с помощью +спецификации которая расширяет спецификацию от которой он зависит:: + + >>> class IR2(IR1): + ... pass + >>> registry.lookup([IR2], IP2, '') + 12 + +Мы можем использовать класс реализующий спецификацию для запроса объекта:: + + >>> class C2: + ... zope.interface.implements(IR2) + + >>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '') + 12 + +и объект может быть запрошен для интерфейсов которые предоставляемый объектом +интерфейс расширяет:: + + >>> registry.lookup([IR1], IP1, '') + 12 + >>> registry.lookup([IR2], IP1, '') + 12 + +Но если вы требуете спецификацию которая не расширяет спецификацию от которой +зависит объект, вы не получите ничего:: + + >>> registry.lookup([zope.interface.Interface], IP1, '') + +Между прочим, вы можете передать значение по умолчанию при запросе:: + + >>> registry.lookup([zope.interface.Interface], IP1, '', 42) + 42 + +Если вы пробуете получить интерфейс который объект не предоставляет вы также +не получите ничего:: + + >>> class IP3(IP2): + ... pass + >>> registry.lookup([IR1], IP3, '') + +Вы также не получите ничего если вы используете неверное имя:: + + >>> registry.lookup([IR1], IP1, 'bob') + >>> registry.register([IR1], IP2, 'bob', "Bob's 12") + >>> registry.lookup([IR1], IP1, 'bob') + "Bob's 12" + +Вы можете не использовать имя при запросе:: + + >>> registry.lookup([IR1], IP1) + 12 + +Если мы регистрируем объект который предоставляет IP1:: + + >>> registry.register([IR1], IP1, '', 11) + +тогда этот объект будет иметь преимущество перед O(12):: + + >>> registry.lookup([IR1], IP1, '') + 11 + +Также, если мы регистрируем объект для IR2 тогда он будет иметь преимущество +когда используется IR2:: + + >>> registry.register([IR2], IP1, '', 21) + >>> registry.lookup([IR2], IP1, '') + 21 + +Поиск того, что (если вообще что-то) зарегистрировано +----------------------------------------------------- + +Мы можем спросить есть-ли адаптер зарегистрированный для набора интерфейсов. +Это отличается от обычного запроса так как здесь мы ищем точное совпадение:: + + >>> print registry.registered([IR1], IP1) + 11 + + >>> print registry.registered([IR1], IP2) + 12 + + >>> print registry.registered([IR1], IP2, 'bob') + Bob's 12 + + + >>> print registry.registered([IR2], IP1) + 21 + + >>> print registry.registered([IR2], IP2) + None + +В последнем примере, None был возвращен потому, что для данного интерфейса +ничего не было зарегистрировано. + +lookup1 +------- + +Запрос одиночного адаптера - это наиболее частая операция и для нее есть +специализированная версия запроса которая получает на вход единственный +требуемый интерфейс:: + + >>> registry.lookup1(IR2, IP1, '') + 21 + >>> registry.lookup1(IR2, IP1) + 21 + +Адаптация на практике +--------------------- + +Реестр адаптеров предназначен для поддержки адаптации когда один объект +реализующий интерфейс адаптируется к другому объекту который поддерживает +другой интерфейс. Реестр адаптеров также поддерживает вычисление адаптеров. В +этом случае мы должны регистрировать фабрики для адаптеров:: + + >>> class IR(zope.interface.Interface): + ... pass + + >>> class X: + ... zope.interface.implements(IR) + + >>> class Y: + ... zope.interface.implements(IP1) + ... def __init__(self, context): + ... self.context = context + + >>> registry.register([IR], IP1, '', Y) + +В этом случае мы регистрируем класс как фабрику. Теперь мы можем вызвать +`queryAdapter` для получения адаптированного объекта:: + + >>> x = X() + >>> y = registry.queryAdapter(x, IP1) + >>> y.__class__.__name__ + 'Y' + >>> y.context is x + True + +Мы также можем регистрировать и запрашивать по имени:: + + >>> class Y2(Y): + ... pass + + >>> registry.register([IR], IP1, 'bob', Y2) + >>> y = registry.queryAdapter(x, IP1, 'bob') + >>> y.__class__.__name__ + 'Y2' + >>> y.context is x + True + +Когда фабрика для адаптера возвращает `None` - это рассматривается как если бы +адаптер не был найден. Это позволяет нам избежать адаптации (по желанию) и дает +возможность фабрике адаптера определить возможна ли адаптация основываясь на +состоянии объекта который адаптируется:: + + >>> def factory(context): + ... if context.name == 'object': + ... return 'adapter' + ... return None + + >>> class Object(object): + ... zope.interface.implements(IR) + ... name = 'object' + + >>> registry.register([IR], IP1, 'conditional', factory) + >>> obj = Object() + >>> registry.queryAdapter(obj, IP1, 'conditional') + 'adapter' + >>> obj.name = 'no object' + >>> registry.queryAdapter(obj, IP1, 'conditional') is None + True + >>> registry.queryAdapter(obj, IP1, 'conditional', 'default') + 'default' + +Альтернативный метод для предоставления такой же функциональности как и +`queryAdapter()` - это `adapter_hook()`:: + + >>> y = registry.adapter_hook(IP1, x) + >>> y.__class__.__name__ + 'Y' + >>> y.context is x + True + >>> y = registry.adapter_hook(IP1, x, 'bob') + >>> y.__class__.__name__ + 'Y2' + >>> y.context is x + True + +`adapter_hook()` просто меняет порядок аргументов для объекта и интерфейса. Это +используется для встраивания в механизм вызовов интерфейсов. + +Адаптеры по умолчанию +--------------------- + +Иногда вы можете захотеть предоставить адаптер который не будет ничего +адаптировать. Для этого нужно передать None как требуемый интерфейс:: + + >>> registry.register([None], IP1, '', 1) + +после этого вы можете использовать этот адаптер для интерфейсов для которых у +вас нет конкретного адаптера:: + + >>> class IQ(zope.interface.Interface): + ... pass + >>> registry.lookup([IQ], IP1, '') + 1 + +Конечно, конкретные адаптеры все еще используются когда необходимо:: + + >>> registry.lookup([IR2], IP1, '') + 21 + +Адаптеры классов +---------------- + +Вы можете регистрировать адаптеры для определений классов, что будет похоже на +регистрацию их для классов:: + + >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21') + >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '') + 'C21' + +Адаптеры для словарей +--------------------- + +В какой-то момент было невозможно регистрировать адаптеры основанные на +словарях из-за ошибки. Давайте удостоверимся что это теперь работает:: + + >>> adapter = {} + >>> registry.register((), IQ, '', adapter) + >>> registry.lookup((), IQ, '') is adapter + True + +Удаление регистрации +-------------------- + +Вы можете удалить регистрацию регистрируя None вместо объекта:: + + >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None) + >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '') + 21 + +Конечно это значит, что None не может быть зарегистрирован. Это исключение к +утверждению выше о том, что реестр не заботиться о том, что регистрируется. + +Мульти-адаптеры +=============== + +Вы можете адаптировать несколько спецификаций:: + + >>> registry.register([IR1, IQ], IP2, '', '1q2') + >>> registry.lookup([IR1, IQ], IP2, '') + '1q2' + >>> registry.lookup([IR2, IQ], IP1, '') + '1q2' + + >>> class IS(zope.interface.Interface): + ... pass + >>> registry.lookup([IR2, IS], IP1, '') + + >>> class IQ2(IQ): + ... pass + + >>> registry.lookup([IR2, IQ2], IP1, '') + '1q2' + + >>> registry.register([IR1, IQ2], IP2, '', '1q22') + >>> registry.lookup([IR2, IQ2], IP1, '') + '1q22' + +Мульти-адаптация +---------------- + +Вы можете адаптировать несколько объектов:: + + >>> class Q: + ... zope.interface.implements(IQ) + +Как и с одиночными адаптерами, мы регистрируем фабрику которая возвращает +класс:: + + >>> class IM(zope.interface.Interface): + ... pass + >>> class M: + ... zope.interface.implements(IM) + ... def __init__(self, x, q): + ... self.x, self.q = x, q + >>> registry.register([IR, IQ], IM, '', M) + +И затем мы можем вызвать `queryMultiAdapter` для вычисления адаптера:: + + >>> q = Q() + >>> m = registry.queryMultiAdapter((x, q), IM) + >>> m.__class__.__name__ + 'M' + >>> m.x is x and m.q is q + True + +и, конечно, мы можем использовать имена:: + + >>> class M2(M): + ... pass + >>> registry.register([IR, IQ], IM, 'bob', M2) + >>> m = registry.queryMultiAdapter((x, q), IM, 'bob') + >>> m.__class__.__name__ + 'M2' + >>> m.x is x and m.q is q + True + +Адаптеры по умолчанию +--------------------- + +Как и для одиночных адаптеров вы можете определить адаптер по умолчанию передав +None вместо *первой* спецификации:: + + >>> registry.register([None, IQ], IP2, '', 'q2') + >>> registry.lookup([IS, IQ], IP2, '') + 'q2' + +Нулевые адаптеры +================ + +Вы можете также адаптировать без спецификации:: + + >>> registry.register([], IP2, '', 2) + >>> registry.lookup([], IP2, '') + 2 + >>> registry.lookup([], IP1, '') + 2 + +Перечисление именованных адаптеров +---------------------------------- + +Адаптеры имеют имена. Иногда это полезно для получения всех именованных +адаптеров для заданного интерфейса:: + + >>> adapters = list(registry.lookupAll([IR1], IP1)) + >>> adapters.sort() + >>> assert adapters == [(u'', 11), (u'bob', "Bob's 12")] + +Это работает также и для мульти-адаптеров:: + + >>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob') + >>> adapters = list(registry.lookupAll([IR2, IQ2], IP1)) + >>> adapters.sort() + >>> assert adapters == [(u'', '1q22'), (u'bob', '1q2 for bob')] + +И даже для нулевых адаптеров:: + + >>> registry.register([], IP2, 'bob', 3) + >>> adapters = list(registry.lookupAll([], IP1)) + >>> adapters.sort() + >>> assert adapters == [(u'', 2), (u'bob', 3)] + +Подписки +======== + +Обычно мы хотим запросить объект который наиболее близко соответствует +спецификации. Иногда мы хотим получить все объекты которые соответствуют +какой-либо спецификации. Мы используем подписки для этого. Мы подписываем +объекты для спецификаций и затем позже находим все подписанные объекты:: + + >>> registry.subscribe([IR1], IP2, 'sub12 1') + >>> registry.subscriptions([IR1], IP2) + ['sub12 1'] + +Заметьте, что в отличие от обычных адаптеров подписки не имеют имен. + +Вы можете иметь несколько подписчиков для одной спецификации:: + + >>> registry.subscribe([IR1], IP2, 'sub12 2') + >>> registry.subscriptions([IR1], IP2) + ['sub12 1', 'sub12 2'] + +Если подписчики зарегистрированы для одних и тех же требуемых интерфейсов, они +возвращаются в порядке определения. + +Вы можете зарегистрировать подписчики для всех спецификаций используя None:: + + >>> registry.subscribe([None], IP1, 'sub_1') + >>> registry.subscriptions([IR2], IP1) + ['sub_1', 'sub12 1', 'sub12 2'] + +Заметьте, что новый подписчик возвращается первым. Подписчики определенные +для менее общих требуемых интерфейсов возвращаются перед подписчиками +для более общих интерфейсов. + +Подписки могут смешиваться между несколькими совместимыми спецификациями:: + + >>> registry.subscriptions([IR2], IP1) + ['sub_1', 'sub12 1', 'sub12 2'] + >>> registry.subscribe([IR1], IP1, 'sub11') + >>> registry.subscriptions([IR2], IP1) + ['sub_1', 'sub12 1', 'sub12 2', 'sub11'] + >>> registry.subscribe([IR2], IP2, 'sub22') + >>> registry.subscriptions([IR2], IP1) + ['sub_1', 'sub12 1', 'sub12 2', 'sub11', 'sub22'] + >>> registry.subscriptions([IR2], IP2) + ['sub12 1', 'sub12 2', 'sub22'] + +Подписки могут существовать для нескольких спецификаций:: + + >>> registry.subscribe([IR1, IQ], IP2, 'sub1q2') + >>> registry.subscriptions([IR1, IQ], IP2) + ['sub1q2'] + +Как и с одиночными подписчиками и адаптерами без подписок, вы можете определить +None для первого требуемого интерфейса, что бы задать значение по умолчанию:: + + >>> registry.subscribe([None, IQ], IP2, 'sub_q2') + >>> registry.subscriptions([IS, IQ], IP2) + ['sub_q2'] + >>> registry.subscriptions([IR1, IQ], IP2) + ['sub_q2', 'sub1q2'] + +Вы можете создать подписки которые независимы от любых спецификаций:: + + >>> list(registry.subscriptions([], IP1)) + [] + + >>> registry.subscribe([], IP2, 'sub2') + >>> registry.subscriptions([], IP1) + ['sub2'] + >>> registry.subscribe([], IP1, 'sub1') + >>> registry.subscriptions([], IP1) + ['sub2', 'sub1'] + >>> registry.subscriptions([], IP2) + ['sub2'] + +Удаление регистрации подписчиков +-------------------------------- + +Мы можем удалять регистрацию подписчиков. При удалении регистрации подписчика +мы можем удалить регистрацию заданного адаптера:: + + >>> registry.unsubscribe([IR1], IP1, 'sub11') + >>> registry.subscriptions([IR1], IP1) + ['sub_1', 'sub12 1', 'sub12 2'] + +Если мы не задаем никакого значения тогда подписки будут удалены для всех +подписчиков совпадающих с заданным интерфейсом:: + + >>> registry.unsubscribe([IR1], IP2) + >>> registry.subscriptions([IR1], IP1) + ['sub_1'] + +Адаптеры подписки +----------------- + +Обычно мы регистрируем фабрики для адаптеров которые затем позволяют нам +вычислять адаптеры, но с подписками мы получаем несколько адаптеров. Это пример +подписчика для нескольких объектов:: + + >>> registry.subscribe([IR, IQ], IM, M) + >>> registry.subscribe([IR, IQ], IM, M2) + + >>> subscribers = registry.subscribers((x, q), IM) + >>> len(subscribers) + 2 + >>> class_names = [s.__class__.__name__ for s in subscribers] + >>> class_names.sort() + >>> class_names + ['M', 'M2'] + >>> [(s.x is x and s.q is q) for s in subscribers] + [True, True] + +подписчики фабрик адаптеров не могут возвращать None:: + + >>> def M3(x, y): + ... return None + + >>> registry.subscribe([IR, IQ], IM, M3) + >>> subscribers = registry.subscribers((x, q), IM) + >>> len(subscribers) + 2 + +Обработчики +----------- + +Обработчик - это подписанная фабрика которая не возвращает нормального +значения. Она возвращает None. Обработчик отличается от адаптеров тем, что он +делает всю работу когда вызывается фабрика. + +Для регистрации обработчика надо просто передать None как предоставляемый +интерфейс:: + + >>> def handler(event): + ... print 'handler', event + + >>> registry.subscribe([IR1], None, handler) + >>> registry.subscriptions([IR1], None) == [handler] + True diff -Nru zope3-3.4.0/src/zope/interface/adapter.txt zope3-3.5~bzr18/src/zope/interface/adapter.txt --- zope3-3.4.0/src/zope/interface/adapter.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/adapter.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,543 @@ +================ +Adapter Registry +================ + +Adapter registries provide a way to register objects that depend on +one or more interface specifications and provide (perhaps indirectly) +some interface. In addition, the registrations have names. (You can +think of the names as qualifiers of the provided interfaces.) + +The term "interface specification" refers both to interfaces and to +interface declarations, such as declarations of interfaces implemented +by a class. + + +Single Adapters +=============== + +Let's look at a simple example, using a single required specification:: + + >>> from zope.interface.adapter import AdapterRegistry + >>> import zope.interface + + >>> class IR1(zope.interface.Interface): + ... pass + >>> class IP1(zope.interface.Interface): + ... pass + >>> class IP2(IP1): + ... pass + + >>> registry = AdapterRegistry() + +We'll register an object that depends on IR1 and "provides" IP2:: + + >>> registry.register([IR1], IP2, '', 12) + +Given the registration, we can look it up again:: + + >>> registry.lookup([IR1], IP2, '') + 12 + +Note that we used an integer in the example. In real applications, +one would use some objects that actually depend on or provide +interfaces. The registry doesn't care about what gets registered, so +we'll use integers and strings to keep the examples simple. There is +one exception. Registering a value of None unregisters any +previously-registered value. + +If an object depends on a specification, it can be looked up with a +specification that extends the specification that it depends on:: + + >>> class IR2(IR1): + ... pass + >>> registry.lookup([IR2], IP2, '') + 12 + +We can use a class implementation specification to look up the object:: + + >>> class C2: + ... zope.interface.implements(IR2) + + >>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '') + 12 + + +and it can be looked up for interfaces that its provided interface +extends:: + + >>> registry.lookup([IR1], IP1, '') + 12 + >>> registry.lookup([IR2], IP1, '') + 12 + +But if you require a specification that doesn't extend the specification the +object depends on, you won't get anything:: + + >>> registry.lookup([zope.interface.Interface], IP1, '') + +By the way, you can pass a default value to lookup:: + + >>> registry.lookup([zope.interface.Interface], IP1, '', 42) + 42 + +If you try to get an interface the object doesn't provide, you also +won't get anything:: + + >>> class IP3(IP2): + ... pass + >>> registry.lookup([IR1], IP3, '') + +You also won't get anything if you use the wrong name:: + + >>> registry.lookup([IR1], IP1, 'bob') + >>> registry.register([IR1], IP2, 'bob', "Bob's 12") + >>> registry.lookup([IR1], IP1, 'bob') + "Bob's 12" + +You can leave the name off when doing a lookup:: + + >>> registry.lookup([IR1], IP1) + 12 + +If we register an object that provides IP1:: + + >>> registry.register([IR1], IP1, '', 11) + +then that object will be prefered over O(12):: + + >>> registry.lookup([IR1], IP1, '') + 11 + +Also, if we register an object for IR2, then that will be prefered +when using IR2:: + + >>> registry.register([IR2], IP1, '', 21) + >>> registry.lookup([IR2], IP1, '') + 21 + +Finding out what, if anything, is registered +-------------------------------------------- + +We can ask if there is an adapter registered for a collection of +interfaces. This is different than lookup, because it looks for an +exact match. + + >>> print registry.registered([IR1], IP1) + 11 + + >>> print registry.registered([IR1], IP2) + 12 + + >>> print registry.registered([IR1], IP2, 'bob') + Bob's 12 + + + >>> print registry.registered([IR2], IP1) + 21 + + >>> print registry.registered([IR2], IP2) + None + +In the last example, None was returned because nothing was registered +exactly for the given interfaces. + +lookup1 +------- + +Lookup of single adapters is common enough that there is a specialized +version of lookup that takes a single required interface:: + + >>> registry.lookup1(IR2, IP1, '') + 21 + >>> registry.lookup1(IR2, IP1) + 21 + +Actual Adaptation +----------------- + +The adapter registry is intended to support adaptation, where one +object that implements an interface is adapted to another object that +supports a different interface. The adapter registry supports the +computation of adapters. In this case, we have to register adapter +factories:: + + >>> class IR(zope.interface.Interface): + ... pass + + >>> class X: + ... zope.interface.implements(IR) + + >>> class Y: + ... zope.interface.implements(IP1) + ... def __init__(self, context): + ... self.context = context + + >>> registry.register([IR], IP1, '', Y) + +In this case, we registered a class as the factory. Now we can call +`queryAdapter` to get the adapted object:: + + >>> x = X() + >>> y = registry.queryAdapter(x, IP1) + >>> y.__class__.__name__ + 'Y' + >>> y.context is x + True + +We can register and lookup by name too:: + + >>> class Y2(Y): + ... pass + + >>> registry.register([IR], IP1, 'bob', Y2) + >>> y = registry.queryAdapter(x, IP1, 'bob') + >>> y.__class__.__name__ + 'Y2' + >>> y.context is x + True + +When the adapter factory produces `None`, then this is treated as if no +adapter has been found. This allows us to prevent adaptation (when desired) +and let the adapter factory determine whether adaptation is possible based on +the state of the object being adapted. + + >>> def factory(context): + ... if context.name == 'object': + ... return 'adapter' + ... return None + + >>> class Object(object): + ... zope.interface.implements(IR) + ... name = 'object' + + >>> registry.register([IR], IP1, 'conditional', factory) + >>> obj = Object() + >>> registry.queryAdapter(obj, IP1, 'conditional') + 'adapter' + >>> obj.name = 'no object' + >>> registry.queryAdapter(obj, IP1, 'conditional') is None + True + >>> registry.queryAdapter(obj, IP1, 'conditional', 'default') + 'default' + +An alternate method that provides the same function as `queryAdapter()` is +`adapter_hook()`:: + + >>> y = registry.adapter_hook(IP1, x) + >>> y.__class__.__name__ + 'Y' + >>> y.context is x + True + >>> y = registry.adapter_hook(IP1, x, 'bob') + >>> y.__class__.__name__ + 'Y2' + >>> y.context is x + True + +The `adapter_hook()` simply switches the order of the object and +interface arguments. It is used to hook into the interface call +mechanism. + + +Default Adapters +---------------- + +Sometimes, you want to provide an adapter that will adapt anything. +For that, provide None as the required interface:: + + >>> registry.register([None], IP1, '', 1) + +then we can use that adapter for interfaces we don't have specific +adapters for:: + + >>> class IQ(zope.interface.Interface): + ... pass + >>> registry.lookup([IQ], IP1, '') + 1 + +Of course, specific adapters are still used when applicable:: + + >>> registry.lookup([IR2], IP1, '') + 21 + +Class adapters +-------------- + +You can register adapters for class declarations, which is almost the +same as registering them for a class:: + + >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21') + >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '') + 'C21' + +Dict adapters +------------- + +At some point it was impossible to register dictionary-based adapters due a +bug. Let's make sure this works now: + + >>> adapter = {} + >>> registry.register((), IQ, '', adapter) + >>> registry.lookup((), IQ, '') is adapter + True + +Unregistering +------------- + +You can unregister by registering None, rather than an object:: + + >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None) + >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '') + 21 + +Of course, this means that None can't be registered. This is an +exception to the statement, made earlier, that the registry doesn't +care what gets registered. + +Multi-adapters +============== + +You can adapt multiple specifications:: + + >>> registry.register([IR1, IQ], IP2, '', '1q2') + >>> registry.lookup([IR1, IQ], IP2, '') + '1q2' + >>> registry.lookup([IR2, IQ], IP1, '') + '1q2' + + >>> class IS(zope.interface.Interface): + ... pass + >>> registry.lookup([IR2, IS], IP1, '') + + >>> class IQ2(IQ): + ... pass + + >>> registry.lookup([IR2, IQ2], IP1, '') + '1q2' + + >>> registry.register([IR1, IQ2], IP2, '', '1q22') + >>> registry.lookup([IR2, IQ2], IP1, '') + '1q22' + +Multi-adaptation +---------------- + +You can adapt multiple objects:: + + >>> class Q: + ... zope.interface.implements(IQ) + +As with single adapters, we register a factory, which is often a class:: + + >>> class IM(zope.interface.Interface): + ... pass + >>> class M: + ... zope.interface.implements(IM) + ... def __init__(self, x, q): + ... self.x, self.q = x, q + >>> registry.register([IR, IQ], IM, '', M) + +And then we can call `queryMultiAdapter` to compute an adapter:: + + >>> q = Q() + >>> m = registry.queryMultiAdapter((x, q), IM) + >>> m.__class__.__name__ + 'M' + >>> m.x is x and m.q is q + True + +and, of course, we can use names:: + + >>> class M2(M): + ... pass + >>> registry.register([IR, IQ], IM, 'bob', M2) + >>> m = registry.queryMultiAdapter((x, q), IM, 'bob') + >>> m.__class__.__name__ + 'M2' + >>> m.x is x and m.q is q + True + +Default Adapters +---------------- + +As with single adapters, you can define default adapters by specifying +None for the *first* specification:: + + >>> registry.register([None, IQ], IP2, '', 'q2') + >>> registry.lookup([IS, IQ], IP2, '') + 'q2' + +Null Adapters +============= + +You can also adapt no specification:: + + >>> registry.register([], IP2, '', 2) + >>> registry.lookup([], IP2, '') + 2 + >>> registry.lookup([], IP1, '') + 2 + +Listing named adapters +---------------------- + +Adapters are named. Sometimes, it's useful to get all of the named +adapters for given interfaces:: + + >>> adapters = list(registry.lookupAll([IR1], IP1)) + >>> adapters.sort() + >>> assert adapters == [(u'', 11), (u'bob', "Bob's 12")] + +This works for multi-adapters too:: + + >>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob') + >>> adapters = list(registry.lookupAll([IR2, IQ2], IP1)) + >>> adapters.sort() + >>> assert adapters == [(u'', '1q22'), (u'bob', '1q2 for bob')] + +And even null adapters:: + + >>> registry.register([], IP2, 'bob', 3) + >>> adapters = list(registry.lookupAll([], IP1)) + >>> adapters.sort() + >>> assert adapters == [(u'', 2), (u'bob', 3)] + +Subscriptions +============= + +Normally, we want to look up an object that most-closely matches a +specification. Sometimes, we want to get all of the objects that +match some specification. We use subscriptions for this. We +subscribe objects against specifications and then later find all of +the subscribed objects:: + + >>> registry.subscribe([IR1], IP2, 'sub12 1') + >>> registry.subscriptions([IR1], IP2) + ['sub12 1'] + +Note that, unlike regular adapters, subscriptions are unnamed. + +You can have multiple subscribers for the same specification:: + + >>> registry.subscribe([IR1], IP2, 'sub12 2') + >>> registry.subscriptions([IR1], IP2) + ['sub12 1', 'sub12 2'] + +If subscribers are registered for the same required interfaces, they +are returned in the order of definition. + +You can register subscribers for all specifications using None:: + + >>> registry.subscribe([None], IP1, 'sub_1') + >>> registry.subscriptions([IR2], IP1) + ['sub_1', 'sub12 1', 'sub12 2'] + +Note that the new subscriber is returned first. Subscribers defined +for less general required interfaces are returned before subscribers +for more general interfaces. + +Subscriptions may be combined over multiple compatible specifications:: + + >>> registry.subscriptions([IR2], IP1) + ['sub_1', 'sub12 1', 'sub12 2'] + >>> registry.subscribe([IR1], IP1, 'sub11') + >>> registry.subscriptions([IR2], IP1) + ['sub_1', 'sub12 1', 'sub12 2', 'sub11'] + >>> registry.subscribe([IR2], IP2, 'sub22') + >>> registry.subscriptions([IR2], IP1) + ['sub_1', 'sub12 1', 'sub12 2', 'sub11', 'sub22'] + >>> registry.subscriptions([IR2], IP2) + ['sub12 1', 'sub12 2', 'sub22'] + +Subscriptions can be on multiple specifications:: + + >>> registry.subscribe([IR1, IQ], IP2, 'sub1q2') + >>> registry.subscriptions([IR1, IQ], IP2) + ['sub1q2'] + +As with single subscriptions and non-subscription adapters, you can +specify None for the first required interface, to specify a default:: + + >>> registry.subscribe([None, IQ], IP2, 'sub_q2') + >>> registry.subscriptions([IS, IQ], IP2) + ['sub_q2'] + >>> registry.subscriptions([IR1, IQ], IP2) + ['sub_q2', 'sub1q2'] + +You can have subscriptions that are indepenent of any specifications:: + + >>> list(registry.subscriptions([], IP1)) + [] + + >>> registry.subscribe([], IP2, 'sub2') + >>> registry.subscriptions([], IP1) + ['sub2'] + >>> registry.subscribe([], IP1, 'sub1') + >>> registry.subscriptions([], IP1) + ['sub2', 'sub1'] + >>> registry.subscriptions([], IP2) + ['sub2'] + +Unregistering subscribers +------------------------- + +We can unregister subscribers. When unregistering a subscriber, we +can unregister a specific subscriber:: + + >>> registry.unsubscribe([IR1], IP1, 'sub11') + >>> registry.subscriptions([IR1], IP1) + ['sub_1', 'sub12 1', 'sub12 2'] + +If we don't specify a value, then all subscribers matching the given +interfaces will be unsubscribed: + + >>> registry.unsubscribe([IR1], IP2) + >>> registry.subscriptions([IR1], IP1) + ['sub_1'] + + +Subscription adapters +--------------------- + +We normally register adapter factories, which then allow us to compute +adapters, but with subscriptions, we get multiple adapters. Here's an +example of multiple-object subscribers:: + + >>> registry.subscribe([IR, IQ], IM, M) + >>> registry.subscribe([IR, IQ], IM, M2) + + >>> subscribers = registry.subscribers((x, q), IM) + >>> len(subscribers) + 2 + >>> class_names = [s.__class__.__name__ for s in subscribers] + >>> class_names.sort() + >>> class_names + ['M', 'M2'] + >>> [(s.x is x and s.q is q) for s in subscribers] + [True, True] + +adapter factory subcribers can't return None values:: + + >>> def M3(x, y): + ... return None + + >>> registry.subscribe([IR, IQ], IM, M3) + >>> subscribers = registry.subscribers((x, q), IM) + >>> len(subscribers) + 2 + +Handlers +-------- + +A handler is a subscriber factory that doesn't produce any normal +output. It returns None. A handler is unlike adapters in that it does +all of its work when the factory is called. + +To register a handler, simply provide None as the provided interface:: + + >>> def handler(event): + ... print 'handler', event + + >>> registry.subscribe([IR1], None, handler) + >>> registry.subscriptions([IR1], None) == [handler] + True diff -Nru zope3-3.4.0/src/zope/interface/advice.py zope3-3.5~bzr18/src/zope/interface/advice.py --- zope3-3.4.0/src/zope/interface/advice.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/advice.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,203 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Class advice. + +This module was adapted from 'protocols.advice', part of the Python +Enterprise Application Kit (PEAK). Please notify the PEAK authors +(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or +Zope-specific changes are required, so that the PEAK version of this module +can be kept in sync. + +PEAK is a Python application framework that interoperates with (but does +not require) Zope 3 and Twisted. It provides tools for manipulating UML +models, object-relational persistence, aspect-oriented programming, and more. +Visit the PEAK home page at http://peak.telecommunity.com for more information. + +$Id: advice.py 110699 2010-04-09 08:16:17Z regebro $ +""" + +from types import FunctionType +try: + from types import ClassType + __python3 = False +except ImportError: + __python3 = True + +import sys + +def getFrameInfo(frame): + """Return (kind,module,locals,globals) for a frame + + 'kind' is one of "exec", "module", "class", "function call", or "unknown". + """ + + f_locals = frame.f_locals + f_globals = frame.f_globals + + sameNamespace = f_locals is f_globals + hasModule = '__module__' in f_locals + hasName = '__name__' in f_globals + + sameName = hasModule and hasName + sameName = sameName and f_globals['__name__']==f_locals['__module__'] + + module = hasName and sys.modules.get(f_globals['__name__']) or None + + namespaceIsModule = module and module.__dict__ is f_globals + + if not namespaceIsModule: + # some kind of funky exec + kind = "exec" + elif sameNamespace and not hasModule: + kind = "module" + elif sameName and not sameNamespace: + kind = "class" + elif not sameNamespace: + kind = "function call" + else: + # How can you have f_locals is f_globals, and have '__module__' set? + # This is probably module-level code, but with a '__module__' variable. + kind = "unknown" + return kind, module, f_locals, f_globals + + +def addClassAdvisor(callback, depth=2): + """Set up 'callback' to be passed the containing class upon creation + + This function is designed to be called by an "advising" function executed + in a class suite. The "advising" function supplies a callback that it + wishes to have executed when the containing class is created. The + callback will be given one argument: the newly created containing class. + The return value of the callback will be used in place of the class, so + the callback should return the input if it does not wish to replace the + class. + + The optional 'depth' argument to this function determines the number of + frames between this function and the targeted class suite. 'depth' + defaults to 2, since this skips this function's frame and one calling + function frame. If you use this function from a function called directly + in the class suite, the default will be correct, otherwise you will need + to determine the correct depth yourself. + + This function works by installing a special class factory function in + place of the '__metaclass__' of the containing class. Therefore, only + callbacks *after* the last '__metaclass__' assignment in the containing + class will be executed. Be sure that classes using "advising" functions + declare any '__metaclass__' *first*, to ensure all callbacks are run.""" + + frame = sys._getframe(depth) + kind, module, caller_locals, caller_globals = getFrameInfo(frame) + + # This causes a problem when zope interfaces are used from doctest. + # In these cases, kind == "exec". + # + #if kind != "class": + # raise SyntaxError( + # "Advice must be in the body of a class statement" + # ) + + previousMetaclass = caller_locals.get('__metaclass__') + if __python3: + defaultMetaclass = caller_globals.get('__metaclass__', type) + else: + defaultMetaclass = caller_globals.get('__metaclass__', ClassType) + + + def advise(name, bases, cdict): + + if '__metaclass__' in cdict: + del cdict['__metaclass__'] + + if previousMetaclass is None: + if bases: + # find best metaclass or use global __metaclass__ if no bases + meta = determineMetaclass(bases) + else: + meta = defaultMetaclass + + elif isClassAdvisor(previousMetaclass): + # special case: we can't compute the "true" metaclass here, + # so we need to invoke the previous metaclass and let it + # figure it out for us (and apply its own advice in the process) + meta = previousMetaclass + + else: + meta = determineMetaclass(bases, previousMetaclass) + + newClass = meta(name,bases,cdict) + + # this lets the callback replace the class completely, if it wants to + return callback(newClass) + + # introspection data only, not used by inner function + advise.previousMetaclass = previousMetaclass + advise.callback = callback + + # install the advisor + caller_locals['__metaclass__'] = advise + + +def isClassAdvisor(ob): + """True if 'ob' is a class advisor function""" + return isinstance(ob,FunctionType) and hasattr(ob,'previousMetaclass') + + +def determineMetaclass(bases, explicit_mc=None): + """Determine metaclass from 1+ bases and optional explicit __metaclass__""" + + meta = [getattr(b,'__class__',type(b)) for b in bases] + + if explicit_mc is not None: + # The explicit metaclass needs to be verified for compatibility + # as well, and allowed to resolve the incompatible bases, if any + meta.append(explicit_mc) + + if len(meta)==1: + # easy case + return meta[0] + + candidates = minimalBases(meta) # minimal set of metaclasses + + if not candidates: + # they're all "classic" classes + assert(not __python3) # This should not happen under Python 3 + return ClassType + + elif len(candidates)>1: + # We could auto-combine, but for now we won't... + raise TypeError("Incompatible metatypes",bases) + + # Just one, return it + return candidates[0] + + +def minimalBases(classes): + """Reduce a list of base classes to its ordered minimum equivalent""" + + if not __python3: + classes = [c for c in classes if c is not ClassType] + candidates = [] + + for m in classes: + for n in classes: + if issubclass(n,m) and m is not n: + break + else: + # m has no subclasses in 'classes' + if m in candidates: + candidates.remove(m) # ensure that we're later in the list + candidates.append(m) + + return candidates + diff -Nru zope3-3.4.0/src/zope/interface/common/idatetime.py zope3-3.5~bzr18/src/zope/interface/common/idatetime.py --- zope3-3.4.0/src/zope/interface/common/idatetime.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/idatetime.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,577 @@ +############################################################################## +# 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. +############################################################################## +"""Datetime interfaces. + +This module is called idatetime because if it were called datetime the import +of the real datetime would fail. + +$Id: idatetime.py 110736 2010-04-11 10:59:30Z regebro $ +""" + +from zope.interface import Interface, Attribute +from zope.interface import classImplements + +from datetime import timedelta, date, datetime, time, tzinfo + + +class ITimeDeltaClass(Interface): + """This is the timedelta class interface.""" + + min = Attribute("The most negative timedelta object") + + max = Attribute("The most positive timedelta object") + + resolution = Attribute( + "The smallest difference between non-equal timedelta objects") + + +class ITimeDelta(ITimeDeltaClass): + """Represent the difference between two datetime objects. + + Supported operators: + + - add, subtract timedelta + - unary plus, minus, abs + - compare to timedelta + - multiply, divide by int/long + + In addition, datetime supports subtraction of two datetime objects + returning a timedelta, and addition or subtraction of a datetime + and a timedelta giving a datetime. + + Representation: (days, seconds, microseconds). + """ + + days = Attribute("Days between -999999999 and 999999999 inclusive") + + seconds = Attribute("Seconds between 0 and 86399 inclusive") + + microseconds = Attribute("Microseconds between 0 and 999999 inclusive") + + +class IDateClass(Interface): + """This is the date class interface.""" + + min = Attribute("The earliest representable date") + + max = Attribute("The latest representable date") + + resolution = Attribute( + "The smallest difference between non-equal date objects") + + def today(): + """Return the current local time. + + This is equivalent to date.fromtimestamp(time.time())""" + + def fromtimestamp(timestamp): + """Return the local date from a POSIX timestamp (like time.time()) + + This may raise ValueError, if the timestamp is out of the range of + values supported by the platform C localtime() function. It's common + for this to be restricted to years from 1970 through 2038. Note that + on non-POSIX systems that include leap seconds in their notion of a + timestamp, leap seconds are ignored by fromtimestamp(). + """ + + def fromordinal(ordinal): + """Return the date corresponding to the proleptic Gregorian ordinal. + + January 1 of year 1 has ordinal 1. ValueError is raised unless + 1 <= ordinal <= date.max.toordinal(). + For any date d, date.fromordinal(d.toordinal()) == d. + """ + + +class IDate(IDateClass): + """Represents a date (year, month and day) in an idealized calendar. + + Operators: + + __repr__, __str__ + __cmp__, __hash__ + __add__, __radd__, __sub__ (add/radd only with timedelta arg) + """ + + year = Attribute("Between MINYEAR and MAXYEAR inclusive.") + + month = Attribute("Between 1 and 12 inclusive") + + day = Attribute( + "Between 1 and the number of days in the given month of the given year.") + + def replace(year, month, day): + """Return a date with the same value. + + Except for those members given new values by whichever keyword + arguments are specified. For example, if d == date(2002, 12, 31), then + d.replace(day=26) == date(2000, 12, 26). + """ + + def timetuple(): + """Return a 9-element tuple of the form returned by time.localtime(). + + The hours, minutes and seconds are 0, and the DST flag is -1. + d.timetuple() is equivalent to + (d.year, d.month, d.day, 0, 0, 0, d.weekday(), d.toordinal() - + date(d.year, 1, 1).toordinal() + 1, -1) + """ + + def toordinal(): + """Return the proleptic Gregorian ordinal of the date + + January 1 of year 1 has ordinal 1. For any date object d, + date.fromordinal(d.toordinal()) == d. + """ + + def weekday(): + """Return the day of the week as an integer. + + Monday is 0 and Sunday is 6. For example, + date(2002, 12, 4).weekday() == 2, a Wednesday. + + See also isoweekday(). + """ + + def isoweekday(): + """Return the day of the week as an integer. + + Monday is 1 and Sunday is 7. For example, + date(2002, 12, 4).isoweekday() == 3, a Wednesday. + + See also weekday(), isocalendar(). + """ + + def isocalendar(): + """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). + + The ISO calendar is a widely used variant of the Gregorian calendar. + See http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm for a good + explanation. + + The ISO year consists of 52 or 53 full weeks, and where a week starts + on a Monday and ends on a Sunday. The first week of an ISO year is the + first (Gregorian) calendar week of a year containing a Thursday. This + is called week number 1, and the ISO year of that Thursday is the same + as its Gregorian year. + + For example, 2004 begins on a Thursday, so the first week of ISO year + 2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004, so + that date(2003, 12, 29).isocalendar() == (2004, 1, 1) and + date(2004, 1, 4).isocalendar() == (2004, 1, 7). + """ + + def isoformat(): + """Return a string representing the date in ISO 8601 format. + + This is 'YYYY-MM-DD'. + For example, date(2002, 12, 4).isoformat() == '2002-12-04'. + """ + + def __str__(): + """For a date d, str(d) is equivalent to d.isoformat().""" + + def ctime(): + """Return a string representing the date. + + For example date(2002, 12, 4).ctime() == 'Wed Dec 4 00:00:00 2002'. + d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())) + on platforms where the native C ctime() function + (which time.ctime() invokes, but which date.ctime() does not invoke) + conforms to the C standard. + """ + + def strftime(format): + """Return a string representing the date. + + Controlled by an explicit format string. Format codes referring to + hours, minutes or seconds will see 0 values. + """ + + +class IDateTimeClass(Interface): + """This is the datetime class interface.""" + + min = Attribute("The earliest representable datetime") + + max = Attribute("The latest representable datetime") + + resolution = Attribute( + "The smallest possible difference between non-equal datetime objects") + + def today(): + """Return the current local datetime, with tzinfo None. + + This is equivalent to datetime.fromtimestamp(time.time()). + See also now(), fromtimestamp(). + """ + + def now(tz=None): + """Return the current local date and time. + + If optional argument tz is None or not specified, this is like today(), + but, if possible, supplies more precision than can be gotten from going + through a time.time() timestamp (for example, this may be possible on + platforms supplying the C gettimeofday() function). + + Else tz must be an instance of a class tzinfo subclass, and the current + date and time are converted to tz's time zone. In this case the result + is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)). + + See also today(), utcnow(). + """ + + def utcnow(): + """Return the current UTC date and time, with tzinfo None. + + This is like now(), but returns the current UTC date and time, as a + naive datetime object. + + See also now(). + """ + + def fromtimestamp(timestamp, tz=None): + """Return the local date and time corresponding to the POSIX timestamp. + + Same as is returned by time.time(). If optional argument tz is None or + not specified, the timestamp is converted to the platform's local date + and time, and the returned datetime object is naive. + + Else tz must be an instance of a class tzinfo subclass, and the + timestamp is converted to tz's time zone. In this case the result is + equivalent to + tz.fromutc(datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz)). + + fromtimestamp() may raise ValueError, if the timestamp is out of the + range of values supported by the platform C localtime() or gmtime() + functions. It's common for this to be restricted to years in 1970 + through 2038. Note that on non-POSIX systems that include leap seconds + in their notion of a timestamp, leap seconds are ignored by + fromtimestamp(), and then it's possible to have two timestamps + differing by a second that yield identical datetime objects. + + See also utcfromtimestamp(). + """ + + def utcfromtimestamp(timestamp): + """Return the UTC datetime from the POSIX timestamp with tzinfo None. + + This may raise ValueError, if the timestamp is out of the range of + values supported by the platform C gmtime() function. It's common for + this to be restricted to years in 1970 through 2038. + + See also fromtimestamp(). + """ + + def fromordinal(ordinal): + """Return the datetime from the proleptic Gregorian ordinal. + + January 1 of year 1 has ordinal 1. ValueError is raised unless + 1 <= ordinal <= datetime.max.toordinal(). + The hour, minute, second and microsecond of the result are all 0, and + tzinfo is None. + """ + + def combine(date, time): + """Return a new datetime object. + + Its date members are equal to the given date object's, and whose time + and tzinfo members are equal to the given time object's. For any + datetime object d, d == datetime.combine(d.date(), d.timetz()). + If date is a datetime object, its time and tzinfo members are ignored. + """ + + +class IDateTime(IDate, IDateTimeClass): + """Object contains all the information from a date object and a time object. + """ + + year = Attribute("Year between MINYEAR and MAXYEAR inclusive") + + month = Attribute("Month between 1 and 12 inclusive") + + day = Attribute( + "Day between 1 and the number of days in the given month of the year") + + hour = Attribute("Hour in range(24)") + + minute = Attribute("Minute in range(60)") + + second = Attribute("Second in range(60)") + + microsecond = Attribute("Microsecond in range(1000000)") + + tzinfo = Attribute( + """The object passed as the tzinfo argument to the datetime constructor + or None if none was passed""") + + def date(): + """Return date object with same year, month and day.""" + + def time(): + """Return time object with same hour, minute, second, microsecond. + + tzinfo is None. See also method timetz(). + """ + + def timetz(): + """Return time object with same hour, minute, second, microsecond, + and tzinfo. + + See also method time(). + """ + + def replace(year, month, day, hour, minute, second, microsecond, tzinfo): + """Return a datetime with the same members, except for those members + given new values by whichever keyword arguments are specified. + + Note that tzinfo=None can be specified to create a naive datetime from + an aware datetime with no conversion of date and time members. + """ + + def astimezone(tz): + """Return a datetime object with new tzinfo member tz, adjusting the + date and time members so the result is the same UTC time as self, but + in tz's local time. + + tz must be an instance of a tzinfo subclass, and its utcoffset() and + dst() methods must not return None. self must be aware (self.tzinfo + must not be None, and self.utcoffset() must not return None). + + If self.tzinfo is tz, self.astimezone(tz) is equal to self: no + adjustment of date or time members is performed. Else the result is + local time in time zone tz, representing the same UTC time as self: + after astz = dt.astimezone(tz), astz - astz.utcoffset() + will usually have the same date and time members as dt - dt.utcoffset(). + The discussion of class tzinfo explains the cases at Daylight Saving + Time transition boundaries where this cannot be achieved (an issue only + if tz models both standard and daylight time). + + If you merely want to attach a time zone object tz to a datetime dt + without adjustment of date and time members, use dt.replace(tzinfo=tz). + If you merely want to remove the time zone object from an aware + datetime dt without conversion of date and time members, use + dt.replace(tzinfo=None). + + Note that the default tzinfo.fromutc() method can be overridden in a + tzinfo subclass to effect the result returned by astimezone(). + """ + + def utcoffset(): + """Return the timezone offset in minutes east of UTC (negative west of + UTC).""" + + def dst(): + """Return 0 if DST is not in effect, or the DST offset (in minutes + eastward) if DST is in effect. + """ + + def tzname(): + """Return the timezone name.""" + + def timetuple(): + """Return a 9-element tuple of the form returned by time.localtime().""" + + def utctimetuple(): + """Return UTC time tuple compatilble with time.gmtimr().""" + + def toordinal(): + """Return the proleptic Gregorian ordinal of the date. + + The same as self.date().toordinal(). + """ + + def weekday(): + """Return the day of the week as an integer. + + Monday is 0 and Sunday is 6. The same as self.date().weekday(). + See also isoweekday(). + """ + + def isoweekday(): + """Return the day of the week as an integer. + + Monday is 1 and Sunday is 7. The same as self.date().isoweekday. + See also weekday(), isocalendar(). + """ + + def isocalendar(): + """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). + + The same as self.date().isocalendar(). + """ + + def isoformat(sep='T'): + """Return a string representing the date and time in ISO 8601 format. + + YYYY-MM-DDTHH:MM:SS.mmmmmm or YYYY-MM-DDTHH:MM:SS if microsecond is 0 + + If utcoffset() does not return None, a 6-character string is appended, + giving the UTC offset in (signed) hours and minutes: + + YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or YYYY-MM-DDTHH:MM:SS+HH:MM + if microsecond is 0. + + The optional argument sep (default 'T') is a one-character separator, + placed between the date and time portions of the result. + """ + + def __str__(): + """For a datetime instance d, str(d) is equivalent to d.isoformat(' '). + """ + + def ctime(): + """Return a string representing the date and time. + + datetime(2002, 12, 4, 20, 30, 40).ctime() == 'Wed Dec 4 20:30:40 2002'. + d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())) on + platforms where the native C ctime() function (which time.ctime() + invokes, but which datetime.ctime() does not invoke) conforms to the + C standard. + """ + + def strftime(format): + """Return a string representing the date and time. + + This is controlled by an explicit format string. + """ + + +class ITimeClass(Interface): + """This is the time class interface.""" + + min = Attribute("The earliest representable time") + + max = Attribute("The latest representable time") + + resolution = Attribute( + "The smallest possible difference between non-equal time objects") + + +class ITime(ITimeClass): + """Represent time with time zone. + + Operators: + + __repr__, __str__ + __cmp__, __hash__ + """ + + hour = Attribute("Hour in range(24)") + + minute = Attribute("Minute in range(60)") + + second = Attribute("Second in range(60)") + + microsecond = Attribute("Microsecond in range(1000000)") + + tzinfo = Attribute( + """The object passed as the tzinfo argument to the time constructor + or None if none was passed.""") + + def replace(hour, minute, second, microsecond, tzinfo): + """Return a time with the same value. + + Except for those members given new values by whichever keyword + arguments are specified. Note that tzinfo=None can be specified + to create a naive time from an aware time, without conversion of the + time members. + """ + + def isoformat(): + """Return a string representing the time in ISO 8601 format. + + That is HH:MM:SS.mmmmmm or, if self.microsecond is 0, HH:MM:SS + If utcoffset() does not return None, a 6-character string is appended, + giving the UTC offset in (signed) hours and minutes: + HH:MM:SS.mmmmmm+HH:MM or, if self.microsecond is 0, HH:MM:SS+HH:MM + """ + + def __str__(): + """For a time t, str(t) is equivalent to t.isoformat().""" + + def strftime(format): + """Return a string representing the time. + + This is controlled by an explicit format string. + """ + + def utcoffset(): + """Return the timezone offset in minutes east of UTC (negative west of + UTC). + + If tzinfo is None, returns None, else returns + self.tzinfo.utcoffset(None), and raises an exception if the latter + doesn't return None or a timedelta object representing a whole number + of minutes with magnitude less than one day. + """ + + def dst(): + """Return 0 if DST is not in effect, or the DST offset (in minutes + eastward) if DST is in effect. + + If tzinfo is None, returns None, else returns self.tzinfo.dst(None), + and raises an exception if the latter doesn't return None, or a + timedelta object representing a whole number of minutes with + magnitude less than one day. + """ + + def tzname(): + """Return the timezone name. + + If tzinfo is None, returns None, else returns self.tzinfo.tzname(None), + or raises an exception if the latter doesn't return None or a string + object. + """ + + +class ITZInfo(Interface): + """Time zone info class. + """ + + def utcoffset(dt): + """Return offset of local time from UTC, in minutes east of UTC. + + If local time is west of UTC, this should be negative. + Note that this is intended to be the total offset from UTC; + for example, if a tzinfo object represents both time zone and DST + adjustments, utcoffset() should return their sum. If the UTC offset + isn't known, return None. Else the value returned must be a timedelta + object specifying a whole number of minutes in the range -1439 to 1439 + inclusive (1440 = 24*60; the magnitude of the offset must be less + than one day). + """ + + def dst(dt): + """Return the daylight saving time (DST) adjustment, in minutes east + of UTC, or None if DST information isn't known. + """ + + def tzname(dt): + """Return the time zone name corresponding to the datetime object as + a string. + """ + + def fromutc(dt): + """Return an equivalent datetime in self's local time.""" + + +classImplements(timedelta, ITimeDelta) +classImplements(date, IDate) +classImplements(datetime, IDateTime) +classImplements(time, ITime) +classImplements(tzinfo, ITZInfo) + +## directlyProvides(timedelta, ITimeDeltaClass) +## directlyProvides(date, IDateClass) +## directlyProvides(datetime, IDateTimeClass) +## directlyProvides(time, ITimeClass) diff -Nru zope3-3.4.0/src/zope/interface/common/__init__.py zope3-3.5~bzr18/src/zope/interface/common/__init__.py --- zope3-3.4.0/src/zope/interface/common/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/interface/common/interfaces.py zope3-3.5~bzr18/src/zope/interface/common/interfaces.py --- zope3-3.4.0/src/zope/interface/common/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,101 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Interfaces for standard python exceptions + +$Id: interfaces.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +from zope.interface import Interface +from zope.interface import classImplements + +class IException(Interface): pass +class IStandardError(IException): pass +class IWarning(IException): pass +class ISyntaxError(IStandardError): pass +class ILookupError(IStandardError): pass +class IValueError(IStandardError): pass +class IRuntimeError(IStandardError): pass +class IArithmeticError(IStandardError): pass +class IAssertionError(IStandardError): pass +class IAttributeError(IStandardError): pass +class IDeprecationWarning(IWarning): pass +class IEOFError(IStandardError): pass +class IEnvironmentError(IStandardError): pass +class IFloatingPointError(IArithmeticError): pass +class IIOError(IEnvironmentError): pass +class IImportError(IStandardError): pass +class IIndentationError(ISyntaxError): pass +class IIndexError(ILookupError): pass +class IKeyError(ILookupError): pass +class IKeyboardInterrupt(IStandardError): pass +class IMemoryError(IStandardError): pass +class INameError(IStandardError): pass +class INotImplementedError(IRuntimeError): pass +class IOSError(IEnvironmentError): pass +class IOverflowError(IArithmeticError): pass +class IOverflowWarning(IWarning): pass +class IReferenceError(IStandardError): pass +class IRuntimeWarning(IWarning): pass +class IStopIteration(IException): pass +class ISyntaxWarning(IWarning): pass +class ISystemError(IStandardError): pass +class ISystemExit(IException): pass +class ITabError(IIndentationError): pass +class ITypeError(IStandardError): pass +class IUnboundLocalError(INameError): pass +class IUnicodeError(IValueError): pass +class IUserWarning(IWarning): pass +class IZeroDivisionError(IArithmeticError): pass + +classImplements(ArithmeticError, IArithmeticError) +classImplements(AssertionError, IAssertionError) +classImplements(AttributeError, IAttributeError) +classImplements(DeprecationWarning, IDeprecationWarning) +classImplements(EnvironmentError, IEnvironmentError) +classImplements(EOFError, IEOFError) +classImplements(Exception, IException) +classImplements(FloatingPointError, IFloatingPointError) +classImplements(ImportError, IImportError) +classImplements(IndentationError, IIndentationError) +classImplements(IndexError, IIndexError) +classImplements(IOError, IIOError) +classImplements(KeyboardInterrupt, IKeyboardInterrupt) +classImplements(KeyError, IKeyError) +classImplements(LookupError, ILookupError) +classImplements(MemoryError, IMemoryError) +classImplements(NameError, INameError) +classImplements(NotImplementedError, INotImplementedError) +classImplements(OSError, IOSError) +classImplements(OverflowError, IOverflowError) +try: + classImplements(OverflowWarning, IOverflowWarning) +except NameError: + pass # OverflowWarning was removed in Python 2.5 +classImplements(ReferenceError, IReferenceError) +classImplements(RuntimeError, IRuntimeError) +classImplements(RuntimeWarning, IRuntimeWarning) +classImplements(StandardError, IStandardError) +classImplements(StopIteration, IStopIteration) +classImplements(SyntaxError, ISyntaxError) +classImplements(SyntaxWarning, ISyntaxWarning) +classImplements(SystemError, ISystemError) +classImplements(SystemExit, ISystemExit) +classImplements(TabError, ITabError) +classImplements(TypeError, ITypeError) +classImplements(UnboundLocalError, IUnboundLocalError) +classImplements(UnicodeError, IUnicodeError) +classImplements(UserWarning, IUserWarning) +classImplements(ValueError, IValueError) +classImplements(Warning, IWarning) +classImplements(ZeroDivisionError, IZeroDivisionError) + diff -Nru zope3-3.4.0/src/zope/interface/common/mapping.py zope3-3.5~bzr18/src/zope/interface/common/mapping.py --- zope3-3.4.0/src/zope/interface/common/mapping.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/mapping.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,127 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Mapping Interfaces + +$Id: mapping.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +from zope.interface import Interface + +class IItemMapping(Interface): + """Simplest readable mapping object + """ + + def __getitem__(key): + """Get a value for a key + + A KeyError is raised if there is no value for the key. + """ + + +class IReadMapping(IItemMapping): + """Basic mapping interface + """ + + def get(key, default=None): + """Get a value for a key + + The default is returned if there is no value for the key. + """ + + def __contains__(key): + """Tell if a key exists in the mapping.""" + + +class IWriteMapping(Interface): + """Mapping methods for changing data""" + + def __delitem__(key): + """Delete a value from the mapping using the key.""" + + def __setitem__(key, value): + """Set a new item in the mapping.""" + + +class IEnumerableMapping(IReadMapping): + """Mapping objects whose items can be enumerated. + """ + + def keys(): + """Return the keys of the mapping object. + """ + + def __iter__(): + """Return an iterator for the keys of the mapping object. + """ + + def values(): + """Return the values of the mapping object. + """ + + def items(): + """Return the items of the mapping object. + """ + + def __len__(): + """Return the number of items. + """ + +class IMapping(IWriteMapping, IEnumerableMapping): + ''' Simple mapping interface ''' + +class IIterableMapping(IEnumerableMapping): + + def iterkeys(): + "iterate over keys; equivalent to __iter__" + + def itervalues(): + "iterate over values" + + def iteritems(): + "iterate over items" + +class IClonableMapping(Interface): + + def copy(): + "return copy of dict" + +class IExtendedReadMapping(IIterableMapping): + + def has_key(key): + """Tell if a key exists in the mapping; equivalent to __contains__""" + +class IExtendedWriteMapping(IWriteMapping): + + def clear(): + "delete all items" + + def update(d): + " Update D from E: for k in E.keys(): D[k] = E[k]" + + def setdefault(key, default=None): + "D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D" + + def pop(k, *args): + """remove specified key and return the corresponding value + *args may contain a single default value, or may not be supplied. + If key is not found, default is returned if given, otherwise + KeyError is raised""" + + def popitem(): + """remove and return some (key, value) pair as a + 2-tuple; but raise KeyError if mapping is empty""" + +class IFullMapping( + IExtendedReadMapping, IExtendedWriteMapping, IClonableMapping, IMapping): + ''' Full mapping interface ''' # IMapping included so tests for IMapping + # succeed with IFullMapping diff -Nru zope3-3.4.0/src/zope/interface/common/sequence.py zope3-3.5~bzr18/src/zope/interface/common/sequence.py --- zope3-3.4.0/src/zope/interface/common/sequence.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/sequence.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,162 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Sequence Interfaces + +$Id: sequence.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +__docformat__ = 'restructuredtext' +from zope import interface + +class IMinimalSequence(interface.Interface): + """Most basic sequence interface. + + All sequences are iterable. This requires at least one of the + following: + + - a `__getitem__()` method that takes a single argument; interger + values starting at 0 must be supported, and `IndexError` should + be raised for the first index for which there is no value, or + + - an `__iter__()` method that returns an iterator as defined in + the Python documentation (http://docs.python.org/lib/typeiter.html). + + """ + + def __getitem__(index): + """`x.__getitem__(index)` <==> `x[index]` + + Declaring this interface does not specify whether `__getitem__` + supports slice objects.""" + +class IFiniteSequence(IMinimalSequence): + + def __len__(): + """`x.__len__()` <==> `len(x)`""" + +class IReadSequence(IFiniteSequence): + """read interface shared by tuple and list""" + + def __contains__(item): + """`x.__contains__(item)` <==> `item in x`""" + + def __lt__(other): + """`x.__lt__(other)` <==> `x < other`""" + + def __le__(other): + """`x.__le__(other)` <==> `x <= other`""" + + def __eq__(other): + """`x.__eq__(other)` <==> `x == other`""" + + def __ne__(other): + """`x.__ne__(other)` <==> `x != other`""" + + def __gt__(other): + """`x.__gt__(other)` <==> `x > other`""" + + def __ge__(other): + """`x.__ge__(other)` <==> `x >= other`""" + + def __add__(other): + """`x.__add__(other)` <==> `x + other`""" + + def __mul__(n): + """`x.__mul__(n)` <==> `x * n`""" + + def __rmul__(n): + """`x.__rmul__(n)` <==> `n * x`""" + + def __getslice__(i, j): + """`x.__getslice__(i, j)` <==> `x[i:j]` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + +class IExtendedReadSequence(IReadSequence): + """Full read interface for lists""" + + def count(item): + """Return number of occurrences of value""" + + def index(item, *args): + """Return first index of value + + `L.index(value, [start, [stop]])` -> integer""" + +class IUniqueMemberWriteSequence(interface.Interface): + """The write contract for a sequence that may enforce unique members""" + + def __setitem__(index, item): + """`x.__setitem__(index, item)` <==> `x[index] = item` + + Declaring this interface does not specify whether `__setitem__` + supports slice objects. + """ + + def __delitem__(index): + """`x.__delitem__(index)` <==> `del x[index]` + + Declaring this interface does not specify whether `__delitem__` + supports slice objects. + """ + + def __setslice__(i, j, other): + """`x.__setslice__(i, j, other)` <==> `x[i:j]=other` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + + def __delslice__(i, j): + """`x.__delslice__(i, j)` <==> `del x[i:j]` + + Use of negative indices is not supported. + + Deprecated since Python 2.0 but still a part of `UserList`. + """ + def __iadd__(y): + """`x.__iadd__(y)` <==> `x += y`""" + + def append(item): + """Append item to end""" + + def insert(index, item): + """Insert item before index""" + + def pop(index=-1): + """Remove and return item at index (default last)""" + + def remove(item): + """Remove first occurrence of value""" + + def reverse(): + """Reverse *IN PLACE*""" + + def sort(cmpfunc=None): + """Stable sort *IN PLACE*; `cmpfunc(x, y)` -> -1, 0, 1""" + + def extend(iterable): + """Extend list by appending elements from the iterable""" + +class IWriteSequence(IUniqueMemberWriteSequence): + """Full write contract for sequences""" + + def __imul__(n): + """`x.__imul__(n)` <==> `x *= n`""" + +class ISequence(IReadSequence, IWriteSequence): + """Full sequence contract""" diff -Nru zope3-3.4.0/src/zope/interface/common/tests/basemapping.py zope3-3.5~bzr18/src/zope/interface/common/tests/basemapping.py --- zope3-3.4.0/src/zope/interface/common/tests/basemapping.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/tests/basemapping.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,109 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Base Mapping tests + +$Id: basemapping.py 110736 2010-04-11 10:59:30Z regebro $ +""" +from operator import __getitem__ + +def testIReadMapping(self, inst, state, absent): + for key in state: + self.assertEqual(inst[key], state[key]) + self.assertEqual(inst.get(key, None), state[key]) + self.failUnless(key in inst) + + for key in absent: + self.assertEqual(inst.get(key, None), None) + self.assertEqual(inst.get(key), None) + self.assertEqual(inst.get(key, self), self) + self.assertRaises(KeyError, __getitem__, inst, key) + + +def test_keys(self, inst, state): + # Return the keys of the mapping object + inst_keys = list(inst.keys()); inst_keys.sort() + state_keys = list(state.keys()) ; state_keys.sort() + self.assertEqual(inst_keys, state_keys) + +def test_iter(self, inst, state): + # Return the keys of the mapping object + inst_keys = list(inst); inst_keys.sort() + state_keys = list(state.keys()) ; state_keys.sort() + self.assertEqual(inst_keys, state_keys) + +def test_values(self, inst, state): + # Return the values of the mapping object + inst_values = list(inst.values()); inst_values.sort() + state_values = list(state.values()) ; state_values.sort() + self.assertEqual(inst_values, state_values) + +def test_items(self, inst, state): + # Return the items of the mapping object + inst_items = list(inst.items()); inst_items.sort() + state_items = list(state.items()) ; state_items.sort() + self.assertEqual(inst_items, state_items) + +def test___len__(self, inst, state): + # Return the number of items + self.assertEqual(len(inst), len(state)) + +def testIEnumerableMapping(self, inst, state): + test_keys(self, inst, state) + test_items(self, inst, state) + test_values(self, inst, state) + test___len__(self, inst, state) + + +class BaseTestIReadMapping(object): + def testIReadMapping(self): + inst = self._IReadMapping__sample() + state = self._IReadMapping__stateDict() + absent = self._IReadMapping__absentKeys() + testIReadMapping(self, inst, state, absent) + + +class BaseTestIEnumerableMapping(BaseTestIReadMapping): + # Mapping objects whose items can be enumerated + def test_keys(self): + # Return the keys of the mapping object + inst = self._IEnumerableMapping__sample() + state = self._IEnumerableMapping__stateDict() + test_keys(self, inst, state) + + def test_values(self): + # Return the values of the mapping object + inst = self._IEnumerableMapping__sample() + state = self._IEnumerableMapping__stateDict() + test_values(self, inst, state) + + def test_items(self): + # Return the items of the mapping object + inst = self._IEnumerableMapping__sample() + state = self._IEnumerableMapping__stateDict() + test_items(self, inst, state) + + def test___len__(self): + # Return the number of items + inst = self._IEnumerableMapping__sample() + state = self._IEnumerableMapping__stateDict() + test___len__(self, inst, state) + + def _IReadMapping__stateDict(self): + return self._IEnumerableMapping__stateDict() + + def _IReadMapping__sample(self): + return self._IEnumerableMapping__sample() + + def _IReadMapping__absentKeys(self): + return self._IEnumerableMapping__absentKeys() diff -Nru zope3-3.4.0/src/zope/interface/common/tests/__init__.py zope3-3.5~bzr18/src/zope/interface/common/tests/__init__.py --- zope3-3.4.0/src/zope/interface/common/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/interface/common/tests/test_idatetime.py zope3-3.5~bzr18/src/zope/interface/common/tests/test_idatetime.py --- zope3-3.4.0/src/zope/interface/common/tests/test_idatetime.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/tests/test_idatetime.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,49 @@ +############################################################################## +# +# 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 for datetime interfaces + +$Id: test_idatetime.py 110536 2010-04-06 02:59:44Z tseaver $ +""" + +import unittest + +from zope.interface.verify import verifyObject, verifyClass +from zope.interface.common.idatetime import ITimeDelta, ITimeDeltaClass +from zope.interface.common.idatetime import IDate, IDateClass +from zope.interface.common.idatetime import IDateTime, IDateTimeClass +from zope.interface.common.idatetime import ITime, ITimeClass, ITZInfo +from datetime import timedelta, date, datetime, time, tzinfo + +class TestDateTimeInterfaces(unittest.TestCase): + + def test_interfaces(self): + verifyObject(ITimeDelta, timedelta(minutes=20)) + verifyObject(IDate, date(2000, 1, 2)) + verifyObject(IDateTime, datetime(2000, 1, 2, 10, 20)) + verifyObject(ITime, time(20, 30, 15, 1234)) + verifyObject(ITZInfo, tzinfo()) + verifyClass(ITimeDeltaClass, timedelta) + verifyClass(IDateClass, date) + verifyClass(IDateTimeClass, datetime) + verifyClass(ITimeClass, time) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestDateTimeInterfaces)) + return suite + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/interface/common/tests/test_import_interfaces.py zope3-3.5~bzr18/src/zope/interface/common/tests/test_import_interfaces.py --- zope3-3.4.0/src/zope/interface/common/tests/test_import_interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/common/tests/test_import_interfaces.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +############################################################################## +# +# Copyright (c) 2006 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 doctest +import unittest + +def test_interface_import(): + """ + >>> import zope.interface.common.interfaces + """ + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite(), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') + diff -Nru zope3-3.4.0/src/zope/interface/declarations.py zope3-3.5~bzr18/src/zope/interface/declarations.py --- zope3-3.4.0/src/zope/interface/declarations.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/declarations.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1397 @@ +############################################################################## +# 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. +############################################################################## +"""Implementation of interface declarations + +There are three flavors of declarations: + + - Declarations are used to simply name declared interfaces. + + - ImplementsDeclarations are used to express the interfaces that a + class implements (that instances of the class provides). + + Implements specifications support inheriting interfaces. + + - ProvidesDeclarations are used to express interfaces directly + provided by objects. + + +$Id: declarations.py 110736 2010-04-11 10:59:30Z regebro $ +""" +__docformat__ = 'restructuredtext' + +import sys +import weakref +from zope.interface.interface import InterfaceClass, Specification +from zope.interface.interface import SpecificationBase +from types import ModuleType, MethodType, FunctionType +from zope.interface.advice import addClassAdvisor + +# Registry of class-implementation specifications +BuiltinImplementationSpecifications = {} + +class Declaration(Specification): + """Interface declarations""" + + def __init__(self, *interfaces): + Specification.__init__(self, _normalizeargs(interfaces)) + + def changed(self, originally_changed): + Specification.changed(self, originally_changed) + try: + del self._v_attrs + except AttributeError: + pass + + def __contains__(self, interface): + """Test whether an interface is in the specification + + for example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration(I2, I3) + >>> spec = Declaration(I4, spec) + >>> int(I1 in spec) + 0 + >>> int(I2 in spec) + 1 + >>> int(I3 in spec) + 1 + >>> int(I4 in spec) + 1 + """ + return self.extends(interface) and interface in self.interfaces() + + def __iter__(self): + """Return an iterator for the interfaces in the specification + + for example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration(I2, I3) + >>> spec = Declaration(I4, spec) + >>> i = iter(spec) + >>> [x.getName() for x in i] + ['I4', 'I2', 'I3'] + >>> list(i) + [] + """ + return self.interfaces() + + def flattened(self): + """Return an iterator of all included and extended interfaces + + for example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration(I2, I3) + >>> spec = Declaration(I4, spec) + >>> i = spec.flattened() + >>> [x.getName() for x in i] + ['I4', 'I2', 'I1', 'I3', 'Interface'] + >>> list(i) + [] + + """ + return iter(self.__iro__) + + def __sub__(self, other): + """Remove interfaces from a specification + + Examples: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration() + >>> [iface.getName() for iface in spec] + [] + >>> spec -= I1 + >>> [iface.getName() for iface in spec] + [] + >>> spec -= Declaration(I1, I2) + >>> [iface.getName() for iface in spec] + [] + >>> spec = Declaration(I2, I4) + >>> [iface.getName() for iface in spec] + ['I2', 'I4'] + >>> [iface.getName() for iface in spec - I4] + ['I2'] + >>> [iface.getName() for iface in spec - I1] + ['I4'] + >>> [iface.getName() for iface + ... in spec - Declaration(I3, I4)] + ['I2'] + + """ + + return Declaration( + *[i for i in self.interfaces() + if not [j for j in other.interfaces() + if i.extends(j, 0)] + ] + ) + + def __add__(self, other): + """Add two specifications or a specification and an interface + + Examples: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration() + >>> [iface.getName() for iface in spec] + [] + >>> [iface.getName() for iface in spec+I1] + ['I1'] + >>> [iface.getName() for iface in I1+spec] + ['I1'] + >>> spec2 = spec + >>> spec += I1 + >>> [iface.getName() for iface in spec] + ['I1'] + >>> [iface.getName() for iface in spec2] + [] + >>> spec2 += Declaration(I3, I4) + >>> [iface.getName() for iface in spec2] + ['I3', 'I4'] + >>> [iface.getName() for iface in spec+spec2] + ['I1', 'I3', 'I4'] + >>> [iface.getName() for iface in spec2+spec] + ['I3', 'I4', 'I1'] + + """ + + seen = {} + result = [] + for i in self.interfaces(): + if i not in seen: + seen[i] = 1 + result.append(i) + for i in other.interfaces(): + if i not in seen: + seen[i] = 1 + result.append(i) + + return Declaration(*result) + + __radd__ = __add__ + + +############################################################################## +# +# Implementation specifications +# +# These specify interfaces implemented by instances of classes + +class Implements(Declaration): + + # class whose specification should be used as additional base + inherit = None + + # interfaces actually declared for a class + declared = () + + __name__ = '?' + + def __repr__(self): + return '' % (self.__name__) + + def __reduce__(self): + return implementedBy, (self.inherit, ) + +def implementedByFallback(cls): + """Return the interfaces implemented for a class' instances + + The value returned is an IDeclaration. + + for example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> class C1(object): + ... implements(I2) + >>> class C2(C1): + ... implements(I3) + >>> [i.getName() for i in implementedBy(C2)] + ['I3', 'I2'] + + Really, any object should be able to receive a successful answer, even + an instance: + + >>> class Callable(object): + ... def __call__(self): + ... return self + + >>> implementedBy(Callable()) + + + Note that the name of the spec ends with a '?', because the `Callable` + instance does not have a `__name__` attribute. + """ + # This also manages storage of implementation specifications + + try: + spec = cls.__dict__.get('__implemented__') + except AttributeError: + + # we can't get the class dict. This is probably due to a + # security proxy. If this is the case, then probably no + # descriptor was installed for the class. + + # We don't want to depend directly on zope.security in + # zope.interface, but we'll try to make reasonable + # accommodations in an indirect way. + + # We'll check to see if there's an implements: + + spec = getattr(cls, '__implemented__', None) + if spec is None: + # There's no spec stred in the class. Maybe its a builtin: + spec = BuiltinImplementationSpecifications.get(cls) + if spec is not None: + return spec + return _empty + + if spec.__class__ == Implements: + # we defaulted to _empty or there was a spec. Good enough. + # Return it. + return spec + + # TODO: need old style __implements__ compatibility? + # Hm, there's an __implemented__, but it's not a spec. Must be + # an old-style declaration. Just compute a spec for it + return Declaration(*_normalizeargs((spec, ))) + + if isinstance(spec, Implements): + return spec + + if spec is None: + spec = BuiltinImplementationSpecifications.get(cls) + if spec is not None: + return spec + + # TODO: need old style __implements__ compatibility? + if spec is not None: + # old-style __implemented__ = foo declaration + spec = (spec, ) # tuplefy, as it might be just an int + spec = Implements(*_normalizeargs(spec)) + spec.inherit = None # old-style implies no inherit + del cls.__implemented__ # get rid of the old-style declaration + else: + try: + bases = cls.__bases__ + except AttributeError: + if not callable(cls): + raise TypeError("ImplementedBy called for non-factory", cls) + bases = () + + spec = Implements(*[implementedBy(c) for c in bases]) + spec.inherit = cls + + spec.__name__ = (getattr(cls, '__module__', '?') or '?') + \ + '.' + (getattr(cls, '__name__', '?') or '?') + + try: + cls.__implemented__ = spec + if not hasattr(cls, '__providedBy__'): + cls.__providedBy__ = objectSpecificationDescriptor + + if (isinstance(cls, DescriptorAwareMetaClasses) + and + '__provides__' not in cls.__dict__): + # Make sure we get a __provides__ descriptor + cls.__provides__ = ClassProvides( + cls, + getattr(cls, '__class__', type(cls)), + ) + + except TypeError: + if not isinstance(cls, type): + raise TypeError("ImplementedBy called for non-type", cls) + BuiltinImplementationSpecifications[cls] = spec + + return spec + +implementedBy = implementedByFallback + +def classImplementsOnly(cls, *interfaces): + """Declare the only interfaces implemented by instances of a class + + The arguments after the class are one or more interfaces or interface + specifications (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) + replace any previous declarations. + + Consider the following example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(Interface): pass + ... + >>> class A(object): + ... implements(I3) + >>> class B(object): + ... implements(I4) + >>> class C(A, B): + ... pass + >>> classImplementsOnly(C, I1, I2) + >>> [i.getName() for i in implementedBy(C)] + ['I1', 'I2'] + + Instances of ``C`` provide only ``I1``, ``I2``, and regardless of + whatever interfaces instances of ``A`` and ``B`` implement. + """ + spec = implementedBy(cls) + spec.declared = () + spec.inherit = None + classImplements(cls, *interfaces) + +def classImplements(cls, *interfaces): + """Declare additional interfaces implemented for instances of a class + + The arguments after the class are one or more interfaces or + interface specifications (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) + are added to any interfaces previously declared. + + Consider the following example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(Interface): pass + ... + >>> class I5(Interface): pass + ... + >>> class A(object): + ... implements(I3) + >>> class B(object): + ... implements(I4) + >>> class C(A, B): + ... pass + >>> classImplements(C, I1, I2) + >>> [i.getName() for i in implementedBy(C)] + ['I1', 'I2', 'I3', 'I4'] + >>> classImplements(C, I5) + >>> [i.getName() for i in implementedBy(C)] + ['I1', 'I2', 'I5', 'I3', 'I4'] + + Instances of ``C`` provide ``I1``, ``I2``, ``I5``, and whatever + interfaces instances of ``A`` and ``B`` provide. + """ + + spec = implementedBy(cls) + spec.declared += tuple(_normalizeargs(interfaces)) + + # compute the bases + bases = [] + seen = {} + for b in spec.declared: + if b not in seen: + seen[b] = 1 + bases.append(b) + + if spec.inherit is not None: + + for c in spec.inherit.__bases__: + b = implementedBy(c) + if b not in seen: + seen[b] = 1 + bases.append(b) + + spec.__bases__ = tuple(bases) + +def _implements_advice(cls): + interfaces, classImplements = cls.__dict__['__implements_advice_data__'] + del cls.__implements_advice_data__ + classImplements(cls, *interfaces) + return cls + + +class implementer: + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + if isinstance(ob, DescriptorAwareMetaClasses): + classImplements(ob, *self.interfaces) + return ob + + spec = Implements(*self.interfaces) + try: + ob.__implemented__ = spec + except AttributeError: + raise TypeError("Can't declare implements", ob) + return ob + +class implementer_only: + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + if isinstance(ob, (FunctionType, MethodType)): + # XXX Does this decorator make sense for anything but classes? + # I don't think so. There can be no inheritance of interfaces + # on a method pr function.... + raise ValueError('The implementor_only decorator is not ' + 'supported for methods or functions.') + else: + # Assume it's a class: + classImplementsOnly(ob, *self.interfaces) + return ob + +def _implements(name, interfaces, classImplements): + frame = sys._getframe(2) + locals = frame.f_locals + + # Try to make sure we were called from a class def. In 2.2.0 we can't + # check for __module__ since it doesn't seem to be added to the locals + # until later on. + if (locals is frame.f_globals) or ( + ('__module__' not in locals) and sys.version_info[:3] > (2, 2, 0)): + raise TypeError(name+" can be used only from a class definition.") + + if '__implements_advice_data__' in locals: + raise TypeError(name+" can be used only once in a class definition.") + + locals['__implements_advice_data__'] = interfaces, classImplements + addClassAdvisor(_implements_advice, depth=3) + +def implements(*interfaces): + """Declare interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously + declared. + + Previous declarations include declarations for base classes + unless implementsOnly was used. + + This function is provided for convenience. It provides a more + convenient way to call classImplements. For example:: + + implements(I1) + + is equivalent to calling:: + + classImplements(C, I1) + + after the class has been created. + + Consider the following example:: + + + >>> from zope.interface import Interface + >>> class IA1(Interface): pass + ... + >>> class IA2(Interface): pass + ... + >>> class IB(Interface): pass + ... + >>> class IC(Interface): pass + ... + >>> class A(object): + ... implements(IA1, IA2) + >>> class B(object): + ... implements(IB) + + >>> class C(A, B): + ... implements(IC) + + >>> ob = C() + >>> int(IA1 in providedBy(ob)) + 1 + >>> int(IA2 in providedBy(ob)) + 1 + >>> int(IB in providedBy(ob)) + 1 + >>> int(IC in providedBy(ob)) + 1 + + Instances of ``C`` implement ``I1``, ``I2``, and whatever interfaces + instances of ``A`` and ``B`` implement. + + """ + _implements("implements", interfaces, classImplements) + +def implementsOnly(*interfaces): + """Declare the only interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + Previous declarations including declarations for base classes + are overridden. + + This function is provided for convenience. It provides a more + convenient way to call classImplementsOnly. For example:: + + implementsOnly(I1) + + is equivalent to calling:: + + classImplementsOnly(I1) + + after the class has been created. + + Consider the following example:: + + >>> from zope.interface import Interface + >>> class IA1(Interface): pass + ... + >>> class IA2(Interface): pass + ... + >>> class IB(Interface): pass + ... + >>> class IC(Interface): pass + ... + >>> class A(object): + ... implements(IA1, IA2) + >>> class B(object): + ... implements(IB) + + >>> class C(A, B): + ... implementsOnly(IC) + + >>> ob = C() + >>> int(IA1 in providedBy(ob)) + 0 + >>> int(IA2 in providedBy(ob)) + 0 + >>> int(IB in providedBy(ob)) + 0 + >>> int(IC in providedBy(ob)) + 1 + + + Instances of ``C`` implement ``IC``, regardless of what + instances of ``A`` and ``B`` implement. + + """ + _implements("implementsOnly", interfaces, classImplementsOnly) + +############################################################################## +# +# Instance declarations + +class Provides(Declaration): # Really named ProvidesClass + """Implement __provides__, the instance-specific specification + + When an object is pickled, we pickle the interfaces that it implements. + """ + + def __init__(self, cls, *interfaces): + self.__args = (cls, ) + interfaces + self._cls = cls + Declaration.__init__(self, *(interfaces + (implementedBy(cls), ))) + + def __reduce__(self): + return Provides, self.__args + + __module__ = 'zope.interface' + + def __get__(self, inst, cls): + """Make sure that a class __provides__ doesn't leak to an instance + + For example: + + >>> from zope.interface import Interface + >>> class IFooFactory(Interface): pass + ... + + >>> class C(object): + ... pass + + >>> C.__provides__ = ProvidesClass(C, IFooFactory) + >>> [i.getName() for i in C.__provides__] + ['IFooFactory'] + >>> getattr(C(), '__provides__', 0) + 0 + + """ + if inst is None and cls is self._cls: + # We were accessed through a class, so we are the class' + # provides spec. Just return this object, but only if we are + # being called on the same class that we were defined for: + return self + + raise AttributeError('__provides__') + +ProvidesClass = Provides + +# Registry of instance declarations +# This is a memory optimization to allow objects to share specifications. +InstanceDeclarations = weakref.WeakValueDictionary() + +def Provides(*interfaces): + """Cache instance declarations + + Instance declarations are shared among instances that have the same + declaration. The declarations are cached in a weak value dictionary. + + (Note that, in the examples below, we are going to make assertions about + the size of the weakvalue dictionary. For the assertions to be + meaningful, we need to force garbage collection to make sure garbage + objects are, indeed, removed from the system. Depending on how Python + is run, we may need to make multiple calls to be sure. We provide a + collect function to help with this: + + >>> import gc + >>> def collect(): + ... for i in range(4): + ... gc.collect() + + ) + + >>> collect() + >>> before = len(InstanceDeclarations) + + >>> class C(object): + ... pass + + >>> from zope.interface import Interface + >>> class I(Interface): + ... pass + + >>> c1 = C() + >>> c2 = C() + + >>> len(InstanceDeclarations) == before + 1 + + >>> directlyProvides(c1, I) + >>> len(InstanceDeclarations) == before + 1 + 1 + + >>> directlyProvides(c2, I) + >>> len(InstanceDeclarations) == before + 1 + 1 + + >>> del c1 + >>> collect() + >>> len(InstanceDeclarations) == before + 1 + 1 + + >>> del c2 + >>> collect() + >>> len(InstanceDeclarations) == before + 1 + """ + + spec = InstanceDeclarations.get(interfaces) + if spec is None: + spec = ProvidesClass(*interfaces) + InstanceDeclarations[interfaces] = spec + + return spec +Provides.__safe_for_unpickling__ = True + +try: + from types import ClassType + DescriptorAwareMetaClasses = ClassType, type +except ImportError: # Python 3 + DescriptorAwareMetaClasses = (type,) + +def directlyProvides(object, *interfaces): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or interface + specifications (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) + replace interfaces previously declared for the object. + + Consider the following example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class IA1(Interface): pass + ... + >>> class IA2(Interface): pass + ... + >>> class IB(Interface): pass + ... + >>> class IC(Interface): pass + ... + >>> class A(object): + ... implements(IA1, IA2) + >>> class B(object): + ... implements(IB) + + >>> class C(A, B): + ... implements(IC) + + >>> ob = C() + >>> directlyProvides(ob, I1, I2) + >>> int(I1 in providedBy(ob)) + 1 + >>> int(I2 in providedBy(ob)) + 1 + >>> int(IA1 in providedBy(ob)) + 1 + >>> int(IA2 in providedBy(ob)) + 1 + >>> int(IB in providedBy(ob)) + 1 + >>> int(IC in providedBy(ob)) + 1 + + The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces + instances have been declared for instances of ``C``. + + To remove directly provided interfaces, use ``directlyProvidedBy`` and + subtract the unwanted interfaces. For example: + + >>> directlyProvides(ob, directlyProvidedBy(ob)-I2) + >>> int(I1 in providedBy(ob)) + 1 + >>> int(I2 in providedBy(ob)) + 0 + + removes I2 from the interfaces directly provided by ``ob``. The object, + ``ob`` no longer directly provides ``I2``, although it might still + provide ``I2`` if it's class implements ``I2``. + + To add directly provided interfaces, use ``directlyProvidedBy`` and + include additional interfaces. For example: + + >>> int(I2 in providedBy(ob)) + 0 + >>> directlyProvides(ob, directlyProvidedBy(ob), I2) + + adds ``I2`` to the interfaces directly provided by ob:: + + >>> int(I2 in providedBy(ob)) + 1 + + """ + + # We need to avoid setting this attribute on meta classes that + # don't support descriptors. + # We can do away with this check when we get rid of the old EC + cls = getattr(object, '__class__', None) + if cls is not None and getattr(cls, '__class__', None) is cls: + # It's a meta class (well, at least it it could be an extension class) + if not isinstance(object, DescriptorAwareMetaClasses): + raise TypeError("Attempt to make an interface declaration on a " + "non-descriptor-aware class") + + interfaces = _normalizeargs(interfaces) + if cls is None: + cls = type(object) + + issub = False + for damc in DescriptorAwareMetaClasses: + if issubclass(cls, damc): + issub = True + break + if issub: + # we have a class or type. We'll use a special descriptor + # that provides some extra caching + object.__provides__ = ClassProvides(object, cls, *interfaces) + else: + object.__provides__ = Provides(cls, *interfaces) + + +def alsoProvides(object, *interfaces): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or interface + specifications (``IDeclaration`` objects). + + The interfaces given (including the interfaces in the specifications) are + added to the interfaces previously declared for the object. + + Consider the following example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class IA1(Interface): pass + ... + >>> class IA2(Interface): pass + ... + >>> class IB(Interface): pass + ... + >>> class IC(Interface): pass + ... + >>> class A(object): + ... implements(IA1, IA2) + >>> class B(object): + ... implements(IB) + + >>> class C(A, B): + ... implements(IC) + + >>> ob = C() + >>> directlyProvides(ob, I1) + >>> int(I1 in providedBy(ob)) + 1 + >>> int(I2 in providedBy(ob)) + 0 + >>> int(IA1 in providedBy(ob)) + 1 + >>> int(IA2 in providedBy(ob)) + 1 + >>> int(IB in providedBy(ob)) + 1 + >>> int(IC in providedBy(ob)) + 1 + + >>> alsoProvides(ob, I2) + >>> int(I1 in providedBy(ob)) + 1 + >>> int(I2 in providedBy(ob)) + 1 + >>> int(IA1 in providedBy(ob)) + 1 + >>> int(IA2 in providedBy(ob)) + 1 + >>> int(IB in providedBy(ob)) + 1 + >>> int(IC in providedBy(ob)) + 1 + + The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces + instances have been declared for instances of ``C``. Notice that the + alsoProvides just extends the provided interfaces. + """ + directlyProvides(object, directlyProvidedBy(object), *interfaces) + +def noLongerProvides(object, interface): + """ + This removes a directly provided interface from an object. + Consider the following two interfaces: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + + ``I1`` is provided through the class, ``I2`` is directly provided + by the object: + + >>> class C(object): + ... implements(I1) + >>> c = C() + >>> alsoProvides(c, I2) + >>> I2.providedBy(c) + True + + Remove I2 from c again: + + >>> noLongerProvides(c, I2) + >>> I2.providedBy(c) + False + + Removing an interface that is provided through the class is not possible: + + >>> noLongerProvides(c, I1) + Traceback (most recent call last): + ... + ValueError: Can only remove directly provided interfaces. + + """ + directlyProvides(object, directlyProvidedBy(object)-interface) + if interface.providedBy(object): + raise ValueError("Can only remove directly provided interfaces.") + +class ClassProvidesBasePy(object): + + def __get__(self, inst, cls): + if cls is self._cls: + # We only work if called on the class we were defined for + + if inst is None: + # We were accessed through a class, so we are the class' + # provides spec. Just return this object as is: + return self + + return self._implements + + raise AttributeError('__provides__') + +ClassProvidesBase = ClassProvidesBasePy + +# Try to get C base: +try: + import _zope_interface_coptimizations +except ImportError: + pass +else: + from _zope_interface_coptimizations import ClassProvidesBase + + +class ClassProvides(Declaration, ClassProvidesBase): + """Special descriptor for class __provides__ + + The descriptor caches the implementedBy info, so that + we can get declarations for objects without instance-specific + interfaces a bit quicker. + + For example: + + >>> from zope.interface import Interface + >>> class IFooFactory(Interface): + ... pass + >>> class IFoo(Interface): + ... pass + >>> class C(object): + ... implements(IFoo) + ... classProvides(IFooFactory) + >>> [i.getName() for i in C.__provides__] + ['IFooFactory'] + + >>> [i.getName() for i in C().__provides__] + ['IFoo'] + """ + + def __init__(self, cls, metacls, *interfaces): + self._cls = cls + self._implements = implementedBy(cls) + self.__args = (cls, metacls, ) + interfaces + Declaration.__init__(self, *(interfaces + (implementedBy(metacls), ))) + + def __reduce__(self): + return self.__class__, self.__args + + # Copy base-class method for speed + __get__ = ClassProvidesBase.__get__ + +def directlyProvidedBy(object): + """Return the interfaces directly provided by the given object + + The value returned is an ``IDeclaration``. + """ + provides = getattr(object, "__provides__", None) + if (provides is None # no spec + or + # We might have gotten the implements spec, as an + # optimization. If so, it's like having only one base, that we + # lop off to exclude class-supplied declarations: + isinstance(provides, Implements) + ): + return _empty + + # Strip off the class part of the spec: + return Declaration(provides.__bases__[:-1]) + +def classProvides(*interfaces): + """Declare interfaces provided directly by a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface specifications + (``IDeclaration`` objects). + + The given interfaces (including the interfaces in the specifications) + are used to create the class's direct-object interface specification. + An error will be raised if the module class has an direct interface + specification. In other words, it is an error to call this function more + than once in a class definition. + + Note that the given interfaces have nothing to do with the interfaces + implemented by instances of the class. + + This function is provided for convenience. It provides a more convenient + way to call directlyProvides for a class. For example:: + + classProvides(I1) + + is equivalent to calling:: + + directlyProvides(theclass, I1) + + after the class has been created. + + For example: + + >>> from zope.interface import Interface + >>> class IFoo(Interface): pass + ... + >>> class IFooFactory(Interface): pass + ... + >>> class C(object): + ... implements(IFoo) + ... classProvides(IFooFactory) + >>> [i.getName() for i in C.__providedBy__] + ['IFooFactory'] + >>> [i.getName() for i in C().__providedBy__] + ['IFoo'] + + if equivalent to: + + >>> from zope.interface import Interface + >>> class IFoo(Interface): pass + ... + >>> class IFooFactory(Interface): pass + ... + >>> class C(object): + ... implements(IFoo) + >>> directlyProvides(C, IFooFactory) + >>> [i.getName() for i in C.__providedBy__] + ['IFooFactory'] + >>> [i.getName() for i in C().__providedBy__] + ['IFoo'] + + """ + frame = sys._getframe(1) + locals = frame.f_locals + + # Try to make sure we were called from a class def + if (locals is frame.f_globals) or ('__module__' not in locals): + raise TypeError("classProvides can be used only from a class definition.") + + if '__provides__' in locals: + raise TypeError( + "classProvides can only be used once in a class definition.") + + locals["__provides__"] = _normalizeargs(interfaces) + + addClassAdvisor(_classProvides_advice, depth=2) + +def _classProvides_advice(cls): + interfaces = cls.__dict__['__provides__'] + del cls.__provides__ + directlyProvides(cls, *interfaces) + return cls + +class provider: + """Class decorator version of classProvides""" + + def __init__(self, *interfaces): + self.interfaces = interfaces + + def __call__(self, ob): + directlyProvides(ob, *self.interfaces) + return ob + +def moduleProvides(*interfaces): + """Declare interfaces provided by a module + + This function is used in a module definition. + + The arguments are one or more interfaces or interface specifications + (``IDeclaration`` objects). + + The given interfaces (including the interfaces in the specifications) are + used to create the module's direct-object interface specification. An + error will be raised if the module already has an interface specification. + In other words, it is an error to call this function more than once in a + module definition. + + This function is provided for convenience. It provides a more convenient + way to call directlyProvides. For example:: + + moduleImplements(I1) + + is equivalent to:: + + directlyProvides(sys.modules[__name__], I1) + """ + frame = sys._getframe(1) + locals = frame.f_locals + + # Try to make sure we were called from a class def + if (locals is not frame.f_globals) or ('__name__' not in locals): + raise TypeError( + "moduleProvides can only be used from a module definition.") + + if '__provides__' in locals: + raise TypeError( + "moduleProvides can only be used once in a module definition.") + + locals["__provides__"] = Provides(ModuleType, + *_normalizeargs(interfaces)) + +############################################################################## +# +# Declaration querying support + +def ObjectSpecification(direct, cls): + """Provide object specifications + + These combine information for the object and for it's classes. + + For example: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(Interface): pass + ... + >>> class I3(Interface): pass + ... + >>> class I31(I3): pass + ... + >>> class I4(Interface): pass + ... + >>> class I5(Interface): pass + ... + >>> class A(object): + ... implements(I1) + >>> class B(object): __implemented__ = I2 + ... + >>> class C(A, B): + ... implements(I31) + >>> c = C() + >>> directlyProvides(c, I4) + >>> [i.getName() for i in providedBy(c)] + ['I4', 'I31', 'I1', 'I2'] + >>> [i.getName() for i in providedBy(c).flattened()] + ['I4', 'I31', 'I3', 'I1', 'I2', 'Interface'] + >>> int(I1 in providedBy(c)) + 1 + >>> int(I3 in providedBy(c)) + 0 + >>> int(providedBy(c).extends(I3)) + 1 + >>> int(providedBy(c).extends(I31)) + 1 + >>> int(providedBy(c).extends(I5)) + 0 + >>> class COnly(A, B): + ... implementsOnly(I31) + >>> class D(COnly): + ... implements(I5) + >>> c = D() + >>> directlyProvides(c, I4) + >>> [i.getName() for i in providedBy(c)] + ['I4', 'I5', 'I31'] + >>> [i.getName() for i in providedBy(c).flattened()] + ['I4', 'I5', 'I31', 'I3', 'Interface'] + >>> int(I1 in providedBy(c)) + 0 + >>> int(I3 in providedBy(c)) + 0 + >>> int(providedBy(c).extends(I3)) + 1 + >>> int(providedBy(c).extends(I1)) + 0 + >>> int(providedBy(c).extends(I31)) + 1 + >>> int(providedBy(c).extends(I5)) + 1 + """ + + return Provides(cls, direct) + +def getObjectSpecification(ob): + + provides = getattr(ob, '__provides__', None) + if provides is not None: + if isinstance(provides, SpecificationBase): + return provides + + try: + cls = ob.__class__ + except AttributeError: + # We can't get the class, so just consider provides + return _empty + + return implementedBy(cls) + +def providedBy(ob): + + # Here we have either a special object, an old-style declaration + # or a descriptor + + # Try to get __providedBy__ + try: + r = ob.__providedBy__ + except AttributeError: + # Not set yet. Fall back to lower-level thing that computes it + return getObjectSpecification(ob) + + try: + # We might have gotten a descriptor from an instance of a + # class (like an ExtensionClass) that doesn't support + # descriptors. We'll make sure we got one by trying to get + # the only attribute, which all specs have. + r.extends + + except AttributeError: + + # The object's class doesn't understand descriptors. + # Sigh. We need to get an object descriptor, but we have to be + # careful. We want to use the instance's __provides__, if + # there is one, but only if it didn't come from the class. + + try: + r = ob.__provides__ + except AttributeError: + # No __provides__, so just fall back to implementedBy + return implementedBy(ob.__class__) + + # We need to make sure we got the __provides__ from the + # instance. We'll do this by making sure we don't get the same + # thing from the class: + + try: + cp = ob.__class__.__provides__ + except AttributeError: + # The ob doesn't have a class or the class has no + # provides, assume we're done: + return r + + if r is cp: + # Oops, we got the provides from the class. This means + # the object doesn't have it's own. We should use implementedBy + return implementedBy(ob.__class__) + + return r + +class ObjectSpecificationDescriptorPy(object): + """Implement the `__providedBy__` attribute + + The `__providedBy__` attribute computes the interfaces peovided by + an object. + """ + + def __get__(self, inst, cls): + """Get an object specification for an object + + For example: + + >>> from zope.interface import Interface + >>> class IFoo(Interface): pass + ... + >>> class IFooFactory(Interface): pass + ... + >>> class C(object): + ... implements(IFoo) + ... classProvides(IFooFactory) + >>> [i.getName() for i in C.__providedBy__] + ['IFooFactory'] + >>> [i.getName() for i in C().__providedBy__] + ['IFoo'] + + """ + + # Get an ObjectSpecification bound to either an instance or a class, + # depending on how we were accessed. + + if inst is None: + return getObjectSpecification(cls) + + provides = getattr(inst, '__provides__', None) + if provides is not None: + return provides + + return implementedBy(cls) + +ObjectSpecificationDescriptor = ObjectSpecificationDescriptorPy + +############################################################################## + +def _normalizeargs(sequence, output = None): + """Normalize declaration arguments + + Normalization arguments might contain Declarions, tuples, or single + interfaces. + + Anything but individial interfaces or implements specs will be expanded. + """ + if output is None: + output = [] + + cls = sequence.__class__ + if InterfaceClass in cls.__mro__ or Implements in cls.__mro__: + output.append(sequence) + else: + for v in sequence: + _normalizeargs(v, output) + + return output + +_empty = Declaration() + +try: + import _zope_interface_coptimizations +except ImportError: + pass +else: + from _zope_interface_coptimizations import implementedBy, providedBy + from _zope_interface_coptimizations import getObjectSpecification + from _zope_interface_coptimizations import ObjectSpecificationDescriptor + +objectSpecificationDescriptor = ObjectSpecificationDescriptor() diff -Nru zope3-3.4.0/src/zope/interface/document.py zope3-3.5~bzr18/src/zope/interface/document.py --- zope3-3.4.0/src/zope/interface/document.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/document.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,107 @@ +############################################################################## +# +# 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. +# +############################################################################## +""" Pretty-Print an Interface object as structured text (Yum) + +This module provides a function, asStructuredText, for rendering an +interface as structured text. + +$Id: document.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +import zope.interface + +def asStructuredText(I, munge=0): + """ Output structured text format. Note, this will whack any existing + 'structured' format of the text. """ + + r = [I.getName()] + outp = r.append + level = 1 + + if I.getDoc(): + outp(_justify_and_indent(_trim_doc_string(I.getDoc()), level)) + + bases = [base + for base in I.__bases__ + if base is not zope.interface.Interface + ] + if bases: + outp(_justify_and_indent("This interface extends:", level, munge)) + level += 1 + for b in bases: + item = "o %s" % b.getName() + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + level -= 1 + + namesAndDescriptions = I.namesAndDescriptions() + namesAndDescriptions.sort() + + outp(_justify_and_indent("Attributes:", level, munge)) + level += 1 + for name, desc in namesAndDescriptions: + if not hasattr(desc, 'getSignatureString'): # ugh... + item = "%s -- %s" % (desc.getName(), + desc.getDoc() or 'no documentation') + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + level -= 1 + + outp(_justify_and_indent("Methods:", level, munge)) + level += 1 + for name, desc in namesAndDescriptions: + if hasattr(desc, 'getSignatureString'): # ugh... + item = "%s%s -- %s" % (desc.getName(), + desc.getSignatureString(), + desc.getDoc() or 'no documentation') + outp(_justify_and_indent(_trim_doc_string(item), level, munge)) + + return "\n\n".join(r) + "\n\n" + + +def _trim_doc_string(text): + """ Trims a doc string to make it format + correctly with structured text. """ + + lines = text.replace('\r\n', '\n').split('\n') + nlines = [lines.pop(0)] + if lines: + min_indent = min([len(line) - len(line.lstrip()) + for line in lines]) + for line in lines: + nlines.append(line[min_indent:]) + + return '\n'.join(nlines) + + +def _justify_and_indent(text, level, munge=0, width=72): + """ indent and justify text, rejustify (munge) if specified """ + + indent = " " * level + + if munge: + lines = [] + line = indent + text = text.split() + + for word in text: + line = ' '.join([line, word]) + if len(line) > width: + lines.append(line) + line = indent + else: + lines.append(line) + + return '\n'.join(lines) + + else: + return indent + \ + text.strip().replace("\r\n", "\n") .replace("\n", "\n" + indent) diff -Nru zope3-3.4.0/src/zope/interface/exceptions.py zope3-3.5~bzr18/src/zope/interface/exceptions.py --- zope3-3.4.0/src/zope/interface/exceptions.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/exceptions.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Interface-specific exceptions + +$Id: exceptions.py 110536 2010-04-06 02:59:44Z tseaver $ +""" + +class Invalid(Exception): + """A specification is violated + """ + +class DoesNotImplement(Invalid): + """ This object does not implement """ + def __init__(self, interface): + self.interface = interface + + def __str__(self): + return """An object does not implement interface %(interface)s + + """ % self.__dict__ + +class BrokenImplementation(Invalid): + """An attribute is not completely implemented. + """ + + def __init__(self, interface, name): + self.interface=interface + self.name=name + + def __str__(self): + return """An object has failed to implement interface %(interface)s + + The %(name)s attribute was not provided. + """ % self.__dict__ + +class BrokenMethodImplementation(Invalid): + """An method is not completely implemented. + """ + + def __init__(self, method, mess): + self.method=method + self.mess=mess + + def __str__(self): + return """The implementation of %(method)s violates its contract + because %(mess)s. + """ % self.__dict__ + +class InvalidInterface(Exception): + """The interface has invalid contents + """ + +class BadImplements(TypeError): + """An implementation assertion is invalid + + because it doesn't contain an interface or a sequence of valid + implementation assertions. + """ diff -Nru zope3-3.4.0/src/zope/interface/_flatten.py zope3-3.5~bzr18/src/zope/interface/_flatten.py --- zope3-3.4.0/src/zope/interface/_flatten.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/_flatten.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,37 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Adapter-style interface registry + +See Adapter class. + +$Id: _flatten.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +from zope.interface import Declaration + +def _flatten(implements, include_None=0): + + try: + r = implements.flattened() + except AttributeError: + if implements is None: + r=() + else: + r = Declaration(implements).flattened() + + if not include_None: + return r + + r = list(r) + r.append(None) + return r diff -Nru zope3-3.4.0/src/zope/interface/human.ru.txt zope3-3.5~bzr18/src/zope/interface/human.ru.txt --- zope3-3.4.0/src/zope/interface/human.ru.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/human.ru.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,156 @@ +=============================== +Использование реестра адаптеров +=============================== + +Данный документ содержит небольшую демонстрацию пакета ``zope.interface`` и его +реестра адаптеров. Документ рассчитывался как конкретный, но более узкий пример +того как использовать интерфейсы и адаптеры вне Zope 3. + +Сначала нам необходимо импортировать пакет для работы с интерфейсами:: + + >>> import zope.interface + +Теперь мы разработаем интерфейс для нашего объекта - простого файла. Наш файл +будет содержать всего один атрибут - body, в котором фактически будет сохранено +содержимое файла:: + + >>> class IFile(zope.interface.Interface): + ... + ... body = zope.interface.Attribute(u'Содержимое файла.') + ... + +Для статистики нам часто необходимо знать размер файла. Но было бы несколько +топорно реализовывать определение размера прямо для объекта файла, т.к. размер +больше относится к мета-данным. Таким образом мы создаем еще один интерфейс для +представления размера какого-либо объекта:: + + >>> class ISize(zope.interface.Interface): + ... + ... def getSize(): + ... 'Return the size of an object.' + ... + +Теперь мы должны создать класс реализующий наш файл. Необходимо что бы наш +объект хранил информацию о том, что он реализует интерфейс `IFile`. Мы также +создаем атрибут с содержимым файла по умолчанию (для упрощения нашего +примера):: + + >>> class File(object): + ... + ... zope.interface.implements(IFile) + ... body = 'foo bar' + ... + +Дальше мы создаем адаптер, который будет предоставлять интерфейс `ISize` +получая любой объект предоставляющий интерфейс `IFile`. По соглашению мы +используем атрибут `__used_for__` для указания интерфейса который как мы +ожидаем предоставляет адаптируемый объект, `IFile` в нашем случае. На самом +деле этот атрибут используется только для документирования. В случае если +адаптер используется для нескольких интерфейсов можно указать их все в виде +кортежа. + +Опять же по соглашению конструктор адаптера получает один аргумент - context +(контекст). В нашем случае контекст - это экземпляр `IFile` (объект, +предоставляющий `IFile`) который используется для получения из него размера. +Так же по соглашению контекст сохраняется а адаптере в атрибуте с именем +`context`. Twisted комьюнити ссылается на контекст как на объект `original`. +Таким образом можно также дать аргументу любое подходящее имя, например +`file`:: + + >>> class FileSize(object): + ... + ... zope.interface.implements(ISize) + ... __used_for__ = IFile + ... + ... def __init__(self, context): + ... self.context = context + ... + ... def getSize(self): + ... return len(self.context.body) + ... + +Теперь когда мы написали наш адаптер мы должны зарегистрировать его в реестре +адаптеров, что бы его можно было запросить когда он понадобится. Здесь нет +какого-либо глобального реестра адаптеров, таким образом мы должны +самостоятельно создать для нашего примера реестр:: + + >>> from zope.interface.adapter import AdapterRegistry + >>> registry = AdapterRegistry() + +Реестр содержит отображение того, что адаптер реализует на основе другого +интерфейса который предоставляет объект. Поэтому дальше мы регистрируем адаптер +который адаптирует интерфейс `IFile` к интерфейсу `ISize`. Первый аргумент к +методу `register()` реестра - это список адаптируемых интерфейсов. В нашем +случае мы имеем только один адаптируемый интерфейс - `IFile`. Список +интерфейсов имеет смысл для использования концепции мульти-адаптеров, которые +требуют нескольких оригинальных объектов для адаптации к новому интерфейсу. В +этой ситуации конструктор адаптера будет требовать новый аргумент для каждого +оригинального интерфейса. + +Второй аргумент метода `register()` - это интерфейс который предоставляет +адаптер, в нашем случае `ISize`. Третий аргумент - имя адаптера. Сейчас нам не +важно имя адаптера и мы передаем его как пустую строку. Обычно имена полезны +если используются адаптеры для одинакового набора интерфейсов, но в различных +ситуациях. Последний аргумент - это класс адаптера:: + + >>> registry.register([IFile], ISize, '', FileSize) + +Теперь мы можем использовать реестр для запроса адаптера:: + + >>> registry.lookup1(IFile, ISize, '') + + +Попробуем более практичный пример. Создадим экземпляр `File` и создадим адаптер +использующий запрос реестра. Затем мы увидим возвращает ли адаптер корректный +размер при вызове `getSize()`:: + + >>> file = File() + >>> size = registry.lookup1(IFile, ISize, '')(file) + >>> size.getSize() + 7 + +На самом деле это не очень практично, т.к. нам нужно самим передавать все +аргументы методу запроса. Существует некоторый синтаксический леденец который +позволяет нам получить экземпляр адаптера просто вызвав `ISize(file)`. Что бы +использовать эту функциональность нам понадобится добавить наш реестр к списку +adapter_hooks, который находится в модуле с адаптерами. Этот список хранит +коллекцию вызываемых объектов которые вызываются автоматически когда вызывается +IFoo(obj); их предназначение - найти адаптеры которые реализуют интерфейс для +определенного экземпляра контекста. + +Необходимо реализовать свою собственную функцию для поиска адаптера; данный +пример описывает одну из простейших функций для использования с реестром, но +также можно реализовать поисковые функции которые, например, используют +кэширование, или адаптеры сохраняемые в базе. Функция поиска должна принимать +желаемый на выходе интерфейс (в нашем случае `ISize`) как первый аргумент и +контекст для адаптации (`file`) как второй. Функция должна вернуть адаптер, +т.е. экземпляр `FileSize`:: + + >>> def hook(provided, object): + ... adapter = registry.lookup1(zope.interface.providedBy(object), + ... provided, '') + ... return adapter(object) + ... + +Теперь мы просто добавляем нашу функцию к списку `adapter_hooks`:: + + >>> from zope.interface.interface import adapter_hooks + >>> adapter_hooks.append(hook) + +Как только функция зарегистрирована мы можем использовать желаемый синтаксис:: + + >>> size = ISize(file) + >>> size.getSize() + 7 + +После нам нужно прибраться за собой, что бы другие получили чистый список +`adaper_hooks` после нас:: + + >>> adapter_hooks.remove(hook) + +Это все. Здесь намеренно отложена дискуссия об именованных и мульти-адаптерах, +т.к. данный текст рассчитан как практическое и простое введение в интерфейсы и +адаптеры Zope 3. Для более подробной информации имеет смысл прочитать +`adapter.txt` из пакета `zope.interface`, что бы получить более формальное, +справочное и полное трактование пакета. Внимание: многие жаловались, что +`adapter.txt` приводит их мозг к расплавленному состоянию! diff -Nru zope3-3.4.0/src/zope/interface/human.txt zope3-3.5~bzr18/src/zope/interface/human.txt --- zope3-3.4.0/src/zope/interface/human.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/human.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,152 @@ +========================== +Using the Adapter Registry +========================== + +This is a small demonstration of the ``zope.interface`` package including its +adapter registry. It is intended to provide a concrete but narrow example on +how to use interfaces and adapters outside of Zope 3. + +First we have to import the interface package:: + + >>> import zope.interface + +We now develop an interface for our object, which is a simple file in this +case. For now we simply support one attribute, the body, which contains the +actual file contents:: + + >>> class IFile(zope.interface.Interface): + ... + ... body = zope.interface.Attribute('Contents of the file.') + ... + +For statistical reasons we often want to know the size of a file. However, it +would be clumsy to implement the size directly in the file object, since the +size really represents meta-data. Thus we create another interface that +provides the size of something:: + + >>> class ISize(zope.interface.Interface): + ... + ... def getSize(): + ... 'Return the size of an object.' + ... + +Now we need to implement the file. It is essential that the object states +that it implements the `IFile` interface. We also provide a default body +value (just to make things simpler for this example):: + + >>> class File(object): + ... + ... zope.interface.implements(IFile) + ... body = 'foo bar' + ... + +Next we implement an adapter that can provide the `ISize` interface given any +object providing `IFile`. By convention we use `__used_for__` to specify the +interface that we expect the adapted object to provide, in our case +`IFile`. However, this attribute is not used for anything. If you have +multiple interfaces for which an adapter is used, just specify the interfaces +via a tuple. + +Again by convention, the constructor of an adapter takes one argument, the +context. The context in this case is an instance of `File` (providing `IFile`) +that is used to extract the size from. Also by convention the context is +stored in an attribute named `context` on the adapter. The twisted community +refers to the context as the `original` object. However, you may feel free to +use a specific argument name, such as `file`:: + + >>> class FileSize(object): + ... + ... zope.interface.implements(ISize) + ... __used_for__ = IFile + ... + ... def __init__(self, context): + ... self.context = context + ... + ... def getSize(self): + ... return len(self.context.body) + ... + +Now that we have written our adapter, we have to register it with an adapter +registry, so that it can be looked up when needed. There is no such thing as a +global registry; thus we have to instantiate one for our example manually:: + + >>> from zope.interface.adapter import AdapterRegistry + >>> registry = AdapterRegistry() + + +The registry keeps a map of what adapters implement based on another +interface, the object already provides. Therefore, we next have to register an +adapter that adapts from `IFile` to `ISize`. The first argument to +the registry's `register()` method is a list of original interfaces.In our +cause we have only one original interface, `IFile`. A list makes sense, since +the interface package has the concept of multi-adapters, which are adapters +that require multiple objects to adapt to a new interface. In these +situations, your adapter constructor will require an argument for each +specified interface. + +The second argument is the interface the adapter provides, in our case +`ISize`. The third argument is the name of the adapter. Since we do not care +about names, we simply leave it as an empty string. Names are commonly useful, +if you have adapters for the same set of interfaces, but they are useful in +different situations. The last argument is simply the adapter class:: + + >>> registry.register([IFile], ISize, '', FileSize) + +You can now use the the registry to lookup the adapter:: + + >>> registry.lookup1(IFile, ISize, '') + + +Let's get a little bit more practical. Let's create a `File` instance and +create the adapter using a registry lookup. Then we see whether the adapter +returns the correct size by calling `getSize()`:: + + >>> file = File() + >>> size = registry.lookup1(IFile, ISize, '')(file) + >>> size.getSize() + 7 + +However, this is not very practical, since I have to manually pass in the +arguments to the lookup method. There is some syntactic candy that will allow +us to get an adapter instance by simply calling `ISize(file)`. To make use of +this functionality, we need to add our registry to the adapter_hooks list, +which is a member of the adapters module. This list stores a collection of +callables that are automatically invoked when IFoo(obj) is called; their +purpose is to locate adapters that implement an interface for a certain +context instance. + +You are required to implement your own adapter hook; this example covers one +of the simplest hooks that use the registry, but you could implement one that +used an adapter cache or persistent adapters, for instance. The helper hook is +required to expect as first argument the desired output interface (for us +`ISize`) and as the second argument the context of the adapter (here +`file`). The function returns an adapter, i.e. a `FileSize` instance:: + + >>> def hook(provided, object): + ... adapter = registry.lookup1(zope.interface.providedBy(object), + ... provided, '') + ... return adapter(object) + ... + +We now just add the hook to an `adapter_hooks` list:: + + >>> from zope.interface.interface import adapter_hooks + >>> adapter_hooks.append(hook) + +Once the hook is registered, you can use the desired syntax:: + + >>> size = ISize(file) + >>> size.getSize() + 7 + +Now we have to cleanup after ourselves, so that others after us have a clean +`adapter_hooks` list:: + + >>> adapter_hooks.remove(hook) + +That's it. I have intentionally left out a discussion of named adapters and +multi-adapters, since this text is intended as a practical and simple +introduction to Zope 3 interfaces and adapters. You might want to read the +`adapter.txt` in the `zope.interface` package for a more formal, referencial +and complete treatment of the package. Warning: People have reported that +`adapter.txt` makes their brain feel soft! diff -Nru zope3-3.4.0/src/zope/interface/index.txt zope3-3.5~bzr18/src/zope/interface/index.txt --- zope3-3.4.0/src/zope/interface/index.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/index.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,29 @@ +Welcome to zope.interface's documentation! +========================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + README + adapter + human + verify + +По-русски +========= + +.. toctree:: + :maxdepth: 2 + + README.ru + adapter.ru + human.ru + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff -Nru zope3-3.4.0/src/zope/interface/__init__.py zope3-3.5~bzr18/src/zope/interface/__init__.py --- zope3-3.4.0/src/zope/interface/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,81 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Interfaces + +This package implements the Python "scarecrow" proposal. + +The package exports two objects, `Interface` and `Attribute` directly. It also +exports several helper methods. Interface is used to create an interface with +a class statement, as in: + + class IMyInterface(Interface): + '''Interface documentation + ''' + + def meth(arg1, arg2): + '''Documentation for meth + ''' + + # Note that there is no self argument + +To find out what you can do with interfaces, see the interface +interface, `IInterface` in the `interfaces` module. + +The package has several public modules: + + o `declarations` provides utilities to declare interfaces on objects. It + also provides a wide range of helpful utilities that aid in managing + declared interfaces. Most of its public names are however imported here. + + o `document` has a utility for documenting an interface as structured text. + + o `exceptions` has the interface-defined exceptions + + o `interfaces` contains a list of all public interfaces for this package. + + o `verify` has utilities for verifying implementations of interfaces. + +See the module doc strings for more information. + +$Id: __init__.py 110699 2010-04-09 08:16:17Z regebro $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface.interface import Interface, _wire + +# Need to actually get the interface elements to implement the right interfaces +_wire() +del _wire + +from zope.interface.interface import Attribute, invariant, taggedValue + +from zope.interface.declarations import providedBy, implementedBy +from zope.interface.declarations import classImplements, classImplementsOnly +from zope.interface.declarations import directlyProvidedBy, directlyProvides +from zope.interface.declarations import alsoProvides, provider +from zope.interface.declarations import implementer, implementer_only +from zope.interface.declarations import implements, implementsOnly +from zope.interface.declarations import classProvides, moduleProvides +from zope.interface.declarations import noLongerProvides, Declaration +from zope.interface.exceptions import Invalid + +# The following are to make spec pickles cleaner +from zope.interface.declarations import Provides + + +from zope.interface.interfaces import IInterfaceDeclaration + +moduleProvides(IInterfaceDeclaration) + +__all__ = ('Interface', 'Attribute') + tuple(IInterfaceDeclaration) diff -Nru zope3-3.4.0/src/zope/interface/interface.py zope3-3.5~bzr18/src/zope/interface/interface.py --- zope3-3.4.0/src/zope/interface/interface.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/interface.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,820 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Interface object implementation +""" +from __future__ import generators + +import sys +from types import FunctionType +import weakref + +from zope.interface.exceptions import Invalid +from zope.interface.ro import ro + + +CO_VARARGS = 4 +CO_VARKEYWORDS = 8 +TAGGED_DATA = '__interface_tagged_values__' + +_decorator_non_return = object() + +def invariant(call): + f_locals = sys._getframe(1).f_locals + tags = f_locals.setdefault(TAGGED_DATA, {}) + invariants = tags.setdefault('invariants', []) + invariants.append(call) + return _decorator_non_return + + +def taggedValue(key, value): + """Attaches a tagged value to an interface at definition time.""" + f_locals = sys._getframe(1).f_locals + tagged_values = f_locals.setdefault(TAGGED_DATA, {}) + tagged_values[key] = value + return _decorator_non_return + + +class Element(object): + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + #implements(IElement) + + def __init__(self, __name__, __doc__=''): + """Create an 'attribute' description + """ + if not __doc__ and __name__.find(' ') >= 0: + __doc__ = __name__ + __name__ = None + + self.__name__=__name__ + self.__doc__=__doc__ + self.__tagged_values = {} + + def getName(self): + """ Returns the name of the object. """ + return self.__name__ + + def getDoc(self): + """ Returns the documentation for the object. """ + return self.__doc__ + + def getTaggedValue(self, tag): + """ Returns the value associated with 'tag'. """ + return self.__tagged_values[tag] + + def queryTaggedValue(self, tag, default=None): + """ Returns the value associated with 'tag'. """ + return self.__tagged_values.get(tag, default) + + def getTaggedValueTags(self): + """ Returns a list of all tags. """ + return self.__tagged_values.keys() + + def setTaggedValue(self, tag, value): + """ Associates 'value' with 'key'. """ + self.__tagged_values[tag] = value + +class SpecificationBasePy(object): + + def providedBy(self, ob): + """Is the interface implemented by an object + + >>> from zope.interface import * + >>> class I1(Interface): + ... pass + >>> class C(object): + ... implements(I1) + >>> c = C() + >>> class X(object): + ... pass + >>> x = X() + >>> I1.providedBy(x) + False + >>> I1.providedBy(C) + False + >>> I1.providedBy(c) + True + >>> directlyProvides(x, I1) + >>> I1.providedBy(x) + True + >>> directlyProvides(C, I1) + >>> I1.providedBy(C) + True + + """ + spec = providedBy(ob) + return self in spec._implied + + def implementedBy(self, cls): + """Test whether the specification is implemented by a class or factory. + Raise TypeError if argument is neither a class nor a callable.""" + spec = implementedBy(cls) + return self in spec._implied + + def isOrExtends(self, interface): + """Is the interface the same as or extend the given interface + + Examples:: + + >>> from zope.interface import Interface + >>> from zope.interface.declarations import Declaration + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration() + >>> int(spec.extends(Interface)) + 1 + >>> spec = Declaration(I2) + >>> int(spec.extends(Interface)) + 1 + >>> int(spec.extends(I1)) + 1 + >>> int(spec.extends(I2)) + 1 + >>> int(spec.extends(I3)) + 0 + >>> int(spec.extends(I4)) + 0 + + """ + return interface in self._implied + + __call__ = isOrExtends + +SpecificationBase = SpecificationBasePy +try: + from _zope_interface_coptimizations import SpecificationBase +except ImportError: + pass + +_marker = object() +class InterfaceBasePy(object): + """Base class that wants to be replaced with a C base :) + """ + + def __call__(self, obj, alternate=_marker): + """Adapt an object to the interface + """ + conform = getattr(obj, '__conform__', None) + if conform is not None: + adapter = self._call_conform(conform) + if adapter is not None: + return adapter + + adapter = self.__adapt__(obj) + + if adapter is not None: + return adapter + elif alternate is not _marker: + return alternate + else: + raise TypeError("Could not adapt", obj, self) + + def __adapt__(self, obj): + """Adapt an object to the reciever + """ + if self.providedBy(obj): + return obj + + for hook in adapter_hooks: + adapter = hook(self, obj) + if adapter is not None: + return adapter + + +InterfaceBase = InterfaceBasePy +try: + from _zope_interface_coptimizations import InterfaceBase +except ImportError: + pass + + +adapter_hooks = [] +try: + from _zope_interface_coptimizations import adapter_hooks +except ImportError: + pass + + +class Specification(SpecificationBase): + """Specifications + + An interface specification is used to track interface declarations + and component registrations. + + This class is a base class for both interfaces themselves and for + interface specifications (declarations). + + Specifications are mutable. If you reassign their cases, their + relations with other specifications are adjusted accordingly. + + For example: + + >>> from zope.interface import Interface + >>> class I1(Interface): + ... pass + >>> class I2(I1): + ... pass + >>> class I3(I2): + ... pass + + >>> [i.__name__ for i in I1.__bases__] + ['Interface'] + + >>> [i.__name__ for i in I2.__bases__] + ['I1'] + + >>> I3.extends(I1) + 1 + + >>> I2.__bases__ = (Interface, ) + + >>> [i.__name__ for i in I2.__bases__] + ['Interface'] + + >>> I3.extends(I1) + 0 + + """ + + # Copy some base class methods for speed + isOrExtends = SpecificationBase.isOrExtends + providedBy = SpecificationBase.providedBy + + def __init__(self, bases=()): + self._implied = {} + self.dependents = weakref.WeakKeyDictionary() + self.__bases__ = tuple(bases) + + def subscribe(self, dependent): + self.dependents[dependent] = self.dependents.get(dependent, 0) + 1 + + def unsubscribe(self, dependent): + n = self.dependents.get(dependent, 0) - 1 + if not n: + del self.dependents[dependent] + elif n > 0: + self.dependents[dependent] = n + else: + raise KeyError(dependent) + + def __setBases(self, bases): + # Register ourselves as a dependent of our old bases + for b in self.__bases__: + b.unsubscribe(self) + + # Register ourselves as a dependent of our bases + self.__dict__['__bases__'] = bases + for b in bases: + b.subscribe(self) + + self.changed(self) + + __bases__ = property( + + lambda self: self.__dict__.get('__bases__', ()), + __setBases, + ) + + def changed(self, originally_changed): + """We, or something we depend on, have changed + """ + try: + del self._v_attrs + except AttributeError: + pass + + implied = self._implied + implied.clear() + + ancestors = ro(self) + + try: + if Interface not in ancestors: + ancestors.append(Interface) + except NameError: + pass # defining Interface itself + + self.__sro__ = tuple(ancestors) + self.__iro__ = tuple([ancestor for ancestor in ancestors + if isinstance(ancestor, InterfaceClass) + ]) + + for ancestor in ancestors: + # We directly imply our ancestors: + implied[ancestor] = () + + # Now, advise our dependents of change: + for dependent in self.dependents.keys(): + dependent.changed(originally_changed) + + + def interfaces(self): + """Return an iterator for the interfaces in the specification + + for example:: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Specification((I2, I3)) + >>> spec = Specification((I4, spec)) + >>> i = spec.interfaces() + >>> [x.getName() for x in i] + ['I4', 'I2', 'I3'] + >>> list(i) + [] + """ + seen = {} + for base in self.__bases__: + for interface in base.interfaces(): + if interface not in seen: + seen[interface] = 1 + yield interface + + + def extends(self, interface, strict=True): + """Does the specification extend the given interface? + + Test whether an interface in the specification extends the + given interface + + Examples:: + + >>> from zope.interface import Interface + >>> from zope.interface.declarations import Declaration + >>> class I1(Interface): pass + ... + >>> class I2(I1): pass + ... + >>> class I3(Interface): pass + ... + >>> class I4(I3): pass + ... + >>> spec = Declaration() + >>> int(spec.extends(Interface)) + 1 + >>> spec = Declaration(I2) + >>> int(spec.extends(Interface)) + 1 + >>> int(spec.extends(I1)) + 1 + >>> int(spec.extends(I2)) + 1 + >>> int(spec.extends(I3)) + 0 + >>> int(spec.extends(I4)) + 0 + >>> I2.extends(I2) + 0 + >>> I2.extends(I2, False) + 1 + >>> I2.extends(I2, strict=False) + 1 + + """ + return ((interface in self._implied) + and + ((not strict) or (self != interface)) + ) + + def weakref(self, callback=None): + return weakref.ref(self, callback) + + def get(self, name, default=None): + """Query for an attribute description + """ + try: + attrs = self._v_attrs + except AttributeError: + attrs = self._v_attrs = {} + attr = attrs.get(name) + if attr is None: + for iface in self.__iro__: + attr = iface.direct(name) + if attr is not None: + attrs[name] = attr + break + + if attr is None: + return default + else: + return attr + +class InterfaceClass(Element, InterfaceBase, Specification): + """Prototype (scarecrow) Interfaces Implementation.""" + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + #implements(IInterface) + + def __init__(self, name, bases=(), attrs=None, __doc__=None, + __module__=None): + + if attrs is None: + attrs = {} + + if __module__ is None: + __module__ = attrs.get('__module__') + if isinstance(__module__, str): + del attrs['__module__'] + else: + try: + # Figure out what module defined the interface. + # This is how cPython figures out the module of + # a class, but of course it does it in C. :-/ + __module__ = sys._getframe(1).f_globals['__name__'] + except (AttributeError, KeyError): + pass + + self.__module__ = __module__ + + d = attrs.get('__doc__') + if d is not None: + if not isinstance(d, Attribute): + if __doc__ is None: + __doc__ = d + del attrs['__doc__'] + + if __doc__ is None: + __doc__ = '' + + Element.__init__(self, name, __doc__) + + tagged_data = attrs.pop(TAGGED_DATA, None) + if tagged_data is not None: + for key, val in tagged_data.items(): + self.setTaggedValue(key, val) + + for base in bases: + if not isinstance(base, InterfaceClass): + raise TypeError('Expected base interfaces') + + Specification.__init__(self, bases) + + # Make sure that all recorded attributes (and methods) are of type + # `Attribute` and `Method` + for name, attr in attrs.items(): + if name == '__locals__': + # This happens under Python 3 sometimes, not sure why. /regebro + continue + if isinstance(attr, Attribute): + attr.interface = self + if not attr.__name__: + attr.__name__ = name + elif isinstance(attr, FunctionType): + attrs[name] = fromFunction(attr, self, name=name) + elif attr is _decorator_non_return: + del attrs[name] + else: + raise InvalidInterface("Concrete attribute, " + name) + + self.__attrs = attrs + + self.__identifier__ = "%s.%s" % (self.__module__, self.__name__) + + def interfaces(self): + """Return an iterator for the interfaces in the specification + + for example:: + + >>> from zope.interface import Interface + >>> class I1(Interface): pass + ... + >>> + >>> i = I1.interfaces() + >>> [x.getName() for x in i] + ['I1'] + >>> list(i) + [] + """ + yield self + + def getBases(self): + return self.__bases__ + + def isEqualOrExtendedBy(self, other): + """Same interface or extends?""" + return self == other or other.extends(self) + + def names(self, all=False): + """Return the attribute names defined by the interface.""" + if not all: + return self.__attrs.keys() + + r = self.__attrs.copy() + + for base in self.__bases__: + r.update(dict.fromkeys(base.names(all))) + + return r.keys() + + def __iter__(self): + return iter(self.names(all=True)) + + def namesAndDescriptions(self, all=False): + """Return attribute names and descriptions defined by interface.""" + if not all: + return self.__attrs.items() + + r = {} + for base in self.__bases__[::-1]: + r.update(dict(base.namesAndDescriptions(all))) + + r.update(self.__attrs) + + return r.items() + + def getDescriptionFor(self, name): + """Return the attribute description for the given name.""" + r = self.get(name) + if r is not None: + return r + + raise KeyError(name) + + __getitem__ = getDescriptionFor + + def __contains__(self, name): + return self.get(name) is not None + + def direct(self, name): + return self.__attrs.get(name) + + def queryDescriptionFor(self, name, default=None): + return self.get(name, default) + + def deferred(self): + """Return a defered class corresponding to the interface.""" + if hasattr(self, "_deferred"): return self._deferred + + klass={} + exec "class %s: pass" % self.__name__ in klass + klass=klass[self.__name__] + + self.__d(klass) + + self._deferred=klass + + return klass + + def validateInvariants(self, obj, errors=None): + """validate object to defined invariants.""" + for call in self.queryTaggedValue('invariants', []): + try: + call(obj) + except Invalid, e: + if errors is None: + raise + else: + errors.append(e) + for base in self.__bases__: + try: + base.validateInvariants(obj, errors) + except Invalid: + if errors is None: + raise + if errors: + raise Invalid(errors) + + def _getInterface(self, ob, name): + """Retrieve a named interface.""" + return None + + def __d(self, klass): + for k, v in self.__attrs.items(): + if isinstance(v, Method) and not (k in klass.__dict__): + setattr(klass, k, v) + + for b in self.__bases__: + b.__d(klass) + + def __repr__(self): + try: + return self._v_repr + except AttributeError: + name = self.__name__ + m = self.__module__ + if m: + name = '%s.%s' % (m, name) + r = "<%s %s>" % (self.__class__.__name__, name) + self._v_repr = r + return r + + def _call_conform(self, conform): + try: + return conform(self) + except TypeError: + # We got a TypeError. It might be an error raised by + # the __conform__ implementation, or *we* may have + # made the TypeError by calling an unbound method + # (object is a class). In the later case, we behave + # as though there is no __conform__ method. We can + # detect this case by checking whether there is more + # than one traceback object in the traceback chain: + if sys.exc_info()[2].tb_next is not None: + # There is more than one entry in the chain, so + # reraise the error: + raise + # This clever trick is from Phillip Eby + + return None + + def __reduce__(self): + return self.__name__ + + def __cmp(self, o1, o2): + # Yes, I did mean to name this __cmp, rather than __cmp__. + # It is a private method used by __lt__ and __gt__. + # I don't want to override __eq__ because I want the default + # __eq__, which is really fast. + """Make interfaces sortable + + TODO: It would ne nice if: + + More specific interfaces should sort before less specific ones. + Otherwise, sort on name and module. + + But this is too complicated, and we're going to punt on it + for now. + + For now, sort on interface and module name. + + None is treated as a pseudo interface that implies the loosest + contact possible, no contract. For that reason, all interfaces + sort before None. + + """ + if o1 == o2: + return 0 + + if o1 is None: + return 1 + if o2 is None: + return -1 + + n1 = (getattr(o1, '__name__', ''), + getattr(getattr(o1, '__module__', None), '__name__', '')) + n2 = (getattr(o2, '__name__', ''), + getattr(getattr(o2, '__module__', None), '__name__', '')) + + return (n1 > n2) - (n1 < n2) + + def __lt__(self, other): + c = self.__cmp(self, other) + #print '<', self, other, c < 0, c + return c < 0 + + def __gt__(self, other): + c = self.__cmp(self, other) + #print '>', self, other, c > 0, c + return c > 0 + + +Interface = InterfaceClass("Interface", __module__ = 'zope.interface') + +class Attribute(Element): + """Attribute descriptions + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + # implements(IAttribute) + + interface = None + + +class Method(Attribute): + """Method interfaces + + The idea here is that you have objects that describe methods. + This provides an opportunity for rich meta-data. + """ + + # We can't say this yet because we don't have enough + # infrastructure in place. + # + # implements(IMethod) + + def __call__(self, *args, **kw): + raise BrokenImplementation(self.interface, self.__name__) + + def getSignatureInfo(self): + return {'positional': self.positional, + 'required': self.required, + 'optional': self.optional, + 'varargs': self.varargs, + 'kwargs': self.kwargs, + } + + def getSignatureString(self): + sig = [] + for v in self.positional: + sig.append(v) + if v in self.optional.keys(): + sig[-1] += "=" + `self.optional[v]` + if self.varargs: + sig.append("*" + self.varargs) + if self.kwargs: + sig.append("**" + self.kwargs) + + return "(%s)" % ", ".join(sig) + + +def fromFunction(func, interface=None, imlevel=0, name=None): + name = name or func.__name__ + method = Method(name, func.__doc__) + defaults = func.func_defaults or () + code = func.func_code + # Number of positional arguments + na = code.co_argcount-imlevel + names = code.co_varnames[imlevel:] + opt = {} + # Number of required arguments + nr = na-len(defaults) + if nr < 0: + defaults=defaults[-nr:] + nr = 0 + + # Determine the optional arguments. + opt.update(dict(zip(names[nr:], defaults))) + + method.positional = names[:na] + method.required = names[:nr] + method.optional = opt + + argno = na + + # Determine the function's variable argument's name (i.e. *args) + if code.co_flags & CO_VARARGS: + method.varargs = names[argno] + argno = argno + 1 + else: + method.varargs = None + + # Determine the function's keyword argument's name (i.e. **kw) + if code.co_flags & CO_VARKEYWORDS: + method.kwargs = names[argno] + else: + method.kwargs = None + + method.interface = interface + + for key, value in func.__dict__.items(): + method.setTaggedValue(key, value) + + return method + + +def fromMethod(meth, interface=None, name=None): + func = meth.im_func + return fromFunction(func, interface, imlevel=1, name=name) + + +# Now we can create the interesting interfaces and wire them up: +def _wire(): + from zope.interface.declarations import classImplements + + from zope.interface.interfaces import IAttribute + classImplements(Attribute, IAttribute) + + from zope.interface.interfaces import IMethod + classImplements(Method, IMethod) + + from zope.interface.interfaces import IInterface + classImplements(InterfaceClass, IInterface) + + from zope.interface.interfaces import ISpecification + classImplements(Specification, ISpecification) + +# We import this here to deal with module dependencies. +from zope.interface.declarations import implementedBy +from zope.interface.declarations import providedBy +from zope.interface.exceptions import InvalidInterface +from zope.interface.exceptions import BrokenImplementation diff -Nru zope3-3.4.0/src/zope/interface/interfaces.py zope3-3.5~bzr18/src/zope/interface/interfaces.py --- zope3-3.4.0/src/zope/interface/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,748 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Interface Package Interfaces + +$Id: interfaces.py 110699 2010-04-09 08:16:17Z regebro $ +""" +__docformat__ = 'restructuredtext' + + +from zope.interface import Interface +from zope.interface.interface import Attribute + +class IElement(Interface): + """Objects that have basic documentation and tagged values. + """ + + __name__ = Attribute('__name__', 'The object name') + __doc__ = Attribute('__doc__', 'The object doc string') + + def getTaggedValue(tag): + """Returns the value associated with `tag`. + + Raise a `KeyError` of the tag isn't set. + """ + + def queryTaggedValue(tag, default=None): + """Returns the value associated with `tag`. + + Return the default value of the tag isn't set. + """ + + def getTaggedValueTags(): + """Returns a list of all tags.""" + + def setTaggedValue(tag, value): + """Associates `value` with `key`.""" + + +class IAttribute(IElement): + """Attribute descriptors""" + + interface = Attribute('interface', + 'Stores the interface instance in which the ' + 'attribute is located.') + + +class IMethod(IAttribute): + """Method attributes""" + + def getSignatureInfo(): + """Returns the signature information. + + This method returns a dictionary with the following keys: + + o `positional` - All positional arguments. + + o `required` - A list of all required arguments. + + o `optional` - A list of all optional arguments. + + o `varargs` - The name of the varargs argument. + + o `kwargs` - The name of the kwargs argument. + """ + + def getSignatureString(): + """Return a signature string suitable for inclusion in documentation. + + This method returns the function signature string. For example, if you + have `func(a, b, c=1, d='f')`, then the signature string is `(a, b, + c=1, d='f')`. + """ + +class ISpecification(Interface): + """Object Behavioral specifications""" + + def extends(other, strict=True): + """Test whether a specification extends another + + The specification extends other if it has other as a base + interface or if one of it's bases extends other. + + If strict is false, then the specification extends itself. + """ + + def isOrExtends(other): + """Test whether the specification is or extends another + """ + + def weakref(callback=None): + """Return a weakref to the specification + + This method is, regrettably, needed to allow weakrefs to be + computed to security-proxied specifications. While the + zope.interface package does not require zope.security or + zope.proxy, it has to be able to coexist with it. + + """ + + __bases__ = Attribute("""Base specifications + + A tuple if specifications from which this specification is + directly derived. + + """) + + __sro__ = Attribute("""Specification-resolution order + + A tuple of the specification and all of it's ancestor + specifications from most specific to least specific. + + (This is similar to the method-resolution order for new-style classes.) + """) + + __iro__ = Attribute("""Interface-resolution order + + A tuple of the of the specification's ancestor interfaces from + most specific to least specific. The specification itself is + included if it is an interface. + + (This is similar to the method-resolution order for new-style classes.) + """) + + def get(name, default=None): + """Look up the description for a name + + If the named attribute is not defined, the default is + returned. + """ + + +class IInterface(ISpecification, IElement): + """Interface objects + + Interface objects describe the behavior of an object by containing + useful information about the object. This information includes: + + o Prose documentation about the object. In Python terms, this + is called the "doc string" of the interface. In this element, + you describe how the object works in prose language and any + other useful information about the object. + + o Descriptions of attributes. Attribute descriptions include + the name of the attribute and prose documentation describing + the attributes usage. + + o Descriptions of methods. Method descriptions can include: + + - Prose "doc string" documentation about the method and its + usage. + + - A description of the methods arguments; how many arguments + are expected, optional arguments and their default values, + the position or arguments in the signature, whether the + method accepts arbitrary arguments and whether the method + accepts arbitrary keyword arguments. + + o Optional tagged data. Interface objects (and their attributes and + methods) can have optional, application specific tagged data + associated with them. Examples uses for this are examples, + security assertions, pre/post conditions, and other possible + information you may want to associate with an Interface or its + attributes. + + Not all of this information is mandatory. For example, you may + only want the methods of your interface to have prose + documentation and not describe the arguments of the method in + exact detail. Interface objects are flexible and let you give or + take any of these components. + + Interfaces are created with the Python class statement using + either Interface.Interface or another interface, as in:: + + from zope.interface import Interface + + class IMyInterface(Interface): + '''Interface documentation''' + + def meth(arg1, arg2): + '''Documentation for meth''' + + # Note that there is no self argument + + class IMySubInterface(IMyInterface): + '''Interface documentation''' + + def meth2(): + '''Documentation for meth2''' + + You use interfaces in two ways: + + o You assert that your object implement the interfaces. + + There are several ways that you can assert that an object + implements an interface: + + 1. Call zope.interface.implements in your class definition. + + 2. Call zope.interfaces.directlyProvides on your object. + + 3. Call 'zope.interface.classImplements' to assert that instances + of a class implement an interface. + + For example:: + + from zope.interface import classImplements + + classImplements(some_class, some_interface) + + This approach is useful when it is not an option to modify + the class source. Note that this doesn't affect what the + class itself implements, but only what its instances + implement. + + o You query interface meta-data. See the IInterface methods and + attributes for details. + + """ + + def providedBy(object): + """Test whether the interface is implemented by the object + + Return true of the object asserts that it implements the + interface, including asserting that it implements an extended + interface. + """ + + def implementedBy(class_): + """Test whether the interface is implemented by instances of the class + + Return true of the class asserts that its instances implement the + interface, including asserting that they implement an extended + interface. + """ + + def names(all=False): + """Get the interface attribute names + + Return a sequence of the names of the attributes, including + methods, included in the interface definition. + + Normally, only directly defined attributes are included. If + a true positional or keyword argument is given, then + attributes defined by base classes will be included. + """ + + def namesAndDescriptions(all=False): + """Get the interface attribute names and descriptions + + Return a sequence of the names and descriptions of the + attributes, including methods, as name-value pairs, included + in the interface definition. + + Normally, only directly defined attributes are included. If + a true positional or keyword argument is given, then + attributes defined by base classes will be included. + """ + + def __getitem__(name): + """Get the description for a name + + If the named attribute is not defined, a KeyError is raised. + """ + + def direct(name): + """Get the description for the name if it was defined by the interface + + If the interface doesn't define the name, returns None. + """ + + def validateInvariants(obj, errors=None): + """Validate invariants + + Validate object to defined invariants. If errors is None, + raises first Invalid error; if errors is a list, appends all errors + to list, then raises Invalid with the errors as the first element + of the "args" tuple.""" + + def __contains__(name): + """Test whether the name is defined by the interface""" + + def __iter__(): + """Return an iterator over the names defined by the interface + + The names iterated include all of the names defined by the + interface directly and indirectly by base interfaces. + """ + + __module__ = Attribute("""The name of the module defining the interface""") + +class IDeclaration(ISpecification): + """Interface declaration + + Declarations are used to express the interfaces implemented by + classes or provided by objects. + """ + + def __contains__(interface): + """Test whether an interface is in the specification + + Return true if the given interface is one of the interfaces in + the specification and false otherwise. + """ + + def __iter__(): + """Return an iterator for the interfaces in the specification + """ + + def flattened(): + """Return an iterator of all included and extended interfaces + + An iterator is returned for all interfaces either included in + or extended by interfaces included in the specifications + without duplicates. The interfaces are in "interface + resolution order". The interface resolution order is such that + base interfaces are listed after interfaces that extend them + and, otherwise, interfaces are included in the order that they + were defined in the specification. + """ + + def __sub__(interfaces): + """Create an interface specification with some interfaces excluded + + The argument can be an interface or an interface + specifications. The interface or interfaces given in a + specification are subtracted from the interface specification. + + Removing an interface that is not in the specification does + not raise an error. Doing so has no effect. + + Removing an interface also removes sub-interfaces of the interface. + + """ + + def __add__(interfaces): + """Create an interface specification with some interfaces added + + The argument can be an interface or an interface + specifications. The interface or interfaces given in a + specification are added to the interface specification. + + Adding an interface that is already in the specification does + not raise an error. Doing so has no effect. + """ + + def __nonzero__(): + """Return a true value of the interface specification is non-empty + """ + +class IInterfaceDeclaration(Interface): + """Declare and check the interfaces of objects + + The functions defined in this interface are used to declare the + interfaces that objects provide and to query the interfaces that have + been declared. + + Interfaces can be declared for objects in two ways: + + - Interfaces are declared for instances of the object's class + + - Interfaces are declared for the object directly. + + The interfaces declared for an object are, therefore, the union of + interfaces declared for the object directly and the interfaces + declared for instances of the object's class. + + Note that we say that a class implements the interfaces provided + by it's instances. An instance can also provide interfaces + directly. The interfaces provided by an object are the union of + the interfaces provided directly and the interfaces implemented by + the class. + """ + + def providedBy(ob): + """Return the interfaces provided by an object + + This is the union of the interfaces directly provided by an + object and interfaces implemented by it's class. + + The value returned is an IDeclaration. + """ + + def implementedBy(class_): + """Return the interfaces implemented for a class' instances + + The value returned is an IDeclaration. + """ + + def classImplements(class_, *interfaces): + """Declare additional interfaces implemented for instances of a class + + The arguments after the class are one or more interfaces or + interface specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously + declared. + + Consider the following example:: + + class C(A, B): + ... + + classImplements(C, I1, I2) + + + Instances of ``C`` provide ``I1``, ``I2``, and whatever interfaces + instances of ``A`` and ``B`` provide. + """ + + def implementer(*interfaces): + """Create a decorator for declaring interfaces implemented by a facory + + A callable is returned that makes an implements declaration on + objects passed to it. + """ + + def classImplementsOnly(class_, *interfaces): + """Declare the only interfaces implemented by instances of a class + + The arguments after the class are one or more interfaces or + interface specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) replace any previous declarations. + + Consider the following example:: + + class C(A, B): + ... + + classImplements(C, IA, IB. IC) + classImplementsOnly(C. I1, I2) + + Instances of ``C`` provide only ``I1``, ``I2``, and regardless of + whatever interfaces instances of ``A`` and ``B`` implement. + """ + + def implementer_only(*interfaces): + """Create a decorator for declaring the only interfaces implemented + + A callable is returned that makes an implements declaration on + objects passed to it. + """ + + def directlyProvidedBy(object): + """Return the interfaces directly provided by the given object + + The value returned is an IDeclaration. + """ + + def directlyProvides(object, *interfaces): + """Declare interfaces declared directly for an object + + The arguments after the object are one or more interfaces or + interface specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) replace interfaces previously + declared for the object. + + Consider the following example:: + + class C(A, B): + ... + + ob = C() + directlyProvides(ob, I1, I2) + + The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces + instances have been declared for instances of ``C``. + + To remove directly provided interfaces, use ``directlyProvidedBy`` and + subtract the unwanted interfaces. For example:: + + directlyProvides(ob, directlyProvidedBy(ob)-I2) + + removes I2 from the interfaces directly provided by + ``ob``. The object, ``ob`` no longer directly provides ``I2``, + although it might still provide ``I2`` if it's class + implements ``I2``. + + To add directly provided interfaces, use ``directlyProvidedBy`` and + include additional interfaces. For example:: + + directlyProvides(ob, directlyProvidedBy(ob), I2) + + adds I2 to the interfaces directly provided by ob. + """ + + def alsoProvides(object, *interfaces): + """Declare additional interfaces directly for an object:: + + alsoProvides(ob, I1) + + is equivalent to:: + + directlyProvides(ob, directlyProvidedBy(ob), I1) + """ + + def noLongerProvides(object, interface): + """Remove an interface from the list of an object's directly + provided interfaces:: + + noLongerProvides(ob, I1) + + is equivalent to:: + + directlyProvides(ob, directlyProvidedBy(ob)-I1) + + with the exception that if ``I1`` is an interface that is + provided by ``ob`` through the class's implementation, + ValueError is raised. + """ + + def implements(*interfaces): + """Declare interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + The interfaces given (including the interfaces in the + specifications) are added to any interfaces previously + declared. + + Previous declarations include declarations for base classes + unless implementsOnly was used. + + This function is provided for convenience. It provides a more + convenient way to call classImplements. For example:: + + implements(I1) + + is equivalent to calling:: + + classImplements(C, I1) + + after the class has been created. + + Consider the following example:: + + class C(A, B): + implements(I1, I2) + + + Instances of ``C`` implement ``I1``, ``I2``, and whatever interfaces + instances of ``A`` and ``B`` implement. + """ + + def implementsOnly(*interfaces): + """Declare the only interfaces implemented by instances of a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + Previous declarations including declarations for base classes + are overridden. + + This function is provided for convenience. It provides a more + convenient way to call classImplementsOnly. For example:: + + implementsOnly(I1) + + is equivalent to calling:: + + classImplementsOnly(I1) + + after the class has been created. + + Consider the following example:: + + class C(A, B): + implementsOnly(I1, I2) + + + Instances of ``C`` implement ``I1``, ``I2``, regardless of what + instances of ``A`` and ``B`` implement. + """ + + def classProvides(*interfaces): + """Declare interfaces provided directly by a class + + This function is called in a class definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + The given interfaces (including the interfaces in the + specifications) are used to create the class's direct-object + interface specification. An error will be raised if the module + class has an direct interface specification. In other words, it is + an error to call this function more than once in a class + definition. + + Note that the given interfaces have nothing to do with the + interfaces implemented by instances of the class. + + This function is provided for convenience. It provides a more + convenient way to call directlyProvides for a class. For example:: + + classProvides(I1) + + is equivalent to calling:: + + directlyProvides(theclass, I1) + + after the class has been created. + """ + def provider(*interfaces): + """A class decorator version of classProvides""" + + def moduleProvides(*interfaces): + """Declare interfaces provided by a module + + This function is used in a module definition. + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + The given interfaces (including the interfaces in the + specifications) are used to create the module's direct-object + interface specification. An error will be raised if the module + already has an interface specification. In other words, it is + an error to call this function more than once in a module + definition. + + This function is provided for convenience. It provides a more + convenient way to call directlyProvides for a module. For example:: + + moduleImplements(I1) + + is equivalent to:: + + directlyProvides(sys.modules[__name__], I1) + """ + + def Declaration(*interfaces): + """Create an interface specification + + The arguments are one or more interfaces or interface + specifications (IDeclaration objects). + + A new interface specification (IDeclaration) with + the given interfaces is returned. + """ + +class IAdapterRegistry(Interface): + """Provide an interface-based registry for adapters + + This registry registers objects that are in some sense "from" a + sequence of specification to an interface and a name. + + No specific semantics are assumed for the registered objects, + however, the most common application will be to register factories + that adapt objects providing required specifications to a provided + interface. + """ + + def register(required, provided, name, value): + """Register a value + + A value is registered for a *sequence* of required specifications, a + provided interface, and a name. + """ + + def registered(required, provided, name=u''): + """Return the component registered for the given interfaces and name + + Unlike the lookup method, this methods won't retrieve + components registered for more specific required interfaces or + less specific provided interfaces. + + If no component was registered exactly for the given + interfaces and name, then None is returned. + + """ + + def lookup(required, provided, name='', default=None): + """Lookup a value + + A value is looked up based on a *sequence* of required + specifications, a provided interface, and a name. + """ + + def queryMultiAdapter(objects, provided, name=u'', default=None): + """Adapt a sequence of objects to a named, provided, interface + """ + + def lookup1(required, provided, name=u'', default=None): + """Lookup a value using a single required interface + + A value is looked up based on a single required + specifications, a provided interface, and a name. + """ + + def queryAdapter(object, provided, name=u'', default=None): + """Adapt an object using a registered adapter factory. + """ + + def adapter_hook(provided, object, name=u'', default=None): + """Adapt an object using a registered adapter factory. + """ + + def lookupAll(required, provided): + """Find all adapters from the required to the provided interfaces + + An iterable object is returned that provides name-value two-tuples. + """ + + def names(required, provided): + """Return the names for which there are registered objects + """ + + def subscribe(required, provided, subscriber, name=u''): + """Register a subscriber + + A subscriber is registered for a *sequence* of required + specifications, a provided interface, and a name. + + Multiple subscribers may be registered for the same (or + equivalent) interfaces. + """ + + def subscriptions(required, provided, name=u''): + """Get a sequence of subscribers + + Subscribers for a *sequence* of required interfaces, and a provided + interface are returned. + """ + + def subscribers(objects, provided, name=u''): + """Get a sequence of subscription adapters + """ diff -Nru zope3-3.4.0/src/zope/interface/README.ru.txt zope3-3.5~bzr18/src/zope/interface/README.ru.txt --- zope3-3.4.0/src/zope/interface/README.ru.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/README.ru.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,803 @@ +========== +Интерфейсы +========== + +.. contents:: + +Интерфейсы - это объекты специфицирующие (документирующие) внешнее поведение +объектов которые их "предоставляют". Интерфейсы определяют поведение через +следующие составляющие: + +- Неформальную документацию в строках документации + +- Определения атрибутов + +- Инварианты - условия, которые должны соблюдаться для объектов предоставляющих + интерфейс + +Определения атрибутов описывают конкретные атрибуты. Они определяют +имя атрибута и предоставляют документацию и ограничения для значений +атрибута. Определения атрибутов могут быть заданы несколькими путями +как мы увидим ниже. + +Определение интерфейсов +======================= + +Интерфейсы определяются с использованием ключевого слова class:: + + >>> import zope.interface + >>> class IFoo(zope.interface.Interface): + ... """Foo blah blah""" + ... + ... x = zope.interface.Attribute("""X blah blah""") + ... + ... def bar(q, r=None): + ... """bar blah blah""" + +В примере выше мы создали интерфейс `IFoo`. Мы наследуем его от +класса `zope.interface.Interface`, который является родительским интерфейсом +для всех интерфейсов, как `object` - это родительский класс для всех новых +классов [#create]_. Данный интерфейс не является классом, а является +Интерфейсом, экземпляром `InterfaceClass`:: + + >>> type(IFoo) + + +Мы можем запросить у интерфейса его документацию:: + + >>> IFoo.__doc__ + 'Foo blah blah' + +и его имя:: + + >>> IFoo.__name__ + 'IFoo' + +и даже модуль в котором он определен:: + + >>> IFoo.__module__ + '__main__' + +Наш интерфейс определяет два атрибута: + +`x` + Это простейшая форма определения атрибутов. Определяются имя + и строка документации. Формально здесь не определяется ничего более. + +`bar` + Это метод. Методы определяются как обычные функции. Метод - это просто + атрибут который должен быть вызываемым с указанием сигнатуры, + предоставляемой определением функции. + + Надо отметить, что аргумент `self` не указывается для `bar`. Интерфейс + документирует как объект *используется*. Когда методы экземпляров классов + вызываются мы не передаем аргумент `self`, таким образом аргумент `self` + не включается и в сигнатуру интерфейса. Аргумент `self` в методах + экземпляров классов на самом деле деталь реализации экземпляров классов + в Python. Другие объекты кроме экземпляров классов могут предоставлять + интерфейсы и их методы могут не быть методами экземпляров классов. Для + примера модули могут предоставлять интерфейсы и их методы обычно просто + функции. Даже экземпляры могут иметь методы не являющиеся методами + экземпляров класса. + +Мы можем получить доступ к атрибутам определенным интерфейсом используя +синтаксис доступа к элементам массива:: + + >>> x = IFoo['x'] + >>> type(x) + + >>> x.__name__ + 'x' + >>> x.__doc__ + 'X blah blah' + + >>> IFoo.get('x').__name__ + 'x' + + >>> IFoo.get('y') + +Можно использовать `in` для определения содержит ли интерфейс +определенное имя:: + + >>> 'x' in IFoo + True + +Мы можем использовать итератор для интерфейсов что бы получить все имена +которые интерфейсы определяют:: + + >>> names = list(IFoo) + >>> names.sort() + >>> names + ['bar', 'x'] + +Надо помнить, что интерфейсы не являются классами. Мы не можем получить +доступ к определениям атрибутов через доступ к атрибутам интерфейсов:: + + >>> IFoo.x + Traceback (most recent call last): + File "", line 1, in ? + AttributeError: 'InterfaceClass' object has no attribute 'x' + +Методы также предоставляют доступ к сигнатуре метода:: + + >>> bar = IFoo['bar'] + >>> bar.getSignatureString() + '(q, r=None)' + +Объявление интерфейсов +====================== + +Определив интерфейс мы можем теперь *объявить*, что объекты предоставляют их. +Перед описанием деталей определим некоторые термины: + +*предоставлять* + Мы говорим, что объекты *предоставляют* интерфейсы. Если объект + предоставляет интерфейс, тогда интерфейс специфицирует поведение объекта. + Другими словами, интерфейсы специфицируют поведение объектов которые + предоставляют их. + +*реализовать* + Мы обычно говорим что классы *реализуют* интерфейсы. Если класс + реализует интерфейс, тогда экземпляры этого класса предоставляют + данный интерфейс. Объекты предоставляют интерфейсы которые их классы + реализуют [#factory]_. (Объекты также могут предоставлять интерфейсы напрямую + плюс к тем которые реализуют их классы.) + + Важно помнить, что классы обычно не предоставляют интерфейсы которые + они реализуют. + + Мы можем обобщить это до фабрик. Для любого вызываемого объекта мы можем + объявить что он производит объекты которые предоставляют какие-либо + интерфейсы сказав, что фабрика реализует данные интерфейсы. + +Теперь после того как мы определили эти термины мы можем поговорить об +API для объявления интерфейсов. + +Объявление реализуемых интерфейсов +---------------------------------- + +Наиболее часто используемый путь для объявления интерфейсов - это использование +функции implements в определении класса:: + + >>> class Foo: + ... zope.interface.implements(IFoo) + ... + ... def __init__(self, x=None): + ... self.x = x + ... + ... def bar(self, q, r=None): + ... return q, r, self.x + ... + ... def __repr__(self): + ... return "Foo(%s)" % self.x + +В этом примере мы объявили, что `Foo` реализует `IFoo`. Это значит, что +экземпляры `Foo` предоставляют `IFoo`. После данного объявления есть +несколько путей для анализа объявлений. Во-первых мы можем спросить +что интерфейс реализован классом:: + + >>> IFoo.implementedBy(Foo) + True + +Также мы можем спросить если интерфейс предоставляется объектами класса:: + + >>> foo = Foo() + >>> IFoo.providedBy(foo) + True + +Конечно `Foo` не предоставляет `IFoo`, он реализует его:: + + >>> IFoo.providedBy(Foo) + False + +Мы можем также узнать какие интерфейсы реализуются объектами:: + + >>> list(zope.interface.implementedBy(Foo)) + [] + +Это ошибка спрашивать про интерфейсы реализуемые не вызываемым объектом:: + + >>> IFoo.implementedBy(foo) + Traceback (most recent call last): + ... + TypeError: ('ImplementedBy called for non-factory', Foo(None)) + + >>> list(zope.interface.implementedBy(foo)) + Traceback (most recent call last): + ... + TypeError: ('ImplementedBy called for non-factory', Foo(None)) + +Также можно узнать какие интерфейсы предоставляются объектами:: + + >>> list(zope.interface.providedBy(foo)) + [] + >>> list(zope.interface.providedBy(Foo)) + [] + +Мы можем объявить интерфейсы реализуемые другими фабриками (кроме классов). +Это можно сделать используя декоратор `implementer` (в стиле Python 2.4). +Для версий Python ниже 2.4 это будет выглядеть следующим образом:: + + >>> def yfoo(y): + ... foo = Foo() + ... foo.y = y + ... return foo + >>> yfoo = zope.interface.implementer(IFoo)(yfoo) + + >>> list(zope.interface.implementedBy(yfoo)) + [] + +Надо заметить, что декоратор implementer может модифицировать свои аргументы. +Вызывающая сторона не должна предполагать, что всегда будет создаваться +новый объект. + +XXX: Double check and update these version numbers, and translate to russian: + +In zope.interface 3.5.1 and lower, the implementor decorator can not +be used for classes, but in 3.5.2 and higher it can: + + >>> Foo = zope.interface.implementer(IFoo)(Foo) + >>> list(zope.interface.providedBy(Foo())) + [] + +Note that class decorators using the @implementor(IFoo) syntax are only +supported in Python 2.6 and later. + + +Объявление предоставляемых интерфейсов +-------------------------------------- + +Мы можем объявлять интерфейсы напрямую предоставляемые объектами. Предположим +что мы хотим документировать что делает метод `__init__` класса `Foo`. Это +*точно* не часть `IFoo`. Обычно мы не должны напрямую вызывать метод `__init__` +для экземпляров Foo. Скорее метод `__init__` является частью метода `__call__` +класса `Foo`:: + + >>> class IFooFactory(zope.interface.Interface): + ... """Create foos""" + ... + ... def __call__(x=None): + ... """Create a foo + ... + ... The argument provides the initial value for x ... + ... """ + +У нас есть класс предоставляющий данный интерфейс, таким образом мы можем +объявить интерфейс класса:: + + >>> zope.interface.directlyProvides(Foo, IFooFactory) + +Теперь мы видим, что Foo уже предоставляет интерфейсы:: + + >>> list(zope.interface.providedBy(Foo)) + [] + >>> IFooFactory.providedBy(Foo) + True + +Объявление интерфейсов класса достаточно частая операция и для нее есть +специальная функция объявления `classProvides`, которая позволяет объявлять +интерфейсы при определении класса:: + + >>> class Foo2: + ... zope.interface.implements(IFoo) + ... zope.interface.classProvides(IFooFactory) + ... + ... def __init__(self, x=None): + ... self.x = x + ... + ... def bar(self, q, r=None): + ... return q, r, self.x + ... + ... def __repr__(self): + ... return "Foo(%s)" % self.x + + >>> list(zope.interface.providedBy(Foo2)) + [] + >>> IFooFactory.providedBy(Foo2) + True + +Похожая функция `moduleProvides` поддерживает объявление интерфейсов при +определении модуля. Для примера смотрите использование вызова +`moduleProvides` в `zope.interface.__init__`, который объявляет, что +пакет `zope.interface` предоставляет `IInterfaceDeclaration`. + +Иногда мы хотим объявить интерфейсы экземпляров, даже если эти экземпляры +уже берут интерфейсы от своих классов. Предположим, что мы создаем новый +интерфейс `ISpecial`:: + + >>> class ISpecial(zope.interface.Interface): + ... reason = zope.interface.Attribute("Reason why we're special") + ... def brag(): + ... "Brag about being special" + +Мы можем сделать созданный экземпляр foo специальным предоставив атрибуты +`reason` и `brag`:: + + >>> foo.reason = 'I just am' + >>> def brag(): + ... return "I'm special!" + >>> foo.brag = brag + >>> foo.reason + 'I just am' + >>> foo.brag() + "I'm special!" + +и объявив интерфейс:: + + >>> zope.interface.directlyProvides(foo, ISpecial) + +таким образом новый интерфейс включается в список предоставляемых интерфейсов:: + + >>> ISpecial.providedBy(foo) + True + >>> list(zope.interface.providedBy(foo)) + [, ] + +Мы также можем определить, что интерфейсы напрямую предоставляются +объектами:: + + >>> list(zope.interface.directlyProvidedBy(foo)) + [] + + >>> newfoo = Foo() + >>> list(zope.interface.directlyProvidedBy(newfoo)) + [] + +Наследуемые объявления +---------------------- + +Обычно объявления наследуются:: + + >>> class SpecialFoo(Foo): + ... zope.interface.implements(ISpecial) + ... reason = 'I just am' + ... def brag(self): + ... return "I'm special because %s" % self.reason + + >>> list(zope.interface.implementedBy(SpecialFoo)) + [, ] + + >>> list(zope.interface.providedBy(SpecialFoo())) + [, ] + +Иногда мы не хотим наследовать объявления. В этом случае мы можем +использовать `implementsOnly` вместо `implements`:: + + >>> class Special(Foo): + ... zope.interface.implementsOnly(ISpecial) + ... reason = 'I just am' + ... def brag(self): + ... return "I'm special because %s" % self.reason + + >>> list(zope.interface.implementedBy(Special)) + [] + + >>> list(zope.interface.providedBy(Special())) + [] + +Внешние объявления +------------------ + +Обычно мы создаем объявления реализации как часть объявления класса. Иногда +мы можем захотеть создать объявления вне объявления класса. Для примера, +мы можем хотеть объявить интерфейсы для классов которые писали не мы. +Для этого может использоваться функция `classImplements`:: + + >>> class C: + ... pass + + >>> zope.interface.classImplements(C, IFoo) + >>> list(zope.interface.implementedBy(C)) + [] + +Мы можем использовать `classImplementsOnly` для исключения наследуемых +интерфейсов:: + + >>> class C(Foo): + ... pass + + >>> zope.interface.classImplementsOnly(C, ISpecial) + >>> list(zope.interface.implementedBy(C)) + [] + +Объекты объявлений +------------------ + +Когда мы объявляем интерфейсы мы создаем объект *объявления*. Когда мы +запрашиваем объявления возвращается объект объявления:: + + >>> type(zope.interface.implementedBy(Special)) + + +Объекты объявления и объекты интерфейсов во многом похожи друг на друга. +На самом деле они даже имеют общий базовый класс. Важно понять, что они могут +использоваться там где в объявлениях ожидаются интерфейсы. Вот простой +пример:: + + >>> class Special2(Foo): + ... zope.interface.implementsOnly( + ... zope.interface.implementedBy(Foo), + ... ISpecial, + ... ) + ... reason = 'I just am' + ... def brag(self): + ... return "I'm special because %s" % self.reason + +Объявление здесь практически такое же как +``zope.interface.implements(ISpecial)``, отличие только в порядке +интерфейсов в итоговом объявления:: + + >>> list(zope.interface.implementedBy(Special2)) + [, ] + +Наследование интерфейсов +======================== + +Интерфейсы могут расширять другие интерфейсы. Они делают это просто +показывая эти интерфейсы как базовые:: + + >>> class IBlat(zope.interface.Interface): + ... """Blat blah blah""" + ... + ... y = zope.interface.Attribute("y blah blah") + ... def eek(): + ... """eek blah blah""" + + >>> IBlat.__bases__ + (,) + + >>> class IBaz(IFoo, IBlat): + ... """Baz blah""" + ... def eek(a=1): + ... """eek in baz blah""" + ... + + >>> IBaz.__bases__ + (, ) + + >>> names = list(IBaz) + >>> names.sort() + >>> names + ['bar', 'eek', 'x', 'y'] + +Заметим, что `IBaz` переопределяет eek:: + + >>> IBlat['eek'].__doc__ + 'eek blah blah' + >>> IBaz['eek'].__doc__ + 'eek in baz blah' + +Мы были осторожны переопределяя eek совместимым путем. Когда интерфейс +расширяется, расширенный интерфейс должен быть совместимым [#compat]_ с +расширяемыми интерфейсами. + +Мы можем запросить расширяет ли один из интерфейсов другой:: + + >>> IBaz.extends(IFoo) + True + >>> IBlat.extends(IFoo) + False + +Заметим, что интерфейсы не расширяют сами себя:: + + >>> IBaz.extends(IBaz) + False + +Иногда мы можем хотеть что бы они расширяли сами себя, но вместо этого +мы можем использовать `isOrExtends`:: + + >>> IBaz.isOrExtends(IBaz) + True + >>> IBaz.isOrExtends(IFoo) + True + >>> IFoo.isOrExtends(IBaz) + False + +Когда мы применяем итерацию к интерфейсу мы получаем все имена которые он +определяет включая имена определенные для базовых интерфейсов. Иногда +мы хотим получить *только* имена определенные интерфейсом напрямую. +Для этого мы используем метод `names`:: + + >>> list(IBaz.names()) + ['eek'] + +Наследование в случае определения атрибутов +-------------------------------------------- + +Интерфейс может переопределять определения атрибутов из базовых интерфейсов. +Если два базовых интерфейса определяют один и тот же атрибут атрибут +наследуется от более специфичного интерфейса. Для примера:: + + >>> class IBase(zope.interface.Interface): + ... + ... def foo(): + ... "base foo doc" + + >>> class IBase1(IBase): + ... pass + + >>> class IBase2(IBase): + ... + ... def foo(): + ... "base2 foo doc" + + >>> class ISub(IBase1, IBase2): + ... pass + +Определение ISub для foo будет из IBase2 т.к. IBase2 более специфичен для +IBase:: + + >>> ISub['foo'].__doc__ + 'base2 foo doc' + +Заметим, что это отличается от поиска в глубину. + +Иногда полезно узнать, что интерфейс определяет атрибут напрямую. Мы можем +использовать метод direct для получения напрямую определенных атрибутов:: + + >>> IBase.direct('foo').__doc__ + 'base foo doc' + + >>> ISub.direct('foo') + +Спецификации +------------ + +Интерфейсы и объявления - это специальные случаи спецификаций. Описание +выше для наследования интерфейсов можно применить и к объявлениям и +к спецификациям. Объявления фактически расширяют интерфейсы которые они +объявляют:: + + >>> class Baz(object): + ... zope.interface.implements(IBaz) + + >>> baz_implements = zope.interface.implementedBy(Baz) + >>> baz_implements.__bases__ + (, ) + + >>> baz_implements.extends(IFoo) + True + + >>> baz_implements.isOrExtends(IFoo) + True + >>> baz_implements.isOrExtends(baz_implements) + True + +Спецификации (интерфейсы и объявления) предоставляют атрибут `__sro__` +который описывает спецификацию и всех ее предков:: + + >>> baz_implements.__sro__ + (, + , + , + , + , + ) + +Помеченные значения +=================== + +Интерфейсы и описания атрибутов поддерживают механизм расширения +заимствованный из UML и называемый "помеченные значения" который позволяет +сохранять дополнительные данные:: + + >>> IFoo.setTaggedValue('date-modified', '2004-04-01') + >>> IFoo.setTaggedValue('author', 'Jim Fulton') + >>> IFoo.getTaggedValue('date-modified') + '2004-04-01' + >>> IFoo.queryTaggedValue('date-modified') + '2004-04-01' + >>> IFoo.queryTaggedValue('datemodified') + >>> tags = list(IFoo.getTaggedValueTags()) + >>> tags.sort() + >>> tags + ['author', 'date-modified'] + +Атрибуты функций конвертируются в помеченные значения когда создаются +определения атрибутов метода:: + + >>> class IBazFactory(zope.interface.Interface): + ... def __call__(): + ... "create one" + ... __call__.return_type = IBaz + + >>> IBazFactory['__call__'].getTaggedValue('return_type') + + +Помеченные значения также могут быть определены внутри определения +интерфейса:: + + >>> class IWithTaggedValues(zope.interface.Interface): + ... zope.interface.taggedValue('squish', 'squash') + >>> IWithTaggedValues.getTaggedValue('squish') + 'squash' + +Инварианты +========== + +Интерфейсы могут описывать условия которые должны быть соблюдены для объектов +которые их предоставляют. Эти условия описываются используя один или более +инвариантов. Инварианты - это вызываемые объекты которые будут вызваны +с объектом предоставляющим интерфейс в качестве параметра. Инвариант +должен выкинуть исключение `Invalid` если условие не соблюдено. Например:: + + >>> class RangeError(zope.interface.Invalid): + ... """A range has invalid limits""" + ... def __repr__(self): + ... return "RangeError(%r)" % self.args + + >>> def range_invariant(ob): + ... if ob.max < ob.min: + ... raise RangeError(ob) + +Определив этот инвариант мы можем использовать его в определении интерфейсов:: + + >>> class IRange(zope.interface.Interface): + ... min = zope.interface.Attribute("Lower bound") + ... max = zope.interface.Attribute("Upper bound") + ... + ... zope.interface.invariant(range_invariant) + +Интерфейсы имеют метод для проверки своих инвариантов:: + + >>> class Range(object): + ... zope.interface.implements(IRange) + ... + ... def __init__(self, min, max): + ... self.min, self.max = min, max + ... + ... def __repr__(self): + ... return "Range(%s, %s)" % (self.min, self.max) + + >>> IRange.validateInvariants(Range(1,2)) + >>> IRange.validateInvariants(Range(1,1)) + >>> IRange.validateInvariants(Range(2,1)) + Traceback (most recent call last): + ... + RangeError: Range(2, 1) + +В случае нескольких инвариантов мы можем захотеть остановить проверку после +первой ошибки. Если мы передадим в `validateInvariants` пустой список тогда +будет выкинуто единственное исключение `Invalid` со списком исключений +как аргументом:: + + >>> from zope.interface.exceptions import Invalid + >>> errors = [] + >>> try: + ... IRange.validateInvariants(Range(2,1), errors) + ... except Invalid, e: + ... str(e) + '[RangeError(Range(2, 1))]' + +И список будет заполнен индивидуальными исключениями:: + + >>> errors + [RangeError(Range(2, 1))] + + >>> del errors[:] + +Адаптация +========= + +Интерфейсы могут быть вызваны для осуществления адаптации. Эта семантика +основана на функции adapt из PEP 246. Если объект не может быть адаптирован +будет выкинут TypeError:: + + >>> class I(zope.interface.Interface): + ... pass + + >>> I(0) + Traceback (most recent call last): + ... + TypeError: ('Could not adapt', 0, ) + +только если альтернативное значение не передано как второй аргумент:: + + >>> I(0, 'bob') + 'bob' + +Если объект уже реализует нужный интерфейс он будет возвращен:: + + >>> class C(object): + ... zope.interface.implements(I) + + >>> obj = C() + >>> I(obj) is obj + True + +Если объект реализует __conform__, тогда она будет использована:: + + >>> class C(object): + ... zope.interface.implements(I) + ... def __conform__(self, proto): + ... return 0 + + >>> I(C()) + 0 + +Также если присутствуют функции для вызова адаптации (см. __adapt__) они будут +использованы:: + + >>> from zope.interface.interface import adapter_hooks + >>> def adapt_0_to_42(iface, obj): + ... if obj == 0: + ... return 42 + + >>> adapter_hooks.append(adapt_0_to_42) + >>> I(0) + 42 + + >>> adapter_hooks.remove(adapt_0_to_42) + >>> I(0) + Traceback (most recent call last): + ... + TypeError: ('Could not adapt', 0, ) + + +__adapt__ +--------- + + >>> class I(zope.interface.Interface): + ... pass + +Интерфейсы реализуют метод __adapt__ из PEP 246. Этот метод обычно не +вызывается напрямую. Он вызывается архитектурой адаптации из PEP 246 и методом +__call__ интерфейсов. Метод адаптации отвечает за адаптацию объекта к +получателю. Версия по умолчанию возвращает None:: + + >>> I.__adapt__(0) + +если только переданный объект не предоставляет нужный интерфейс:: + + >>> class C(object): + ... zope.interface.implements(I) + + >>> obj = C() + >>> I.__adapt__(obj) is obj + True + +Функции для вызова адаптации могут быть добавлены (или удалены) для +предоставления адаптации "на заказ". Мы установим глупую функцию которая +адаптирует 0 к 42. Мы устанавливаем функцию просто добавляя ее к списку +adapter_hooks:: + + >>> from zope.interface.interface import adapter_hooks + >>> def adapt_0_to_42(iface, obj): + ... if obj == 0: + ... return 42 + + >>> adapter_hooks.append(adapt_0_to_42) + >>> I.__adapt__(0) + 42 + +Функции должны возвращать либо адаптер, либо None если адаптер не найден. +Функции могут быть удалены удалением их из списка:: + + >>> adapter_hooks.remove(adapt_0_to_42) + >>> I.__adapt__(0) + + +.. [#create] Основная причина по которой мы наследуемся от `Interface` - это + что бы быть уверенными в том, что ключевое слово class будет + создавать интерфейс, а не класс. + + Есть возможность создать интерфейсы вызвав специальный + класс интерфейса напрямую. Делая это, возможно (и в редких + случаях полезно) создать интерфейсы которые не наследуются + от `Interface`. Однако использование этой техники выходит + за рамки данного документа. + +.. [#factory] Классы - это фабрики. Они могут быть вызваны для создания + своих экземпляров. Мы ожидаем что в итоге мы расширим + концепцию реализации на другие типы фабрик, таким образом + мы сможем объявлять интерфейсы предоставляемые созданными + фабриками объектами. + +.. [#compat] Цель - заменяемость. Объект который предоставляет расширенный + интерфейс должен быть заменяем в качестве объектов которые + предоставляют расширяемый интерфейс. В нашем примере объект + который предоставляет IBaz должен быть используемым и + в случае если ожидается объект который предоставляет IBlat. + + Реализация интерфейса не требует этого. Но возможно в дальнейшем + она должна будет делать какие-либо проверки. diff -Nru zope3-3.4.0/src/zope/interface/README.txt zope3-3.5~bzr18/src/zope/interface/README.txt --- zope3-3.4.0/src/zope/interface/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,829 @@ +========== +Interfaces +========== + +Interfaces are objects that specify (document) the external behavior +of objects that "provide" them. An interface specifies behavior +through: + +- Informal documentation in a doc string + +- Attribute definitions + +- Invariants, which are conditions that must hold for objects that + provide the interface + +Attribute definitions specify specific attributes. They define the +attribute name and provide documentation and constraints of attribute +values. Attribute definitions can take a number of forms, as we'll +see below. + +Defining interfaces +=================== + +Interfaces are defined using Python class statements:: + + >>> import zope.interface + >>> class IFoo(zope.interface.Interface): + ... """Foo blah blah""" + ... + ... x = zope.interface.Attribute("""X blah blah""") + ... + ... def bar(q, r=None): + ... """bar blah blah""" + +In the example above, we've created an interface, `IFoo`. We +subclassed `zope.interface.Interface`, which is an ancestor interface for +all interfaces, much as `object` is an ancestor of all new-style +classes [#create]_. The interface is not a class, it's an Interface, +an instance of `InterfaceClass`:: + + >>> type(IFoo) + + +We can ask for the interface's documentation:: + + >>> IFoo.__doc__ + 'Foo blah blah' + +and its name:: + + >>> IFoo.__name__ + 'IFoo' + +and even its module:: + + >>> IFoo.__module__ + '__main__' + +The interface defined two attributes: + +`x` + This is the simplest form of attribute definition. It has a name + and a doc string. It doesn't formally specify anything else. + +`bar` + This is a method. A method is defined via a function definition. A + method is simply an attribute constrained to be a callable with a + particular signature, as provided by the function definition. + + Note that `bar` doesn't take a `self` argument. Interfaces document + how an object is *used*. When calling instance methods, you don't + pass a `self` argument, so a `self` argument isn't included in the + interface signature. The `self` argument in instance methods is + really an implementation detail of Python instances. Other objects, + besides instances can provide interfaces and their methods might not + be instance methods. For example, modules can provide interfaces and + their methods are usually just functions. Even instances can have + methods that are not instance methods. + +You can access the attributes defined by an interface using mapping +syntax:: + + >>> x = IFoo['x'] + >>> type(x) + + >>> x.__name__ + 'x' + >>> x.__doc__ + 'X blah blah' + + >>> IFoo.get('x').__name__ + 'x' + + >>> IFoo.get('y') + +You can use `in` to determine if an interface defines a name:: + + >>> 'x' in IFoo + True + +You can iterate over interfaces to get the names they define:: + + >>> names = list(IFoo) + >>> names.sort() + >>> names + ['bar', 'x'] + +Remember that interfaces aren't classes. You can't access attribute +definitions as attributes of interfaces:: + + >>> IFoo.x + Traceback (most recent call last): + File "", line 1, in ? + AttributeError: 'InterfaceClass' object has no attribute 'x' + +Methods provide access to the method signature:: + + >>> bar = IFoo['bar'] + >>> bar.getSignatureString() + '(q, r=None)' + +TODO + Methods really should have a better API. This is something that + needs to be improved. + +Declaring interfaces +==================== + +Having defined interfaces, we can *declare* that objects provide +them. Before we describe the details, lets define some terms: + +*provide* + We say that objects *provide* interfaces. If an object provides an + interface, then the interface specifies the behavior of the + object. In other words, interfaces specify the behavior of the + objects that provide them. + +*implement* + We normally say that classes *implement* interfaces. If a class + implements an interface, then the instances of the class provide + the interface. Objects provide interfaces that their classes + implement [#factory]_. (Objects can provide interfaces directly, + in addition to what their classes implement.) + + It is important to note that classes don't usually provide the + interfaces that they implement. + + We can generalize this to factories. For any callable object we + can declare that it produces objects that provide some interfaces + by saying that the factory implements the interfaces. + +Now that we've defined these terms, we can talk about the API for +declaring interfaces. + +Declaring implemented interfaces +-------------------------------- + +The most common way to declare interfaces is using the implements +function in a class statement:: + + >>> class Foo: + ... zope.interface.implements(IFoo) + ... + ... def __init__(self, x=None): + ... self.x = x + ... + ... def bar(self, q, r=None): + ... return q, r, self.x + ... + ... def __repr__(self): + ... return "Foo(%s)" % self.x + + +In this example, we declared that `Foo` implements `IFoo`. This means +that instances of `Foo` provide `IFoo`. Having made this declaration, +there are several ways we can introspect the declarations. First, we +can ask an interface whether it is implemented by a class:: + + >>> IFoo.implementedBy(Foo) + True + +And we can ask whether an interface is provided by an object:: + + >>> foo = Foo() + >>> IFoo.providedBy(foo) + True + +Of course, `Foo` doesn't provide `IFoo`, it implements it:: + + >>> IFoo.providedBy(Foo) + False + +We can also ask what interfaces are implemented by an object:: + + >>> list(zope.interface.implementedBy(Foo)) + [] + +It's an error to ask for interfaces implemented by a non-callable +object:: + + >>> IFoo.implementedBy(foo) + Traceback (most recent call last): + ... + TypeError: ('ImplementedBy called for non-factory', Foo(None)) + + >>> list(zope.interface.implementedBy(foo)) + Traceback (most recent call last): + ... + TypeError: ('ImplementedBy called for non-factory', Foo(None)) + +Similarly, we can ask what interfaces are provided by an object:: + + >>> list(zope.interface.providedBy(foo)) + [] + >>> list(zope.interface.providedBy(Foo)) + [] + +We can declare interfaces implemented by other factories (besides +classes). We do this using a Python-2.4-style decorator named +`implementer`. In versions of Python before 2.4, this looks like:: + + >>> def yfoo(y): + ... foo = Foo() + ... foo.y = y + ... return foo + >>> yfoo = zope.interface.implementer(IFoo)(yfoo) + + >>> list(zope.interface.implementedBy(yfoo)) + [] + +Note that the implementer decorator may modify it's argument. Callers +should not assume that a new object is created. + +Using implementer also works on callable objects. This is used by +zope.formlib, as an example. + + >>> class yfactory: + ... def __call__(self, y): + ... foo = Foo() + ... foo.y = y + ... return foo + >>> yfoo = yfactory() + >>> yfoo = zope.interface.implementer(IFoo)(yfoo) + + >>> list(zope.interface.implementedBy(yfoo)) + [] + +XXX: Double check and update these version numbers: + +In zope.interface 3.5.2 and lower, the implementor decorator can not +be used for classes, but in 3.6.0 and higher it can: + + >>> Foo = zope.interface.implementer(IFoo)(Foo) + >>> list(zope.interface.providedBy(Foo())) + [] + +Note that class decorators using the @implementor(IFoo) syntax are only +supported in Python 2.6 and later. + + +Declaring provided interfaces +----------------------------- + +We can declare interfaces directly provided by objects. Suppose that +we want to document what the `__init__` method of the `Foo` class +does. It's not *really* part of `IFoo`. You wouldn't normally call +the `__init__` method on Foo instances. Rather, the `__init__` method +is part of the `Foo`'s `__call__` method:: + + >>> class IFooFactory(zope.interface.Interface): + ... """Create foos""" + ... + ... def __call__(x=None): + ... """Create a foo + ... + ... The argument provides the initial value for x ... + ... """ + +It's the class that provides this interface, so we declare the +interface on the class:: + + >>> zope.interface.directlyProvides(Foo, IFooFactory) + +And then, we'll see that Foo provides some interfaces:: + + >>> list(zope.interface.providedBy(Foo)) + [] + >>> IFooFactory.providedBy(Foo) + True + +Declaring class interfaces is common enough that there's a special +declaration function for it, `classProvides`, that allows the +declaration from within a class statement:: + + >>> class Foo2: + ... zope.interface.implements(IFoo) + ... zope.interface.classProvides(IFooFactory) + ... + ... def __init__(self, x=None): + ... self.x = x + ... + ... def bar(self, q, r=None): + ... return q, r, self.x + ... + ... def __repr__(self): + ... return "Foo(%s)" % self.x + + >>> list(zope.interface.providedBy(Foo2)) + [] + >>> IFooFactory.providedBy(Foo2) + True + +There's a similar function, `moduleProvides`, that supports interface +declarations from within module definitions. For example, see the use +of `moduleProvides` call in `zope.interface.__init__`, which declares that +the package `zope.interface` provides `IInterfaceDeclaration`. + +Sometimes, we want to declare interfaces on instances, even though +those instances get interfaces from their classes. Suppose we create +a new interface, `ISpecial`:: + + >>> class ISpecial(zope.interface.Interface): + ... reason = zope.interface.Attribute("Reason why we're special") + ... def brag(): + ... "Brag about being special" + +We can make an existing foo instance special by providing `reason` +and `brag` attributes:: + + >>> foo.reason = 'I just am' + >>> def brag(): + ... return "I'm special!" + >>> foo.brag = brag + >>> foo.reason + 'I just am' + >>> foo.brag() + "I'm special!" + +and by declaring the interface:: + + >>> zope.interface.directlyProvides(foo, ISpecial) + +then the new interface is included in the provided interfaces:: + + >>> ISpecial.providedBy(foo) + True + >>> list(zope.interface.providedBy(foo)) + [, ] + +We can find out what interfaces are directly provided by an object:: + + >>> list(zope.interface.directlyProvidedBy(foo)) + [] + + >>> newfoo = Foo() + >>> list(zope.interface.directlyProvidedBy(newfoo)) + [] + +Inherited declarations +---------------------- + +Normally, declarations are inherited:: + + >>> class SpecialFoo(Foo): + ... zope.interface.implements(ISpecial) + ... reason = 'I just am' + ... def brag(self): + ... return "I'm special because %s" % self.reason + + >>> list(zope.interface.implementedBy(SpecialFoo)) + [, ] + + >>> list(zope.interface.providedBy(SpecialFoo())) + [, ] + +Sometimes, you don't want to inherit declarations. In that case, you +can use `implementsOnly`, instead of `implements`:: + + >>> class Special(Foo): + ... zope.interface.implementsOnly(ISpecial) + ... reason = 'I just am' + ... def brag(self): + ... return "I'm special because %s" % self.reason + + >>> list(zope.interface.implementedBy(Special)) + [] + + >>> list(zope.interface.providedBy(Special())) + [] + +External declarations +--------------------- + +Normally, we make implementation declarations as part of a class +definition. Sometimes, we may want to make declarations from outside +the class definition. For example, we might want to declare interfaces +for classes that we didn't write. The function `classImplements` can +be used for this purpose:: + + >>> class C: + ... pass + + >>> zope.interface.classImplements(C, IFoo) + >>> list(zope.interface.implementedBy(C)) + [] + +We can use `classImplementsOnly` to exclude inherited interfaces:: + + >>> class C(Foo): + ... pass + + >>> zope.interface.classImplementsOnly(C, ISpecial) + >>> list(zope.interface.implementedBy(C)) + [] + + + +Declaration Objects +------------------- + +When we declare interfaces, we create *declaration* objects. When we +query declarations, declaration objects are returned:: + + >>> type(zope.interface.implementedBy(Special)) + + +Declaration objects and interface objects are similar in many ways. In +fact, they share a common base class. The important thing to realize +about them is that they can be used where interfaces are expected in +declarations. Here's a silly example:: + + >>> class Special2(Foo): + ... zope.interface.implementsOnly( + ... zope.interface.implementedBy(Foo), + ... ISpecial, + ... ) + ... reason = 'I just am' + ... def brag(self): + ... return "I'm special because %s" % self.reason + +The declaration here is almost the same as +``zope.interface.implements(ISpecial)``, except that the order of +interfaces in the resulting declaration is different:: + + >>> list(zope.interface.implementedBy(Special2)) + [, ] + + +Interface Inheritance +===================== + +Interfaces can extend other interfaces. They do this simply by listing +the other interfaces as base interfaces:: + + >>> class IBlat(zope.interface.Interface): + ... """Blat blah blah""" + ... + ... y = zope.interface.Attribute("y blah blah") + ... def eek(): + ... """eek blah blah""" + + >>> IBlat.__bases__ + (,) + + >>> class IBaz(IFoo, IBlat): + ... """Baz blah""" + ... def eek(a=1): + ... """eek in baz blah""" + ... + + >>> IBaz.__bases__ + (, ) + + >>> names = list(IBaz) + >>> names.sort() + >>> names + ['bar', 'eek', 'x', 'y'] + +Note that `IBaz` overrides eek:: + + >>> IBlat['eek'].__doc__ + 'eek blah blah' + >>> IBaz['eek'].__doc__ + 'eek in baz blah' + +We were careful to override eek in a compatible way. When extending +an interface, the extending interface should be compatible [#compat]_ +with the extended interfaces. + +We can ask whether one interface extends another:: + + >>> IBaz.extends(IFoo) + True + >>> IBlat.extends(IFoo) + False + +Note that interfaces don't extend themselves:: + + >>> IBaz.extends(IBaz) + False + +Sometimes we wish they did, but we can, instead use `isOrExtends`:: + + >>> IBaz.isOrExtends(IBaz) + True + >>> IBaz.isOrExtends(IFoo) + True + >>> IFoo.isOrExtends(IBaz) + False + +When we iterate over an interface, we get all of the names it defines, +including names defined by base interfaces. Sometimes, we want *just* +the names defined by the interface directly. We bane use the `names` +method for that:: + + >>> list(IBaz.names()) + ['eek'] + +Inheritance of attribute specifications +--------------------------------------- + +An interface may override attribute definitions from base interfaces. +If two base interfaces define the same attribute, the attribute is +inherited from the most specific interface. For example, with:: + + >>> class IBase(zope.interface.Interface): + ... + ... def foo(): + ... "base foo doc" + + >>> class IBase1(IBase): + ... pass + + >>> class IBase2(IBase): + ... + ... def foo(): + ... "base2 foo doc" + + >>> class ISub(IBase1, IBase2): + ... pass + +ISub's definition of foo is the one from IBase2, since IBase2 is more +specific that IBase:: + + >>> ISub['foo'].__doc__ + 'base2 foo doc' + +Note that this differs from a depth-first search. + +Sometimes, it's useful to ask whether an interface defines an +attribute directly. You can use the direct method to get a directly +defined definitions:: + + >>> IBase.direct('foo').__doc__ + 'base foo doc' + + >>> ISub.direct('foo') + +Specifications +-------------- + +Interfaces and declarations are both special cases of specifications. +What we described above for interface inheritance applies to both +declarations and specifications. Declarations actually extend the +interfaces that they declare:: + + >>> class Baz(object): + ... zope.interface.implements(IBaz) + + >>> baz_implements = zope.interface.implementedBy(Baz) + >>> baz_implements.__bases__ + (, ) + + >>> baz_implements.extends(IFoo) + True + + >>> baz_implements.isOrExtends(IFoo) + True + >>> baz_implements.isOrExtends(baz_implements) + True + +Specifications (interfaces and declarations) provide an `__sro__` +that lists the specification and all of it's ancestors:: + + >>> baz_implements.__sro__ + (, + , + , + , + , + ) + + +Tagged Values +============= + +Interfaces and attribute descriptions support an extension mechanism, +borrowed from UML, called "tagged values" that lets us store extra +data:: + + >>> IFoo.setTaggedValue('date-modified', '2004-04-01') + >>> IFoo.setTaggedValue('author', 'Jim Fulton') + >>> IFoo.getTaggedValue('date-modified') + '2004-04-01' + >>> IFoo.queryTaggedValue('date-modified') + '2004-04-01' + >>> IFoo.queryTaggedValue('datemodified') + >>> tags = list(IFoo.getTaggedValueTags()) + >>> tags.sort() + >>> tags + ['author', 'date-modified'] + +Function attributes are converted to tagged values when method +attribute definitions are created:: + + >>> class IBazFactory(zope.interface.Interface): + ... def __call__(): + ... "create one" + ... __call__.return_type = IBaz + + >>> IBazFactory['__call__'].getTaggedValue('return_type') + + +Tagged values can also be defined from within an interface definition:: + + >>> class IWithTaggedValues(zope.interface.Interface): + ... zope.interface.taggedValue('squish', 'squash') + >>> IWithTaggedValues.getTaggedValue('squish') + 'squash' + +Invariants +========== + +Interfaces can express conditions that must hold for objects that +provide them. These conditions are expressed using one or more +invariants. Invariants are callable objects that will be called with +an object that provides an interface. An invariant raises an `Invalid` +exception if the condition doesn't hold. Here's an example:: + + >>> class RangeError(zope.interface.Invalid): + ... """A range has invalid limits""" + ... def __repr__(self): + ... return "RangeError(%r)" % self.args + + >>> def range_invariant(ob): + ... if ob.max < ob.min: + ... raise RangeError(ob) + +Given this invariant, we can use it in an interface definition:: + + >>> class IRange(zope.interface.Interface): + ... min = zope.interface.Attribute("Lower bound") + ... max = zope.interface.Attribute("Upper bound") + ... + ... zope.interface.invariant(range_invariant) + +Interfaces have a method for checking their invariants:: + + >>> class Range(object): + ... zope.interface.implements(IRange) + ... + ... def __init__(self, min, max): + ... self.min, self.max = min, max + ... + ... def __repr__(self): + ... return "Range(%s, %s)" % (self.min, self.max) + + >>> IRange.validateInvariants(Range(1,2)) + >>> IRange.validateInvariants(Range(1,1)) + >>> IRange.validateInvariants(Range(2,1)) + Traceback (most recent call last): + ... + RangeError: Range(2, 1) + +If you have multiple invariants, you may not want to stop checking +after the first error. If you pass a list to `validateInvariants`, +then a single `Invalid` exception will be raised with the list of +exceptions as it's argument:: + + >>> from zope.interface.exceptions import Invalid + >>> errors = [] + >>> try: + ... IRange.validateInvariants(Range(2,1), errors) + ... except Invalid, e: + ... str(e) + '[RangeError(Range(2, 1))]' + +And the list will be filled with the individual exceptions:: + + >>> errors + [RangeError(Range(2, 1))] + + + >>> del errors[:] + +Adaptation +========== + +Interfaces can be called to perform adaptation. + +The semantics are based on those of the PEP 246 adapt function. + +If an object cannot be adapted, then a TypeError is raised:: + + >>> class I(zope.interface.Interface): + ... pass + + >>> I(0) + Traceback (most recent call last): + ... + TypeError: ('Could not adapt', 0, ) + + + +unless an alternate value is provided as a second positional argument:: + + >>> I(0, 'bob') + 'bob' + +If an object already implements the interface, then it will be returned:: + + >>> class C(object): + ... zope.interface.implements(I) + + >>> obj = C() + >>> I(obj) is obj + True + +If an object implements __conform__, then it will be used:: + + >>> class C(object): + ... zope.interface.implements(I) + ... def __conform__(self, proto): + ... return 0 + + >>> I(C()) + 0 + +Adapter hooks (see __adapt__) will also be used, if present:: + + >>> from zope.interface.interface import adapter_hooks + >>> def adapt_0_to_42(iface, obj): + ... if obj == 0: + ... return 42 + + >>> adapter_hooks.append(adapt_0_to_42) + >>> I(0) + 42 + + >>> adapter_hooks.remove(adapt_0_to_42) + >>> I(0) + Traceback (most recent call last): + ... + TypeError: ('Could not adapt', 0, ) + +__adapt__ +--------- + + >>> class I(zope.interface.Interface): + ... pass + +Interfaces implement the PEP 246 __adapt__ method. + +This method is normally not called directly. It is called by the PEP +246 adapt framework and by the interface __call__ operator. + +The adapt method is responsible for adapting an object to the +reciever. + +The default version returns None:: + + >>> I.__adapt__(0) + +unless the object given provides the interface:: + + >>> class C(object): + ... zope.interface.implements(I) + + >>> obj = C() + >>> I.__adapt__(obj) is obj + True + +Adapter hooks can be provided (or removed) to provide custom +adaptation. We'll install a silly hook that adapts 0 to 42. +We install a hook by simply adding it to the adapter_hooks +list:: + + >>> from zope.interface.interface import adapter_hooks + >>> def adapt_0_to_42(iface, obj): + ... if obj == 0: + ... return 42 + + >>> adapter_hooks.append(adapt_0_to_42) + >>> I.__adapt__(0) + 42 + +Hooks must either return an adapter, or None if no adapter can +be found. + +Hooks can be uninstalled by removing them from the list:: + + >>> adapter_hooks.remove(adapt_0_to_42) + >>> I.__adapt__(0) + + +.. [#create] The main reason we subclass `Interface` is to cause the + Python class statement to create an interface, rather + than a class. + + It's possible to create interfaces by calling a special + interface class directly. Doing this, it's possible + (and, on rare occasions, useful) to create interfaces + that don't descend from `Interface`. Using this + technique is beyond the scope of this document. + +.. [#factory] Classes are factories. They can be called to create + their instances. We expect that we will eventually + extend the concept of implementation to other kinds of + factories, so that we can declare the interfaces + provided by the objects created. + +.. [#compat] The goal is substitutability. An object that provides an + extending interface should be substitutable for an object + that provides the extended interface. In our example, an + object that provides IBaz should be usable whereever an + object that provides IBlat is expected. + + The interface implementation doesn't enforce this. + but maybe it should do some checks. diff -Nru zope3-3.4.0/src/zope/interface/ro.py zope3-3.5~bzr18/src/zope/interface/ro.py --- zope3-3.4.0/src/zope/interface/ro.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/ro.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,71 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Compute a resolution order for an object and its bases + +$Id: ro.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +__docformat__ = 'restructuredtext' + + +def ro(object): + """Compute a "resolution order" for an object + """ + return mergeOrderings([_flatten(object)]) + +def mergeOrderings(orderings, seen=None): + """Merge multiple orderings so that within-ordering order is preserved + + Orderings are constrained in such a way that if an object appears + in two or more orderings, then the suffix that begins with the + object must be in both orderings. + + For example: + + >>> _mergeOrderings([ + ... ['x', 'y', 'z'], + ... ['q', 'z'], + ... [1, 3, 5], + ... ['z'] + ... ]) + ['x', 'y', 'q', 1, 3, 5, 'z'] + + """ + + if seen is None: + seen = {} + result = [] + orderings.reverse() + for ordering in orderings: + ordering = list(ordering) + ordering.reverse() + for o in ordering: + if o not in seen: + seen[o] = 1 + result.append(o) + + result.reverse() + return result + +def _flatten(ob): + result = [ob] + i = 0 + for ob in iter(result): + i += 1 + # The recursive calls can be avoided by inserting the base classes + # into the dynamically growing list directly after the currently + # considered object; the iterator makes sure this will keep working + # in the future, since it cannot rely on the length of the list + # by definition. + result[i:i] = ob.__bases__ + return result diff -Nru zope3-3.4.0/src/zope/interface/tests/dummy.py zope3-3.5~bzr18/src/zope/interface/tests/dummy.py --- zope3-3.4.0/src/zope/interface/tests/dummy.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/dummy.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,24 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Dummy Module + +$Id: dummy.py 110736 2010-04-11 10:59:30Z regebro $ +""" +from zope.interface import moduleProvides +from zope.interface.tests.ifoo import IFoo + +moduleProvides(IFoo) + +def bar(baz): + pass diff -Nru zope3-3.4.0/src/zope/interface/tests/foodforthought.txt zope3-3.5~bzr18/src/zope/interface/tests/foodforthought.txt --- zope3-3.4.0/src/zope/interface/tests/foodforthought.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/foodforthought.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,61 @@ +================================ +Food-based subscription examples +================================ + + +This file gives more subscription examples using a cooking-based example:: + + >>> from zope.interface.adapter import AdapterRegistry + >>> registry = AdapterRegistry() + + >>> import zope.interface + >>> class IAnimal(zope.interface.Interface): + ... pass + >>> class IPoultry(IAnimal): + ... pass + >>> class IChicken(IPoultry): + ... pass + >>> class ISeafood(IAnimal): + ... pass + +Adapting to some other interface for which there is no +subscription adapter returns an empty sequence:: + + >>> class IRecipe(zope.interface.Interface): + ... pass + >>> class ISausages(IRecipe): + ... pass + >>> class INoodles(IRecipe): + ... pass + >>> class IKFC(IRecipe): + ... pass + + >>> list(registry.subscriptions([IPoultry], IRecipe)) + [] + +unless we define a subscription:: + + >>> registry.subscribe([IAnimal], ISausages, 'sausages') + >>> list(registry.subscriptions([IPoultry], ISausages)) + ['sausages'] + +And define another subscription adapter:: + + >>> registry.subscribe([IPoultry], INoodles, 'noodles') + >>> meals = list(registry.subscriptions([IPoultry], IRecipe)) + >>> meals.sort() + >>> meals + ['noodles', 'sausages'] + + >>> registry.subscribe([IChicken], IKFC, 'kfc') + >>> meals = list(registry.subscriptions([IChicken], IRecipe)) + >>> meals.sort() + >>> meals + ['kfc', 'noodles', 'sausages'] + +And the answer for poultry hasn't changed:: + + >>> meals = list(registry.subscriptions([IPoultry], IRecipe)) + >>> meals.sort() + >>> meals + ['noodles', 'sausages'] diff -Nru zope3-3.4.0/src/zope/interface/tests/ifoo.py zope3-3.5~bzr18/src/zope/interface/tests/ifoo.py --- zope3-3.4.0/src/zope/interface/tests/ifoo.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/ifoo.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,28 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""IFoo test module + +$Id: ifoo.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +from zope.interface import Interface + +class IFoo(Interface): + """ + Dummy interface for unit tests. + """ + + def bar(baz): + """ + Just a note. + """ diff -Nru zope3-3.4.0/src/zope/interface/tests/__init__.py zope3-3.5~bzr18/src/zope/interface/tests/__init__.py --- zope3-3.4.0/src/zope/interface/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,13 @@ +import os +import unittest + +def additional_tests(): + suites = unittest.TestSuite() + for file in os.listdir(os.path.dirname(__file__)): + if file.endswith('.py') and file!='__init__.py': + name = os.path.splitext(file)[0] + module = __import__('.'.join((__name__, name)), globals(), + locals(), [name]) + if hasattr(module, 'test_suite'): + suites.addTests(module.test_suite()) + return suites diff -Nru zope3-3.4.0/src/zope/interface/tests/m1.py zope3-3.5~bzr18/src/zope/interface/tests/m1.py --- zope3-3.4.0/src/zope/interface/tests/m1.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/m1.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,23 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Test module that declares an interface + +$Id: m1.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +from zope.interface import Interface, moduleProvides + +class I1(Interface): pass +class I2(Interface): pass + +moduleProvides(I1, I2) diff -Nru zope3-3.4.0/src/zope/interface/tests/m2.py zope3-3.5~bzr18/src/zope/interface/tests/m2.py --- zope3-3.4.0/src/zope/interface/tests/m2.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/m2.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Test module that doesn't declare an interface + +$Id: m2.py 110536 2010-04-06 02:59:44Z tseaver $ +""" diff -Nru zope3-3.4.0/src/zope/interface/tests/odd.py zope3-3.5~bzr18/src/zope/interface/tests/odd.py --- zope3-3.4.0/src/zope/interface/tests/odd.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/odd.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,131 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Odd meta class that doesn't subclass type. + +This is used for testing support for ExtensionClass in new interfaces. + + >>> class A(object): + ... __metaclass__ = MetaClass + ... a = 1 + ... + >>> A.__name__ + 'A' + >>> A.__bases__ == (object,) + True + >>> class B(object): + ... __metaclass__ = MetaClass + ... b = 1 + ... + >>> class C(A, B): pass + ... + >>> C.__name__ + 'C' + >>> int(C.__bases__ == (A, B)) + 1 + >>> a = A() + >>> aa = A() + >>> a.a + 1 + >>> aa.a + 1 + >>> aa.a = 2 + >>> a.a + 1 + >>> aa.a + 2 + >>> c = C() + >>> c.a + 1 + >>> c.b + 1 + >>> c.b = 2 + >>> c.b + 2 + >>> C.c = 1 + >>> c.c + 1 + >>> import sys + >>> if sys.version[0] == '2': # This test only makes sense under Python 2.x + ... from types import ClassType + ... assert not isinstance(C, (type, ClassType)) + + >>> int(C.__class__.__class__ is C.__class__) + 1 + +$Id: odd.py 110699 2010-04-09 08:16:17Z regebro $ +""" + +# class OddClass is an odd meta class + +class MetaMetaClass(type): + + def __getattribute__(self, name): + if name == '__class__': + return self + return type.__getattribute__(self, name) + + +class MetaClass(object): + """Odd classes + """ + __metaclass__ = MetaMetaClass + + def __init__(self, name, bases, dict): + self.__name__ = name + self.__bases__ = bases + self.__dict__.update(dict) + + def __call__(self): + return OddInstance(self) + + def __getattr__(self, name): + for b in self.__bases__: + v = getattr(b, name, self) + if v is not self: + return v + raise AttributeError(name) + + def __repr__(self): + return "" % (self.__name__, hex(id(self))) + +class OddInstance(object): + + def __init__(self, cls): + self.__dict__['__class__'] = cls + + def __getattribute__(self, name): + dict = object.__getattribute__(self, '__dict__') + if name == '__dict__': + return dict + v = dict.get(name, self) + if v is not self: + return v + return getattr(dict['__class__'], name) + + def __setattr__(self, name, v): + self.__dict__[name] = v + + def __delattr__(self, name): + del self.__dict__[name] + + def __repr__(self): + return "" % ( + self.__class__.__name__, hex(id(self))) + + + +# DocTest: +if __name__ == "__main__": + import doctest, __main__ + doctest.testmod(__main__, isprivate=lambda *a: False) diff -Nru zope3-3.4.0/src/zope/interface/tests/test_adapter.py zope3-3.5~bzr18/src/zope/interface/tests/test_adapter.py --- zope3-3.4.0/src/zope/interface/tests/test_adapter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_adapter.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,414 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Adapter registry tests + +$Id: test_adapter.py 110736 2010-04-11 10:59:30Z regebro $ +""" +import doctest +import unittest +import zope.interface +from zope.interface.adapter import AdapterRegistry + + +class IF0(zope.interface.Interface): + pass +class IF1(IF0): + pass + +class IB0(zope.interface.Interface): + pass +class IB1(IB0): + pass + +class IR0(zope.interface.Interface): + pass +class IR1(IR0): + pass + +def test_multi_adapter_get_best_match(): + """ + >>> registry = AdapterRegistry() + + >>> class IB2(IB0): + ... pass + >>> class IB3(IB2, IB1): + ... pass + >>> class IB4(IB1, IB2): + ... pass + + >>> registry.register([None, IB1], IR0, '', 'A1') + >>> registry.register([None, IB0], IR0, '', 'A0') + >>> registry.register([None, IB2], IR0, '', 'A2') + + >>> registry.lookup((IF1, IB1), IR0, '') + 'A1' + >>> registry.lookup((IF1, IB2), IR0, '') + 'A2' + >>> registry.lookup((IF1, IB0), IR0, '') + 'A0' + >>> registry.lookup((IF1, IB3), IR0, '') + 'A2' + >>> registry.lookup((IF1, IB4), IR0, '') + 'A1' + """ + +def test_multi_adapter_lookupAll_get_best_matches(): + """ + >>> registry = AdapterRegistry() + + >>> class IB2(IB0): + ... pass + >>> class IB3(IB2, IB1): + ... pass + >>> class IB4(IB1, IB2): + ... pass + + >>> registry.register([None, IB1], IR0, '', 'A1') + >>> registry.register([None, IB0], IR0, '', 'A0') + >>> registry.register([None, IB2], IR0, '', 'A2') + + >>> tuple(registry.lookupAll((IF1, IB1), IR0))[0][1] + 'A1' + >>> tuple(registry.lookupAll((IF1, IB2), IR0))[0][1] + 'A2' + >>> tuple(registry.lookupAll((IF1, IB0), IR0))[0][1] + 'A0' + >>> tuple(registry.lookupAll((IF1, IB3), IR0))[0][1] + 'A2' + >>> tuple(registry.lookupAll((IF1, IB4), IR0))[0][1] + 'A1' + """ + + +def test_multi_adapter_w_default(): + """ + >>> registry = AdapterRegistry() + + >>> registry.register([None, None], IB1, 'bob', 'A0') + + >>> registry.lookup((IF1, IR1), IB0, 'bob') + 'A0' + + >>> registry.register([None, IR0], IB1, 'bob', 'A1') + + >>> registry.lookup((IF1, IR1), IB0, 'bob') + 'A1' + + >>> registry.lookup((IF1, IR1), IB0, 'bruce') + + >>> registry.register([None, IR1], IB1, 'bob', 'A2') + >>> registry.lookup((IF1, IR1), IB0, 'bob') + 'A2' + """ + +def test_multi_adapter_w_inherited_and_multiple_registrations(): + """ + >>> registry = AdapterRegistry() + + >>> class IX(zope.interface.Interface): + ... pass + + >>> registry.register([IF0, IR0], IB1, 'bob', 'A1') + >>> registry.register([IF1, IX], IB1, 'bob', 'AX') + + >>> registry.lookup((IF1, IR1), IB0, 'bob') + 'A1' + """ + +def test_named_adapter_with_default(): + """Query a named simple adapter + + >>> registry = AdapterRegistry() + + If we ask for a named adapter, we won't get a result unless there + is a named adapter, even if the object implements the interface: + + >>> registry.lookup([IF1], IF0, 'bob') + + >>> registry.register([None], IB1, 'bob', 'A1') + >>> registry.lookup([IF1], IB0, 'bob') + 'A1' + + >>> registry.lookup([IF1], IB0, 'bruce') + + >>> registry.register([None], IB0, 'bob', 'A2') + >>> registry.lookup([IF1], IB0, 'bob') + 'A2' + """ + +def test_multi_adapter_gets_closest_provided(): + """ + >>> registry = AdapterRegistry() + >>> registry.register([IF1, IR0], IB0, 'bob', 'A1') + >>> registry.register((IF1, IR0), IB1, 'bob', 'A2') + >>> registry.lookup((IF1, IR1), IB0, 'bob') + 'A1' + + >>> registry = AdapterRegistry() + >>> registry.register([IF1, IR0], IB1, 'bob', 'A2') + >>> registry.register([IF1, IR0], IB0, 'bob', 'A1') + >>> registry.lookup([IF1, IR0], IB0, 'bob') + 'A1' + + >>> registry = AdapterRegistry() + >>> registry.register([IF1, IR0], IB0, 'bob', 'A1') + >>> registry.register([IF1, IR1], IB1, 'bob', 'A2') + >>> registry.lookup([IF1, IR1], IB0, 'bob') + 'A2' + + >>> registry = AdapterRegistry() + >>> registry.register([IF1, IR1], IB1, 'bob', 2) + >>> registry.register([IF1, IR0], IB0, 'bob', 1) + >>> registry.lookup([IF1, IR1], IB0, 'bob') + 2 + """ + +def test_multi_adapter_check_non_default_dont_hide_default(): + """ + >>> registry = AdapterRegistry() + + >>> class IX(zope.interface.Interface): + ... pass + + + >>> registry.register([None, IR0], IB0, 'bob', 1) + >>> registry.register([IF1, IX], IB0, 'bob', 2) + >>> registry.lookup([IF1, IR1], IB0, 'bob') + 1 + """ + +def test_adapter_hook_with_factory_producing_None(): + """ + >>> registry = AdapterRegistry() + >>> default = object() + + >>> class Object1(object): + ... zope.interface.implements(IF0) + >>> class Object2(object): + ... zope.interface.implements(IF0) + + >>> def factory(context): + ... if isinstance(context, Object1): + ... return 'adapter' + ... return None + + >>> registry.register([IF0], IB0, '', factory) + + >>> registry.adapter_hook(IB0, Object1()) + 'adapter' + >>> registry.adapter_hook(IB0, Object2()) is None + True + >>> registry.adapter_hook(IB0, Object2(), default=default) is default + True + """ + +def test_adapter_registry_update_upon_interface_bases_change(): + """ + Let's first create a adapter registry and a simple adaptation hook: + + >>> globalRegistry = AdapterRegistry() + + >>> def _hook(iface, ob, lookup=globalRegistry.lookup1): + ... factory = lookup(zope.interface.providedBy(ob), iface) + ... if factory is None: + ... return None + ... else: + ... return factory(ob) + + >>> zope.interface.interface.adapter_hooks.append(_hook) + + Now we create some interfaces and an implementation: + + >>> class IX(zope.interface.Interface): + ... pass + + >>> class IY(zope.interface.Interface): + ... pass + + >>> class X(object): + ... pass + + >>> class Y(object): + ... zope.interface.implements(IY) + ... def __init__(self, original): + ... self.original=original + + and register an adapter: + + >>> globalRegistry.register((IX,), IY, '', Y) + + at first, we still expect the adapter lookup from `X` to `IY` to fail: + + >>> IY(X()) #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS + Traceback (most recent call last): + ... + TypeError: ('Could not adapt', + , + ) + + But after we declare an interface on the class `X`, it should pass: + + >>> zope.interface.classImplementsOnly(X, IX) + + >>> IY(X()) #doctest: +ELLIPSIS + + + >>> hook = zope.interface.interface.adapter_hooks.pop() + """ + + +def test_changing_declarations(): + """ + + If we change declarations for a class, those adapter lookup should + eflect the changes: + + >>> class I1(zope.interface.Interface): + ... pass + >>> class I2(zope.interface.Interface): + ... pass + + >>> registry = AdapterRegistry() + >>> registry.register([I1], I2, '', 42) + + >>> class C: + ... pass + + >>> registry.lookup([zope.interface.implementedBy(C)], I2, '') + + >>> zope.interface.classImplements(C, I1) + + >>> registry.lookup([zope.interface.implementedBy(C)], I2, '') + 42 + """ + +def test_correct_multi_adapter_lookup(): + """ + >>> registry = AdapterRegistry() + >>> registry.register([IF0, IB1], IR0, '', 'A01') + >>> registry.register([IF1, IB0], IR0, '', 'A10') + >>> registry.lookup((IF1, IB1), IR0, '') + 'A10' + """ + +def test_duplicate_bases(): + """ +There was a bug that caused problems if a spec had multiple bases: + + >>> class I(zope.interface.Interface): + ... pass + >>> class I2(I, I): + ... pass + >>> registry = AdapterRegistry() + >>> registry.register([I2], IR0, 'x', 'X') + >>> registry.lookup((I2, ), IR0, 'x') + 'X' + >>> registry.register([I2], IR0, 'y', 'Y') + >>> registry.lookup((I2, ), IR0, 'x') + 'X' + >>> registry.lookup((I2, ), IR0, 'y') + 'Y' +""" + +def test_register_objects_with_cmp(): + """ + The registry should never use == as that will tend to fail when + objects are picky about what they are compared with: + + >>> class Picky: + ... def __cmp__(self, other): + ... raise TypeError("I\'m too picky for comparison!") + >>> class I(zope.interface.Interface): + ... pass + >>> class I2(I, I): + ... pass + + >>> registry = AdapterRegistry() + >>> picky = Picky() + >>> registry.register([I2], IR0, '', picky) + >>> registry.unregister([I2], IR0, '', picky) + + >>> registry.subscribe([I2], IR0, picky) + >>> registry.unsubscribe([I2], IR0, picky) + + """ + +def test_unregister_cleans_up_empties(): + """ + >>> class I(zope.interface.Interface): + ... pass + >>> class IP(zope.interface.Interface): + ... pass + >>> class C(object): + ... pass + + >>> registry = AdapterRegistry() + + >>> registry.register([], IP, '', C) + >>> registry.register([I], IP, '', C) + >>> registry.register([I], IP, 'name', C) + >>> registry.register([I, I], IP, '', C) + >>> len(registry._adapters) + 3 + >>> map(len, registry._adapters) + [1, 1, 1] + + >>> registry.unregister([], IP, '', C) + >>> registry.unregister([I], IP, '', C) + >>> registry.unregister([I], IP, 'name', C) + >>> registry.unregister([I, I], IP, '', C) + >>> registry._adapters + [] + + """ + +def test_unsubscribe_cleans_up_empties(): + """ + >>> class I1(zope.interface.Interface): + ... pass + >>> class I2(zope.interface.Interface): + ... pass + >>> class IP(zope.interface.Interface): + ... pass + + >>> registry = AdapterRegistry() + >>> def handler(event): + ... pass + + >>> registry.subscribe([I1], I1, handler) + >>> registry.subscribe([I2], I1, handler) + >>> len(registry._subscribers) + 2 + >>> map(len, registry._subscribers) + [0, 2] + + >>> registry.unsubscribe([I1], I1, handler) + >>> registry.unsubscribe([I2], I1, handler) + >>> registry._subscribers + [] + + """ + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('../adapter.txt', '../adapter.ru.txt', + '../human.txt', '../human.ru.txt', + 'foodforthought.txt', + globs={'__name__': '__main__'}), + doctest.DocTestSuite(), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/interface/tests/test_advice.py zope3-3.5~bzr18/src/zope/interface/tests/test_advice.py --- zope3-3.4.0/src/zope/interface/tests/test_advice.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_advice.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,187 @@ + +############################################################################## +# +# 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 advice + +This module was adapted from 'protocols.tests.advice', part of the Python +Enterprise Application Kit (PEAK). Please notify the PEAK authors +(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or +Zope-specific changes are required, so that the PEAK version of this module +can be kept in sync. + +PEAK is a Python application framework that interoperates with (but does +not require) Zope 3 and Twisted. It provides tools for manipulating UML +models, object-relational persistence, aspect-oriented programming, and more. +Visit the PEAK home page at http://peak.telecommunity.com for more information. + +$Id: test_advice.py 110736 2010-04-11 10:59:30Z regebro $ +""" + +import unittest +from unittest import TestCase, makeSuite, TestSuite +from zope.interface.advice import addClassAdvisor, determineMetaclass +from zope.interface.advice import getFrameInfo +import sys + +def ping(log, value): + + def pong(klass): + log.append((value,klass)) + return [klass] + + addClassAdvisor(pong) + +try: + from types import ClassType + + class ClassicClass: + __metaclass__ = ClassType + classLevelFrameInfo = getFrameInfo(sys._getframe()) +except ImportError: + pass + +class NewStyleClass: + __metaclass__ = type + classLevelFrameInfo = getFrameInfo(sys._getframe()) + +moduleLevelFrameInfo = getFrameInfo(sys._getframe()) + +class FrameInfoTest(TestCase): + + classLevelFrameInfo = getFrameInfo(sys._getframe()) + + def checkModuleInfo(self): + kind, module, f_locals, f_globals = moduleLevelFrameInfo + self.assertEquals(kind, "module") + for d in module.__dict__, f_locals, f_globals: + self.assert_(d is globals()) + + def checkClassicClassInfo(self): + kind, module, f_locals, f_globals = ClassicClass.classLevelFrameInfo + self.assertEquals(kind, "class") + + self.assert_(f_locals is ClassicClass.__dict__) # ??? + for d in module.__dict__, f_globals: + self.assert_(d is globals()) + + def checkNewStyleClassInfo(self): + kind, module, f_locals, f_globals = NewStyleClass.classLevelFrameInfo + self.assertEquals(kind, "class") + + for d in module.__dict__, f_globals: + self.assert_(d is globals()) + + def checkCallInfo(self): + kind, module, f_locals, f_globals = getFrameInfo(sys._getframe()) + self.assertEquals(kind, "function call") + self.assert_(f_locals is locals()) # ??? + for d in module.__dict__, f_globals: + self.assert_(d is globals()) + + +class AdviceTests(TestCase): + + def checkOrder(self): + log = [] + class Foo(object): + ping(log, 1) + ping(log, 2) + ping(log, 3) + + # Strip the list nesting + for i in 1,2,3: + self.assert_(isinstance(Foo, list)) + Foo, = Foo + + self.assertEquals(log, [(1, Foo), (2, [Foo]), (3, [[Foo]])]) + + def TODOcheckOutside(self): + # Disabled because the check does not work with doctest tests. + try: + ping([], 1) + except SyntaxError: + pass + else: + raise AssertionError( + "Should have detected advice outside class body" + ) + + def checkDoubleType(self): + if sys.hexversion >= 0x02030000: + return # you can't duplicate bases in 2.3 + class aType(type,type): + ping([],1) + aType, = aType + self.assert_(aType.__class__ is type) + + def checkSingleExplicitMeta(self): + + class M(type): + pass + + class C(M): + __metaclass__ = M + ping([],1) + + C, = C + self.assert_(C.__class__ is M) + + + def checkMixedMetas(self): + + class M1(type): pass + class M2(type): pass + + class B1: __metaclass__ = M1 + class B2: __metaclass__ = M2 + + try: + class C(B1,B2): + ping([],1) + except TypeError: + pass + else: + raise AssertionError("Should have gotten incompatibility error") + + class M3(M1,M2): pass + + class C(B1,B2): + __metaclass__ = M3 + ping([],1) + + self.assert_(isinstance(C,list)) + C, = C + self.assert_(isinstance(C,M3)) + + def checkMetaOfClass(self): + + class metameta(type): + pass + + class meta(type): + __metaclass__ = metameta + + self.assertEquals(determineMetaclass((meta, type)), metameta) + +TestClasses = (AdviceTests, FrameInfoTest) + +def test_suite(): + if sys.version[0] == '2': + return TestSuite([makeSuite(t,'check') for t in TestClasses]) + else: + # Advise metaclasses doesn't work in Python 3 + return [] + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/interface/tests/test_declarations.py zope3-3.5~bzr18/src/zope/interface/tests/test_declarations.py --- zope3-3.4.0/src/zope/interface/tests/test_declarations.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_declarations.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,430 @@ +############################################################################## +# +# 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 the new API for making and checking interface declarations + +$Id: test_declarations.py 110827 2010-04-13 20:33:31Z tseaver $ +""" +import doctest +import unittest + +from zope.interface import Interface, implements +from zope.interface import directlyProvides, providedBy +from zope.interface import classImplements, implementedBy, implementsOnly + +class I1(Interface): pass +class I2(Interface): pass +class I3(Interface): pass +class I4(Interface): pass +class I5(Interface): pass + +class A(object): + implements(I1) +class B(object): + implements(I2) +class C(A, B): + implements(I3) + +class COnly(A, B): + implementsOnly(I3) + +class COnly_old(A, B): + __implemented__ = I3 + +class D(COnly): + implements(I5) + +def test_ObjectSpecification_Simple(): + """ + >>> c = C() + >>> directlyProvides(c, I4) + >>> [i.__name__ for i in providedBy(c)] + ['I4', 'I3', 'I1', 'I2'] + """ + +def test_ObjectSpecification_Simple_w_only(): + """ + >>> c = COnly() + >>> directlyProvides(c, I4) + >>> [i.__name__ for i in providedBy(c)] + ['I4', 'I3'] + """ + +def test_ObjectSpecification_Simple_old_style(): + """ + >>> c = COnly_old() + >>> directlyProvides(c, I4) + >>> [i.__name__ for i in providedBy(c)] + ['I4', 'I3'] + """ + + +class Test(unittest.TestCase): + + # Note that most of the tests are in the doc strings of the + # declarations module. + + def test_backward_compat(self): + + class C1(object): __implemented__ = I1 + class C2(C1): __implemented__ = I2, I5 + class C3(C2): __implemented__ = I3, C2.__implemented__ + + self.assert_(C3.__implemented__.__class__ is tuple) + + self.assertEqual( + [i.getName() for i in providedBy(C3())], + ['I3', 'I2', 'I5'], + ) + + class C4(C3): + implements(I4) + + self.assertEqual( + [i.getName() for i in providedBy(C4())], + ['I4', 'I3', 'I2', 'I5'], + ) + + self.assertEqual( + [i.getName() for i in C4.__implemented__], + ['I4', 'I3', 'I2', 'I5'], + ) + + # Note that C3.__implemented__ should now be a sequence of interfaces + self.assertEqual( + [i.getName() for i in C3.__implemented__], + ['I3', 'I2', 'I5'], + ) + self.failIf(C3.__implemented__.__class__ is tuple) + + def test_module(self): + from zope.interface.tests import m1, m2 + #import zope.interface.tests.m2 + directlyProvides(m2, + m1.I1, + m1.I2, + ) + self.assertEqual(list(providedBy(m1)), + list(providedBy(m2)), + ) + + def test_builtins(self): + # Setup + + intspec = implementedBy(int) + olddeclared = intspec.declared + + classImplements(int, I1) + class myint(int): + implements(I2) + + x = 42 + self.assertEqual([i.getName() for i in providedBy(x)], + ['I1']) + + x = myint(42) + directlyProvides(x, I3) + self.assertEqual([i.getName() for i in providedBy(x)], + ['I3', 'I2', 'I1']) + + # cleanup + intspec.declared = olddeclared + classImplements(int) + + x = 42 + self.assertEqual([i.getName() for i in providedBy(x)], + []) + + +def test_signature_w_no_class_interfaces(): + """ + >>> from zope.interface import * + >>> class C(object): + ... pass + >>> c = C() + >>> list(providedBy(c)) + [] + + >>> class I(Interface): + ... pass + >>> directlyProvides(c, I) + >>> list(providedBy(c)) == list(directlyProvidedBy(c)) + 1 + """ + +def test_classImplement_on_deeply_nested_classes(): + """This test is in response to a bug found, which is why it's a bit + contrived + + >>> from zope.interface import * + >>> class B1(object): + ... pass + >>> class B2(B1): + ... pass + >>> class B3(B2): + ... pass + >>> class D(object): + ... implements() + >>> class S(B3, D): + ... implements() + + This failed due to a bug in the code for finding __providedBy__ + descriptors for old-style classes. + + """ + +def test_pickle_provides_specs(): + """ + >>> from pickle import dumps, loads + >>> a = A() + >>> I2.providedBy(a) + 0 + >>> directlyProvides(a, I2) + >>> I2.providedBy(a) + 1 + >>> a2 = loads(dumps(a)) + >>> I2.providedBy(a2) + 1 + + """ + +def test_that_we_dont_inherit_class_provides(): + """ + >>> from zope.interface import classProvides + >>> class X(object): + ... classProvides(I1) + >>> class Y(X): + ... pass + >>> [i.__name__ for i in X.__provides__] + ['I1'] + >>> Y.__provides__ + Traceback (most recent call last): + ... + AttributeError: __provides__ + + """ + +def test_that_we_dont_inherit_provides_optimizations(): + """ + + When we make a declaration for a class, we install a __provides__ + descriptors that provides a default for instances that don't have + instance-specific declarations: + + >>> class A(object): + ... implements(I1) + + >>> class B(object): + ... implements(I2) + + >>> [i.__name__ for i in A().__provides__] + ['I1'] + >>> [i.__name__ for i in B().__provides__] + ['I2'] + + But it's important that we don't use this for subclasses without + declarations. This would cause incorrect results: + + >>> class X(A, B): + ... pass + + >>> X().__provides__ + Traceback (most recent call last): + ... + AttributeError: __provides__ + + However, if we "induce" a declaration, by calling implementedBy + (even indirectly through providedBy): + + >>> [i.__name__ for i in providedBy(X())] + ['I1', 'I2'] + + + then the optimization will work: + + >>> [i.__name__ for i in X().__provides__] + ['I1', 'I2'] + + """ + +def test_classProvides_before_implements(): + """Special descriptor for class __provides__ + + The descriptor caches the implementedBy info, so that + we can get declarations for objects without instance-specific + interfaces a bit quicker. + + For example:: + + >>> from zope.interface import Interface, classProvides + >>> class IFooFactory(Interface): + ... pass + >>> class IFoo(Interface): + ... pass + >>> class C(object): + ... classProvides(IFooFactory) + ... implements(IFoo) + >>> [i.getName() for i in C.__provides__] + ['IFooFactory'] + + >>> [i.getName() for i in C().__provides__] + ['IFoo'] + """ + +def test_getting_spec_for_proxied_builtin_class(): + """ + + In general, we should be able to get a spec + for a proxied class if someone has declared or + asked for a spec before. + + We don't want to depend on proxies in this (zope.interface) + package, but we do want to work with proxies. Proxies have the + effect that a class's __dict__ cannot be gotten. Further, for + built-in classes, we can't save, and thus, cannot get, any class + attributes. We'll emulate this by treating a plain object as a class: + + >>> cls = object() + + We'll create an implements specification: + + >>> import zope.interface.declarations + >>> impl = zope.interface.declarations.Implements(I1, I2) + + Now, we'll emulate a declaration for a built-in type by putting + it in BuiltinImplementationSpecifications: + + >>> zope.interface.declarations.BuiltinImplementationSpecifications[ + ... cls] = impl + + Now, we should be able to get it back: + + >>> implementedBy(cls) is impl + True + + Of course, we don't want to leave it there. :) + + >>> del zope.interface.declarations.BuiltinImplementationSpecifications[ + ... cls] + + """ + +def test_declaration_get(): + """ + We can get definitions from a declaration: + + >>> import zope.interface + >>> class I1(zope.interface.Interface): + ... a11 = zope.interface.Attribute('a11') + ... a12 = zope.interface.Attribute('a12') + >>> class I2(zope.interface.Interface): + ... a21 = zope.interface.Attribute('a21') + ... a22 = zope.interface.Attribute('a22') + ... a12 = zope.interface.Attribute('a212') + >>> class I11(I1): + ... a11 = zope.interface.Attribute('a111') + + >>> decl = zope.interface.Declaration(I11, I2) + >>> decl.get('a11') is I11.get('a11') + True + >>> decl.get('a12') is I1.get('a12') + True + >>> decl.get('a21') is I2.get('a21') + True + >>> decl.get('a22') is I2.get('a22') + True + >>> decl.get('a') + >>> decl.get('a', 42) + 42 + + We get None even with no interfaces: + + >>> decl = zope.interface.Declaration() + >>> decl.get('a11') + >>> decl.get('a11', 42) + 42 + + We get new data if e change interface bases: + + >>> decl.__bases__ = I11, I2 + >>> decl.get('a11') is I11.get('a11') + True + """ + +def test_classImplements_after_classImplementsOnly_issue_402(): + """http://www.zope.org/Collectors/Zope3-dev/402 + +>>> from zope.interface import * +>>> class I1(Interface): +... pass +>>> class I2(Interface): +... pass +>>> class C: +... implements(I1) +>>> class C2: +... implementsOnly(I2) +>>> class I3(Interface): +... pass + +>>> [i.__name__ for i in providedBy(C2()).__iro__] +['I2', 'Interface'] + +>>> classImplements(C2, I3) +>>> [i.__name__ for i in providedBy(C2()).__iro__] +['I2', 'I3', 'Interface'] + +>>> class I4(Interface): +... pass +>>> classImplements(C2, I4) +>>> [i.__name__ for i in providedBy(C2()).__iro__] +['I2', 'I3', 'I4', 'Interface'] + + +""" + +def test_picklability_of_implements_specifications(): + """ + + Sometimes, we need to pickle implements specs. We should be able + to do so as long as the class is picklable. + + >>> import pickle + >>> pickle.loads(pickle.dumps(implementedBy(C))) is implementedBy(C) + True + + + """ + +def test_provided_by_with_slots(): + """ + + This is an edge case: if the __slots__ of a class contain '__provides__', + using providedBy() on that class should still work (this occurs, for + example, when providing an adapter for a concrete class.) + + >>> import zope.interface + >>> class Slotted(object): + ... __slots__ = ('__provides__') + >>> class IFoo(zope.interface.Interface): + ... pass + >>> IFoo.providedBy(Slotted) + False + + """ + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test), + doctest.DocTestSuite("zope.interface.declarations"), + doctest.DocTestSuite(), + )) diff -Nru zope3-3.4.0/src/zope/interface/tests/test_document.py zope3-3.5~bzr18/src/zope/interface/tests/test_document.py --- zope3-3.4.0/src/zope/interface/tests/test_document.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_document.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,71 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Documentation tests. + +$Id: test_document.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +from unittest import TestCase, main, makeSuite + +from zope.interface import Interface, Attribute + +class Test(TestCase): + + def testBlech(self): + from zope.interface.document import asStructuredText + + self.assertEqual(asStructuredText(I2), '''\ +I2 + + I2 doc + + This interface extends: + + o _I1 + + Attributes: + + a1 -- no documentation + + a2 -- a2 doc + + Methods: + + f21() -- f21 doc + + f22() -- no documentation + + f23() -- f23 doc + +''') + + +def test_suite(): + return makeSuite(Test) + +class _I1(Interface): + def f11(): pass + def f12(): pass + +class I2(_I1): + "I2 doc" + + a1 = Attribute('a1') + a2 = Attribute('a2', 'a2 doc') + + def f21(): "f21 doc" + def f22(): pass + def f23(): "f23 doc" + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/interface/tests/test_element.py zope3-3.5~bzr18/src/zope/interface/tests/test_element.py --- zope3-3.4.0/src/zope/interface/tests/test_element.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_element.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,43 @@ +############################################################################## +# +# 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 Element meta-class. + +$Id: test_element.py 110536 2010-04-06 02:59:44Z tseaver $ +""" + +import unittest +from zope.interface.interface import Element + +class TestElement(unittest.TestCase): + + def test_taggedValues(self): + """Test that we can update tagged values of more than one element + """ + + e1 = Element("foo") + e2 = Element("bar") + e1.setTaggedValue("x", 1) + e2.setTaggedValue("x", 2) + self.assertEqual(e1.getTaggedValue("x"), 1) + self.assertEqual(e2.getTaggedValue("x"), 2) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestElement)) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/interface/tests/test_interface.py zope3-3.5~bzr18/src/zope/interface/tests/test_interface.py --- zope3-3.4.0/src/zope/interface/tests/test_interface.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_interface.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,432 @@ +############################################################################## +# +# 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 Interface implementation +""" +import doctest +import unittest +import sys + +class InterfaceTests(unittest.TestCase): + + def _makeDerivedInterface(self): + from zope.interface import Interface + from zope.interface import Attribute + class _I1(Interface): + + a1 = Attribute("This is an attribute") + + def f11(): + pass + def f12(): + pass + f12.optional = 1 + + class _I1_(_I1): + pass + + class _I1__(_I1_): + pass + + class _I2(_I1__): + def f21(): + pass + def f22(): + pass + f23 = f22 + + return _I2 + + def testInterfaceSetOnAttributes(self): + from zope.interface.tests.unitfixtures import FooInterface + self.assertEqual(FooInterface['foobar'].interface, + FooInterface) + self.assertEqual(FooInterface['aMethod'].interface, + FooInterface) + + def testClassImplements(self): + from zope.interface.tests.unitfixtures import A + from zope.interface.tests.unitfixtures import B + from zope.interface.tests.unitfixtures import C + from zope.interface.tests.unitfixtures import D + from zope.interface.tests.unitfixtures import E + from zope.interface.tests.unitfixtures import I1 + from zope.interface.tests.unitfixtures import I2 + from zope.interface.tests.unitfixtures import IC + self.assert_(IC.implementedBy(C)) + + self.assert_(I1.implementedBy(A)) + self.assert_(I1.implementedBy(B)) + self.assert_(not I1.implementedBy(C)) + self.assert_(I1.implementedBy(D)) + self.assert_(I1.implementedBy(E)) + + self.assert_(not I2.implementedBy(A)) + self.assert_(I2.implementedBy(B)) + self.assert_(not I2.implementedBy(C)) + + # No longer after interfacegeddon + # self.assert_(not I2.implementedBy(D)) + + self.assert_(not I2.implementedBy(E)) + + def testUtil(self): + from zope.interface import implementedBy + from zope.interface import providedBy + from zope.interface.tests.unitfixtures import A + from zope.interface.tests.unitfixtures import B + from zope.interface.tests.unitfixtures import C + from zope.interface.tests.unitfixtures import I1 + from zope.interface.tests.unitfixtures import I2 + from zope.interface.tests.unitfixtures import IC + self.assert_(IC in implementedBy(C)) + self.assert_(I1 in implementedBy(A)) + self.assert_(not I1 in implementedBy(C)) + self.assert_(I2 in implementedBy(B)) + self.assert_(not I2 in implementedBy(C)) + + self.assert_(IC in providedBy(C())) + self.assert_(I1 in providedBy(A())) + self.assert_(not I1 in providedBy(C())) + self.assert_(I2 in providedBy(B())) + self.assert_(not I2 in providedBy(C())) + + + def testObjectImplements(self): + from zope.interface.tests.unitfixtures import A + from zope.interface.tests.unitfixtures import B + from zope.interface.tests.unitfixtures import C + from zope.interface.tests.unitfixtures import D + from zope.interface.tests.unitfixtures import E + from zope.interface.tests.unitfixtures import I1 + from zope.interface.tests.unitfixtures import I2 + from zope.interface.tests.unitfixtures import IC + self.assert_(IC.providedBy(C())) + + self.assert_(I1.providedBy(A())) + self.assert_(I1.providedBy(B())) + self.assert_(not I1.providedBy(C())) + self.assert_(I1.providedBy(D())) + self.assert_(I1.providedBy(E())) + + self.assert_(not I2.providedBy(A())) + self.assert_(I2.providedBy(B())) + self.assert_(not I2.providedBy(C())) + + # Not after interface geddon + # self.assert_(not I2.providedBy(D())) + + self.assert_(not I2.providedBy(E())) + + def testDeferredClass(self): + from zope.interface.tests.unitfixtures import A + from zope.interface.exceptions import BrokenImplementation + a = A() + self.assertRaises(BrokenImplementation, a.ma) + + + def testInterfaceExtendsInterface(self): + from zope.interface.tests.unitfixtures import BazInterface + from zope.interface.tests.unitfixtures import BarInterface + from zope.interface.tests.unitfixtures import BobInterface + from zope.interface.tests.unitfixtures import FunInterface + self.assert_(BazInterface.extends(BobInterface)) + self.assert_(BazInterface.extends(BarInterface)) + self.assert_(BazInterface.extends(FunInterface)) + self.assert_(not BobInterface.extends(FunInterface)) + self.assert_(not BobInterface.extends(BarInterface)) + self.assert_(BarInterface.extends(FunInterface)) + self.assert_(not BarInterface.extends(BazInterface)) + + def testVerifyImplementation(self): + from zope.interface.verify import verifyClass + from zope.interface import Interface + from zope.interface.tests.unitfixtures import Foo + from zope.interface.tests.unitfixtures import FooInterface + from zope.interface.tests.unitfixtures import I1 + self.assert_(verifyClass(FooInterface, Foo)) + self.assert_(Interface.providedBy(I1)) + + def test_names(self): + iface = self._makeDerivedInterface() + names = list(iface.names()) + names.sort() + self.assertEqual(names, ['f21', 'f22', 'f23']) + all = list(iface.names(all=True)) + all.sort() + self.assertEqual(all, ['a1', 'f11', 'f12', 'f21', 'f22', 'f23']) + + def test_namesAndDescriptions(self): + iface = self._makeDerivedInterface() + names = [nd[0] for nd in iface.namesAndDescriptions()] + names.sort() + self.assertEqual(names, ['f21', 'f22', 'f23']) + names = [nd[0] for nd in iface.namesAndDescriptions(1)] + names.sort() + self.assertEqual(names, ['a1', 'f11', 'f12', 'f21', 'f22', 'f23']) + + for name, d in iface.namesAndDescriptions(1): + self.assertEqual(name, d.__name__) + + def test_getDescriptionFor(self): + iface = self._makeDerivedInterface() + self.assertEqual(iface.getDescriptionFor('f11').__name__, 'f11') + self.assertEqual(iface.getDescriptionFor('f22').__name__, 'f22') + self.assertEqual(iface.queryDescriptionFor('f33', self), self) + self.assertRaises(KeyError, iface.getDescriptionFor, 'f33') + + def test___getitem__(self): + iface = self._makeDerivedInterface() + self.assertEqual(iface['f11'].__name__, 'f11') + self.assertEqual(iface['f22'].__name__, 'f22') + self.assertEqual(iface.get('f33', self), self) + self.assertRaises(KeyError, iface.__getitem__, 'f33') + + def test___contains__(self): + iface = self._makeDerivedInterface() + self.failUnless('f11' in iface) + self.failIf('f33' in iface) + + def test___iter__(self): + iface = self._makeDerivedInterface() + names = list(iter(iface)) + names.sort() + self.assertEqual(names, ['a1', 'f11', 'f12', 'f21', 'f22', 'f23']) + + def testAttr(self): + iface = self._makeDerivedInterface() + description = iface.getDescriptionFor('a1') + self.assertEqual(description.__name__, 'a1') + self.assertEqual(description.__doc__, 'This is an attribute') + + def testFunctionAttributes(self): + # Make sure function attributes become tagged values. + from zope.interface import Interface + class ITest(Interface): + def method(): + pass + method.optional = 1 + + method = ITest['method'] + self.assertEqual(method.getTaggedValue('optional'), 1) + + def testInvariant(self): + from zope.interface.exceptions import Invalid + from zope.interface import directlyProvides + from zope.interface.tests.unitfixtures import BarGreaterThanFoo + from zope.interface.tests.unitfixtures import ifFooThenBar + from zope.interface.tests.unitfixtures import IInvariant + from zope.interface.tests.unitfixtures import InvariantC + from zope.interface.tests.unitfixtures import ISubInvariant + # set up + o = InvariantC() + directlyProvides(o, IInvariant) + # a helper + def errorsEqual(self, o, error_len, error_msgs, iface=None): + if iface is None: + iface = IInvariant + self.assertRaises(Invalid, iface.validateInvariants, o) + e = [] + try: + iface.validateInvariants(o, e) + except Invalid, error: + self.assertEquals(error.args[0], e) + else: + self._assert(0) # validateInvariants should always raise + # Invalid + self.assertEquals(len(e), error_len) + msgs = [error.args[0] for error in e] + msgs.sort() + for msg in msgs: + self.assertEquals(msg, error_msgs.pop(0)) + # the tests + self.assertEquals(IInvariant.getTaggedValue('invariants'), + [ifFooThenBar]) + self.assertEquals(IInvariant.validateInvariants(o), None) + o.bar = 27 + self.assertEquals(IInvariant.validateInvariants(o), None) + o.foo = 42 + self.assertEquals(IInvariant.validateInvariants(o), None) + del o.bar + errorsEqual(self, o, 1, ['If Foo, then Bar!']) + # nested interfaces with invariants: + self.assertEquals(ISubInvariant.getTaggedValue('invariants'), + [BarGreaterThanFoo]) + o = InvariantC() + directlyProvides(o, ISubInvariant) + o.foo = 42 + # even though the interface has changed, we should still only have one + # error. + errorsEqual(self, o, 1, ['If Foo, then Bar!'], ISubInvariant) + # however, if we set foo to 0 (Boolean False) and bar to a negative + # number then we'll get the new error + o.foo = 2 + o.bar = 1 + errorsEqual(self, o, 1, ['Please, Boo MUST be greater than Foo!'], + ISubInvariant) + # and if we set foo to a positive number and boo to 0, we'll + # get both errors! + o.foo = 1 + o.bar = 0 + errorsEqual(self, o, 2, ['If Foo, then Bar!', + 'Please, Boo MUST be greater than Foo!'], + ISubInvariant) + # for a happy ending, we'll make the invariants happy + o.foo = 1 + o.bar = 2 + self.assertEquals(IInvariant.validateInvariants(o), None) # woohoo + # now we'll do two invariants on the same interface, + # just to make sure that a small + # multi-invariant interface is at least minimally tested. + o = InvariantC() + directlyProvides(o, IInvariant) + o.foo = 42 + old_invariants = IInvariant.getTaggedValue('invariants') + invariants = old_invariants[:] + invariants.append(BarGreaterThanFoo) # if you really need to mutate, + # then this would be the way to do it. Probably a bad idea, though. :-) + IInvariant.setTaggedValue('invariants', invariants) + # + # even though the interface has changed, we should still only have one + # error. + errorsEqual(self, o, 1, ['If Foo, then Bar!']) + # however, if we set foo to 0 (Boolean False) and bar to a negative + # number then we'll get the new error + o.foo = 2 + o.bar = 1 + errorsEqual(self, o, 1, ['Please, Boo MUST be greater than Foo!']) + # and if we set foo to a positive number and boo to 0, we'll + # get both errors! + o.foo = 1 + o.bar = 0 + errorsEqual(self, o, 2, ['If Foo, then Bar!', + 'Please, Boo MUST be greater than Foo!']) + # for another happy ending, we'll make the invariants happy again + o.foo = 1 + o.bar = 2 + self.assertEquals(IInvariant.validateInvariants(o), None) # bliss + # clean up + IInvariant.setTaggedValue('invariants', old_invariants) + + def test___doc___element(self): + from zope.interface import Interface + from zope.interface import Attribute + class I(Interface): + "xxx" + + self.assertEqual(I.__doc__, "xxx") + self.assertEqual(list(I), []) + + class I(Interface): + "xxx" + + __doc__ = Attribute('the doc') + + self.assertEqual(I.__doc__, "") + self.assertEqual(list(I), ['__doc__']) + + def testIssue228(self): + from zope.interface import Interface + # Test for http://collector.zope.org/Zope3-dev/228 + if sys.version[0] == '3': + # No old style classes in Python 3, so the test becomes moot. + return + class I(Interface): + "xxx" + class Bad: + __providedBy__ = None + # Old style classes don't have a '__class__' attribute + self.failUnlessRaises(AttributeError, I.providedBy, Bad) + + + +if sys.version_info >= (2, 4): + + def test_invariant_as_decorator(): + """Invaiants can be deined in line + + >>> from zope.interface.exceptions import Invalid + >>> from zope.interface import Interface + >>> from zope.interface import Attribute + >>> from zope.interface import implements + >>> from zope.interface import invariant + >>> class IRange(Interface): + ... min = Attribute("Lower bound") + ... max = Attribute("Upper bound") + ... + ... @invariant + ... def range_invariant(ob): + ... if ob.max < ob.min: + ... raise Invalid('max < min') + + + >>> class Range(object): + ... implements(IRange) + ... + ... def __init__(self, min, max): + ... self.min, self.max = min, max + + >>> from zope.interface.exceptions import Invalid + >>> IRange.validateInvariants(Range(1,2)) + >>> IRange.validateInvariants(Range(1,1)) + >>> try: + ... IRange.validateInvariants(Range(2,1)) + ... except Invalid, e: + ... str(e) + 'max < min' + + + """ + + +def test_description_cache_management(): + """ See https://bugs.launchpad.net/zope.interface/+bug/185974 + +There was a bug where the cache used by Specification.get() was not +cleared when the bases were changed. + + >>> from zope.interface import Interface + >>> from zope.interface import Attribute + >>> class I1(Interface): + ... a = Attribute('a') + + >>> class I2(I1): + ... pass + + >>> class I3(I2): + ... pass + + >>> I3.get('a') is I1.get('a') + True + >>> I2.__bases__ = (Interface,) + >>> I3.get('a') is None + True + """ + + +def test_suite(): + suite = unittest.makeSuite(InterfaceTests) + suite.addTest(doctest.DocTestSuite("zope.interface.interface")) + if sys.version_info >= (2, 4): + suite.addTest(doctest.DocTestSuite()) + suite.addTest(doctest.DocFileSuite( + '../README.txt', + globs={'__name__': '__main__'}, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, + )) + suite.addTest(doctest.DocFileSuite( + '../README.ru.txt', + globs={'__name__': '__main__'}, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, + )) + return suite diff -Nru zope3-3.4.0/src/zope/interface/tests/test_odd_declarations.py zope3-3.5~bzr18/src/zope/interface/tests/test_odd_declarations.py --- zope3-3.4.0/src/zope/interface/tests/test_odd_declarations.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_odd_declarations.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,218 @@ +############################################################################## +# +# 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 interface declarations against ExtensionClass-like classes. + +These tests are to make sure we do something sane in the presence of +classic ExtensionClass classes and instances. + +$Id: test_odd_declarations.py 110736 2010-04-11 10:59:30Z regebro $ +""" +import doctest +import unittest + +from zope.interface.tests import odd +from zope.interface import Interface, implements, classProvides +from zope.interface import directlyProvides, providedBy, directlyProvidedBy +from zope.interface import classImplements, classImplementsOnly, implementedBy + +class I1(Interface): pass +class I2(Interface): pass +class I3(Interface): pass +class I31(I3): pass +class I4(Interface): pass +class I5(Interface): pass + +class Odd(object): __metaclass__ = odd.MetaClass + +class B(Odd): __implemented__ = I2 + + +# TODO: We are going to need more magic to make classProvides work with odd +# classes. This will work in the next iteration. For now, we'll use +# a different mechanism. + +# from zope.interface import classProvides + +class A(Odd): + pass +classImplements(A, I1) + +class C(A, B): + pass +classImplements(C, I31) + + +class Test(unittest.TestCase): + + def test_ObjectSpecification(self): + c = C() + directlyProvides(c, I4) + self.assertEqual([i.getName() for i in providedBy(c)], + ['I4', 'I31', 'I1', 'I2'] + ) + self.assertEqual([i.getName() for i in providedBy(c).flattened()], + ['I4', 'I31', 'I3', 'I1', 'I2', 'Interface'] + ) + self.assert_(I1 in providedBy(c)) + self.failIf(I3 in providedBy(c)) + self.assert_(providedBy(c).extends(I3)) + self.assert_(providedBy(c).extends(I31)) + self.failIf(providedBy(c).extends(I5)) + + class COnly(A, B): + pass + classImplementsOnly(COnly, I31) + + class D(COnly): + pass + classImplements(D, I5) + + classImplements(D, I5) + + c = D() + directlyProvides(c, I4) + self.assertEqual([i.getName() for i in providedBy(c)], + ['I4', 'I5', 'I31']) + self.assertEqual([i.getName() for i in providedBy(c).flattened()], + ['I4', 'I5', 'I31', 'I3', 'Interface']) + self.failIf(I1 in providedBy(c)) + self.failIf(I3 in providedBy(c)) + self.assert_(providedBy(c).extends(I3)) + self.failIf(providedBy(c).extends(I1)) + self.assert_(providedBy(c).extends(I31)) + self.assert_(providedBy(c).extends(I5)) + + class COnly(A, B): __implemented__ = I31 + class D(COnly): + pass + classImplements(D, I5) + + classImplements(D, I5) + c = D() + directlyProvides(c, I4) + self.assertEqual([i.getName() for i in providedBy(c)], + ['I4', 'I5', 'I31']) + self.assertEqual([i.getName() for i in providedBy(c).flattened()], + ['I4', 'I5', 'I31', 'I3', 'Interface']) + self.failIf(I1 in providedBy(c)) + self.failIf(I3 in providedBy(c)) + self.assert_(providedBy(c).extends(I3)) + self.failIf(providedBy(c).extends(I1)) + self.assert_(providedBy(c).extends(I31)) + self.assert_(providedBy(c).extends(I5)) + + def test_classImplements(self): + class A(Odd): + implements(I3) + + class B(Odd): + implements(I4) + + class C(A, B): + pass + classImplements(C, I1, I2) + self.assertEqual([i.getName() for i in implementedBy(C)], + ['I1', 'I2', 'I3', 'I4']) + classImplements(C, I5) + self.assertEqual([i.getName() for i in implementedBy(C)], + ['I1', 'I2', 'I5', 'I3', 'I4']) + + def test_classImplementsOnly(self): + class A(Odd): + implements(I3) + + class B(Odd): + implements(I4) + + class C(A, B): + pass + classImplementsOnly(C, I1, I2) + self.assertEqual([i.__name__ for i in implementedBy(C)], + ['I1', 'I2']) + + + def test_directlyProvides(self): + class IA1(Interface): pass + class IA2(Interface): pass + class IB(Interface): pass + class IC(Interface): pass + class A(Odd): + pass + classImplements(A, IA1, IA2) + + class B(Odd): + pass + classImplements(B, IB) + + class C(A, B): + pass + classImplements(C, IC) + + + ob = C() + directlyProvides(ob, I1, I2) + self.assert_(I1 in providedBy(ob)) + self.assert_(I2 in providedBy(ob)) + self.assert_(IA1 in providedBy(ob)) + self.assert_(IA2 in providedBy(ob)) + self.assert_(IB in providedBy(ob)) + self.assert_(IC in providedBy(ob)) + + directlyProvides(ob, directlyProvidedBy(ob)-I2) + self.assert_(I1 in providedBy(ob)) + self.failIf(I2 in providedBy(ob)) + self.failIf(I2 in providedBy(ob)) + directlyProvides(ob, directlyProvidedBy(ob), I2) + self.assert_(I2 in providedBy(ob)) + + def test_directlyProvides_fails_for_odd_class(self): + self.assertRaises(TypeError, directlyProvides, C, I5) + + # see above + def TODO_test_classProvides_fails_for_odd_class(self): + try: + class A(Odd): + classProvides(I1) + except TypeError: + pass # Sucess + self.assert_(False, + "Shouldn't be able to use directlyProvides on odd class." + ) + + def test_implementedBy(self): + class I2(I1): pass + + class C1(Odd): + pass + classImplements(C1, I2) + + class C2(C1): + pass + classImplements(C2, I3) + + self.assertEqual([i.getName() for i in implementedBy(C2)], + ['I3', 'I2']) + + + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(Test)) + suite.addTest(doctest.DocTestSuite(odd)) + return suite + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/interface/tests/test_sorting.py zope3-3.5~bzr18/src/zope/interface/tests/test_sorting.py --- zope3-3.4.0/src/zope/interface/tests/test_sorting.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_sorting.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,49 @@ +############################################################################## +# +# 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 interface sorting + +$Id: test_sorting.py 110699 2010-04-09 08:16:17Z regebro $ +""" + +from unittest import TestCase, TestSuite, main, makeSuite + +from zope.interface import Interface + +class I1(Interface): pass +class I2(I1): pass +class I3(I1): pass +class I4(Interface): pass +class I5(I4): pass +class I6(I2): pass + + +class Test(TestCase): + + def test(self): + l = [I1, I3, I5, I6, I4, I2] + l.sort() + self.assertEqual(l, [I1, I2, I3, I4, I5, I6]) + + def test_w_None(self): + l = [I1, None, I3, I5, I6, I4, I2] + l.sort() + self.assertEqual(l, [I1, I2, I3, I4, I5, I6, None]) + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/interface/tests/test_verify.py zope3-3.5~bzr18/src/zope/interface/tests/test_verify.py --- zope3-3.4.0/src/zope/interface/tests/test_verify.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/test_verify.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,202 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Interface Verify tests + +$Id: test_verify.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +import doctest +import unittest + +from zope.interface import Interface, implements, classImplements, Attribute +from zope.interface.verify import verifyClass, verifyObject +from zope.interface.exceptions import DoesNotImplement, BrokenImplementation +from zope.interface.exceptions import BrokenMethodImplementation + +class Test(unittest.TestCase): + + def testNotImplemented(self): + + class C(object): pass + + class I(Interface): pass + + self.assertRaises(DoesNotImplement, verifyClass, I, C) + + classImplements(C, I) + + verifyClass(I, C) + + def testMissingAttr(self): + + class I(Interface): + def f(): pass + + class C(object): + implements(I) + + self.assertRaises(BrokenImplementation, verifyClass, I, C) + + C.f=lambda self: None + + verifyClass(I, C) + + def testMissingAttr_with_Extended_Interface(self): + + class II(Interface): + def f(): + pass + + class I(II): + pass + + class C(object): + implements(I) + + self.assertRaises(BrokenImplementation, verifyClass, I, C) + + C.f=lambda self: None + + verifyClass(I, C) + + def testWrongArgs(self): + + class I(Interface): + def f(a): pass + + class C(object): + def f(self, b): pass + + implements(I) + + # We no longer require names to match. + #self.assertRaises(BrokenMethodImplementation, verifyClass, I, C) + + C.f=lambda self, a: None + + verifyClass(I, C) + + C.f=lambda self, **kw: None + + self.assertRaises(BrokenMethodImplementation, verifyClass, I, C) + + C.f=lambda self, a, *args: None + + verifyClass(I, C) + + C.f=lambda self, a, *args, **kw: None + + verifyClass(I, C) + + C.f=lambda self, *args: None + + verifyClass(I, C) + + def testExtraArgs(self): + + class I(Interface): + def f(a): pass + + class C(object): + def f(self, a, b): pass + + implements(I) + + self.assertRaises(BrokenMethodImplementation, verifyClass, I, C) + + C.f=lambda self, a: None + + verifyClass(I, C) + + C.f=lambda self, a, b=None: None + + verifyClass(I, C) + + def testNoVar(self): + + class I(Interface): + def f(a, *args): pass + + class C(object): + def f(self, a): pass + + implements(I) + + self.assertRaises(BrokenMethodImplementation, verifyClass, I, C) + + C.f=lambda self, a, *foo: None + + verifyClass(I, C) + + def testNoKW(self): + + class I(Interface): + def f(a, **args): pass + + class C(object): + def f(self, a): pass + + implements(I) + + self.assertRaises(BrokenMethodImplementation, verifyClass, I, C) + + C.f=lambda self, a, **foo: None + + verifyClass(I, C) + + def testModule(self): + + from zope.interface.tests.ifoo import IFoo + from zope.interface.tests import dummy + + verifyObject(IFoo, dummy) + + def testMethodForAttr(self): + + class IFoo(Interface): + foo = Attribute("The foo Attribute") + + + class Foo: + implements(IFoo) + + def foo(self): + pass + + verifyClass(IFoo, Foo) + + def testNonMethodForMethod(self): + + class IBar(Interface): + def foo(): + pass + + class Bar: + implements(IBar) + + foo = 1 + + self.assertRaises(BrokenMethodImplementation, verifyClass, IBar, Bar) + + +def test_suite(): + loader=unittest.TestLoader() + return unittest.TestSuite(( + doctest.DocFileSuite( + '../verify.txt', + optionflags=doctest.NORMALIZE_WHITESPACE), + loader.loadTestsFromTestCase(Test), + )) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/interface/tests/unitfixtures.py zope3-3.5~bzr18/src/zope/interface/tests/unitfixtures.py --- zope3-3.4.0/src/zope/interface/tests/unitfixtures.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/tests/unitfixtures.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,142 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Unit Test Fixtures + +$Id: unitfixtures.py 110536 2010-04-06 02:59:44Z tseaver $ +""" +from zope.interface import Interface, invariant +from zope.interface.interface import Attribute +from zope.interface.exceptions import Invalid + +class mytest(Interface): + pass + +class C(object): + def m1(self, a, b): + "return 1" + return 1 + + def m2(self, a, b): + "return 2" + return 2 + +# testInstancesOfClassImplements + +# YAGNI IC=Interface.impliedInterface(C) +class IC(Interface): + def m1(a, b): + "return 1" + + def m2(a, b): + "return 2" + + + +C.__implemented__=IC + +class I1(Interface): + def ma(): + "blah" + +class I2(I1): pass + +class I3(Interface): pass + +class I4(Interface): pass + +class A(I1.deferred()): + __implemented__=I1 + +class B(object): + __implemented__=I2, I3 + +class D(A, B): pass + +class E(A, B): + __implemented__ = A.__implemented__, C.__implemented__ + + +class FooInterface(Interface): + """ This is an Abstract Base Class """ + + foobar = Attribute("fuzzed over beyond all recognition") + + def aMethod(foo, bar, bingo): + """ This is aMethod """ + + def anotherMethod(foo=6, bar="where you get sloshed", bingo=(1,3,)): + """ This is anotherMethod """ + + def wammy(zip, *argues): + """ yadda yadda """ + + def useless(**keywords): + """ useless code is fun! """ + +class Foo(object): + """ A concrete class """ + + __implemented__ = FooInterface, + + foobar = "yeah" + + def aMethod(self, foo, bar, bingo): + """ This is aMethod """ + return "barf!" + + def anotherMethod(self, foo=6, bar="where you get sloshed", bingo=(1,3,)): + """ This is anotherMethod """ + return "barf!" + + def wammy(self, zip, *argues): + """ yadda yadda """ + return "barf!" + + def useless(self, **keywords): + """ useless code is fun! """ + return "barf!" + +foo_instance = Foo() + +class Blah(object): + pass + +new = Interface.__class__ +FunInterface = new('FunInterface') +BarInterface = new('BarInterface', [FunInterface]) +BobInterface = new('BobInterface') +BazInterface = new('BazInterface', [BobInterface, BarInterface]) + +# fixtures for invariant tests +def ifFooThenBar(obj): + if getattr(obj, 'foo', None) and not getattr(obj, 'bar', None): + raise Invalid('If Foo, then Bar!') +class IInvariant(Interface): + foo = Attribute('foo') + bar = Attribute('bar; must eval to Boolean True if foo does') + invariant(ifFooThenBar) +def BarGreaterThanFoo(obj): + foo = getattr(obj, 'foo', None) + bar = getattr(obj, 'bar', None) + if foo is not None and isinstance(foo, type(bar)): + # type checking should be handled elsewhere (like, say, + # schema); these invariants should be intra-interface + # constraints. This is a hacky way to do it, maybe, but you + # get the idea + if not bar > foo: + raise Invalid('Please, Boo MUST be greater than Foo!') +class ISubInvariant(IInvariant): + invariant(BarGreaterThanFoo) +class InvariantC(object): + pass diff -Nru zope3-3.4.0/src/zope/interface/verify.py zope3-3.5~bzr18/src/zope/interface/verify.py --- zope3-3.4.0/src/zope/interface/verify.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/verify.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,117 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Verify interface implementations + +$Id: verify.py 110699 2010-04-09 08:16:17Z regebro $ +""" +from zope.interface.exceptions import BrokenImplementation, DoesNotImplement +from zope.interface.exceptions import BrokenMethodImplementation +from types import FunctionType, MethodType +from zope.interface.interface import fromMethod, fromFunction, Method +import sys + +# This will be monkey-patched when running under Zope 2, so leave this +# here: +MethodTypes = (MethodType, ) + + +def _verify(iface, candidate, tentative=0, vtype=None): + """Verify that 'candidate' might correctly implements 'iface'. + + This involves: + + o Making sure the candidate defines all the necessary methods + + o Making sure the methods have the correct signature + + o Making sure the candidate asserts that it implements the interface + + Note that this isn't the same as verifying that the class does + implement the interface. + + If optional tentative is true, suppress the "is implemented by" test. + """ + + if vtype == 'c': + tester = iface.implementedBy + else: + tester = iface.providedBy + + if not tentative and not tester(candidate): + raise DoesNotImplement(iface) + + # Here the `desc` is either an `Attribute` or `Method` instance + for name, desc in iface.namesAndDescriptions(1): + try: + attr = getattr(candidate, name) + except AttributeError: + if (not isinstance(desc, Method)) and vtype == 'c': + # We can't verify non-methods on classes, since the + # class may provide attrs in it's __init__. + continue + + raise BrokenImplementation(iface, name) + + if not isinstance(desc, Method): + # If it's not a method, there's nothing else we can test + continue + + if isinstance(attr, FunctionType): + if sys.version[0] == '3' and isinstance(candidate, type): + # This is an "unbound method" in Python 3. + meth = fromFunction(attr, iface, name=name, imlevel=1) + else: + # Nope, just a normal function + meth = fromFunction(attr, iface, name=name) + elif (isinstance(attr, MethodTypes) + and type(attr.im_func) is FunctionType): + meth = fromMethod(attr, iface, name) + else: + if not callable(attr): + raise BrokenMethodImplementation(name, "Not a method") + # sigh, it's callable, but we don't know how to intrspect it, so + # we have to give it a pass. + continue + + # Make sure that the required and implemented method signatures are + # the same. + desc = desc.getSignatureInfo() + meth = meth.getSignatureInfo() + + mess = _incompat(desc, meth) + if mess: + raise BrokenMethodImplementation(name, mess) + + return True + +def verifyClass(iface, candidate, tentative=0): + return _verify(iface, candidate, tentative, vtype='c') + +def verifyObject(iface, candidate, tentative=0): + return _verify(iface, candidate, tentative, vtype='o') + +def _incompat(required, implemented): + #if (required['positional'] != + # implemented['positional'][:len(required['positional'])] + # and implemented['kwargs'] is None): + # return 'imlementation has different argument names' + if len(implemented['required']) > len(required['required']): + return 'implementation requires too many arguments' + if ((len(implemented['positional']) < len(required['positional'])) + and not implemented['varargs']): + return "implementation doesn't allow enough arguments" + if required['kwargs'] and not implemented['kwargs']: + return "implementation doesn't support keyword arguments" + if required['varargs'] and not implemented['varargs']: + return "implementation doesn't support variable arguments" diff -Nru zope3-3.4.0/src/zope/interface/verify.txt zope3-3.5~bzr18/src/zope/interface/verify.txt --- zope3-3.4.0/src/zope/interface/verify.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/verify.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,127 @@ +=================================== +Verifying interface implementations +=================================== + +The ``zope.interface.verify`` module provides functions that test whether a +given interface is implemented by a class or provided by an object, resp. + + +Verifying classes +================= + +This is covered by unit tests defined in ``zope.interface.tests.test_verify``. + + +Verifying objects +================= + +An object provides an interface if + +- either its class declares that it implements the interfaces, or the object + declares that it directly provides the interface + +- the object defines all the methods required by the interface + +- all the methods have the correct signature + +- the object defines all non-method attributes required by the interface + +This doctest currently covers only the latter item. + +Testing for attributes +---------------------- + +Attributes of the object, be they defined by its class or added by its +``__init__`` method, will be recognized: + +>>> from zope.interface import Interface, Attribute, implements +>>> from zope.interface.exceptions import BrokenImplementation +>>> class IFoo(Interface): +... x = Attribute("The X attribute") +... y = Attribute("The Y attribute") + +>>> class Foo(object): +... implements(IFoo) +... x = 1 +... def __init__(self): +... self.y = 2 + +>>> from zope.interface.verify import verifyObject +>>> verifyObject(IFoo, Foo()) +True + +If either attribute is missing, verification will fail: + +>>> class Foo(object): +... implements(IFoo) +... x = 1 + +>>> try: #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS +... verifyObject(IFoo, Foo()) +... except BrokenImplementation, e: +... print str(e) +An object has failed to implement interface + + The y attribute was not provided. + + +>>> class Foo(object): +... implements(IFoo) +... def __init__(self): +... self.y = 2 + +>>> try: #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS +... verifyObject(IFoo, Foo()) +... except BrokenImplementation, e: +... print str(e) +An object has failed to implement interface + + The x attribute was not provided. + + +If an attribute is implemented as a property that raises an AttributeError +when trying to get its value, the attribute is considered missing: + +>>> class IFoo(Interface): +... x = Attribute('The X attribute') + +>>> class Foo(object): +... implements(IFoo) +... @property +... def x(self): +... raise AttributeError + +>>> try: #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS +... verifyObject(IFoo, Foo()) +... except BrokenImplementation, e: +... print str(e) +An object has failed to implement interface + + The x attribute was not provided. + + +Any other exception raised by a property will propagate to the caller of +``verifyObject``: + +>>> class Foo(object): +... implements(IFoo) +... @property +... def x(self): +... raise Exception + +>>> verifyObject(IFoo, Foo()) +Traceback (most recent call last): +Exception + +Of course, broken properties that are not required by the interface don't do +any harm: + +>>> class Foo(object): +... implements(IFoo) +... x = 1 +... @property +... def y(self): +... raise Exception + +>>> verifyObject(IFoo, Foo()) +True diff -Nru zope3-3.4.0/src/zope/interface/_zope_interface_coptimizations.c zope3-3.5~bzr18/src/zope/interface/_zope_interface_coptimizations.c --- zope3-3.4.0/src/zope/interface/_zope_interface_coptimizations.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/_zope_interface_coptimizations.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1687 @@ +/*########################################################################### + # + # 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. + # + ############################################################################*/ + +#include "Python.h" +#include "structmember.h" + +#define TYPE(O) ((PyTypeObject*)(O)) +#define OBJECT(O) ((PyObject*)(O)) +#define CLASSIC(O) ((PyClassObject*)(O)) +#ifndef PyVarObject_HEAD_INIT +#define PyVarObject_HEAD_INIT(a, b) PyObject_HEAD_INIT(a) b, +#endif +#ifndef Py_TYPE +#define Py_TYPE(o) ((o)->ob_type) +#endif + +static PyObject *str__dict__, *str__implemented__, *strextends; +static PyObject *BuiltinImplementationSpecifications, *str__provides__; +static PyObject *str__class__, *str__providedBy__; +static PyObject *empty, *fallback, *str_implied, *str_cls, *str_implements; +static PyObject *str__conform__, *str_call_conform, *adapter_hooks; +static PyObject *str_uncached_lookup, *str_uncached_lookupAll; +static PyObject *str_uncached_subscriptions; +static PyObject *str_registry, *strro, *str_generation, *strchanged; + +static PyTypeObject *Implements; + +static int imported_declarations = 0; + +static int +import_declarations(void) +{ + PyObject *declarations, *i; + + declarations = PyImport_ImportModule("zope.interface.declarations"); + if (declarations == NULL) + return -1; + + BuiltinImplementationSpecifications = PyObject_GetAttrString( + declarations, "BuiltinImplementationSpecifications"); + if (BuiltinImplementationSpecifications == NULL) + return -1; + + empty = PyObject_GetAttrString(declarations, "_empty"); + if (empty == NULL) + return -1; + + fallback = PyObject_GetAttrString(declarations, "implementedByFallback"); + if (fallback == NULL) + return -1; + + + + i = PyObject_GetAttrString(declarations, "Implements"); + if (i == NULL) + return -1; + + if (! PyType_Check(i)) + { + PyErr_SetString(PyExc_TypeError, + "zope.interface.declarations.Implements is not a type"); + return -1; + } + + Implements = (PyTypeObject *)i; + + Py_DECREF(declarations); + + imported_declarations = 1; + return 0; +} + +static PyTypeObject SpecType; /* Forward */ + +static PyObject * +implementedByFallback(PyObject *cls) +{ + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + return PyObject_CallFunctionObjArgs(fallback, cls, NULL); +} + +static PyObject * +implementedBy(PyObject *ignored, PyObject *cls) +{ + /* Fast retrieval of implements spec, if possible, to optimize + common case. Use fallback code if we get stuck. + */ + + PyObject *dict = NULL, *spec; + + if (PyType_Check(cls)) + { + dict = TYPE(cls)->tp_dict; + Py_XINCREF(dict); + } + + if (dict == NULL) + dict = PyObject_GetAttr(cls, str__dict__); + + if (dict == NULL) + { + /* Probably a security proxied class, use more expensive fallback code */ + PyErr_Clear(); + return implementedByFallback(cls); + } + + spec = PyObject_GetItem(dict, str__implemented__); + Py_DECREF(dict); + if (spec) + { + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + if (PyObject_TypeCheck(spec, Implements)) + return spec; + + /* Old-style declaration, use more expensive fallback code */ + Py_DECREF(spec); + return implementedByFallback(cls); + } + + PyErr_Clear(); + + /* Maybe we have a builtin */ + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + + spec = PyDict_GetItem(BuiltinImplementationSpecifications, cls); + if (spec != NULL) + { + Py_INCREF(spec); + return spec; + } + + /* We're stuck, use fallback */ + return implementedByFallback(cls); +} + +static PyObject * +getObjectSpecification(PyObject *ignored, PyObject *ob) +{ + PyObject *cls, *result; + + result = PyObject_GetAttr(ob, str__provides__); + if (result != NULL && PyObject_TypeCheck(result, &SpecType)) + return result; + + PyErr_Clear(); + + /* We do a getattr here so as not to be defeated by proxies */ + cls = PyObject_GetAttr(ob, str__class__); + if (cls == NULL) + { + PyErr_Clear(); + if (imported_declarations == 0 && import_declarations() < 0) + return NULL; + Py_INCREF(empty); + return empty; + } + + result = implementedBy(NULL, cls); + Py_DECREF(cls); + + return result; +} + +static PyObject * +providedBy(PyObject *ignored, PyObject *ob) +{ + PyObject *result, *cls, *cp; + + result = PyObject_GetAttr(ob, str__providedBy__); + if (result == NULL) + { + PyErr_Clear(); + return getObjectSpecification(NULL, ob); + } + + + /* We want to make sure we have a spec. We can't do a type check + because we may have a proxy, so we'll just try to get the + only attribute. + */ + if (PyObject_TypeCheck(result, &SpecType) + || + PyObject_HasAttr(result, strextends) + ) + return result; + + /* + The object's class doesn't understand descriptors. + Sigh. We need to get an object descriptor, but we have to be + careful. We want to use the instance's __provides__,l if + there is one, but only if it didn't come from the class. + */ + Py_DECREF(result); + + cls = PyObject_GetAttr(ob, str__class__); + if (cls == NULL) + return NULL; + + result = PyObject_GetAttr(ob, str__provides__); + if (result == NULL) + { + /* No __provides__, so just fall back to implementedBy */ + PyErr_Clear(); + result = implementedBy(NULL, cls); + Py_DECREF(cls); + return result; + } + + cp = PyObject_GetAttr(cls, str__provides__); + if (cp == NULL) + { + /* The the class has no provides, assume we're done: */ + PyErr_Clear(); + Py_DECREF(cls); + return result; + } + + if (cp == result) + { + /* + Oops, we got the provides from the class. This means + the object doesn't have it's own. We should use implementedBy + */ + Py_DECREF(result); + result = implementedBy(NULL, cls); + } + + Py_DECREF(cls); + Py_DECREF(cp); + + return result; +} + +/* + Get an attribute from an inst dict. Return a borrowed reference. + + This has a number of advantages: + + - It avoids layers of Python api + + - It doesn't waste time looking for descriptors + + - It fails wo raising an exception, although that shouldn't really + matter. + +*/ +static PyObject * +inst_attr(PyObject *self, PyObject *name) +{ + PyObject **dictp, *v; + + dictp = _PyObject_GetDictPtr(self); + if (dictp && *dictp && (v = PyDict_GetItem(*dictp, name))) + return v; + PyErr_SetObject(PyExc_AttributeError, name); + return NULL; +} + + +static PyObject * +Spec_extends(PyObject *self, PyObject *other) +{ + PyObject *implied; + + implied = inst_attr(self, str_implied); + if (implied == NULL) + return NULL; + +#ifdef Py_True + if (PyDict_GetItem(implied, other) != NULL) + { + Py_INCREF(Py_True); + return Py_True; + } + Py_INCREF(Py_False); + return Py_False; +#else + return PyInt_FromLong(PyDict_GetItem(implied, other) != NULL); +#endif +} + +static char Spec_extends__doc__[] = +"Test whether a specification is or extends another" +; + +static char Spec_providedBy__doc__[] = +"Test whether an interface is implemented by the specification" +; + +static PyObject * +Spec_call(PyObject *self, PyObject *args, PyObject *kw) +{ + PyObject *spec; + + if (! PyArg_ParseTuple(args, "O", &spec)) + return NULL; + return Spec_extends(self, spec); +} + +static PyObject * +Spec_providedBy(PyObject *self, PyObject *ob) +{ + PyObject *decl, *item; + + decl = providedBy(NULL, ob); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecType)) + item = Spec_extends(decl, self); + else + /* decl is probably a security proxy. We have to go the long way + around. + */ + item = PyObject_CallFunctionObjArgs(decl, self, NULL); + + Py_DECREF(decl); + return item; +} + + +static char Spec_implementedBy__doc__[] = +"Test whether the specification is implemented by a class or factory.\n" +"Raise TypeError if argument is neither a class nor a callable." +; + +static PyObject * +Spec_implementedBy(PyObject *self, PyObject *cls) +{ + PyObject *decl, *item; + + decl = implementedBy(NULL, cls); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecType)) + item = Spec_extends(decl, self); + else + item = PyObject_CallFunctionObjArgs(decl, self, NULL); + + Py_DECREF(decl); + return item; +} + +static struct PyMethodDef Spec_methods[] = { + {"providedBy", + (PyCFunction)Spec_providedBy, METH_O, + Spec_providedBy__doc__}, + {"implementedBy", + (PyCFunction)Spec_implementedBy, METH_O, + Spec_implementedBy__doc__}, + {"isOrExtends", (PyCFunction)Spec_extends, METH_O, + Spec_extends__doc__}, + + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject SpecType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "SpecificationBase", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)Spec_call, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + "Base type for Specification objects", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ Spec_methods, +}; + +static PyObject * +OSD_descr_get(PyObject *self, PyObject *inst, PyObject *cls) +{ + PyObject *provides; + + if (inst == NULL) + return getObjectSpecification(NULL, cls); + + provides = PyObject_GetAttr(inst, str__provides__); + if (provides != NULL) + return provides; + PyErr_Clear(); + return implementedBy(NULL, cls); +} + +static PyTypeObject OSDType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "ObjectSpecificationDescriptor", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE , + "Object Specification Descriptor", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ 0, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)OSD_descr_get, +}; + +static PyObject * +CPB_descr_get(PyObject *self, PyObject *inst, PyObject *cls) +{ + PyObject *mycls, *implements; + + mycls = inst_attr(self, str_cls); + if (mycls == NULL) + return NULL; + + if (cls == mycls) + { + if (inst == NULL) + { + Py_INCREF(self); + return OBJECT(self); + } + + implements = inst_attr(self, str_implements); + Py_XINCREF(implements); + return implements; + } + + PyErr_SetObject(PyExc_AttributeError, str__provides__); + return NULL; +} + +static PyTypeObject CPBType = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_interface_coptimizations." + "ClassProvidesBase", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + "C Base class for ClassProvides", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ &SpecType, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)CPB_descr_get, +}; + +/* ==================================================================== */ +/* ========== Begin: __call__ and __adapt__ =========================== */ + +/* + def __adapt__(self, obj): + """Adapt an object to the reciever + """ + if self.providedBy(obj): + return obj + + for hook in adapter_hooks: + adapter = hook(self, obj) + if adapter is not None: + return adapter + + +*/ +static PyObject * +__adapt__(PyObject *self, PyObject *obj) +{ + PyObject *decl, *args, *adapter; + int implements, i, l; + + decl = providedBy(NULL, obj); + if (decl == NULL) + return NULL; + + if (PyObject_TypeCheck(decl, &SpecType)) + { + PyObject *implied; + + implied = inst_attr(decl, str_implied); + if (implied == NULL) + { + Py_DECREF(decl); + return NULL; + } + + implements = PyDict_GetItem(implied, self) != NULL; + Py_DECREF(decl); + } + else + { + /* decl is probably a security proxy. We have to go the long way + around. + */ + PyObject *r; + r = PyObject_CallFunctionObjArgs(decl, self, NULL); + Py_DECREF(decl); + if (r == NULL) + return NULL; + implements = PyObject_IsTrue(r); + Py_DECREF(r); + } + + if (implements) + { + Py_INCREF(obj); + return obj; + } + + l = PyList_GET_SIZE(adapter_hooks); + args = PyTuple_New(2); + if (args == NULL) + return NULL; + Py_INCREF(self); + PyTuple_SET_ITEM(args, 0, self); + Py_INCREF(obj); + PyTuple_SET_ITEM(args, 1, obj); + for (i = 0; i < l; i++) + { + adapter = PyObject_CallObject(PyList_GET_ITEM(adapter_hooks, i), args); + if (adapter == NULL || adapter != Py_None) + { + Py_DECREF(args); + return adapter; + } + Py_DECREF(adapter); + } + + Py_DECREF(args); + + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef ib_methods[] = { + {"__adapt__", (PyCFunction)__adapt__, METH_O, + "Adapt an object to the reciever"}, + {NULL, NULL} /* sentinel */ +}; + +/* + def __call__(self, obj, alternate=_marker): + conform = getattr(obj, '__conform__', None) + if conform is not None: + adapter = self._call_conform(conform) + if adapter is not None: + return adapter + + adapter = self.__adapt__(obj) + + if adapter is not None: + return adapter + elif alternate is not _marker: + return alternate + else: + raise TypeError("Could not adapt", obj, self) +*/ +static PyObject * +ib_call(PyObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *conform, *obj, *alternate=NULL, *adapter; + + static char *kwlist[] = {"obj", "alternate", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, + &obj, &alternate)) + return NULL; + + conform = PyObject_GetAttr(obj, str__conform__); + if (conform != NULL) + { + adapter = PyObject_CallMethodObjArgs(self, str_call_conform, + conform, NULL); + Py_DECREF(conform); + if (adapter == NULL || adapter != Py_None) + return adapter; + Py_DECREF(adapter); + } + else + PyErr_Clear(); + + adapter = __adapt__(self, obj); + if (adapter == NULL || adapter != Py_None) + return adapter; + Py_DECREF(adapter); + + if (alternate != NULL) + { + Py_INCREF(alternate); + return alternate; + } + + adapter = Py_BuildValue("sOO", "Could not adapt", obj, self); + if (adapter != NULL) + { + PyErr_SetObject(PyExc_TypeError, adapter); + Py_DECREF(adapter); + } + return NULL; +} + +static PyTypeObject InterfaceBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "InterfaceBase", + /* tp_basicsize */ 0, + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)0, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)ib_call, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE , + /* tp_doc */ "Interface base type providing __call__ and __adapt__", + /* tp_traverse */ (traverseproc)0, + /* tp_clear */ (inquiry)0, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ ib_methods, +}; + +/* =================== End: __call__ and __adapt__ ==================== */ +/* ==================================================================== */ + +/* ==================================================================== */ +/* ========================== Begin: Lookup Bases ===================== */ + +typedef struct { + PyObject_HEAD + PyObject *_cache; + PyObject *_mcache; + PyObject *_scache; +} lookup; + +typedef struct { + PyObject_HEAD + PyObject *_cache; + PyObject *_mcache; + PyObject *_scache; + PyObject *_verify_ro; + PyObject *_verify_generations; +} verify; + +static int +lookup_traverse(lookup *self, visitproc visit, void *arg) +{ + int vret; + + if (self->_cache) { + vret = visit(self->_cache, arg); + if (vret != 0) + return vret; + } + + if (self->_mcache) { + vret = visit(self->_mcache, arg); + if (vret != 0) + return vret; + } + + if (self->_scache) { + vret = visit(self->_scache, arg); + if (vret != 0) + return vret; + } + + return 0; +} + +static int +lookup_clear(lookup *self) +{ + Py_CLEAR(self->_cache); + Py_CLEAR(self->_mcache); + Py_CLEAR(self->_scache); + return 0; +} + +static void +lookup_dealloc(lookup *self) +{ + lookup_clear(self); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +/* + def changed(self, ignored=None): + self._cache.clear() + self._mcache.clear() + self._scache.clear() +*/ +static PyObject * +lookup_changed(lookup *self, PyObject *ignored) +{ + lookup_clear(self); + Py_INCREF(Py_None); + return Py_None; +} + +#define ASSURE_DICT(N) if (N == NULL) { N = PyDict_New(); \ + if (N == NULL) return NULL; \ + } + +/* + def _getcache(self, provided, name): + cache = self._cache.get(provided) + if cache is None: + cache = {} + self._cache[provided] = cache + if name: + c = cache.get(name) + if c is None: + c = {} + cache[name] = c + cache = c + return cache +*/ +static PyObject * +_subcache(PyObject *cache, PyObject *key) +{ + PyObject *subcache; + + subcache = PyDict_GetItem(cache, key); + if (subcache == NULL) + { + int status; + + subcache = PyDict_New(); + if (subcache == NULL) + return NULL; + status = PyDict_SetItem(cache, key, subcache); + Py_DECREF(subcache); + if (status < 0) + return NULL; + } + + return subcache; +} +static PyObject * +_getcache(lookup *self, PyObject *provided, PyObject *name) +{ + PyObject *cache; + + ASSURE_DICT(self->_cache); + cache = _subcache(self->_cache, provided); + if (cache == NULL) + return NULL; + + if (name != NULL && PyObject_IsTrue(name)) + cache = _subcache(cache, name); + + return cache; +} + + +/* + def lookup(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + if len(required) == 1: + result = cache.get(required[0], _not_in_mapping) + else: + result = cache.get(tuple(required), _not_in_mapping) + + if result is _not_in_mapping: + result = self._uncached_lookup(required, provided, name) + if len(required) == 1: + cache[required[0]] = result + else: + cache[tuple(required)] = result + + if result is None: + return default + + return result +*/ +static PyObject * +tuplefy(PyObject *v) +{ + if (! PyTuple_Check(v)) + { + v = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), v, NULL); + if (v == NULL) + return NULL; + } + else + Py_INCREF(v); + + return v; +} +static PyObject * +_lookup(lookup *self, + PyObject *required, PyObject *provided, PyObject *name, + PyObject *default_) +{ + PyObject *result, *key, *cache; + + cache = _getcache(self, provided, name); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == NULL) + return NULL; + + if (PyTuple_GET_SIZE(required) == 1) + key = PyTuple_GET_ITEM(required, 0); + else + key = required; + + result = PyDict_GetItem(cache, key); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs(OBJECT(self), str_uncached_lookup, + required, provided, name, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, key, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + if (result == Py_None && default_ != NULL) + { + Py_DECREF(Py_None); + Py_INCREF(default_); + return default_; + } + + return result; +} +static PyObject * +lookup_lookup(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + return _lookup(self, required, provided, name, default_); +} + + +/* + def lookup1(self, required, provided, name=u'', default=None): + cache = self._getcache(provided, name) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + return self.lookup((required, ), provided, name, default) + + if result is None: + return default + + return result +*/ +static PyObject * +_lookup1(lookup *self, + PyObject *required, PyObject *provided, PyObject *name, + PyObject *default_) +{ + PyObject *result, *cache; + + cache = _getcache(self, provided, name); + if (cache == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + PyObject *tup; + + tup = PyTuple_New(1); + if (tup == NULL) + return NULL; + Py_INCREF(required); + PyTuple_SET_ITEM(tup, 0, required); + result = _lookup(self, tup, provided, name, default_); + Py_DECREF(tup); + } + else + Py_INCREF(result); + + return result; +} +static PyObject * +lookup_lookup1(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + return _lookup1(self, required, provided, name, default_); +} + +/* + def adapter_hook(self, provided, object, name=u'', default=None): + required = providedBy(object) + cache = self._getcache(provided, name) + factory = cache.get(required, _not_in_mapping) + if factory is _not_in_mapping: + factory = self.lookup((required, ), provided, name) + + if factory is not None: + result = factory(object) + if result is not None: + return result + + return default +*/ +static PyObject * +_adapter_hook(lookup *self, + PyObject *provided, PyObject *object, PyObject *name, + PyObject *default_) +{ + PyObject *required, *factory, *result; + + required = providedBy(NULL, object); + if (required == NULL) + return NULL; + + factory = _lookup1(self, required, provided, name, Py_None); + Py_DECREF(required); + if (factory == NULL) + return NULL; + + if (factory != Py_None) + { + result = PyObject_CallFunctionObjArgs(factory, object, NULL); + Py_DECREF(factory); + if (result == NULL || result != Py_None) + return result; + } + else + result = factory; /* None */ + + if (default_ == NULL || default_ == result) /* No default specified, */ + return result; /* Return None. result is owned None */ + + Py_DECREF(result); + Py_INCREF(default_); + + return default_; +} +static PyObject * +lookup_adapter_hook(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"provided", "object", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &provided, &object, &name, &default_)) + return NULL; + + return _adapter_hook(self, provided, object, name, default_); +} + +static PyObject * +lookup_queryAdapter(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"object", "provided", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &object, &provided, &name, &default_)) + return NULL; + + return _adapter_hook(self, provided, object, name, default_); +} + +/* + def lookupAll(self, required, provided): + cache = self._mcache.get(provided) + if cache is None: + cache = {} + self._mcache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_lookupAll(required, provided) + cache[required] = result + + return result +*/ +static PyObject * +_lookupAll(lookup *self, PyObject *required, PyObject *provided) +{ + PyObject *cache, *result; + + ASSURE_DICT(self->_mcache); + cache = _subcache(self->_mcache, provided); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs(OBJECT(self), str_uncached_lookupAll, + required, provided, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, required, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + return result; +} +static PyObject * +lookup_lookupAll(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + return _lookupAll(self, required, provided); +} + +/* + def subscriptions(self, required, provided): + cache = self._scache.get(provided) + if cache is None: + cache = {} + self._scache[provided] = cache + + required = tuple(required) + result = cache.get(required, _not_in_mapping) + if result is _not_in_mapping: + result = self._uncached_subscriptions(required, provided) + cache[required] = result + + return result +*/ +static PyObject * +_subscriptions(lookup *self, PyObject *required, PyObject *provided) +{ + PyObject *cache, *result; + + ASSURE_DICT(self->_scache); + cache = _subcache(self->_scache, provided); + if (cache == NULL) + return NULL; + + required = tuplefy(required); + if (required == NULL) + return NULL; + + result = PyDict_GetItem(cache, required); + if (result == NULL) + { + int status; + + result = PyObject_CallMethodObjArgs( + OBJECT(self), str_uncached_subscriptions, + required, provided, NULL); + if (result == NULL) + { + Py_DECREF(required); + return NULL; + } + status = PyDict_SetItem(cache, required, result); + Py_DECREF(required); + if (status < 0) + { + Py_DECREF(result); + return NULL; + } + } + else + { + Py_INCREF(result); + Py_DECREF(required); + } + + return result; +} +static PyObject * +lookup_subscriptions(lookup *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + return _subscriptions(self, required, provided); +} + +static struct PyMethodDef lookup_methods[] = { + {"changed", (PyCFunction)lookup_changed, METH_O, ""}, + {"lookup", (PyCFunction)lookup_lookup, METH_KEYWORDS, ""}, + {"lookup1", (PyCFunction)lookup_lookup1, METH_KEYWORDS, ""}, + {"queryAdapter", (PyCFunction)lookup_queryAdapter, METH_KEYWORDS, ""}, + {"adapter_hook", (PyCFunction)lookup_adapter_hook, METH_KEYWORDS, ""}, + {"lookupAll", (PyCFunction)lookup_lookupAll, METH_KEYWORDS, ""}, + {"subscriptions", (PyCFunction)lookup_subscriptions, METH_KEYWORDS, ""}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject LookupBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "LookupBase", + /* tp_basicsize */ sizeof(lookup), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)&lookup_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ "", + /* tp_traverse */ (traverseproc)lookup_traverse, + /* tp_clear */ (inquiry)lookup_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ lookup_methods, +}; + +static int +verifying_traverse(verify *self, visitproc visit, void *arg) +{ + int vret; + + vret = lookup_traverse((lookup *)self, visit, arg); + if (vret != 0) + return vret; + + if (self->_verify_ro) { + vret = visit(self->_verify_ro, arg); + if (vret != 0) + return vret; + } + if (self->_verify_generations) { + vret = visit(self->_verify_generations, arg); + if (vret != 0) + return vret; + } + + return 0; +} + +static int +verifying_clear(verify *self) +{ + lookup_clear((lookup *)self); + Py_CLEAR(self->_verify_generations); + Py_CLEAR(self->_verify_ro); + return 0; +} + + +static void +verifying_dealloc(verify *self) +{ + verifying_clear(self); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +/* + def changed(self, originally_changed): + super(VerifyingBasePy, self).changed(originally_changed) + self._verify_ro = self._registry.ro[1:] + self._verify_generations = [r._generation for r in self._verify_ro] +*/ +static PyObject * +_generations_tuple(PyObject *ro) +{ + int i, l; + PyObject *generations; + + l = PyTuple_GET_SIZE(ro); + generations = PyTuple_New(l); + for (i=0; i < l; i++) + { + PyObject *generation; + + generation = PyObject_GetAttr(PyTuple_GET_ITEM(ro, i), str_generation); + if (generation == NULL) + { + Py_DECREF(generations); + return NULL; + } + PyTuple_SET_ITEM(generations, i, generation); + } + + return generations; +} +static PyObject * +verifying_changed(verify *self, PyObject *ignored) +{ + PyObject *t, *ro; + + verifying_clear(self); + + t = PyObject_GetAttr(OBJECT(self), str_registry); + if (t == NULL) + return NULL; + ro = PyObject_GetAttr(t, strro); + Py_DECREF(t); + if (ro == NULL) + return NULL; + + t = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), ro, NULL); + Py_DECREF(ro); + if (t == NULL) + return NULL; + + ro = PyTuple_GetSlice(t, 1, PyTuple_GET_SIZE(t)); + Py_DECREF(t); + if (ro == NULL) + return NULL; + + self->_verify_generations = _generations_tuple(ro); + if (self->_verify_generations == NULL) + { + Py_DECREF(ro); + return NULL; + } + + self->_verify_ro = ro; + + Py_INCREF(Py_None); + return Py_None; +} + +/* + def _verify(self): + if ([r._generation for r in self._verify_ro] + != self._verify_generations): + self.changed(None) +*/ +static int +_verify(verify *self) +{ + PyObject *changed_result; + + if (self->_verify_ro != NULL && self->_verify_generations != NULL) + { + PyObject *generations; + int changed; + + generations = _generations_tuple(self->_verify_ro); + if (generations == NULL) + return -1; + + changed = PyObject_RichCompareBool(self->_verify_generations, + generations, Py_NE); + Py_DECREF(generations); + if (changed == -1) + return -1; + + if (changed == 0) + return 0; + } + + changed_result = PyObject_CallMethodObjArgs(OBJECT(self), strchanged, + Py_None, NULL); + if (changed_result == NULL) + return -1; + + Py_DECREF(changed_result); + return 0; +} + +static PyObject * +verifying_lookup(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookup((lookup *)self, required, provided, name, default_); +} + +static PyObject * +verifying_lookup1(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", "name", "default", NULL}; + PyObject *required, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &required, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookup1((lookup *)self, required, provided, name, default_); +} + +static PyObject * +verifying_adapter_hook(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"provided", "object", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &provided, &object, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _adapter_hook((lookup *)self, provided, object, name, default_); +} + +static PyObject * +verifying_queryAdapter(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"object", "provided", "name", "default", NULL}; + PyObject *object, *provided, *name=NULL, *default_=NULL; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, + &object, &provided, &name, &default_)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _adapter_hook((lookup *)self, provided, object, name, default_); +} + +static PyObject * +verifying_lookupAll(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _lookupAll((lookup *)self, required, provided); +} + +static PyObject * +verifying_subscriptions(verify *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"required", "provided", NULL}; + PyObject *required, *provided; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, + &required, &provided)) + return NULL; + + if (_verify(self) < 0) + return NULL; + + return _subscriptions((lookup *)self, required, provided); +} + +static struct PyMethodDef verifying_methods[] = { + {"changed", (PyCFunction)verifying_changed, METH_O, ""}, + {"lookup", (PyCFunction)verifying_lookup, METH_KEYWORDS, ""}, + {"lookup1", (PyCFunction)verifying_lookup1, METH_KEYWORDS, ""}, + {"queryAdapter", (PyCFunction)verifying_queryAdapter, METH_KEYWORDS, ""}, + {"adapter_hook", (PyCFunction)verifying_adapter_hook, METH_KEYWORDS, ""}, + {"lookupAll", (PyCFunction)verifying_lookupAll, METH_KEYWORDS, ""}, + {"subscriptions", (PyCFunction)verifying_subscriptions, METH_KEYWORDS, ""}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject VerifyingBase = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "_zope_interface_coptimizations." + "VerifyingBase", + /* tp_basicsize */ sizeof(verify), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)&verifying_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ 0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ "", + /* tp_traverse */ (traverseproc)verifying_traverse, + /* tp_clear */ (inquiry)verifying_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ verifying_methods, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ &LookupBase, +}; + +/* ========================== End: Lookup Bases ======================= */ +/* ==================================================================== */ + + + +static struct PyMethodDef m_methods[] = { + {"implementedBy", (PyCFunction)implementedBy, METH_O, + "Interfaces implemented by a class or factory.\n" + "Raises TypeError if argument is neither a class nor a callable."}, + {"getObjectSpecification", (PyCFunction)getObjectSpecification, METH_O, + "Get an object's interfaces (internal api)"}, + {"providedBy", (PyCFunction)providedBy, METH_O, + "Get an object's interfaces"}, + + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + +#if PY_MAJOR_VERSION >= 3 +static char module_doc[] = "C optimizations for zope.interface\n\n" + "$Id: _zope_interface_coptimizations.c 111871 2010-05-02 17:19:14Z tseaver $"; + +static struct PyModuleDef _zic_module = { + PyModuleDef_HEAD_INIT, + "_zope_interface_coptimizations", + module_doc, + -1, + m_methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +static PyObject * +init(void) +{ + PyObject *m; + +#if PY_MAJOR_VERSION < 3 +#define DEFINE_STRING(S) \ + if(! (str ## S = PyString_FromString(# S))) return NULL +#else +#define DEFINE_STRING(S) \ + if(! (str ## S = PyUnicode_FromString(# S))) return NULL +#endif + + DEFINE_STRING(__dict__); + DEFINE_STRING(__implemented__); + DEFINE_STRING(__provides__); + DEFINE_STRING(__class__); + DEFINE_STRING(__providedBy__); + DEFINE_STRING(extends); + DEFINE_STRING(_implied); + DEFINE_STRING(_implements); + DEFINE_STRING(_cls); + DEFINE_STRING(__conform__); + DEFINE_STRING(_call_conform); + DEFINE_STRING(_uncached_lookup); + DEFINE_STRING(_uncached_lookupAll); + DEFINE_STRING(_uncached_subscriptions); + DEFINE_STRING(_registry); + DEFINE_STRING(_generation); + DEFINE_STRING(ro); + DEFINE_STRING(changed); +#undef DEFINE_STRING + adapter_hooks = PyList_New(0); + if (adapter_hooks == NULL) + return NULL; + + /* Initialize types: */ + SpecType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&SpecType) < 0) + return NULL; + OSDType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&OSDType) < 0) + return NULL; + CPBType.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&CPBType) < 0) + return NULL; + + InterfaceBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&InterfaceBase) < 0) + return NULL; + + LookupBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&LookupBase) < 0) + return NULL; + + VerifyingBase.tp_new = PyBaseObject_Type.tp_new; + if (PyType_Ready(&VerifyingBase) < 0) + return NULL; + + #if PY_MAJOR_VERSION < 3 + /* Create the module and add the functions */ + m = Py_InitModule3("_zope_interface_coptimizations", m_methods, + "C optimizations for zope.interface\n\n" + "$Id: _zope_interface_coptimizations.c 111871 2010-05-02 17:19:14Z tseaver $"); + #else + m = PyModule_Create(&_zic_module); + #endif + if (m == NULL) + return NULL; + + /* Add types: */ + if (PyModule_AddObject(m, "SpecificationBase", OBJECT(&SpecType)) < 0) + return NULL; + if (PyModule_AddObject(m, "ObjectSpecificationDescriptor", + (PyObject *)&OSDType) < 0) + return NULL; + if (PyModule_AddObject(m, "ClassProvidesBase", OBJECT(&CPBType)) < 0) + return NULL; + if (PyModule_AddObject(m, "InterfaceBase", OBJECT(&InterfaceBase)) < 0) + return NULL; + if (PyModule_AddObject(m, "LookupBase", OBJECT(&LookupBase)) < 0) + return NULL; + if (PyModule_AddObject(m, "VerifyingBase", OBJECT(&VerifyingBase)) < 0) + return NULL; + if (PyModule_AddObject(m, "adapter_hooks", adapter_hooks) < 0) + return NULL; + return m; +} + +#if PY_MAJOR_VERSION < 3 +PyMODINIT_FUNC +init_zope_interface_coptimizations(void) +{ + init(); +} +#else +PyInit__zope_interface_coptimizations(void) +{ + return init(); +} +#endif diff -Nru zope3-3.4.0/src/zope/interface/_zope_interface_coptimizations.py zope3-3.5~bzr18/src/zope/interface/_zope_interface_coptimizations.py --- zope3-3.4.0/src/zope/interface/_zope_interface_coptimizations.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/interface/_zope_interface_coptimizations.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_zope_interface_coptimizations.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/zope/lifecycleevent/__init__.py zope3-3.5~bzr18/src/zope/lifecycleevent/__init__.py --- zope3-3.4.0/src/zope/lifecycleevent/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/lifecycleevent/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,150 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Life cycle events + +$Id: __init__.py 107299 2009-12-29 20:08:53Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +from zope.component.interfaces import ObjectEvent +from zope.interface import implements +from zope.event import notify + +from zope.lifecycleevent.interfaces import IObjectCreatedEvent +from zope.lifecycleevent.interfaces import IObjectModifiedEvent +from zope.lifecycleevent.interfaces import IObjectCopiedEvent +from zope.lifecycleevent.interfaces import IObjectMovedEvent +from zope.lifecycleevent.interfaces import IObjectAddedEvent +from zope.lifecycleevent.interfaces import IObjectRemovedEvent +from zope.lifecycleevent.interfaces import IAttributes +from zope.lifecycleevent.interfaces import ISequence + + +class ObjectCreatedEvent(ObjectEvent): + """An object has been created""" + + implements(IObjectCreatedEvent) + + +class Attributes(object) : + """ + Describes modified attributes of an interface. + + >>> from zope.lifecycleevent.interfaces import IObjectMovedEvent + >>> desc = Attributes(IObjectMovedEvent, "newName", "newParent") + >>> desc.interface == IObjectMovedEvent + True + >>> 'newName' in desc.attributes + True + """ + + implements(IAttributes) + + def __init__(self, interface, *attributes) : + self.interface = interface + self.attributes = attributes + + +class Sequence(object) : + """ + Describes modified keys of an interface. + + >>> from zope.container.interfaces import IContainer + >>> desc = Sequence(IContainer, 'foo', 'bar') + >>> desc.interface == IContainer + True + >>> desc.keys + ('foo', 'bar') + + """ + + implements(ISequence) + + def __init__(self, interface, *keys) : + self.interface = interface + self.keys = keys + +class ObjectModifiedEvent(ObjectEvent): + """An object has been modified""" + + implements(IObjectModifiedEvent) + + def __init__(self, object, *descriptions) : + """ + Init with a list of modification descriptions. + + >>> from zope.interface import implements, Interface, Attribute + >>> class ISample(Interface) : + ... field = Attribute("A test field") + >>> class Sample(object) : + ... implements(ISample) + + >>> obj = Sample() + >>> obj.field = 42 + >>> notify(ObjectModifiedEvent(obj, Attributes(ISample, "field"))) + + """ + super(ObjectModifiedEvent, self).__init__(object) + self.descriptions = descriptions + + +def modified(object, *descriptions): + notify(ObjectModifiedEvent(object, *descriptions)) + + +class ObjectCopiedEvent(ObjectCreatedEvent): + """An object has been copied""" + + implements(IObjectCopiedEvent) + + def __init__(self, object, original): + super(ObjectCopiedEvent, self).__init__(object) + self.original = original + +class ObjectMovedEvent(ObjectEvent): + """An object has been moved""" + + implements(IObjectMovedEvent) + + def __init__(self, object, oldParent, oldName, newParent, newName): + ObjectEvent.__init__(self, object) + self.oldParent = oldParent + self.oldName = oldName + self.newParent = newParent + self.newName = newName + +class ObjectAddedEvent(ObjectMovedEvent): + """An object has been added to a container""" + + implements(IObjectAddedEvent) + + def __init__(self, object, newParent=None, newName=None): + if newParent is None: + newParent = object.__parent__ + if newName is None: + newName = object.__name__ + ObjectMovedEvent.__init__(self, object, None, None, newParent, newName) + +class ObjectRemovedEvent(ObjectMovedEvent): + """An object has been removed from a container""" + + implements(IObjectRemovedEvent) + + def __init__(self, object, oldParent=None, oldName=None): + if oldParent is None: + oldParent = object.__parent__ + if oldName is None: + oldName = object.__name__ + ObjectMovedEvent.__init__(self, object, oldParent, oldName, None, None) + diff -Nru zope3-3.4.0/src/zope/lifecycleevent/interfaces.py zope3-3.5~bzr18/src/zope/lifecycleevent/interfaces.py --- zope3-3.4.0/src/zope/lifecycleevent/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/lifecycleevent/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,90 @@ +############################################################################## +# +# Copyright (c) 2002, 2003 Zope Corporation 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. +# +############################################################################## +"""Event-related interfaces + +$Id: interfaces.py 100032 2009-05-17 16:21:45Z chrism $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface, Attribute +import zope.component.interfaces + +class IObjectCreatedEvent(zope.component.interfaces.IObjectEvent): + """An object has been created. + + The location will usually be ``None`` for this event.""" + + +class IObjectCopiedEvent(IObjectCreatedEvent): + """An object has been copied""" + + original = Attribute("The original from which the copy was made") + + +class IObjectModifiedEvent(zope.component.interfaces.IObjectEvent): + """An object has been modified""" + + +class IModificationDescription(Interface) : + """ Marker interface for descriptions of object modifications. + + Can be used as a parameter of an IObjectModifiedEvent.""" + + +class IAttributes(IModificationDescription) : + """ Describes the attributes of an interface. + + """ + + interface = Attribute("The involved interface.") + attributes = Attribute("A sequence of modified attributes.") + + +class ISequence(IModificationDescription) : + """ Describes the modified keys of a sequence-like interface. + + """ + + interface = Attribute("The involved interface.") + keys = Attribute("A sequence of modified keys.") + + +############################################################################## +# Moving Objects + +class IObjectMovedEvent(zope.component.interfaces.IObjectEvent): + """An object has been moved.""" + + oldParent = Attribute("The old location parent for the object.") + oldName = Attribute("The old location name for the object.") + newParent = Attribute("The new location parent for the object.") + newName = Attribute("The new location name for the object.") + + +############################################################################## +# Adding objects + +class IObjectAddedEvent(IObjectMovedEvent): + """An object has been added to a container.""" + + +############################################################################## +# Removing objects + + +class IObjectRemovedEvent(IObjectMovedEvent): + """An object has been removed from a container.""" + + + diff -Nru zope3-3.4.0/src/zope/lifecycleevent/README.txt zope3-3.5~bzr18/src/zope/lifecycleevent/README.txt --- zope3-3.4.0/src/zope/lifecycleevent/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/lifecycleevent/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,44 @@ +================= +Life-cycle events +================= + +In Zope, events are used by components to inform each other about +relevant new objects and object modifications. + +To keep all subscribers up to date it is indispensable that the life +cycle of an object is accompanied by various events. + + >>> from zope.event import notify + >>> from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent + + >>> class Sample(object) : + ... "Test class" + + >>> obj = Sample() + >>> notify(ObjectCreatedEvent(obj)) + + >>> obj.modified = True + >>> notify(ObjectModifiedEvent(obj)) + +Some event consumers like catalogs and caches may need more information to +update themselves in an efficient manner. The necessary information can be +provided as optional modification descriptions of the ObjectModifiedEvent. + +Some examples: + + >>> from zope.interface import Interface, Attribute, implements + >>> class IFile(Interface): + ... data = Attribute("Data") + ... + + >>> class File(object): + ... implements(IFile) + ... + + >>> file = File() + >>> file.data = "123" + >>> notify(ObjectModifiedEvent(obj, IFile)) + +This says that we modified something via IFile. Note that an interface is an +acceptable description. In fact, we might allow pretty much anything as a +description and it depends on your needs what kind of descriptions you use. diff -Nru zope3-3.4.0/src/zope/lifecycleevent/tests.py zope3-3.5~bzr18/src/zope/lifecycleevent/tests.py --- zope3-3.4.0/src/zope/lifecycleevent/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/lifecycleevent/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,170 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Object Event Tests + +$Id: tests.py 111739 2010-04-30 21:20:20Z hannosch $ +""" + +import doctest +import unittest + +import zope.component.testing +from zope.lifecycleevent import ObjectModifiedEvent + + +class TestObjectModifiedEvent(unittest.TestCase): + + klass = ObjectModifiedEvent + object = object() + + def setUp(self): + self.event = self.klass(self.object) + + def testGetObject(self): + self.assertEqual(self.event.object, self.object) + +class TestObjectMovedEvent(unittest.TestCase): + + def _getTargetClass(self): + from zope.lifecycleevent import ObjectMovedEvent + return ObjectMovedEvent + + def _makeOne(self, *arg): + return self._getTargetClass()(*arg) + + def test_it(self): + ob = Context() + old_parent = Context() + new_parent = Context() + event = self._makeOne(ob, old_parent, 'old_name', new_parent, + 'new_name') + self.assertEqual(event.object, ob) + self.assertEqual(event.oldParent, old_parent) + self.assertEqual(event.newParent, new_parent) + self.assertEqual(event.newName, 'new_name') + self.assertEqual(event.oldName, 'old_name') + + def test_verifyClass(self): + from zope.interface.verify import verifyClass + from zope.lifecycleevent.interfaces import IObjectMovedEvent + verifyClass(IObjectMovedEvent, self._getTargetClass()) + + def test_verifyObject(self): + from zope.interface.verify import verifyObject + from zope.lifecycleevent.interfaces import IObjectMovedEvent + verifyObject(IObjectMovedEvent, + self._makeOne(None, None, None, None, None) + ) + +class TestObjectAddedEvent(unittest.TestCase): + + def _getTargetClass(self): + from zope.lifecycleevent import ObjectAddedEvent + return ObjectAddedEvent + + def _makeOne(self, *arg): + return self._getTargetClass()(*arg) + + def test_it(self): + ob = Context() + new_parent = Context() + event = self._makeOne(ob, new_parent, 'new_name') + self.assertEqual(event.object, ob) + self.assertEqual(event.newParent, new_parent) + self.assertEqual(event.newName, 'new_name') + self.assertEqual(event.oldParent, None) + self.assertEqual(event.oldName, None) + + def test_it_Nones(self): + ob = Context() + new_parent = Context() + ob.__parent__ = new_parent + ob.__name__ = 'new_name' + event = self._makeOne(ob, None, None) + self.assertEqual(event.object, ob) + self.assertEqual(event.newParent, new_parent) + self.assertEqual(event.newName, 'new_name') + self.assertEqual(event.oldParent, None) + self.assertEqual(event.oldName, None) + + def test_verifyClass(self): + from zope.interface.verify import verifyClass + from zope.lifecycleevent.interfaces import IObjectAddedEvent + verifyClass(IObjectAddedEvent, self._getTargetClass()) + + def test_verifyObject(self): + from zope.interface.verify import verifyObject + from zope.lifecycleevent.interfaces import IObjectAddedEvent + parent = Context() + ob = Context() + verifyObject(IObjectAddedEvent, self._makeOne(ob, parent, 'new_name')) + +class TestObjectRemovedEvent(unittest.TestCase): + + def _getTargetClass(self): + from zope.lifecycleevent import ObjectRemovedEvent + return ObjectRemovedEvent + + def _makeOne(self, *arg): + return self._getTargetClass()(*arg) + + def test_it(self): + ob = Context() + parent = Context() + event = self._makeOne(ob, parent, 'name') + self.assertEqual(event.object, ob) + self.assertEqual(event.newParent, None) + self.assertEqual(event.newName, None) + self.assertEqual(event.oldParent, parent) + self.assertEqual(event.oldName, 'name') + + def test_it_Nones(self): + ob = Context() + parent = Context() + ob.__parent__ = parent + ob.__name__ = 'name' + event = self._makeOne(ob, None, None) + self.assertEqual(event.object, ob) + self.assertEqual(event.newParent, None) + self.assertEqual(event.newName, None) + self.assertEqual(event.oldParent, parent) + self.assertEqual(event.oldName, 'name') + + def test_verifyClass(self): + from zope.interface.verify import verifyClass + from zope.lifecycleevent.interfaces import IObjectRemovedEvent + verifyClass(IObjectRemovedEvent, self._getTargetClass()) + + def test_verifyObject(self): + from zope.interface.verify import verifyObject + from zope.lifecycleevent.interfaces import IObjectRemovedEvent + parent = object() + ob = object() + verifyObject(IObjectRemovedEvent, self._makeOne(ob, parent, 'new_name')) + +class Context: + pass + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(TestObjectModifiedEvent), + unittest.makeSuite(TestObjectMovedEvent), + unittest.makeSuite(TestObjectAddedEvent), + unittest.makeSuite(TestObjectRemovedEvent), + doctest.DocFileSuite('README.txt', + tearDown=zope.component.testing.tearDown), + )) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/location/configure.zcml zope3-3.5~bzr18/src/zope/location/configure.zcml --- zope3-3.4.0/src/zope/location/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/location/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + diff -Nru zope3-3.4.0/src/zope/location/__init__.py zope3-3.5~bzr18/src/zope/location/__init__.py --- zope3-3.4.0/src/zope/location/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/location/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2003-2009 Zope Corporation 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. +# +############################################################################## +"""Locations + +$Id: __init__.py 95519 2009-01-29 19:39:10Z ctheune $ +""" +__docformat__ = 'restructuredtext' + +from zope.location.interfaces import ILocation +from zope.location.location import Location, locate, LocationIterator +from zope.location.location import inside, LocationProxy diff -Nru zope3-3.4.0/src/zope/location/interfaces.py zope3-3.5~bzr18/src/zope/location/interfaces.py --- zope3-3.4.0/src/zope/location/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/location/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,126 @@ +############################################################################## +# +# Copyright (c) 2003-2009 Zope Corporation 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. +# +############################################################################## +"""Location framework interfaces + +$Id: interfaces.py 105794 2009-11-18 07:10:50Z tlotze $ +""" +__docformat__ = 'restructuredtext' + +import zope.interface +import zope.schema + +# BBB +from zope.component.interfaces import IPossibleSite, ISite + + +class ILocation(zope.interface.Interface): + """Objects that can be located in a hierachy. + + Given a parent and a name an object can be located within that parent. The + locatable object's `__name__` and `__parent__` attributes store this + information. + + Located objects form a hierarchy that can be used to build file-system-like + structures. For example in Zope `ILocation` is used to build URLs and to + support security machinery. + + To retrieve an object from its parent using its name, the `ISublocation` + interface provides the `sublocations` method to iterate over all objects + located within the parent. The object searched for can be found by reading + each sublocation's __name__ attribute. + + """ + + __parent__ = zope.interface.Attribute("The parent in the location hierarchy.") + + __name__ = zope.schema.TextLine( + title=u"The name within the parent", + description=u"The object can be looked up from the parent's " + "sublocations using this name.", + required=False, + default=None) + +# The IContained interface was moved from zope.container to here in +# zope.container 3.8.2 to break dependency cycles. It is not actually +# used within this package, but is depended upon by external +# consumers. + +class IContained(ILocation): + """Objects contained in containers.""" + +class ILocationInfo(zope.interface.Interface): + """Provides supplemental information for located objects. + + Requires that the object has been given a location in a hierarchy. + + """ + + def getRoot(): + """Return the root object of the hierarchy.""" + + def getPath(): + """Return the physical path to the object as a string. + + Uses '/' as the path segment separator. + + """ + + def getParent(): + """Returns the container the object was traversed via. + + Returns None if the object is a containment root. + Raises TypeError if the object doesn't have enough context to get the + parent. + + """ + + def getParents(): + """Returns a list starting with the object's parent followed by + each of its parents. + + Raises a TypeError if the object is not connected to a containment + root. + + """ + + def getName(): + """Return the last segment of the physical path.""" + + def getNearestSite(): + """Return the site the object is contained in + + If the object is a site, the object itself is returned. + + """ + + +class ISublocations(zope.interface.Interface): + """Provide access to sublocations of an object. + + All objects with the same parent object are called the ``sublocations`` of + that parent. + + """ + + def sublocations(): + """Return an iterable of the object's sublocations.""" + + +class IRoot(zope.interface.Interface): + """Marker interface to designate root objects within a location hierarchy. + """ + + +class LocationError(KeyError, LookupError): + """There is no object for a given location.""" diff -Nru zope3-3.4.0/src/zope/location/location.py zope3-3.5~bzr18/src/zope/location/location.py --- zope3-3.4.0/src/zope/location/location.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/location/location.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,123 @@ +############################################################################## +# +# Copyright (c) 2003-2009 Zope Corporation 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. +# +############################################################################## +"""Location support + +$Id: location.py 95519 2009-01-29 19:39:10Z ctheune $ +""" +__docformat__ = 'restructuredtext' + +import zope.interface +import zope.component + +# XXX import error when doing import zope.location.interfaces :/ +from zope.location.interfaces import ILocation +from zope.proxy import ProxyBase, non_overridable +from zope.proxy.decorator import DecoratorSpecificationDescriptor + + +class Location(object): + """Mix-in that implements ILocation. + + It provides the `__parent__` and `__name__` attributes. + + """ + zope.interface.implements(ILocation) + + __parent__ = None + __name__ = None + + +def locate(obj, parent, name=None): + """Update a location's coordinates.""" + obj.__parent__ = parent + obj.__name__ = name + + +def located(obj, parent, name=None): + """Ensure and return the location of an object. + + Updates the location's coordinates. + + """ + location = zope.location.interfaces.ILocation(obj) + locate(location, parent, name) + return location + + +def LocationIterator(object): + """Iterate over an object and all of its parents.""" + while object is not None: + yield object + object = getattr(object, '__parent__', None) + + +def inside(l1, l2): + """Test whether l1 is a successor of l2. + + l1 is a successor of l2 if l2 is in the chain of parents of l1 or l2 + is l1. + + """ + while l1 is not None: + if l1 is l2: + return True + l1 = l1.__parent__ + return False + +class ClassAndInstanceDescr(object): + + def __init__(self, *args): + self.funcs = args + + def __get__(self, inst, cls): + if inst is None: + return self.funcs[1](cls) + return self.funcs[0](inst) + + +class LocationProxy(ProxyBase): + """Location-object proxy + + This is a non-picklable proxy that can be put around objects that + don't implement `ILocation`. + + """ + + zope.component.adapts(zope.interface.Interface) + zope.interface.implements(ILocation) + + __slots__ = '__parent__', '__name__' + __safe_for_unpickling__ = True + + def __new__(self, ob, container=None, name=None): + return ProxyBase.__new__(self, ob) + + def __init__(self, ob, container=None, name=None): + ProxyBase.__init__(self, ob) + self.__parent__ = container + self.__name__ = name + + @non_overridable + def __reduce__(self, proto=None): + raise TypeError("Not picklable") + + + __doc__ = ClassAndInstanceDescr( + lambda inst: getProxiedObject(inst).__doc__, + lambda cls, __doc__ = __doc__: __doc__, + ) + + __reduce_ex__ = __reduce__ + + __providedBy__ = DecoratorSpecificationDescriptor() diff -Nru zope3-3.4.0/src/zope/location/location.txt zope3-3.5~bzr18/src/zope/location/location.txt --- zope3-3.4.0/src/zope/location/location.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/location/location.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,191 @@ +======== +Location +======== + +Location Base Class +------------------- + +The `Location` base class is a stupid mix-in that defines `__parent__` and +`__name__` attributes. + +Usage within an Object field: + + >>> from zope.interface import implements, Interface + >>> from zope.schema import Object + >>> from zope.schema.fieldproperty import FieldProperty + >>> from zope.location.interfaces import ILocation + >>> from zope.location.location import Location + + >>> class IA(Interface): + ... location = Object(schema=ILocation, required=False, default=None) + >>> class A(object): + ... implements(IA) + ... location = FieldProperty(IA['location']) + + >>> a = A() + >>> a.location = Location() + + >>> loc = Location(); loc.__name__ = u'foo' + >>> a.location = loc + + >>> loc = Location(); loc.__name__ = None + >>> a.location = loc + + >>> loc = Location(); loc.__name__ = 'foo' + >>> a.location = loc + Traceback (most recent call last): + ... + WrongContainedType: ([WrongType('foo', , '__name__')], 'location') + + +The `inside` Function +--------------------- + +The `inside` function tells if l1 is inside l2. L1 is inside l2 if l2 is an +ancestor of l1. + + >>> o1 = Location() + >>> o2 = Location(); o2.__parent__ = o1 + >>> o3 = Location(); o3.__parent__ = o2 + >>> o4 = Location(); o4.__parent__ = o3 + + >>> from zope.location.location import inside + + >>> inside(o1, o1) + True + + >>> inside(o2, o1) + True + + >>> inside(o3, o1) + True + + >>> inside(o4, o1) + True + + >>> inside(o1, o4) + False + + >>> inside(o1, None) + False + + +LocationProxy +------------- + +The LocationProxy is a non-picklable proxy that can be put around +objects that don't implement `ILocation`. + + >>> from zope.location.location import LocationProxy + >>> l = [1, 2, 3] + >>> ILocation.providedBy(l) + False + >>> p = LocationProxy(l, "Dad", "p") + >>> p + [1, 2, 3] + >>> ILocation.providedBy(p) + True + >>> p.__parent__ + 'Dad' + >>> p.__name__ + 'p' + + >>> import pickle + >>> p2 = pickle.dumps(p) + Traceback (most recent call last): + ... + TypeError: Not picklable + +Proxies should get their doc strings from the object they proxy: + + >>> p.__doc__ == l.__doc__ + True + +If we get a "located class" somehow, its doc string well be available +through proxy as well: + + >>> class LocalClass(object): + ... """This is class that can be located""" + + >>> p = LocationProxy(LocalClass) + >>> p.__doc__ == LocalClass.__doc__ + True + +LocationInterator +----------------- + +This function allows us to iterate over object and all its parents. + + >>> from zope.location.location import LocationIterator + + >>> o1 = Location() + >>> o2 = Location() + >>> o3 = Location() + >>> o3.__parent__ = o2 + >>> o2.__parent__ = o1 + + >>> iter = LocationIterator(o3) + >>> iter.next() is o3 + True + >>> iter.next() is o2 + True + >>> iter.next() is o1 + True + >>> iter.next() + Traceback (most recent call last): + ... + StopIteration + + +The `located` function +---------------------- + +`located` locates an object in another and returns it: + + >>> from zope.location.location import located + >>> a = Location() + >>> parent = Location() + >>> a_located = located(a, parent, 'a') + >>> a_located is a + True + >>> a_located.__parent__ is parent + True + >>> a_located.__name__ + 'a' + +If we locate the object again, nothing special happens: + + >>> a_located_2 = located(a_located, parent, 'a') + >>> a_located_2 is a_located + True + +If the object does not provide ILocation an adapter can be provided: + + >>> import zope.interface + >>> import zope.component + >>> sm = zope.component.getGlobalSiteManager() + >>> sm.registerAdapter(LocationProxy, required=(zope.interface.Interface,)) + + >>> l = [1, 2, 3] + >>> parent = Location() + >>> l_located = located(l, parent, 'l') + >>> l_located.__parent__ is parent + True + >>> l_located.__name__ + 'l' + >>> l_located is l + False + >>> type(l_located) + + >>> l_located_2 = located(l_located, parent, 'l') + >>> l_located_2 is l_located + True + +When changing the name, we still do not get a different proxied object: + + >>> l_located_3 = located(l_located, parent, 'new-name') + >>> l_located_3 is l_located_2 + True + + >>> sm.unregisterAdapter(LocationProxy, required=(zope.interface.Interface,)) + True diff -Nru zope3-3.4.0/src/zope/location/pickling.py zope3-3.5~bzr18/src/zope/location/pickling.py --- zope3-3.4.0/src/zope/location/pickling.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/location/pickling.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,56 @@ +############################################################################## +# +# Copyright (c) 2003-2009 Zope Corporation 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. +# +############################################################################## +"""Location copying/pickling support + +$Id: pickling.py 107320 2009-12-29 22:47:42Z hannosch $ +""" +__docformat__ = 'restructuredtext' + +from zope.component import adapts +from zope.interface import implements +from zope.location.interfaces import ILocation +from zope.location.location import inside + +try: + from zope.copy.interfaces import ICopyHook, ResumeCopy +except ImportError: + raise NotImplementedError("zope.location.pickling is not supported " + "because zope.copy is not available") + + +class LocationCopyHook(object): + """Copy hook to preserve copying referenced objects that are not + located inside object that's being copied. + """ + + adapts(ILocation) + implements(ICopyHook) + + def __init__(self, context): + self.context = context + + def __call__(self, toplevel, register): + if not inside(self.context, toplevel): + return self.context + raise ResumeCopy + +# BBB 2009-09-02 +# The locationCopy was replaced by more generic "clone" function +# in the zope.copy package. This reference may be removed someday. +from zope.copy import clone as locationCopy + +# BBB 2009-09-02 +# The CopyPersistent was made more generic and moved to the +# zope.copy package. This reference may be removed someday. +from zope.copy import CopyPersistent diff -Nru zope3-3.4.0/src/zope/location/tests.py zope3-3.5~bzr18/src/zope/location/tests.py --- zope3-3.4.0/src/zope/location/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/location/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2003-2009 Zope Corporation 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. +# +############################################################################## +"""Location support tests + +$Id: tests.py 107320 2009-12-29 22:47:42Z hannosch $ +""" + +import doctest +import unittest + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('location.txt'), + doctest.DocTestSuite('zope.location.traversing'), + )) diff -Nru zope3-3.4.0/src/zope/location/traversing.py zope3-3.5~bzr18/src/zope/location/traversing.py --- zope3-3.4.0/src/zope/location/traversing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/location/traversing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,378 @@ +############################################################################## +# +# Copyright (c) 2003-2009 Zope Corporation 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. +# +############################################################################## +"""Classes to support implenting IContained + +$Id: traversing.py 105796 2009-11-18 09:06:48Z tlotze $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +import zope.component.interfaces +import zope.interface +from zope.location.interfaces import ILocationInfo +from zope.location.interfaces import ILocation, IRoot +from zope.location.location import Location + + +class LocationPhysicallyLocatable(object): + """Provide location information for location objects + + >>> from zope.interface.verify import verifyObject + >>> info = LocationPhysicallyLocatable(Location()) + >>> verifyObject(ILocationInfo, info) + True + + """ + + zope.component.adapts(ILocation) + zope.interface.implements(ILocationInfo) + + def __init__(self, context): + self.context = context + + def getRoot(self): + """Get the root location for a location. + + See ILocationInfo + + The root location is a location that contains the given + location and that implements IContainmentRoot. + + >>> root = Location() + >>> zope.interface.directlyProvides(root, IRoot) + >>> LocationPhysicallyLocatable(root).getRoot() is root + True + + >>> o1 = Location(); o1.__parent__ = root + >>> LocationPhysicallyLocatable(o1).getRoot() is root + True + + >>> o2 = Location(); o2.__parent__ = o1 + >>> LocationPhysicallyLocatable(o2).getRoot() is root + True + + We'll get a TypeError if we try to get the location fo a + rootless object: + + >>> o1.__parent__ = None + >>> LocationPhysicallyLocatable(o1).getRoot() + Traceback (most recent call last): + ... + TypeError: Not enough context to determine location root + >>> LocationPhysicallyLocatable(o2).getRoot() + Traceback (most recent call last): + ... + TypeError: Not enough context to determine location root + + If we screw up and create a location cycle, it will be caught: + + >>> o1.__parent__ = o2 + >>> LocationPhysicallyLocatable(o1).getRoot() + Traceback (most recent call last): + ... + TypeError: Maximum location depth exceeded, """ \ + """probably due to a a location cycle. + """ + context = self.context + max = 9999 + while context is not None: + if IRoot.providedBy(context): + return context + context = context.__parent__ + max -= 1 + if max < 1: + raise TypeError("Maximum location depth exceeded, " + "probably due to a a location cycle.") + + raise TypeError("Not enough context to determine location root") + + def getPath(self): + """Get the path of a location. + + See ILocationInfo + + This is an "absolute path", rooted at a root object. + + >>> root = Location() + >>> zope.interface.directlyProvides(root, IRoot) + >>> LocationPhysicallyLocatable(root).getPath() + u'/' + + >>> o1 = Location(); o1.__parent__ = root; o1.__name__ = 'o1' + >>> LocationPhysicallyLocatable(o1).getPath() + u'/o1' + + >>> o2 = Location(); o2.__parent__ = o1; o2.__name__ = u'o2' + >>> LocationPhysicallyLocatable(o2).getPath() + u'/o1/o2' + + It is an error to get the path of a rootless location: + + >>> o1.__parent__ = None + >>> LocationPhysicallyLocatable(o1).getPath() + Traceback (most recent call last): + ... + TypeError: Not enough context to determine location root + + >>> LocationPhysicallyLocatable(o2).getPath() + Traceback (most recent call last): + ... + TypeError: Not enough context to determine location root + + If we screw up and create a location cycle, it will be caught: + + >>> o1.__parent__ = o2 + >>> LocationPhysicallyLocatable(o1).getPath() + Traceback (most recent call last): + ... + TypeError: Maximum location depth exceeded, """ \ + """probably due to a a location cycle. + + """ + + path = [] + context = self.context + max = 9999 + while context is not None: + if IRoot.providedBy(context): + if path: + path.append('') + path.reverse() + return u'/'.join(path) + else: + return u'/' + path.append(context.__name__) + context = context.__parent__ + max -= 1 + if max < 1: + raise TypeError("Maximum location depth exceeded, " + "probably due to a a location cycle.") + + raise TypeError("Not enough context to determine location root") + + def getParent(self): + """Returns the container the object was traversed via. + + Returns None if the object is a containment root. + Raises TypeError if the object doesn't have enough context to get the + parent. + + >>> root = Location() + >>> zope.interface.directlyProvides(root, IRoot) + >>> o1 = Location() + >>> o2 = Location() + + >>> LocationPhysicallyLocatable(o2).getParent() # doctest: +ELLIPSIS + Traceback (most recent call last): + TypeError: ('Not enough context information to get parent', ) + + >>> o1.__parent__ = root + >>> LocationPhysicallyLocatable(o1).getParent() == root + True + + >>> o2.__parent__ = o1 + >>> LocationPhysicallyLocatable(o2).getParent() == o1 + True + + """ + parent = getattr(self.context, '__parent__', None) + if parent is not None: + return parent + + raise TypeError('Not enough context information to get parent', + self.context) + + def getParents(self): + """Returns a list starting with the object's parent followed by + each of its parents. + + Raises a TypeError if the object is not connected to a containment + root. + + >>> root = Location() + >>> zope.interface.directlyProvides(root, IRoot) + >>> o1 = Location() + >>> o2 = Location() + >>> o1.__parent__ = root + >>> o2.__parent__ = o1 + >>> LocationPhysicallyLocatable(o2).getParents() == [o1, root] + True + + If the last parent is not an IRoot object, TypeError will be + raised as statet before. + + >>> zope.interface.noLongerProvides(root, IRoot) + >>> LocationPhysicallyLocatable(o2).getParents() + Traceback (most recent call last): + ... + TypeError: Not enough context information to get all parents + + """ + # XXX Merge this implementation with getPath. This was refactored + # from zope.traversing. + parents = [] + w = self.context + while 1: + w = w.__parent__ + if w is None: + break + parents.append(w) + + if parents and IRoot.providedBy(parents[-1]): + return parents + + raise TypeError("Not enough context information to get all parents") + + def getName(self): + """Get a location name + + See ILocationInfo + + >>> o1 = Location(); o1.__name__ = u'o1' + >>> LocationPhysicallyLocatable(o1).getName() + u'o1' + """ + return self.context.__name__ + + def getNearestSite(self): + """return the nearest site, see ILocationInfo + + >>> o1 = Location() + >>> o1.__name__ = 'o1' + >>> LocationPhysicallyLocatable(o1).getNearestSite() + Traceback (most recent call last): + ... + TypeError: Not enough context information to get all parents + + >>> root = Location() + >>> zope.interface.directlyProvides(root, IRoot) + >>> o1 = Location() + >>> o1.__name__ = 'o1' + >>> o1.__parent__ = root + >>> LocationPhysicallyLocatable(o1).getNearestSite() is root + True + + >>> zope.interface.directlyProvides( + ... o1, zope.component.interfaces.ISite) + >>> LocationPhysicallyLocatable(o1).getNearestSite() is o1 + True + + >>> o2 = Location() + >>> o2.__parent__ = o1 + >>> LocationPhysicallyLocatable(o2).getNearestSite() is o1 + True + + """ + if zope.component.interfaces.ISite.providedBy(self.context): + return self.context + for parent in self.getParents(): + if zope.component.interfaces.ISite.providedBy(parent): + return parent + return self.getRoot() + +class RootPhysicallyLocatable(object): + """Provide location information for the root object + + >>> from zope.interface.verify import verifyObject + >>> info = RootPhysicallyLocatable(None) + >>> verifyObject(ILocationInfo, info) + True + + This adapter is very simple, because there's no places to search + for parents and nearest sites, so we are only working with context + object, knowing that its the root object already. + + """ + + zope.component.adapts(IRoot) + zope.interface.implements(ILocationInfo) + + def __init__(self, context): + self.context = context + + def getRoot(self): + """See ILocationInfo + + No need to search for root when our context is already root :) + + >>> o1 = object() + >>> RootPhysicallyLocatable(o1).getRoot() is o1 + True + + """ + return self.context + + def getPath(self): + """See ILocationInfo + + Root object is at the top of the tree, so always return ``/``. + + >>> o1 = object() + >>> RootPhysicallyLocatable(o1).getPath() + u'/' + + """ + return u'/' + + def getName(self): + """See ILocationInfo + + Always return empty unicode string for the root object + + >>> o1 = object() + >>> RootPhysicallyLocatable(o1).getName() + u'' + + """ + return u'' + + def getParent(self): + """Returns the container the object was traversed via. + + Returns None if the object is a containment root. + Raises TypeError if the object doesn't have enough context to get the + parent. + + >>> o1 = object() + >>> RootPhysicallyLocatable(o1).getParent() + + """ + return None + + def getParents(self): + """See ILocationInfo + + There's no parents for the root object, return empty list. + + >>> o1 = object() + >>> RootPhysicallyLocatable(o1).getParents() + [] + + """ + return [] + + def getNearestSite(self): + """See ILocationInfo + + Return object itself as the nearest site, because there's no + other place to look for. It's also usual that the root is the + site as well. + + + >>> o1 = object() + >>> RootPhysicallyLocatable(o1).getNearestSite() is o1 + True + + """ + return self.context diff -Nru zope3-3.4.0/src/zope/minmax/__init__.py zope3-3.5~bzr18/src/zope/minmax/__init__.py --- zope3-3.4.0/src/zope/minmax/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/minmax/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +from zope.minmax._minmax import Maximum, Minimum diff -Nru zope3-3.4.0/src/zope/minmax/interfaces.py zope3-3.5~bzr18/src/zope/minmax/interfaces.py --- zope3-3.4.0/src/zope/minmax/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/minmax/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,13 @@ +import persistent.interfaces +import zope.interface + +class IAbstractValue(persistent.interfaces.IPersistent): + """A persistent value with the conflict resolution. + + The values are expected to be homogeneous. + """ + + value = zope.interface.Attribute('The initial value') + + def __nonzero__(): + """Return Boolean cast of the value as True or False.""" diff -Nru zope3-3.4.0/src/zope/minmax/_minmax.py zope3-3.5~bzr18/src/zope/minmax/_minmax.py --- zope3-3.4.0/src/zope/minmax/_minmax.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/minmax/_minmax.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,33 @@ +import persistent +import zope.interface + +from zope.minmax import interfaces + +class AbstractValue(persistent.Persistent): + + zope.interface.implements(interfaces.IAbstractValue) + + def __init__(self, value=None): + self.value = value + + def __getstate__(self): + return self.value + + def __setstate__(self, value): + self.value = value + + def __nonzero__(self): + return bool(self.value) + + def _p_resolveConflict(self, old, commited, new): + raise NotImplementedError() + +class Maximum(AbstractValue): + def _p_resolveConflict(self, old, commited, new): + # including old does not seem logical but it's open to discussion + return max(commited, new) + +class Minimum(AbstractValue): + def _p_resolveConflict(self, old, commited, new): + # including old does not seem logical but it's open to discussion + return min(commited, new) diff -Nru zope3-3.4.0/src/zope/minmax/minmax.txt zope3-3.5~bzr18/src/zope/minmax/minmax.txt --- zope3-3.4.0/src/zope/minmax/minmax.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/minmax/minmax.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,163 @@ +=================================================== +Conflict Resolution using Maximum or Minimum Values +=================================================== + +The `zope.minmax.AbstractValue` class provides a super class which can +be subclassed to store arbitrary *homogeneous* values in a persistent +storage and apply different conflict resolution policies. + +The subclasses defined here are resolving the conflicts using always +either the maximum or the minimum of the conflicting values. + +Maximum +------- + +The `zope.minmax.Maximum` class always resolves conflicts favoring the +maximum value. Let's instantiate one object and verify that it +satisfies the interface. + + >>> import zope.minmax + >>> import zope.interface.verify + >>> max_favored = zope.minmax.Maximum() + >>> zope.interface.verify.verifyObject( + ... zope.minmax.interfaces.IAbstractValue, max_favored) + True + +We can confirm that the initial value is zero. + + >>> bool(max_favored) + False + >>> print max_favored.value + None + +Now, we can store a new value in the object. + + >>> max_favored.value = 11 + >>> print max_favored.value + 11 + >>> bool(max_favored) + True + +Or we can use the methods. + + >>> max_favored.__setstate__(4532) + >>> max_favored.__getstate__() + 4532 + >>> print max_favored.value + 4532 + >>> bool(max_favored) + True + +Do notice that using a direct assignment to the value attribute is a +more natural use. + +Minimum +------- + +The `zope.minmax.Minimum` class always resolves conflicts favoring the +minimum value. Again, we instantiate an object and verify that it +satisfies the interface. + + >>> min_favored = zope.minmax.Minimum() + >>> zope.interface.verify.verifyObject( + ... zope.minmax.interfaces.IAbstractValue, min_favored) + True + +We need a confirmation that the initial value is zero. + + >>> bool(min_favored) + False + >>> print min_favored.value + None + +Let's populate this one too. + + >>> min_favored.value = 22 + >>> print min_favored.value + 22 + >>> bool(min_favored) + True + +Or we can use the methods, again. + + >>> min_favored.__setstate__(8796) + >>> min_favored.__getstate__() + 8796 + >>> print min_favored.value + 8796 + >>> bool(min_favored) + True + +Please, notice, again, that using a direct assignment to the value +attribute is a more natural use. + +Conflict Resolution +------------------- + +Now, we need to exercise the conflict resolution interface. +First for the `zope.minmax.Maximum`: + +Let's try differing values larger than the old value. + + >>> max_favored._p_resolveConflict(max_favored.value, 4536, 4535) + 4536 + >>> max_favored._p_resolveConflict(max_favored.value, 4573, 4574) + 4574 + +What happens when all the values are equal, including the old. + + >>> max_favored._p_resolveConflict(max_favored.value, 4532, 4532) + 4532 + +Notice that when the old value is larger than both the committed and +new, it is still disregarded. + + >>> max_favored._p_resolveConflict(max_favored.value, 4531, 4530) + 4531 + +Now, the `zope.minmax.Minimum`: + +Let's try differing values smaller than the old value. + + >>> min_favored._p_resolveConflict(min_favored.value, 8792, 8791) + 8791 + >>> min_favored._p_resolveConflict(min_favored.value, 8785, 8786) + 8785 + +What happens when all the values are equal, including the old. + + >>> min_favored._p_resolveConflict(min_favored.value, 8796, 8796) + 8796 + +Notice that when the old value is smaller than both the committed and +new, it is still disregarded. + + >>> min_favored._p_resolveConflict(min_favored.value, 8798, 8799) + 8798 + +How about an example that is not numerical? + + >>> max_word = zope.minmax.Maximum('joy') + >>> print max_word.value + joy + >>> bool(max_word) + True + >>> max_word._p_resolveConflict(max_word.value, 'happiness', 'exuberance') + 'happiness' + >>> max_word._p_resolveConflict(max_word.value, 'exuberance', 'happiness') + 'happiness' + >>> min_word = zope.minmax.Minimum(max_word.value) + >>> print min_word.value + joy + >>> bool(min_word) + True + >>> min_word._p_resolveConflict(min_word.value, 'happiness', 'exuberance') + 'exuberance' + >>> min_word._p_resolveConflict(min_word.value, 'exuberance', 'happiness') + 'exuberance' + +As indicated, we don't need to have numbers, just *homegeneous* items. +The homogeneous values are not really inherently required. However, it +makes no sense to apply min() or max() on, say, one number and one +string. Simply, the ordering relations do not work at all on +heterogeneous values. diff -Nru zope3-3.4.0/src/zope/minmax/tests.py zope3-3.5~bzr18/src/zope/minmax/tests.py --- zope3-3.4.0/src/zope/minmax/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/minmax/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ +import doctest +import unittest + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('minmax.txt'), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/pagetemplate/architecture.txt zope3-3.5~bzr18/src/zope/pagetemplate/architecture.txt --- zope3-3.4.0/src/zope/pagetemplate/architecture.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/architecture.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +===================================== +ZPT (Zope Page-Template) Architecture +===================================== + +There are a number of major components that make up the page-template +architecture: + +- The TAL *compiler* and *interpreter*. This is responsible for + compiling source files and for executing compiled templates. See + the `zope.tal` package for more information. + +- An *expression engine* is responsible for compiling expressions and + for creating expression execution contexts. It is common for + applications to override expression engines to provide custom + expression support or to change the way expressions are implemented. + The `zope.app.pagetemplate` package uses this to implement trusted + and untrusted evaluation; a different engine is used for each, with + different implementations of the same type of expressions. + + Expression contexts support execution of expressions and provide + APIs for setting up variable scopes and setting variables. The + expression contexts are passed to the TAL interpreter at execution + time. + + The most commonly used expression implementation is that found in + `zope.tales`. + +- Page templates tie everything together. They assemble an expression + engine with the TAL interpreter and orchestrate management of source + and compiled template data. See `zope.pagetemplate.interfaces`. + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/engine.py zope3-3.5~bzr18/src/zope/pagetemplate/engine.py --- zope3-3.4.0/src/zope/pagetemplate/engine.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/engine.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,478 @@ +############################################################################## +# +# Copyright (c) 2002-2009 Zope Corporation 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 +# +############################################################################## +"""Expression engine configuration and registration. + +Each expression engine can have its own expression types and base names. + +$Id: engine.py 106749 2009-12-18 10:50:06Z gotcha $ +""" +__docformat__ = 'restructuredtext' + +import sys + +from zope import component +from zope.interface import implements +from zope.component.interfaces import ComponentLookupError +from zope.traversing.interfaces import IPathAdapter, ITraversable +from zope.traversing.interfaces import TraversalError +from zope.traversing.adapters import traversePathElement +from zope.security.untrustedpython import rcompile +from zope.security.proxy import ProxyFactory, removeSecurityProxy +from zope.security.untrustedpython.builtins import SafeBuiltins +from zope.i18n import translate + +from zope.tales.expressions import PathExpr, StringExpr, NotExpr, DeferExpr +from zope.tales.expressions import SimpleModuleImporter +from zope.tales.pythonexpr import PythonExpr +from zope.tales.tales import ExpressionEngine, Context + +from zope.pagetemplate.i18n import ZopeMessageFactory as _ + +class InlineCodeError(Exception): + pass + +class ZopeTraverser(object): + + def __init__(self, proxify=None): + if proxify is None: + self.proxify = lambda x: x + else: + self.proxify = proxify + + def __call__(self, object, path_items, econtext): + """Traverses a sequence of names, first trying attributes then items. + """ + request = getattr(econtext, 'request', None) + path_items = list(path_items) + path_items.reverse() + + while path_items: + name = path_items.pop() + + # special-case dicts for performance reasons + if getattr(object, '__class__', None) == dict: + object = object[name] + else: + object = traversePathElement(object, name, path_items, + request=request) + object = self.proxify(object) + return object + +zopeTraverser = ZopeTraverser(ProxyFactory) + +class ZopePathExpr(PathExpr): + + def __init__(self, name, expr, engine): + super(ZopePathExpr, self).__init__(name, expr, engine, zopeTraverser) + +trustedZopeTraverser = ZopeTraverser() + +class TrustedZopePathExpr(PathExpr): + + def __init__(self, name, expr, engine): + super(TrustedZopePathExpr, self).__init__(name, expr, engine, + trustedZopeTraverser) + + +# Create a version of the restricted built-ins that uses a safe +# version of getattr() that wraps values in security proxies where +# appropriate: + + +class ZopePythonExpr(PythonExpr): + + def __call__(self, econtext): + __traceback_info__ = self.text + vars = self._bind_used_names(econtext, SafeBuiltins) + return eval(self._code, vars) + + def _compile(self, text, filename): + return rcompile.compile(text, filename, 'eval') + + +class ZopeContextBase(Context): + """Base class for both trusted and untrusted evaluation contexts.""" + + def translate(self, msgid, domain=None, mapping=None, default=None): + return translate(msgid, domain, mapping, + context=self.request, default=default) + + evaluateInlineCode = False + + def evaluateCode(self, lang, code): + if not self.evaluateInlineCode: + raise InlineCodeError( + _('Inline Code Evaluation is deactivated, which means that ' + 'you cannot have inline code snippets in your Page ' + 'Template. Activate Inline Code Evaluation and try again.')) + + # TODO This is only needed when self.evaluateInlineCode is true, + # so should only be needed for zope.app.pythonpage. + from zope.app.interpreter.interfaces import IInterpreter + interpreter = component.queryUtility(IInterpreter, lang) + if interpreter is None: + error = _('No interpreter named "${lang_name}" was found.', + mapping={'lang_name': lang}) + raise InlineCodeError(error) + + globals = self.vars.copy() + result = interpreter.evaluateRawCode(code, globals) + # Add possibly new global variables. + old_names = self.vars.keys() + for name, value in globals.items(): + if name not in old_names: + self.setGlobal(name, value) + return result + + +class ZopeContext(ZopeContextBase): + """Evaluation context for untrusted programs.""" + + def evaluateMacro(self, expr): + """evaluateMacro gets security-proxied macro programs when this + is run with the zopeTraverser, and in other untrusted + situations. This will cause evaluation to fail in + zope.tal.talinterpreter, which knows nothing of security proxies. + Therefore, this method removes any proxy from the evaluated + expression. + + >>> output = [('version', 'xxx'), ('mode', 'html'), ('other', 'things')] + >>> def expression(context): + ... return ProxyFactory(output) + ... + >>> zc = ZopeContext(ExpressionEngine, {}) + >>> out = zc.evaluateMacro(expression) + >>> type(out) + + + The method does some trivial checking to make sure we are getting + back a macro like we expect: it must be a sequence of sequences, in + which the first sequence must start with 'version', and the second + must start with 'mode'. + + >>> del output[0] + >>> zc.evaluateMacro(expression) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: ('unexpected result from macro evaluation.', ...) + + >>> del output[:] + >>> zc.evaluateMacro(expression) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: ('unexpected result from macro evaluation.', ...) + + >>> output = None + >>> zc.evaluateMacro(expression) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: ('unexpected result from macro evaluation.', ...) + """ + macro = removeSecurityProxy(Context.evaluateMacro(self, expr)) + # we'll do some basic checks that it is the sort of thing we expect + problem = False + try: + problem = macro[0][0] != 'version' or macro[1][0] != 'mode' + except (TypeError, IndexError): + problem = True + if problem: + raise ValueError('unexpected result from macro evaluation.', macro) + return macro + + def setContext(self, name, value): + # Hook to allow subclasses to do things like adding security proxies + Context.setContext(self, name, ProxyFactory(value)) + + +class TrustedZopeContext(ZopeContextBase): + """Evaluation context for trusted programs.""" + + +class AdapterNamespaces(object): + """Simulate tales function namespaces with adapter lookup. + + When we are asked for a namespace, we return an object that + actually computes an adapter when called: + + To demonstrate this, we need to register an adapter: + + >>> from zope.component.testing import setUp, tearDown + >>> setUp() + >>> from zope.component import provideAdapter + >>> def adapter1(ob): + ... return 1 + >>> adapter1.__component_adapts__ = (None,) + >>> provideAdapter(adapter1, None, IPathAdapter, 'a1') + + Now, with this adapter in place, we can try out the namespaces: + + >>> ob = object() + >>> namespaces = AdapterNamespaces() + >>> namespace = namespaces['a1'] + >>> namespace(ob) + 1 + >>> namespace = namespaces['a2'] + >>> namespace(ob) + Traceback (most recent call last): + ... + KeyError: 'a2' + + + Cleanup: + + >>> tearDown() + """ + + def __init__(self): + self.namespaces = {} + + def __getitem__(self, name): + namespace = self.namespaces.get(name) + if namespace is None: + def namespace(object): + try: + return component.getAdapter(object, IPathAdapter, name) + except ComponentLookupError: + raise KeyError(name) + + self.namespaces[name] = namespace + return namespace + + +class ZopeBaseEngine(ExpressionEngine): + + _create_context = ZopeContext + + def __init__(self): + ExpressionEngine.__init__(self) + self.namespaces = AdapterNamespaces() + + def getContext(self, __namespace=None, **namespace): + if __namespace: + if namespace: + namespace.update(__namespace) + else: + namespace = __namespace + + context = self._create_context(self, namespace) + + # Put request into context so path traversal can find it + if 'request' in namespace: + context.request = namespace['request'] + + # Put context into context so path traversal can find it + if 'context' in namespace: + context.context = namespace['context'] + + return context + +class ZopeEngine(ZopeBaseEngine): + """Untrusted expression engine. + + This engine does not allow modules to be imported; only modules + already available may be accessed:: + + >>> modname = 'zope.pagetemplate.tests.trusted' + >>> engine = _Engine() + >>> context = engine.getContext(engine.getBaseNames()) + + >>> modname in sys.modules + False + >>> context.evaluate('modules/' + modname) + Traceback (most recent call last): + ... + KeyError: 'zope.pagetemplate.tests.trusted' + + (The use of ``KeyError`` is an unfortunate implementation detail; I + think this should be a ``TraversalError``.) + + Modules which have already been imported by trusted code are + available, wrapped in security proxies:: + + >>> m = context.evaluate('modules/sys') + >>> m.__name__ + 'sys' + >>> m._getframe + Traceback (most recent call last): + ... + ForbiddenAttribute: ('_getframe', ) + + The results of Python expressions evaluated by this engine are + wrapped in security proxies:: + + >>> r = context.evaluate('python: {12: object()}.values') + >>> type(r) + + >>> r = context.evaluate('python: {12: object()}.values()[0].__class__') + >>> type(r) + + + General path expressions provide objects that are wrapped in + security proxies as well:: + + >>> from zope.component.testing import setUp, tearDown + >>> from zope.security.checker import NamesChecker, defineChecker + + >>> class Container(dict): + ... implements(ITraversable) + ... def traverse(self, name, further_path): + ... return self[name] + + >>> setUp() + >>> defineChecker(Container, NamesChecker(['traverse'])) + >>> d = engine.getBaseNames() + >>> foo = Container() + >>> foo.__name__ = 'foo' + >>> d['foo'] = ProxyFactory(foo) + >>> foo['bar'] = bar = Container() + >>> bar.__name__ = 'bar' + >>> bar.__parent__ = foo + >>> bar['baz'] = baz = Container() + >>> baz.__name__ = 'baz' + >>> baz.__parent__ = bar + >>> context = engine.getContext(d) + + >>> o1 = context.evaluate('foo/bar') + >>> o1.__name__ + 'bar' + >>> type(o1) + + + >>> o2 = context.evaluate('foo/bar/baz') + >>> o2.__name__ + 'baz' + >>> type(o2) + + >>> o3 = o2.__parent__ + >>> type(o3) + + >>> o1 == o3 + True + + >>> o1 is o2 + False + + Note that this engine special-cases dicts during path traversal: + it traverses only to their items, but not to their attributes + (e.g. methods on dicts), because of performance reasons: + + >>> d = engine.getBaseNames() + >>> d['adict'] = {'items': 123} + >>> d['anotherdict'] = {} + >>> context = engine.getContext(d) + >>> context.evaluate('adict/items') + 123 + >>> context.evaluate('anotherdict/keys') + Traceback (most recent call last): + ... + KeyError: 'keys' + + >>> tearDown() + + """ + + def getFunctionNamespace(self, namespacename): + """ Returns the function namespace """ + return ProxyFactory( + super(ZopeEngine, self).getFunctionNamespace(namespacename)) + +class TrustedZopeEngine(ZopeBaseEngine): + """Trusted expression engine. + + This engine allows modules to be imported:: + + >>> modname = 'zope.pagetemplate.tests.trusted' + >>> engine = _TrustedEngine() + >>> context = engine.getContext(engine.getBaseNames()) + + >>> modname in sys.modules + False + >>> m = context.evaluate('modules/' + modname) + >>> m.__name__ == modname + True + >>> modname in sys.modules + True + + Since this is trusted code, we can look at whatever is in the + module, not just __name__ or what's declared in a security + assertion:: + + >>> m.x + 42 + + Clean up after ourselves:: + + >>> del sys.modules[modname] + + """ + + _create_context = TrustedZopeContext + + +class TraversableModuleImporter(SimpleModuleImporter): + + implements(ITraversable) + + def traverse(self, name, further_path): + try: + return self[name] + except KeyError: + raise TraversalError(self, name) + + +def _Engine(engine=None): + if engine is None: + engine = ZopeEngine() + engine = _create_base_engine(engine, ZopePathExpr) + engine.registerType('python', ZopePythonExpr) + + # Using a proxy around sys.modules allows page templates to use + # modules for which security declarations have been made, but + # disallows execution of any import-time code for modules, which + # should not be allowed to happen during rendering. + engine.registerBaseName('modules', ProxyFactory(sys.modules)) + + return engine + +def _TrustedEngine(engine=None): + if engine is None: + engine = TrustedZopeEngine() + engine = _create_base_engine(engine, TrustedZopePathExpr) + engine.registerType('python', PythonExpr) + engine.registerBaseName('modules', TraversableModuleImporter()) + return engine + +def _create_base_engine(engine, pathtype): + for pt in pathtype._default_type_names: + engine.registerType(pt, pathtype) + engine.registerType('string', StringExpr) + engine.registerType('not', NotExpr) + engine.registerType('defer', DeferExpr) + return engine + + +Engine = _Engine() +TrustedEngine = _TrustedEngine() + + +class AppPT(object): + + def pt_getEngine(self): + return Engine + + +class TrustedAppPT(object): + + def pt_getEngine(self): + return TrustedEngine diff -Nru zope3-3.4.0/src/zope/pagetemplate/i18n.py zope3-3.5~bzr18/src/zope/pagetemplate/i18n.py --- zope3-3.4.0/src/zope/pagetemplate/i18n.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/i18n.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Customization of zope.i18n for the Zope application server + +$Id: i18n.py 100365 2009-05-25 18:57:21Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +# import this as _ to create i18n messages in the zope domain +from zope.i18nmessageid import MessageFactory +ZopeMessageFactory = MessageFactory('zope') diff -Nru zope3-3.4.0/src/zope/pagetemplate/__init__.py zope3-3.5~bzr18/src/zope/pagetemplate/__init__.py --- zope3-3.4.0/src/zope/pagetemplate/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Page Templates + +$Id: __init__.py 33326 2005-07-15 14:21:04Z hdima $ +""" diff -Nru zope3-3.4.0/src/zope/pagetemplate/interfaces.py zope3-3.5~bzr18/src/zope/pagetemplate/interfaces.py --- zope3-3.4.0/src/zope/pagetemplate/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,107 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Interface that describes the 'macros' attribute of a PageTemplate. + +$Id: interfaces.py 78577 2007-08-04 10:23:32Z andreasjung $ +""" +from zope.interface import Interface, Attribute + + +class IPageTemplate(Interface): + """Objects that can render page templates + """ + + def __call__(*args, **kw): + """Render a page template + + The argument handling is specific to particular + implementations. Normally, however, positional arguments are + bound to the top-level `args` variable and keyword arguments + are bound to the top-level `options` variable. + """ + + def pt_edit(source, content_type): + """Set the source and content type + """ + + def pt_errors(namespace): + """Return a sequence of strings that describe errors in the template. + + The errors may occur when the template is compiled or + rendered. + + `namespace` is the set of names passed to the TALES expression + evaluator, similar to what's returned by pt_getContext(). + + This can be used to let a template author know what went wrong + when an attempt was made to render the template. + """ + + def read(): + """Get the template source + """ + + macros = Attribute("An object that implements the __getitem__ " + "protocol, containing page template macros.") + +class IPageTemplateSubclassing(IPageTemplate): + """Behavior that may be overridden or used by subclasses + """ + + + def pt_getContext(**kw): + """Compute a dictionary of top-level template names + + Responsible for returning the set of + top-level names supported in path expressions + + """ + + def pt_getEngine(): + """Returns the TALES expression evaluator. + """ + + def pt_getEngineContext(namespace): + """Return an execution context from the expression engine.""" + + def __call__(*args, **kw): + """Render a page template + + This is sometimes overridden to provide additional argument + binding. + """ + + def pt_source_file(): + """return some text describing where a bit of ZPT code came from. + + This could be a file path, a object path, etc. + """ + + def _cook(): + """Compile the source + + Results are saved in the variables: _v_errors, _v_warnings, + _v_program, and _v_macros, and the flag _v_cooked is set. + """ + def _cook_check(): + """Compiles the source if necessary + + Subclasses might override this to influence the decision about + whether compilation is necessary. + """ + + content_type = Attribute("The content-type of the generated output") + + expand = Attribute( + "Flag indicating whether the read method should expand macros") diff -Nru zope3-3.4.0/src/zope/pagetemplate/pagetemplatefile.py zope3-3.5~bzr18/src/zope/pagetemplate/pagetemplatefile.py --- zope3-3.4.0/src/zope/pagetemplate/pagetemplatefile.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/pagetemplatefile.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,134 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Filesystem Page Template module + +Zope object encapsulating a Page Template from the filesystem. + +$Id: pagetemplatefile.py 73025 2007-03-07 10:44:01Z zagy $ +""" + +__all__ = ("PageTemplateFile",) + +import os +import sys +import re +import logging + +from zope.pagetemplate.pagetemplate import PageTemplate + +DEFAULT_ENCODING = "utf-8" + +meta_pattern = re.compile( + r'\s*\s*', + re.IGNORECASE) + +def package_home(gdict): + filename = gdict["__file__"] + return os.path.dirname(filename) + +class PageTemplateFile(PageTemplate): + "Zope wrapper for filesystem Page Template using TAL, TALES, and METAL" + + _v_last_read = 0 + + def __init__(self, filename, _prefix=None): + path = self.get_path_from_prefix(_prefix) + self.filename = os.path.join(path, filename) + if not os.path.isfile(self.filename): + raise ValueError("No such file", self.filename) + + def get_path_from_prefix(self, _prefix): + if isinstance(_prefix, str): + path = _prefix + else: + if _prefix is None: + _prefix = sys._getframe(2).f_globals + path = package_home(_prefix) + return path + + def _prepare_html(self, text): + match = meta_pattern.search(text) + if match is not None: + type_, encoding = match.groups() + # TODO: Shouldn't / stripping + # be in PageTemplate.__call__()? + text = meta_pattern.sub("", text) + else: + type_ = None + encoding = DEFAULT_ENCODING + return unicode(text, encoding), type_ + + def _read_file(self): + __traceback_info__ = self.filename + f = open(self.filename, "rb") + try: + text = f.read(XML_PREFIX_MAX_LENGTH) + except: + f.close() + raise + type_ = sniff_type(text) + if type_ == "text/xml": + text += f.read() + else: + # For HTML, we really want the file read in text mode: + f.close() + f = open(self.filename) + text = f.read() + text, type_ = self._prepare_html(text) + f.close() + return text, type_ + + def _cook_check(self): + if self._v_last_read and not __debug__: + return + __traceback_info__ = self.filename + try: + mtime = os.path.getmtime(self.filename) + except OSError: + mtime = 0 + if self._v_program is not None and mtime == self._v_last_read: + return + text, type_ = self._read_file() + self.pt_edit(text, type_) + self._cook() + if self._v_errors: + logging.error('PageTemplateFile: Error in template %s: %s', + self.filename, '\n'.join(self._v_errors)) + return + self._v_last_read = mtime + + def pt_source_file(self): + return self.filename + + def __getstate__(self): + raise TypeError("non-picklable object") + +XML_PREFIXES = [ + "') + if errend >= 0: + text = text[errend + 3:] + if text[:1] == "\n": + text = text[1:] + if self._text != text: + self._text = text + + # Always cook on an update, even if the source is the same; + # the content-type might have changed. + self._cook() + + def read(self, request=None): + """Gets the source, sometimes with macros expanded.""" + self._cook_check() + if not self._v_errors: + if not self.expand: + return self._text + try: + # This gets called, if macro expansion is turned on. + # Note that an empty dictionary is fine for the context at + # this point, since we are not evaluating the template. + return self.pt_render(self.pt_getContext(self, request), source=1) + except: + return ('%s\n Macro expansion failed\n %s\n-->\n%s' % + (_error_start, "%s: %s" % sys.exc_info()[:2], + self._text) ) + + return ('%s\n %s\n-->\n%s' % (_error_start, + '\n'.join(self._v_errors), + self._text)) + + def pt_source_file(self): + """To be overridden.""" + return None + + def _cook_check(self): + if not self._v_cooked: + self._cook() + + def _cook(self): + """Compile the TAL and METAL statments. + + Cooking must not fail due to compilation errors in templates. + """ + engine = self.pt_getEngine() + source_file = self.pt_source_file() + if self.content_type == 'text/html': + gen = TALGenerator(engine, xml=0, source_file=source_file) + parser = HTMLTALParser(gen) + else: + gen = TALGenerator(engine, source_file=source_file) + parser = TALParser(gen) + + self._v_errors = () + try: + parser.parseString(self._text) + self._v_program, self._v_macros = parser.getCode() + except: + self._v_errors = ["Compilation failed", + "%s: %s" % sys.exc_info()[:2]] + self._v_cooked = 1 + + +class PTRuntimeError(RuntimeError): + '''The Page Template has template errors that prevent it from rendering.''' + pass + + +class PageTemplateTracebackSupplement(object): + #implements(ITracebackSupplement) + + def __init__(self, pt, namespace): + self.manageable_object = pt + self.warnings = [] + e = pt.pt_errors(namespace) + if e: + self.warnings.extend(e) diff -Nru zope3-3.4.0/src/zope/pagetemplate/readme.txt zope3-3.5~bzr18/src/zope/pagetemplate/readme.txt --- zope3-3.4.0/src/zope/pagetemplate/readme.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/readme.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,62 @@ +============== +Page Templates +============== + +Introduction +------------ + +Page Templates provide an elegant templating mechanism that achieves a +clean separation of presentation and application logic while allowing +for designers to work with templates in their visual editing tools +(FrontPage, Dreamweaver, GoLive, etc.). + +This document focuses on usage of Page Templates outside of a Zope +context, it does *not* explain how to write page templates as there +are several resources on the web which do so. + +Simple Usage +------------ + +Using Page Templates outside of Zope3 is very easy and straight +forward. A quick example:: + + >>> from zope.pagetemplate.pagetemplatefile import PageTemplateFile + >>> my_pt = PageTemplateFile('hello_world.pt') + >>> my_pt() + u'Hello World' + +Subclassing PageTemplates +------------------------- + +Lets say we want to alter page templates such that keyword arguments +appear as top level items in the namespace. We can subclass +`PageTemplate` and alter the default behavior of `pt_getContext()` to +add them in:: + + from zope.pagetemplate.pagetemplate import PageTemplate + + class mypt(PageTemplate): + def pt_getContext(self, args=(), options={}, **kw): + rval = PageTemplate.pt_getContext(self, args=args) + options.update(rval) + return options + + class foo: + def getContents(self): return 'hi' + +So now we can bind objects in a more arbitrary fashion, like the +following:: + + template = """ + + + Good Stuff Here + + + """ + + pt = mypt() + pt.write(template) + pt(das_object=foo()) + +See `interfaces.py`. diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/batch.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/batch.py --- zope3-3.4.0/src/zope/pagetemplate/tests/batch.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/batch.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,118 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Batching support tests + +$Id: batch.py 38178 2005-08-30 21:50:19Z mj $ +""" + +class batch(object): + """Create a sequence batch""" + + def __init__(self, sequence, size, start=0, end=0, + orphan=3, overlap=0): + + start=start+1 + + start,end,sz=opt(start,end,size,orphan,sequence) + + self._last=end-1 + self._first=start-1 + + self._sequence=sequence + self._size=size + self._start=start + self._end=end + self._orphan=orphan + self._overlap=overlap + + def previous_sequence(self): return self._first + + def previous_sequence_end_number(self): + start,end,spam=opt(0, self._start-1+self._overlap, + self._size, self._orphan, self._sequence) + return end + + def previous_sequence_start_number(self): + start,end,spam=opt(0, self._start-1+self._overlap, + self._size, self._orphan, self._sequence) + return start + + def previous_sequence_end_item(self): + start,end,spam=opt(0, self._start-1+self._overlap, + self._size, self._orphan, self._sequence) + return self._sequence[end-1] + + def previous_sequence_start_item(self): + start,end,spam=opt(0, self._start-1+self._overlap, + self._size, self._orphan, self._sequence) + return self._sequence[start-1] + + def next_sequence_end_number(self): + start,end,spam=opt(self._end+1-self._overlap, 0, + self._size, self._orphan, self._sequence) + return end + + def next_sequence_start_number(self): + start,end,spam=opt(self._end+1-self._overlap, 0, + self._size, self._orphan, self._sequence) + return start + + def next_sequence_end_item(self): + start,end,spam=opt(self._end+1-self._overlap, 0, + self._size, self._orphan, self._sequence) + return self._sequence[end-1] + + def next_sequence_start_item(self): + start,end,spam=opt(self._end+1-self._overlap, 0, + self._size, self._orphan, self._sequence) + return self._sequence[start-1] + + + def next_sequence(self): + try: self._sequence[self._end] + except IndexError: return 0 + else: return 1 + + def __getitem__(self, index): + if index > self._last: raise IndexError(index) + return self._sequence[index+self._first] + +def opt(start,end,size,orphan,sequence): + if size < 1: + if start > 0 and end > 0 and end >= start: + size=end+1-start + else: size=7 + + if start > 0: + + try: sequence[start-1] + except: start=len(sequence) + + if end > 0: + if end < start: end=start + else: + end=start+size-1 + try: sequence[end+orphan-1] + except: end=len(sequence) + elif end > 0: + try: sequence[end-1] + except: end=len(sequence) + start=end+1-size + if start - 1 < orphan: start=1 + else: + start=1 + end=start+size-1 + try: sequence[end+orphan-1] + except: end=len(sequence) + return start,end,size diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/__init__.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/__init__.py --- zope3-3.4.0/src/zope/pagetemplate/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/checknotexpression.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checknotexpression.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/checknotexpression.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checknotexpression.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,9 @@ + + + +
not:python:0
+
not:python:1
+
not: python:1
+
not:python:range(1,20)
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/checknothing.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checknothing.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/checknothing.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checknothing.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + + + Hello World! + + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/checkpathalt.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checkpathalt.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/checkpathalt.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checkpathalt.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ + + +
+

1

+

2

+

3

+

4

+

5

+ +

Z

+

Z

+

Z

+ +

Z

+
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/checkpathnothing.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checkpathnothing.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/checkpathnothing.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checkpathnothing.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + + + Hello World! + + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/checkwithxmlheader.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checkwithxmlheader.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/checkwithxmlheader.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/checkwithxmlheader.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,5 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/dtml1.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/dtml1.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/dtml1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/dtml1.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,19 @@ + + Test of documentation templates + + blah +
+
The arguments to this test program were:
+
+
    +
  • + Argument number 99 + is default +
  • +
+
+
+

No arguments were given.

+ And thats da trooth. + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/dtml3.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/dtml3.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/dtml3.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/dtml3.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,33 @@ +Test of documentation templates + +
+ The arguments were: + + (previous start item-previous end item) + +
+ +
??.
+
Argument 99 was ??
+
+
+ + (next start item-next end item) + +
+

+ No arguments were given. +

+ And I am 100% sure! + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/globalsshadowlocals.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/globalsshadowlocals.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/globalsshadowlocals.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/globalsshadowlocals.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ + + + +
+ Should be 2 here! +
+
+ Should be 1 here! +
+
+ Should be 3 here! +
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/__init__.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/__init__.py --- zope3-3.4.0/src/zope/pagetemplate/tests/input/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/loop1.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/loop1.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/loop1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/loop1.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,15 @@ + + +Loop doc + + +

Choose your type:

+ + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/stringexpression.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/stringexpression.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/stringexpression.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/stringexpression.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + + + This is the title + + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/teeshop1.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/teeshop1.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/teeshop1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/teeshop1.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,87 @@ + + +Zope Stuff + + + + + + + + + +
+ + + + +
+
+
+ + + + + + + +
apparelmugstoysmisc
+
+
+
+ + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
Description: + This is the tee for those who LOVE Zope. Show your heart + on your tee. +

+
Price:12.99
+
+ + + + + +
+
+
+
+

 

+ + + + + + +
Copyright 2000 4AM + Productions, Inc.. All rights reserved.
+ Questions or problems should be directed to + the webmaster, 254-412-0846.
+

 

+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/teeshop2.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/teeshop2.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/teeshop2.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/teeshop2.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ + +
+Body +
+ diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/teeshoplaf.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/teeshoplaf.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/teeshoplaf.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/teeshoplaf.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,73 @@ + + +Zope Stuff + + + + + + + + + +
+ + + + +
+
+
+ + + + + + + +
apparelmugstoysmisc
+
+
+
+ + + + +
+ + + + + + + +

+ + + + + +
This is the tee for those who LOVE Zope. Show your heart on + your tee. +

+
+
+
+
+

+ + + + + + + +
+ Copyright © 2000 + 4AM Productions, Inc.. + All rights reserved.
+ Questions or problems should be directed to + the webmaster, + 254-412-0846.
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/input/translation.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/translation.html --- zope3-3.4.0/src/zope/pagetemplate/tests/input/translation.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/input/translation.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ +

Define and translate message id in ZPT

+

Insert Message object here

diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/checknotexpression.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checknotexpression.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/checknotexpression.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checknotexpression.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,9 @@ + + + +
not:python:0
+ + + + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/checknothing.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checknothing.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/checknothing.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checknothing.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/checkpathalt.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checkpathalt.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/checkpathalt.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checkpathalt.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ + + +
+

X

+

X

+

X

+

X

+

X

+ +

Z

+

Z

+

Z

+ +

c

+
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/checkpathnothing.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checkpathnothing.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/checkpathnothing.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checkpathnothing.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/checkwithxmlheader.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checkwithxmlheader.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/checkwithxmlheader.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/checkwithxmlheader.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,4 @@ + + +Hello! + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/dtml1a.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/dtml1a.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/dtml1a.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/dtml1a.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ + + Test of documentation templates + + +
+
The arguments to this test program were:
+
+
    +
  • + Argument number 1 + is one +
  • + Argument number 2 + is two +
  • + Argument number 3 + is three +
  • + Argument number 4 + is cha +
  • + Argument number 5 + is cha +
  • + Argument number 6 + is cha +
  • +
+
+
+ + And thats da trooth. + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/dtml1b.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/dtml1b.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/dtml1b.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/dtml1b.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ + + Test of documentation templates + +

No arguments were given.

+ And thats da trooth. + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/dtml3.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/dtml3.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/dtml3.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/dtml3.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,30 @@ +Test of documentation templates + +
+ The arguments were: + +
+ +
one.
+
Argument 1 was one
+
+
two.
+
Argument 2 was two
+
+
three.
+
Argument 3 was three
+
+
four.
+
Argument 4 was four
+
+
five.
+
Argument 5 was five
+
+
+ + (six-ten) + +
+ + And I am 100% sure! + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/globalsshadowlocals.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/globalsshadowlocals.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/globalsshadowlocals.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/globalsshadowlocals.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ + + + +
+ 2 +
+
+ 1 +
+
+ 3 +
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/__init__.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/__init__.py --- zope3-3.4.0/src/zope/pagetemplate/tests/output/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/loop1.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/loop1.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/loop1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/loop1.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ + + +Loop doc + + +

Choose your type:

+ + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/stringexpression.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/stringexpression.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/stringexpression.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/stringexpression.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + + + Hello World! + + + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/teeshop1.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/teeshop1.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/teeshop1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/teeshop1.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,106 @@ + + +Zope Stuff + + + + + + + + + +
+ + + + +
+
+
+ + + + + + + +
apparelmugstoysmisc
+
+
+
+ + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + +
Description: + This is the tee for those who LOVE Zope. Show your heart on your tee. +

+
Price:12.99
+
+ + + + + + + + + +
Description: + This is the tee for Jim Fulton. He's the Zope Pope! +

+
Price:11.99
+
+ + + + + +
+
+
+
+

+ + + + + + + +
+ Copyright © 2000 + 4AM Productions, Inc.. + All rights reserved.
+ Questions or problems should be directed to + the webmaster, + 254-412-0846.
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/teeshop2.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/teeshop2.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/teeshop2.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/teeshop2.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,50 @@ + + +Zope Stuff + + + + + + + + + +
+ + + + +
+
+
+ + + + + + + +
apparelmugstoysmisc
+
+
+
+Body +
+

+ + + + + + + +
+ Copyright © 2000 + 4AM Productions, Inc.. + All rights reserved.
+ Questions or problems should be directed to + the webmaster, + 254-412-0846.
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/teeshoplaf.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/teeshoplaf.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/teeshoplaf.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/teeshoplaf.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,73 @@ + + +Zope Stuff + + + + + + + + + +
+ + + + +
+
+
+ + + + + + + +
apparelmugstoysmisc
+
+
+
+ + + + +
+ + + + + + + +

+ + + + + +
This is the tee for those who LOVE Zope. Show your heart on + your tee. +

+
+
+
+
+

+ + + + + + + +
+ Copyright © 2000 + 4AM Productions, Inc.. + All rights reserved.
+ Questions or problems should be directed to + the webmaster, + 254-412-0846.
+ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/output/translation.html zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/translation.html --- zope3-3.4.0/src/zope/pagetemplate/tests/output/translation.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/output/translation.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ +

Define and translate message id in ZPT

+

Translate this!

diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/test_basictemplate.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/test_basictemplate.py --- zope3-3.4.0/src/zope/pagetemplate/tests/test_basictemplate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/test_basictemplate.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,162 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Basic Page Template tests + +$Id: test_basictemplate.py 72023 2007-01-14 13:54:17Z philikon $ +""" +import unittest + +from zope.pagetemplate.tests import util +from zope.pagetemplate.pagetemplate import PageTemplate + +class BasicTemplateTests(unittest.TestCase): + + def setUp(self): + self.t = PageTemplate() + + def test_if_in_var(self): + # DTML test 1: if, in, and var: + pass # for unittest + """ + %(comment)[ blah %(comment)] + Test of documentation templates + + %(if args)[ +
The arguments to this test program were:

+

+
    + %(in args)[ +
  • Argument number %(num)d was %(arg)s + %(in args)] +

+ %(if args)] + %(else args)[ + No arguments were given.

+ %(else args)] + And thats da trooth. + + """ + tal = util.read_input('dtml1.html') + self.t.write(tal) + + aa = util.argv(('one', 'two', 'three', 'cha', 'cha', 'cha')) + o = self.t(content=aa) + expect = util.read_output('dtml1a.html') + + util.check_xml(expect, o) + + aa = util.argv(()) + o = self.t(content=aa) + expect = util.read_output('dtml1b.html') + util.check_xml(expect, o) + + def test_batches_and_formatting(self): + # DTML test 3: batches and formatting: + pass # for unittest + """ + Test of documentation templates + + + The arguments were: + + + (- + ) + + +

+ +
.
+
Argument was
+ + (- + ) + + +
+ + No arguments were given.

+ + And I\'m 100% sure! + + """ + tal = util.read_input('dtml3.html') + self.t.write(tal) + + aa = util.argv(('one', 'two', 'three', 'four', 'five', + 'six', 'seven', 'eight', 'nine', 'ten', + 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', + 'sixteen', 'seventeen', 'eighteen', 'nineteen', + 'twenty', + )) + from zope.pagetemplate.tests import batch + o = self.t(content=aa, batch=batch.batch(aa.args, 5)) + + expect = util.read_output('dtml3.html') + util.check_xml(expect, o) + + def test_on_error_in_slot_filler(self): + # The `here` isn't defined, so the macro definition is + # expected to catch the error that gets raised. + text = '''\ +

+
+
+ cool +
+
+ +
+
+

+

+
+ ''' + self.t.write(text) + self.t() + + def test_on_error_in_slot_default(self): + # The `here` isn't defined, so the macro definition is + # expected to catch the error that gets raised. + text = '''\ +
+
+
+
+
+
+
+ +
+
+ ''' + self.t.write(text) + self.t() + + def test_unicode_html(self): + text = u'

\xe4\xf6\xfc\xdf

' + + # test with HTML parser + self.t.pt_edit(text, 'text/html') + self.assertEquals(self.t().strip(), text) + + # test with XML parser + self.t.pt_edit(text, 'text/xml') + self.assertEquals(self.t().strip(), text) + +def test_suite(): + return unittest.makeSuite(BasicTemplateTests) + +if __name__ == '__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/test_engine.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/test_engine.py --- zope3-3.4.0/src/zope/pagetemplate/tests/test_engine.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/test_engine.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,94 @@ +############################################################################## +# +# Copyright (c) 2004-2009 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Doc tests for the pagetemplate's 'engine' module + +$Id: test_engine.py 111005 2010-04-16 20:28:34Z tseaver $ +""" +import unittest + +class DummyNamespace(object): + + def __init__(self, context): + self.context = context + +class EngineTests(unittest.TestCase): + + def setUp(self): + from zope.component.testing import setUp + setUp() + + def tearDown(self): + from zope.component.testing import tearDown + tearDown() + + def test_function_namespaces_return_secured_proxies(self): + # See https://bugs.launchpad.net/zope3/+bug/98323 + from zope.component import provideAdapter + from zope.traversing.interfaces import IPathAdapter + from zope.pagetemplate.engine import _Engine + from zope.proxy import isProxy + provideAdapter(DummyNamespace, (None,), IPathAdapter, name='test') + engine = _Engine() + namespace = engine.getFunctionNamespace('test') + self.failUnless(isProxy(namespace)) + +class DummyEngine(object): + + def getTypes(self): + return {} + +class DummyContext(object): + + _engine = DummyEngine() + + def __init__(self, **kw): + self.vars = kw + +class ZopePythonExprTests(unittest.TestCase): + + def test_simple(self): + from zope.pagetemplate.engine import ZopePythonExpr + expr = ZopePythonExpr('python', 'max(a,b)', DummyEngine()) + self.assertEqual(expr(DummyContext(a=1, b=2)), 2) + + def test_allowed_module_name(self): + from zope.pagetemplate.engine import ZopePythonExpr + expr = ZopePythonExpr('python', '__import__("sys").__name__', + DummyEngine()) + self.assertEqual(expr(DummyContext()), 'sys') + + def test_forbidden_module_name(self): + from zope.pagetemplate.engine import ZopePythonExpr + from zope.security.interfaces import Forbidden + expr = ZopePythonExpr('python', '__import__("sys").exit', + DummyEngine()) + self.assertRaises(Forbidden, expr, DummyContext()) + + def test_disallowed_builtin(self): + from zope.pagetemplate.engine import ZopePythonExpr + expr = ZopePythonExpr('python', 'open("x", "w")', DummyEngine()) + self.assertRaises(NameError, expr, DummyContext()) + + +def test_suite(): + from doctest import DocTestSuite + suite = unittest.TestSuite() + suite.addTest(DocTestSuite('zope.pagetemplate.engine')) + suite.addTest(unittest.makeSuite(EngineTests)) + suite.addTest(unittest.makeSuite(ZopePythonExprTests)) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/test_htmltests.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/test_htmltests.py --- zope3-3.4.0/src/zope/pagetemplate/tests/test_htmltests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/test_htmltests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,143 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Page Template HTML Tests + +$Id: test_htmltests.py 73025 2007-03-07 10:44:01Z zagy $ +""" +import unittest + +from zope.pagetemplate.tests import util +from zope.pagetemplate.pagetemplate import PageTemplate + + +class Folder(object): + context = property(lambda self: self) + +class HTMLTests(unittest.TestCase): + + def setUp(self): + self.folder = f = Folder() + f.laf = PageTemplate() + f.t = PageTemplate() + + def getProducts(self): + return [ + {'description': 'This is the tee for those who LOVE Zope. ' + 'Show your heart on your tee.', + 'price': 12.99, 'image': 'smlatee.jpg' + }, + {'description': 'This is the tee for Jim Fulton. ' + 'He\'s the Zope Pope!', + 'price': 11.99, 'image': 'smpztee.jpg' + }, + ] + + def test_1(self): + laf = self.folder.laf + laf.write(util.read_input('teeshoplaf.html')) + expect = util.read_output('teeshoplaf.html') + util.check_html(expect, laf()) + + def test_2(self): + self.folder.laf.write(util.read_input('teeshoplaf.html')) + + t = self.folder.t + t.write(util.read_input('teeshop2.html')) + expect = util.read_output('teeshop2.html') + out = t(laf = self.folder.laf, getProducts = self.getProducts) + util.check_html(expect, out) + + + def test_3(self): + self.folder.laf.write(util.read_input('teeshoplaf.html')) + + t = self.folder.t + t.write(util.read_input('teeshop1.html')) + expect = util.read_output('teeshop1.html') + out = t(laf = self.folder.laf, getProducts = self.getProducts) + util.check_html(expect, out) + + def test_SimpleLoop(self): + t = self.folder.t + t.write(util.read_input('loop1.html')) + expect = util.read_output('loop1.html') + out = t() + util.check_html(expect, out) + + def test_GlobalsShadowLocals(self): + t = self.folder.t + t.write(util.read_input('globalsshadowlocals.html')) + expect = util.read_output('globalsshadowlocals.html') + out = t() + util.check_html(expect, out) + + def test_StringExpressions(self): + t = self.folder.t + t.write(util.read_input('stringexpression.html')) + expect = util.read_output('stringexpression.html') + out = t() + util.check_html(expect, out) + + def test_ReplaceWithNothing(self): + t = self.folder.t + t.write(util.read_input('checknothing.html')) + expect = util.read_output('checknothing.html') + out = t() + util.check_html(expect, out) + + def test_WithXMLHeader(self): + t = self.folder.t + t.write(util.read_input('checkwithxmlheader.html')) + expect = util.read_output('checkwithxmlheader.html') + out = t() + util.check_html(expect, out) + + def test_NotExpression(self): + t = self.folder.t + t.write(util.read_input('checknotexpression.html')) + expect = util.read_output('checknotexpression.html') + out = t() + util.check_html(expect, out) + + def test_PathNothing(self): + t = self.folder.t + t.write(util.read_input('checkpathnothing.html')) + expect = util.read_output('checkpathnothing.html') + out = t() + util.check_html(expect, out) + + def test_PathAlt(self): + t = self.folder.t + t.write(util.read_input('checkpathalt.html')) + expect = util.read_output('checkpathalt.html') + out = t() + util.check_html(expect, out) + + def test_translation(self): + from zope.i18nmessageid import MessageFactory + _ = MessageFactory('pttest') + msg = _("Translate this!") + + t = self.folder.t + t.write(util.read_input('translation.html')) + expect = util.read_output('translation.html') + out = t(msg=msg) + util.check_html(expect, out) + + +def test_suite(): + return unittest.makeSuite(HTMLTests) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/testpackage/content.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/testpackage/content.py --- zope3-3.4.0/src/zope/pagetemplate/tests/testpackage/content.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/testpackage/content.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,28 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Content + +$Id: content.py 26559 2004-07-15 21:22:32Z srichter $ +""" + +class Content(object): + def getSomething(self): + return 42 + + +class PTComponent(object): + def __init__(self, content, request): + self.content = content + + index = PageTemplateFile("view.pt", engine_name="unrestricted") diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/testpackage/__init__.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/testpackage/__init__.py --- zope3-3.4.0/src/zope/pagetemplate/tests/testpackage/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/testpackage/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/testpackage/view.pt zope3-3.5~bzr18/src/zope/pagetemplate/tests/testpackage/view.pt --- zope3-3.4.0/src/zope/pagetemplate/tests/testpackage/view.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/testpackage/view.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/test_ptfile.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/test_ptfile.py --- zope3-3.4.0/src/zope/pagetemplate/tests/test_ptfile.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/test_ptfile.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,189 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 of PageTemplateFile. + +$Id: test_ptfile.py 87228 2008-06-07 17:50:55Z philikon $ +""" +import os +import tempfile +import unittest + +from zope.pagetemplate.pagetemplatefile import PageTemplateFile + + +class TypeSniffingTestCase(unittest.TestCase): + + TEMPFILENAME = tempfile.mktemp() + + def tearDown(self): + if os.path.exists(self.TEMPFILENAME): + os.unlink(self.TEMPFILENAME) + + def get_pt(self, text): + f = open(self.TEMPFILENAME, "wb") + f.write(text) + f.close() + pt = PageTemplateFile(self.TEMPFILENAME) + pt.read() + return pt + + def check_content_type(self, text, expected_type): + pt = self.get_pt(text) + self.assertEqual(pt.content_type, expected_type) + + def test_sniffer_xml_ascii(self): + self.check_content_type( + "", + "text/xml") + self.check_content_type( + "", + "text/xml") + + def test_sniffer_xml_utf8(self): + # w/out byte order mark + self.check_content_type( + "", + "text/xml") + self.check_content_type( + "", + "text/xml") + # with byte order mark + self.check_content_type( + "\xef\xbb\xbf", + "text/xml") + self.check_content_type( + "\xef\xbb\xbf", + "text/xml") + + def test_sniffer_xml_utf16_be(self): + # w/out byte order mark + self.check_content_type( + "\0<\0?\0x\0m\0l\0 \0v\0e\0r\0s\0i\0o\0n\0=\0'\01\0.\0000\0'" + "\0 \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\08\0'\0?\0>" + "\0<\0d\0o\0c\0/\0>", + "text/xml") + self.check_content_type( + "\0<\0?\0x\0m\0l\0\t\0v\0e\0r\0s\0i\0o\0n\0=\0'\01\0.\0000\0'" + "\0 \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\08\0'\0?\0>" + "\0<\0d\0o\0c\0/\0>", + "text/xml") + # with byte order mark + self.check_content_type( + "\xfe\xff" + "\0<\0?\0x\0m\0l\0 \0v\0e\0r\0s\0i\0o\0n\0=\0'\01\0.\0000\0'" + "\0 \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\08\0'\0?\0>" + "\0<\0d\0o\0c\0/\0>", + "text/xml") + self.check_content_type( + "\xfe\xff" + "\0<\0?\0x\0m\0l\0\t\0v\0e\0r\0s\0i\0o\0n\0=\0'\01\0.\0000\0'" + "\0 \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\08\0'\0?\0>" + "\0<\0d\0o\0c\0/\0>", + "text/xml") + + def test_sniffer_xml_utf16_le(self): + # w/out byte order mark + self.check_content_type( + "<\0?\0x\0m\0l\0 \0v\0e\0r\0s\0i\0o\0n\0=\0'\01\0.\0000\0'\0" + " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\08\0'\0?\0>\0" + "<\0d\0o\0c\0/\0>\n", + "text/xml") + self.check_content_type( + "<\0?\0x\0m\0l\0\t\0v\0e\0r\0s\0i\0o\0n\0=\0'\01\0.\0000\0'\0" + " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\08\0'\0?\0>\0" + "<\0d\0o\0c\0/\0>\0", + "text/xml") + # with byte order mark + self.check_content_type( + "\xff\xfe" + "<\0?\0x\0m\0l\0 \0v\0e\0r\0s\0i\0o\0n\0=\0'\01\0.\0000\0'\0" + " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\08\0'\0?\0>\0" + "<\0d\0o\0c\0/\0>\0", + "text/xml") + self.check_content_type( + "\xff\xfe" + "<\0?\0x\0m\0l\0\t\0v\0e\0r\0s\0i\0o\0n\0=\0'\01\0.\0000\0'\0" + " \0e\0n\0c\0o\0d\0i\0n\0g\0=\0'\0u\0t\0f\0-\08\0'\0?\0>\0" + "<\0d\0o\0c\0/\0>\0", + "text/xml") + + HTML_PUBLIC_ID = "-//W3C//DTD HTML 4.01 Transitional//EN" + HTML_SYSTEM_ID = "http://www.w3.org/TR/html4/loose.dtd" + + def test_sniffer_html_ascii(self): + self.check_content_type( + "" + % self.HTML_SYSTEM_ID, + "text/html") + self.check_content_type( + "sample document", + "text/html") + + # TODO: This reflects a case that simply isn't handled by the + # sniffer; there are many, but it gets it right more often than + # before. + def donttest_sniffer_xml_simple(self): + self.check_content_type("", + "text/xml") + + def test_html_default_encoding(self): + pt = self.get_pt( + "" + # 'Test' in russian (utf-8) + "\xd0\xa2\xd0\xb5\xd1\x81\xd1\x82" + "") + rendered = pt() + self.failUnless(isinstance(rendered, unicode)) + self.failUnlessEqual(rendered.strip(), + u"" + u"\u0422\u0435\u0441\u0442" + u"") + + def test_html_encoding_by_meta(self): + pt = self.get_pt( + "" + # 'Test' in russian (windows-1251) + "\xd2\xe5\xf1\xf2" + '' + "") + rendered = pt() + self.failUnless(isinstance(rendered, unicode)) + self.failUnlessEqual(rendered.strip(), + u"" + u"\u0422\u0435\u0441\u0442" + u"") + + def test_xhtml(self): + pt = self.get_pt( + "" + # 'Test' in russian (windows-1251) + "\xd2\xe5\xf1\xf2" + '' + "") + rendered = pt() + self.failUnless(isinstance(rendered, unicode)) + self.failUnlessEqual(rendered.strip(), + u"" + u"\u0422\u0435\u0441\u0442" + u"") + + + +def test_suite(): + return unittest.makeSuite(TypeSniffingTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/trusted.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/trusted.py --- zope3-3.4.0/src/zope/pagetemplate/tests/trusted.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/trusted.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,22 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 of a module imported by a trusted module. + +This module won't be imported by an untrusted template using a +path:modules/... expression. + +$Id: trusted.py 100365 2009-05-25 18:57:21Z tseaver $ +""" + +x = 42 diff -Nru zope3-3.4.0/src/zope/pagetemplate/tests/util.py zope3-3.5~bzr18/src/zope/pagetemplate/tests/util.py --- zope3-3.4.0/src/zope/pagetemplate/tests/util.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pagetemplate/tests/util.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,111 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 + +$Id: util.py 38178 2005-08-30 21:50:19Z mj $ +""" +import os +import re +import sys + + +class Bruce(object): + __allow_access_to_unprotected_subobjects__=1 + def __str__(self): return 'bruce' + def __int__(self): return 42 + def __float__(self): return 42.0 + def keys(self): return ['bruce']*7 + def values(self): return [self]*7 + def items(self): return [('bruce',self)]*7 + def __len__(self): return 7 + def __getitem__(self,index): + if ininstance(index, int) and (index < 0 or index > 6): + raise IndexError(index) + return self + isDocTemp = 0 + def __getattr__(self,name): + if name.startswith('_'): + raise AttributeError(name) + return self + +bruce = Bruce() + +class arg(object): + __allow_access_to_unprotected_subobjects__ = 1 + def __init__(self,nn,aa): self.num, self.arg = nn, aa + def __str__(self): return str(self.arg) + +class argv(object): + __allow_access_to_unprotected_subobjects__ = 1 + + def __init__(self, argv=sys.argv[1:]): + args = self.args = [] + for aa in argv: + args.append(arg(len(args)+1,aa)) + + def items(self): + return map(lambda a: ('spam%d' % a.num, a), self.args) + + def values(self): return self.args + + def getRoot(self): + return self + + context = property(lambda self: self) + +def nicerange(lo, hi): + if hi <= lo+1: + return str(lo+1) + else: + return "%d,%d" % (lo+1, hi) + +def dump(tag, x, lo, hi): + for i in xrange(lo, hi): + print '%s %s' % (tag, x[i]), + +def check_html(s1, s2): + s1 = normalize_html(s1) + s2 = normalize_html(s2) + assert s1==s2, (s1, s2, "HTML Output Changed") + +def check_xml(s1, s2): + s1 = normalize_xml(s1) + s2 = normalize_xml(s2) + assert s1==s2, ("XML Output Changed:\n%r\n\n%r" % (s1, s2)) + +def normalize_html(s): + s = re.sub(r"[ \t]+", " ", s) + s = re.sub(r"/>", ">", s) + return s + +def normalize_xml(s): + s = re.sub(r"\s+", " ", s) + s = re.sub(r"(?s)\s+<", "<", s) + s = re.sub(r"(?s)>\s+", ">", s) + return s + + +import zope.pagetemplate.tests + +dir = os.path.dirname(zope.pagetemplate.tests.__file__) +input_dir = os.path.join(dir, 'input') +output_dir = os.path.join(dir, 'output') + +def read_input(filename): + filename = os.path.join(input_dir, filename) + return open(filename, 'r').read() + +def read_output(filename): + filename = os.path.join(output_dir, filename) + return open(filename, 'r').read() diff -Nru zope3-3.4.0/src/zope/password/configure.zcml zope3-3.5~bzr18/src/zope/password/configure.zcml --- zope3-3.4.0/src/zope/password/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/password/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/password/__init__.py zope3-3.5~bzr18/src/zope/password/__init__.py --- zope3-3.4.0/src/zope/password/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/password/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,16 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +""" +$Id: __init__.py 97560 2009-03-06 11:47:04Z nadako $ +""" diff -Nru zope3-3.4.0/src/zope/password/interfaces.py zope3-3.5~bzr18/src/zope/password/interfaces.py --- zope3-3.4.0/src/zope/password/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/password/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""Password manager interface + +$Id: interfaces.py 97563 2009-03-06 12:05:22Z nadako $ +""" +import zope.interface + +class IPasswordManager(zope.interface.Interface): + """Password manager""" + + def encodePassword(password): + """Return encoded data for the given password""" + + def checkPassword(encoded_password, password): + """Return whether the given encoded data coincide with the given password""" diff -Nru zope3-3.4.0/src/zope/password/password.py zope3-3.5~bzr18/src/zope/password/password.py --- zope3-3.4.0/src/zope/password/password.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/password/password.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,257 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""Password managers + +$Id: password.py 97822 2009-03-11 07:59:29Z nadako $ +""" +__docformat__ = 'restructuredtext' + +from base64 import urlsafe_b64encode +from base64 import urlsafe_b64decode +from os import urandom +from random import randint +from codecs import getencoder +try: + from hashlib import md5, sha1 +except ImportError: + # Python 2.4 + from md5 import new as md5 + from sha import new as sha1 + +from zope.interface import implements +from zope.password.interfaces import IPasswordManager + +_encoder = getencoder("utf-8") + + +class PlainTextPasswordManager(object): + """Plain text password manager. + + >>> from zope.interface.verify import verifyObject + + >>> manager = PlainTextPasswordManager() + >>> verifyObject(IPasswordManager, manager) + True + + >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}" + >>> encoded = manager.encodePassword(password) + >>> encoded + u'right \u0410' + >>> manager.checkPassword(encoded, password) + True + >>> manager.checkPassword(encoded, password + u"wrong") + False + """ + + implements(IPasswordManager) + + def encodePassword(self, password): + return password + + def checkPassword(self, encoded_password, password): + return encoded_password == self.encodePassword(password) + + +class SSHAPasswordManager(PlainTextPasswordManager): + """SSHA password manager. + + SSHA is basically SHA1-encoding which also incorporates a salt + into the encoded string. This way, stored passwords are more + robust against dictionary attacks of attackers that could get + access to lists of encoded passwords. + + SSHA is regularly used in LDAP databases and we should be + compatible with passwords used there. + + >>> from zope.interface.verify import verifyObject + + >>> manager = SSHAPasswordManager() + >>> verifyObject(IPasswordManager, manager) + True + + >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}" + >>> encoded = manager.encodePassword(password, salt="") + >>> encoded + '{SSHA}BLTuxxVMXzouxtKVb7gLgNxzdAI=' + + >>> manager.checkPassword(encoded, password) + True + >>> manager.checkPassword(encoded, password + u"wrong") + False + + Using the `slappasswd` utility to encode ``secret``, we get + ``{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv`` as seeded hash. + + Our password manager generates the same value when seeded with the + same salt, so we can be sure, our output is compatible with + standard LDAP tools that also use SSHA:: + + >>> from base64 import urlsafe_b64decode + >>> salt = urlsafe_b64decode('XkOZbw==') + >>> encoded = manager.encodePassword('secret', salt) + >>> encoded + '{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv' + + >>> encoded = manager.encodePassword(password) + >>> manager.checkPassword(encoded, password) + True + >>> manager.checkPassword(encoded, password + u"wrong") + False + + >>> manager.encodePassword(password) != manager.encodePassword(password) + True + """ + + implements(IPasswordManager) + + def encodePassword(self, password, salt=None): + if salt is None: + salt = urandom(4) + hash = sha1(_encoder(password)[0]) + hash.update(salt) + return '{SSHA}' + urlsafe_b64encode( + hash.digest() + salt) + + def checkPassword(self, encoded_password, password): + byte_string = urlsafe_b64decode(encoded_password[6:]) + salt = byte_string[20:] + return encoded_password == self.encodePassword(password, salt) + + +class MD5PasswordManager(PlainTextPasswordManager): + """MD5 password manager. + + Note: use of salt in this password manager is purely + cosmetical. Use SSHA if you want increased security. + + >>> from zope.interface.verify import verifyObject + + >>> manager = MD5PasswordManager() + >>> verifyObject(IPasswordManager, manager) + True + + >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}" + >>> encoded = manager.encodePassword(password, salt="") + >>> encoded + '{MD5}86dddccec45db4599f1ac00018e54139' + >>> manager.checkPassword(encoded, password) + True + >>> manager.checkPassword(encoded, password + u"wrong") + False + + >>> encoded = manager.encodePassword(password) + >>> encoded[-32:] + '86dddccec45db4599f1ac00018e54139' + >>> manager.checkPassword(encoded, password) + True + >>> manager.checkPassword(encoded, password + u"wrong") + False + + >>> manager.encodePassword(password) != manager.encodePassword(password) + True + + The old version of this password manager didn't add the {MD5} to + passwords. Let's check if it can work with old stored passwords. + + >>> encoded = manager.encodePassword(password, salt="") + >>> encoded = encoded[5:] + >>> encoded + '86dddccec45db4599f1ac00018e54139' + + >>> manager.checkPassword(encoded, password) + True + """ + + implements(IPasswordManager) + + def encodePassword(self, password, salt=None): + if salt is None: + salt = "%08x" % randint(0, 0xffffffff) + return '{MD5}%s%s' % (salt, md5(_encoder(password)[0]).hexdigest()) + + def checkPassword(self, encoded_password, password): + if encoded_password.startswith('{MD5}'): + salt = encoded_password[5:-32] + return encoded_password == self.encodePassword(password, salt) + salt = encoded_password[:-32] + return encoded_password == self.encodePassword(password, salt)[5:] + + +class SHA1PasswordManager(PlainTextPasswordManager): + """SHA1 password manager. + + Note: use of salt in this password manager is purely + cosmetical. Use SSHA if you want increased security. + + >>> from zope.interface.verify import verifyObject + + >>> manager = SHA1PasswordManager() + >>> verifyObject(IPasswordManager, manager) + True + + >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}" + >>> encoded = manager.encodePassword(password, salt="") + >>> encoded + '{SHA1}04b4eec7154c5f3a2ec6d2956fb80b80dc737402' + >>> manager.checkPassword(encoded, password) + True + >>> manager.checkPassword(encoded, password + u"wrong") + False + + >>> encoded = manager.encodePassword(password) + >>> encoded[-40:] + '04b4eec7154c5f3a2ec6d2956fb80b80dc737402' + >>> manager.checkPassword(encoded, password) + True + >>> manager.checkPassword(encoded, password + u"wrong") + False + + >>> manager.encodePassword(password) != manager.encodePassword(password) + True + + The old version of this password manager didn't add the {SHA1} to + passwords. Let's check if it can work with old stored passwords. + + >>> encoded = manager.encodePassword(password, salt="") + >>> encoded = encoded[6:] + >>> encoded + '04b4eec7154c5f3a2ec6d2956fb80b80dc737402' + + >>> manager.checkPassword(encoded, password) + True + + """ + + implements(IPasswordManager) + + def encodePassword(self, password, salt=None): + if salt is None: + salt = "%08x" % randint(0, 0xffffffff) + return '{SHA1}%s%s' % (salt, sha1(_encoder(password)[0]).hexdigest()) + + def checkPassword(self, encoded_password, password): + if encoded_password.startswith('{SHA1}'): + salt = encoded_password[6:-40] + return encoded_password == self.encodePassword(password, salt) + salt = encoded_password[:-40] + return encoded_password == self.encodePassword(password, salt)[6:] + + +# Simple registry +managers = [ + ('Plain Text', PlainTextPasswordManager()), + ('MD5', MD5PasswordManager()), + ('SHA1', SHA1PasswordManager()), + ('SSHA', SSHAPasswordManager()), +] diff -Nru zope3-3.4.0/src/zope/password/testing.py zope3-3.5~bzr18/src/zope/password/testing.py --- zope3-3.4.0/src/zope/password/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/password/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,66 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""Setup password managers as utilities + +$Id: testing.py 97822 2009-03-11 07:59:29Z nadako $ +""" +__docformat__ = "reStructuredText" + +from zope.component import provideUtility +from zope.schema.interfaces import IVocabularyFactory + +from zope.password.interfaces import IPasswordManager +from zope.password.password import PlainTextPasswordManager +from zope.password.password import MD5PasswordManager +from zope.password.password import SHA1PasswordManager +from zope.password.password import SSHAPasswordManager +from zope.password.vocabulary import PasswordManagerNamesVocabulary + + +def setUpPasswordManagers(): + """Helper function for setting up password manager utilities for tests + + >>> from zope.component import getUtility + >>> setUpPasswordManagers() + + >>> getUtility(IPasswordManager, 'Plain Text') + + >>> getUtility(IPasswordManager, 'SSHA') + + >>> getUtility(IPasswordManager, 'MD5') + + >>> getUtility(IPasswordManager, 'SHA1') + + + >>> voc = getUtility(IVocabularyFactory, 'Password Manager Names') + >>> voc = voc(None) + >>> voc + + >>> 'SSHA' in voc + True + >>> 'Plain Text' in voc + True + >>> 'SHA1' in voc + True + >>> 'MD5' in voc + True + + """ + provideUtility(PlainTextPasswordManager(), IPasswordManager, 'Plain Text') + provideUtility(SSHAPasswordManager(), IPasswordManager, 'SSHA') + provideUtility(MD5PasswordManager(), IPasswordManager, 'MD5') + provideUtility(SHA1PasswordManager(), IPasswordManager, 'SHA1') + + provideUtility(PasswordManagerNamesVocabulary, + IVocabularyFactory, 'Password Manager Names') diff -Nru zope3-3.4.0/src/zope/password/tests.py zope3-3.5~bzr18/src/zope/password/tests.py --- zope3-3.4.0/src/zope/password/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/password/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,28 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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. +# +############################################################################## +"""Password Managers Tests + +$Id: tests.py 97566 2009-03-06 12:23:42Z nadako $ +""" +import unittest +from zope.testing import doctest + + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite('zope.password.password'), + doctest.DocTestSuite( + 'zope.password.testing', + optionflags=doctest.ELLIPSIS), + )) diff -Nru zope3-3.4.0/src/zope/password/vocabulary.py zope3-3.5~bzr18/src/zope/password/vocabulary.py --- zope3-3.4.0/src/zope/password/vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/password/vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,32 @@ +############################################################################## +# +# Copyright (c) 2009 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. +# +############################################################################## +"""Vocabulary of password manager utility names for use with zope.component and +zope.schema. + +$Id: vocabulary.py 97822 2009-03-11 07:59:29Z nadako $ +""" +from zope.component import getUtilitiesFor +from zope.interface import directlyProvides +from zope.schema.interfaces import IVocabularyFactory +from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm + +from zope.password.interfaces import IPasswordManager + +def PasswordManagerNamesVocabulary(context=None): + terms = [] + for name, util in getUtilitiesFor(IPasswordManager, context): + terms.append(SimpleTerm(name)) + return SimpleVocabulary(terms) + +directlyProvides(PasswordManagerNamesVocabulary, IVocabularyFactory) diff -Nru zope3-3.4.0/src/zope/pluggableauth/authentication.py zope3-3.5~bzr18/src/zope/pluggableauth/authentication.py --- zope3-3.4.0/src/zope/pluggableauth/authentication.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/authentication.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,139 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Pluggable Authentication Utility implementation + +$Id: authentication.py 108453 2010-01-24 23:27:41Z trollfot $ +""" +from zope import component +from zope.authentication.interfaces import ( + IAuthentication, PrincipalLookupError) +from zope.container.btree import BTreeContainer +from zope.interface import implements +from zope.pluggableauth import interfaces +from zope.schema.interfaces import ISourceQueriables +from zope.site.next import queryNextUtility + + +class PluggableAuthentication(BTreeContainer): + + implements( + IAuthentication, + interfaces.IPluggableAuthentication, + ISourceQueriables) + + authenticatorPlugins = () + credentialsPlugins = () + + def __init__(self, prefix=''): + super(PluggableAuthentication, self).__init__() + self.prefix = prefix + + def _plugins(self, names, interface): + for name in names: + plugin = self.get(name) + if not interface.providedBy(plugin): + plugin = component.queryUtility(interface, name, context=self) + if plugin is not None: + yield name, plugin + + def getAuthenticatorPlugins(self): + return self._plugins( + self.authenticatorPlugins, interfaces.IAuthenticatorPlugin) + + def getCredentialsPlugins(self): + return self._plugins( + self.credentialsPlugins, interfaces.ICredentialsPlugin) + + def authenticate(self, request): + authenticatorPlugins = [p for n, p in self.getAuthenticatorPlugins()] + for name, credplugin in self.getCredentialsPlugins(): + credentials = credplugin.extractCredentials(request) + for authplugin in authenticatorPlugins: + if authplugin is None: + continue + info = authplugin.authenticateCredentials(credentials) + if info is None: + continue + info.credentialsPlugin = credplugin + info.authenticatorPlugin = authplugin + principal = component.getMultiAdapter((info, request), + interfaces.IAuthenticatedPrincipalFactory)(self) + principal.id = self.prefix + info.id + return principal + return None + + def getPrincipal(self, id): + if not id.startswith(self.prefix): + next = queryNextUtility(self, IAuthentication) + if next is None: + raise PrincipalLookupError(id) + return next.getPrincipal(id) + id = id[len(self.prefix):] + for name, authplugin in self.getAuthenticatorPlugins(): + info = authplugin.principalInfo(id) + if info is None: + continue + info.credentialsPlugin = None + info.authenticatorPlugin = authplugin + principal = interfaces.IFoundPrincipalFactory(info)(self) + principal.id = self.prefix + info.id + return principal + next = queryNextUtility(self, IAuthentication) + if next is not None: + return next.getPrincipal(self.prefix + id) + raise PrincipalLookupError(id) + + def getQueriables(self): + for name, authplugin in self.getAuthenticatorPlugins(): + queriable = component.queryMultiAdapter((authplugin, self), + interfaces.IQueriableAuthenticator) + if queriable is not None: + yield name, queriable + + def unauthenticatedPrincipal(self): + return None + + def unauthorized(self, id, request): + challengeProtocol = None + + for name, credplugin in self.getCredentialsPlugins(): + protocol = getattr(credplugin, 'challengeProtocol', None) + if challengeProtocol is None or protocol == challengeProtocol: + if credplugin.challenge(request): + if protocol is None: + return + elif challengeProtocol is None: + challengeProtocol = protocol + + if challengeProtocol is None: + next = queryNextUtility(self, IAuthentication) + if next is not None: + next.unauthorized(id, request) + + def logout(self, request): + challengeProtocol = None + + for name, credplugin in self.getCredentialsPlugins(): + protocol = getattr(credplugin, 'challengeProtocol', None) + if challengeProtocol is None or protocol == challengeProtocol: + if credplugin.logout(request): + if protocol is None: + return + elif challengeProtocol is None: + challengeProtocol = protocol + + if challengeProtocol is None: + next = queryNextUtility(self, IAuthentication) + if next is not None: + next.logout(request) diff -Nru zope3-3.4.0/src/zope/pluggableauth/configure.zcml zope3-3.5~bzr18/src/zope/pluggableauth/configure.zcml --- zope3-3.4.0/src/zope/pluggableauth/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/pluggableauth/factories.py zope3-3.5~bzr18/src/zope/pluggableauth/factories.py --- zope3-3.4.0/src/zope/pluggableauth/factories.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/factories.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,305 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Principals related factories + +$Id: factories.py 108453 2010-01-24 23:27:41Z trollfot $ +""" +__docformat__ = "reStructuredText" + +from zope import component +from zope import interface +from zope.authentication.interfaces import IAuthentication +from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal +from zope.event import notify +from zope.pluggableauth import interfaces +from zope.publisher.interfaces import IRequest + + +class PrincipalInfo(object): + """An implementation of IPrincipalInfo used by the principal folder. + + A principal info is created with id, login, title, and description: + + >>> info = PrincipalInfo('users.foo', 'foo', 'Foo', 'An over-used term.') + >>> info + PrincipalInfo('users.foo') + >>> info.id + 'users.foo' + >>> info.login + 'foo' + >>> info.title + 'Foo' + >>> info.description + 'An over-used term.' + + """ + interface.implements(interfaces.IPrincipalInfo) + + def __init__(self, id, login, title, description): + self.id = id + self.login = login + self.title = title + self.description = description + + def __repr__(self): + return 'PrincipalInfo(%r)' % self.id + + +class Principal(object): + """A group-aware implementation of zope.security.interfaces.IPrincipal. + + A principal is created with an ID: + + >>> p = Principal(1) + >>> p + Principal(1) + >>> p.id + 1 + + title and description may also be provided: + + >>> p = Principal('george', 'George', 'A site member.') + >>> p + Principal('george') + >>> p.id + 'george' + >>> p.title + 'George' + >>> p.description + 'A site member.' + + The `groups` is a simple list, filled in by plugins. + + >>> p.groups + [] + + The `allGroups` attribute is a readonly iterable of the full closure of the + groups in the `groups` attribute--that is, if the principal is a direct + member of the 'Administrators' group, and the 'Administrators' group is + a member of the 'Reviewers' group, then p.groups would be + ['Administrators'] and list(p.allGroups) would be + ['Administrators', 'Reviewers']. + + To illustrate this, we'll need to set up a dummy authentication utility, + and a few principals. Our main principal will also gain some groups, as if + plugins had added the groups to the list. This is all setup--skip to the + next block to actually see `allGroups` in action. + + >>> p.groups.extend( + ... ['content_administrators', 'zope_3_project', + ... 'list_administrators', 'zpug']) + >>> editor = Principal('editors', 'Content Editors') + >>> creator = Principal('creators', 'Content Creators') + >>> reviewer = Principal('reviewers', 'Content Reviewers') + >>> reviewer.groups.extend(['editors', 'creators']) + >>> usermanager = Principal('user_managers', 'User Managers') + >>> contentAdmin = Principal( + ... 'content_administrators', 'Content Administrators') + >>> contentAdmin.groups.extend(['reviewers', 'user_managers']) + >>> zope3Dev = Principal('zope_3_project', 'Zope 3 Developer') + >>> zope3ListAdmin = Principal( + ... 'zope_3_list_admin', 'Zope 3 List Administrators') + >>> zope3ListAdmin.groups.append('zope_3_project') # duplicate, but + ... # should only appear in allGroups once + >>> listAdmin = Principal('list_administrators', 'List Administrators') + >>> listAdmin.groups.append('zope_3_list_admin') + >>> zpugMember = Principal('zpug', 'ZPUG Member') + >>> martians = Principal('martians', 'Martians') # not in p's allGroups + >>> group_data = dict((p.id, p) for p in ( + ... editor, creator, reviewer, usermanager, contentAdmin, + ... zope3Dev, zope3ListAdmin, listAdmin, zpugMember, martians)) + >>> class DemoAuth(object): + ... interface.implements(IAuthentication) + ... def getPrincipal(self, id): + ... return group_data[id] + ... + >>> demoAuth = DemoAuth() + >>> component.provideUtility(demoAuth) + + Now, we have a user with the following groups (lowest level are p's direct + groups, and lines show membership): + + editors creators + \------/ + | zope_3_project (duplicate) + reviewers user_managers | + \---------/ zope_3_list_admin + | | + content_administrators zope_3_project list_administrators zpug + + The allGroups value includes all of the shown groups, and with + 'zope_3_project' only appearing once. + + >>> p.groups # doctest: +NORMALIZE_WHITESPACE + ['content_administrators', 'zope_3_project', 'list_administrators', + 'zpug'] + >>> list(p.allGroups) # doctest: +NORMALIZE_WHITESPACE + ['content_administrators', 'reviewers', 'editors', 'creators', + 'user_managers', 'zope_3_project', 'list_administrators', + 'zope_3_list_admin', 'zpug'] + """ + interface.implements(IPrincipal) + + def __init__(self, id, title=u'', description=u''): + self.id = id + self.title = title + self.description = description + self.groups = [] + + def __repr__(self): + return 'Principal(%r)' % self.id + + @property + def allGroups(self): + if self.groups: + seen = set() + principals = component.getUtility(IAuthentication) + stack = [iter(self.groups)] + while stack: + try: + group_id = stack[-1].next() + except StopIteration: + stack.pop() + else: + if group_id not in seen: + yield group_id + seen.add(group_id) + group = principals.getPrincipal(group_id) + stack.append(iter(group.groups)) + + +class AuthenticatedPrincipalFactory(object): + """Creates 'authenticated' principals. + + An authenticated principal is created as a result of an authentication + operation. + + To use the factory, create it with the info (interfaces.IPrincipalInfo) of + the principal to create and a request: + + >>> info = PrincipalInfo('users.mary', 'mary', 'Mary', 'The site admin.') + >>> from zope.publisher.base import TestRequest + >>> request = TestRequest('/') + >>> factory = AuthenticatedPrincipalFactory(info, request) + + The factory must be called with a pluggable-authentication object: + + >>> class Auth: + ... prefix = 'auth.' + >>> auth = Auth() + + >>> principal = factory(auth) + + The factory uses the pluggable authentication and the info to + create a principal with the same ID, title, and description: + + >>> principal.id + 'auth.users.mary' + >>> principal.title + 'Mary' + >>> principal.description + 'The site admin.' + + It also fires an AuthenticatedPrincipalCreatedEvent: + + >>> from zope.component.eventtesting import getEvents + >>> [event] = getEvents(interfaces.IAuthenticatedPrincipalCreated) + >>> event.principal is principal, event.authentication is auth + (True, True) + >>> event.info + PrincipalInfo('users.mary') + >>> event.request is request + True + + Listeners can subscribe to this event to perform additional operations + when the authenticated principal is created. + + For information on how factories are used in the authentication process, + see README.txt. + """ + component.adapts(interfaces.IPrincipalInfo, IRequest) + + interface.implements(interfaces.IAuthenticatedPrincipalFactory) + + def __init__(self, info, request): + self.info = info + self.request = request + + def __call__(self, authentication): + principal = Principal(authentication.prefix + self.info.id, + self.info.title, + self.info.description) + notify(interfaces.AuthenticatedPrincipalCreated( + authentication, principal, self.info, self.request)) + return principal + + +class FoundPrincipalFactory(object): + """Creates 'found' principals. + + A 'found' principal is created as a result of a principal lookup. + + To use the factory, create it with the info (interfaces.IPrincipalInfo) of + the principal to create: + + >>> info = PrincipalInfo('users.sam', 'sam', 'Sam', 'A site user.') + >>> factory = FoundPrincipalFactory(info) + + The factory must be called with a pluggable-authentication object: + + >>> class Auth: + ... prefix = 'auth.' + >>> auth = Auth() + + >>> principal = factory(auth) + + The factory uses the pluggable-authentication object and the info + to create a principal with the same ID, title, and description: + + >>> principal.id + 'auth.users.sam' + >>> principal.title + 'Sam' + >>> principal.description + 'A site user.' + + It also fires a FoundPrincipalCreatedEvent: + + >>> from zope.component.eventtesting import getEvents + >>> [event] = getEvents(interfaces.IFoundPrincipalCreated) + >>> event.principal is principal, event.authentication is auth + (True, True) + >>> event.info + PrincipalInfo('users.sam') + + Listeners can subscribe to this event to perform additional operations + when the 'found' principal is created. + + For information on how factories are used in the authentication process, + see README.txt. + """ + component.adapts(interfaces.IPrincipalInfo) + + interface.implements(interfaces.IFoundPrincipalFactory) + + def __init__(self, info): + self.info = info + + def __call__(self, authentication): + principal = Principal(authentication.prefix + self.info.id, + self.info.title, + self.info.description) + notify(interfaces.FoundPrincipalCreated(authentication, + principal, self.info)) + return principal diff -Nru zope3-3.4.0/src/zope/pluggableauth/__init__.py zope3-3.5~bzr18/src/zope/pluggableauth/__init__.py --- zope3-3.4.0/src/zope/pluggableauth/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Pluggable Authentication Utility + +$Id: __init__.py 108413 2010-01-23 23:32:38Z trollfot $ +""" + +from zope.pluggableauth import interfaces +from zope.pluggableauth.authentication import PluggableAuthentication diff -Nru zope3-3.4.0/src/zope/pluggableauth/interfaces.py zope3-3.5~bzr18/src/zope/pluggableauth/interfaces.py --- zope3-3.4.0/src/zope/pluggableauth/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,251 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Pluggable Authentication Utility Interfaces + +$Id: interfaces.py 108453 2010-01-24 23:27:41Z trollfot $ +""" +__docformat__ = "reStructuredText" + +import zope.interface +from zope.i18nmessageid import MessageFactory +from zope.authentication.interfaces import ILogout +from zope.container.constraints import contains, containers +from zope.container.interfaces import IContainer + +_ = MessageFactory('zope') + + +class IPlugin(zope.interface.Interface): + """A plugin for a pluggable authentication component.""" + + +class IPluggableAuthentication(ILogout, IContainer): + """Provides authentication services with the help of various plugins. + + IPluggableAuthentication implementations will also implement + zope.authentication.interfaces.IAuthentication. The `authenticate` method + of this interface in an IPluggableAuthentication should annotate the + IPrincipalInfo with the credentials plugin and authentication plugin used. + The `getPrincipal` method should annotate the IPrincipalInfo with the + authentication plugin used. + """ + + contains(IPlugin) + + credentialsPlugins = zope.schema.List( + title=_('Credentials Plugins'), + description=_("""Used for extracting credentials. + Names may be of ids of non-utility ICredentialsPlugins contained in + the IPluggableAuthentication, or names of registered + ICredentialsPlugins utilities. Contained non-utility ids mask + utility names."""), + value_type=zope.schema.Choice(vocabulary='CredentialsPlugins'), + default=[], + ) + + authenticatorPlugins = zope.schema.List( + title=_('Authenticator Plugins'), + description=_("""Used for converting credentials to principals. + Names may be of ids of non-utility IAuthenticatorPlugins contained in + the IPluggableAuthentication, or names of registered + IAuthenticatorPlugins utilities. Contained non-utility ids mask + utility names."""), + value_type=zope.schema.Choice(vocabulary='AuthenticatorPlugins'), + default=[], + ) + + def getCredentialsPlugins(): + """Return iterable of (plugin name, actual credentials plugin) pairs. + Looks up names in credentialsPlugins as contained ids of non-utility + ICredentialsPlugins first, then as registered ICredentialsPlugin + utilities. Names that do not resolve are ignored.""" + + def getAuthenticatorPlugins(): + """Return iterable of (plugin name, actual authenticator plugin) pairs. + Looks up names in authenticatorPlugins as contained ids of non-utility + IAuthenticatorPlugins first, then as registered IAuthenticatorPlugin + utilities. Names that do not resolve are ignored.""" + + prefix = zope.schema.TextLine( + title=_('Prefix'), + default=u'', + required=True, + readonly=True, + ) + + def logout(request): + """Performs a logout by delegating to its authenticator plugins.""" + + +class ICredentialsPlugin(IPlugin): + """Handles credentials extraction and challenges per request.""" + + containers(IPluggableAuthentication) + + challengeProtocol = zope.interface.Attribute( + """A challenge protocol used by the plugin. + + If a credentials plugin works with other credentials pluggins, it + and the other cooperating plugins should specify a common (non-None) + protocol. If a plugin returns True from its challenge method, then + other credentials plugins will be called only if they have the same + protocol. + """) + + def extractCredentials(request): + """Ties to extract credentials from a request. + + A return value of None indicates that no credentials could be found. + Any other return value is treated as valid credentials. + """ + + def challenge(request): + """Possibly issues a challenge. + + This is typically done in a protocol-specific way. + + If a challenge was issued, return True, otherwise return False. + """ + + def logout(request): + """Possibly logout. + + If a logout was performed, return True, otherwise return False. + """ + + +class IAuthenticatorPlugin(IPlugin): + """Authenticates a principal using credentials. + + An authenticator may also be responsible for providing information + about and creating principals. + """ + containers(IPluggableAuthentication) + + def authenticateCredentials(credentials): + """Authenticates credentials. + + If the credentials can be authenticated, return an object that provides + IPrincipalInfo. If the plugin cannot authenticate the credentials, + returns None. + """ + + def principalInfo(id): + """Returns an IPrincipalInfo object for the specified principal id. + + If the plugin cannot find information for the id, returns None. + """ + + +class IPrincipalInfo(zope.interface.Interface): + """Minimal information about a principal.""" + + id = zope.interface.Attribute("The principal id.") + + title = zope.interface.Attribute("The principal title.") + + description = zope.interface.Attribute("A description of the principal.") + + credentialsPlugin = zope.interface.Attribute( + """Plugin used to generate the credentials for this principal info. + + Optional. Should be set in IPluggableAuthentication.authenticate. + """) + + authenticatorPlugin = zope.interface.Attribute( + """Plugin used to authenticate the credentials for this principal info. + + Optional. Should be set in IPluggableAuthentication.authenticate and + IPluggableAuthentication.getPrincipal. + """) + + +class IPrincipalFactory(zope.interface.Interface): + """A principal factory.""" + + def __call__(authentication): + """Creates a principal. + + The authentication utility that called the factory is passed + and should be included in the principal-created event. + """ + + +class IFoundPrincipalFactory(IPrincipalFactory): + """A found principal factory.""" + + +class IAuthenticatedPrincipalFactory(IPrincipalFactory): + """An authenticated principal factory.""" + + +class IPrincipalCreated(zope.interface.Interface): + """A principal has been created.""" + + principal = zope.interface.Attribute("The principal that was created") + + authentication = zope.interface.Attribute( + "The authentication utility that created the principal") + + info = zope.interface.Attribute("An object providing IPrincipalInfo.") + + +class IAuthenticatedPrincipalCreated(IPrincipalCreated): + """A principal has been created by way of an authentication operation.""" + + request = zope.interface.Attribute( + "The request the user was authenticated against") + + +class AuthenticatedPrincipalCreated: + """ + >>> from zope.interface.verify import verifyObject + >>> event = AuthenticatedPrincipalCreated("authentication", "principal", + ... "info", "request") + >>> verifyObject(IAuthenticatedPrincipalCreated, event) + True + """ + + zope.interface.implements(IAuthenticatedPrincipalCreated) + + def __init__(self, authentication, principal, info, request): + self.authentication = authentication + self.principal = principal + self.info = info + self.request = request + + +class IFoundPrincipalCreated(IPrincipalCreated): + """A principal has been created by way of a search operation.""" + + +class FoundPrincipalCreated: + """ + >>> from zope.interface.verify import verifyObject + >>> event = FoundPrincipalCreated("authentication", "principal", + ... "info") + >>> verifyObject(IFoundPrincipalCreated, event) + True + """ + + zope.interface.implements(IFoundPrincipalCreated) + + def __init__(self, authentication, principal, info): + self.authentication = authentication + self.principal = principal + self.info = info + + +class IQueriableAuthenticator(zope.interface.Interface): + """Indicates the authenticator provides a search UI for principals.""" diff -Nru zope3-3.4.0/src/zope/pluggableauth/plugins/ftpplugins.py zope3-3.5~bzr18/src/zope/pluggableauth/plugins/ftpplugins.py --- zope3-3.4.0/src/zope/pluggableauth/plugins/ftpplugins.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/plugins/ftpplugins.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,65 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""PAS plugins related to FTP +""" +__docformat__ = 'restructuredtext' + +from zope.interface import implements +from zope.pluggableauth import interfaces +from zope.publisher.interfaces.ftp import IFTPRequest + + +class FTPCredentialsPlugin(object): + + implements(interfaces.ICredentialsPlugin) + + def extractCredentials(self, request): + """Extracts the FTP credentials from a request. + + First we need to create a FTP request that contains some credentials. + Note the path is a required in the envirnoment. + + >>> from zope.publisher.ftp import FTPRequest + >>> from StringIO import StringIO + >>> request = FTPRequest(StringIO(''), + ... {'credentials': ('bob', '123'), + ... 'path': '/a/b/c'}) + + Now we create the plugin and get the credentials. + + >>> plugin = FTPCredentialsPlugin() + >>> plugin.extractCredentials(request) + {'login': u'bob', 'password': u'123'} + + This only works for FTPRequests. + + >>> from zope.publisher.base import TestRequest + >>> print plugin.extractCredentials(TestRequest('/')) + None + + """ + if not IFTPRequest.providedBy(request): + return None + + if request._auth: + login, password = request._auth + return {'login': login.decode('utf-8'), + 'password': password.decode('utf-8')} + return None + + def challenge(self, request): + return False + + def logout(self, request): + return False diff -Nru zope3-3.4.0/src/zope/pluggableauth/plugins/ftpplugins.zcml zope3-3.5~bzr18/src/zope/pluggableauth/plugins/ftpplugins.zcml --- zope3-3.4.0/src/zope/pluggableauth/plugins/ftpplugins.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/plugins/ftpplugins.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/pluggableauth/plugins/generic.py zope3-3.5~bzr18/src/zope/pluggableauth/plugins/generic.py --- zope3-3.4.0/src/zope/pluggableauth/plugins/generic.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/plugins/generic.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,93 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Generic PAS Plugins + +$Id: generic.py 108524 2010-01-26 16:16:33Z trollfot $ +""" +__docformat__ = "reStructuredText" + +from zope.authentication.interfaces import IUnauthenticatedPrincipal +from zope.interface import implements +from zope.pluggableauth import interfaces + + +class NoChallengeCredentialsPlugin(object): + """A plugin that doesn't challenge if the principal is authenticated. + + There are two reasonable ways to handle an unauthorized error for an + authenticated principal: + + - Inform the user of the unauthorized error + + - Let the user login with a different set of credentials + + Since either approach is reasonable, we need to give the site manager + some way of specifying one of the two policies. + + By default, a user will be challenged for a new set of credentials if + unauthorized. A site manager can insert this plugin in the front of the + plugin list to prevent that challenge from occurring. This will + typically result in an 'Unauthorized' message to the user. + + The 'challenge' behavior of the plugin is simple. To illustrate, we'll + create a plugin: + + >>> challenger = NoChallengeCredentialsPlugin() + + and a test request with an authenticated principal: + + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + >>> IUnauthenticatedPrincipal.providedBy(request.principal) + False + + When we challenge using the plugin: + + >>> challenger.challenge(request) + True + + we get a value that signals the PAU that this plugin successfully + challenged the user (even though it actually did nothing). The PAU + will stop trying to challenge and the user will not get a chance to + provide different credentials. The result is typically an error message. + + On the other hand, if the user is unauthenticated: + + >>> class Principal(object): + ... implements(IUnauthenticatedPrincipal) + >>> request.setPrincipal(Principal()) + >>> IUnauthenticatedPrincipal.providedBy(request.principal) + True + + the plugin challenge will return None: + + >>> print challenger.challenge(request) + None + + signaling the PAU that it should try the next plugin for a challenge. If + the PAU is configured properly, the user will receive a challenge and be + allowed to provide different credentials. + """ + implements(interfaces.ICredentialsPlugin) + + def extractCredentials(self, request): + return None + + def challenge(self, request): + if not IUnauthenticatedPrincipal.providedBy(request.principal): + return True + return None + + def logout(self, request): + return False diff -Nru zope3-3.4.0/src/zope/pluggableauth/plugins/generic.zcml zope3-3.5~bzr18/src/zope/pluggableauth/plugins/generic.zcml --- zope3-3.4.0/src/zope/pluggableauth/plugins/generic.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/plugins/generic.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/pluggableauth/plugins/httpplugins.py zope3-3.5~bzr18/src/zope/pluggableauth/plugins/httpplugins.py --- zope3-3.4.0/src/zope/pluggableauth/plugins/httpplugins.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/plugins/httpplugins.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,140 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""PAS plugins related to HTTP + +$Id: httpplugins.py 108413 2010-01-23 23:32:38Z trollfot $ +""" +__docformat__ = "reStructuredText" + +import base64 +from zope.interface import implements, Interface +from zope.publisher.interfaces.http import IHTTPRequest +from zope.schema import TextLine +from zope.pluggableauth import interfaces + + +class IHTTPBasicAuthRealm(Interface): + """HTTP Basic Auth Realm + + Represents the realm string that is used during basic HTTP authentication + """ + + realm = TextLine(title=u'Realm', + description=u'HTTP Basic Authentication Realm', + required=True, + default=u'Zope') + + +class HTTPBasicAuthCredentialsPlugin(object): + + implements(interfaces.ICredentialsPlugin, IHTTPBasicAuthRealm) + + realm = 'Zope' + + protocol = 'http auth' + + def extractCredentials(self, request): + """Extracts HTTP basic auth credentials from a request. + + First we need to create a request that contains some credentials. + + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest( + ... environ={'HTTP_AUTHORIZATION': u'Basic bWdyOm1ncnB3'}) + + Now create the plugin and get the credentials. + + >>> plugin = HTTPBasicAuthCredentialsPlugin() + >>> plugin.extractCredentials(request) + {'login': u'mgr', 'password': u'mgrpw'} + + Make sure we return `None`, if no authentication header has been + specified. + + >>> print plugin.extractCredentials(TestRequest()) + None + + Also, this plugin can *only* handle basic authentication. + + >>> request = TestRequest(environ={'HTTP_AUTHORIZATION': 'foo bar'}) + >>> print plugin.extractCredentials(TestRequest()) + None + + This plugin only works with HTTP requests. + + >>> from zope.publisher.base import TestRequest + >>> print plugin.extractCredentials(TestRequest('/')) + None + + """ + if not IHTTPRequest.providedBy(request): + return None + + if request._auth: + if request._auth.lower().startswith(u'basic '): + credentials = request._auth.split()[-1] + login, password = base64.decodestring(credentials).split(':') + return {'login': login.decode('utf-8'), + 'password': password.decode('utf-8')} + return None + + def challenge(self, request): + """Issues an HTTP basic auth challenge for credentials. + + The challenge is issued by setting the appropriate response headers. + To illustrate, we'll create a plugin: + + >>> plugin = HTTPBasicAuthCredentialsPlugin() + + The plugin adds its challenge to the HTTP response. + + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + >>> response = request.response + >>> plugin.challenge(request) + True + >>> response._status + 401 + >>> response.getHeader('WWW-Authenticate', literal=True) + 'basic realm="Zope"' + + Notice that the realm is quoted, as per RFC 2617. + + The plugin only works with HTTP requests. + + >>> from zope.publisher.base import TestRequest + >>> request = TestRequest('/') + >>> response = request.response + >>> print plugin.challenge(request) + False + + """ + if not IHTTPRequest.providedBy(request): + return False + request.response.setHeader("WWW-Authenticate", + 'basic realm="%s"' % self.realm, + literal=True) + request.response.setStatus(401) + return True + + def logout(self, request): + """Always returns False as logout is not supported by basic auth. + + >>> plugin = HTTPBasicAuthCredentialsPlugin() + >>> from zope.publisher.browser import TestRequest + >>> plugin.logout(TestRequest()) + False + + """ + return False diff -Nru zope3-3.4.0/src/zope/pluggableauth/plugins/httpplugins.zcml zope3-3.5~bzr18/src/zope/pluggableauth/plugins/httpplugins.zcml --- zope3-3.4.0/src/zope/pluggableauth/plugins/httpplugins.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/plugins/httpplugins.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/pluggableauth/plugins/session.py zope3-3.5~bzr18/src/zope/pluggableauth/plugins/session.py --- zope3-3.4.0/src/zope/pluggableauth/plugins/session.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/plugins/session.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,303 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +""" Implementations of the session-based and cookie-based extractor and + challenge plugins. + +$Id: session.py 108524 2010-01-26 16:16:33Z trollfot $ +""" +__docformat__ = 'restructuredtext' + +import transaction +from urllib import urlencode + +from zope.interface import implements, Interface +from zope.schema import TextLine +from zope.publisher.interfaces.http import IHTTPRequest +from zope.session.interfaces import ISession +from zope.traversing.browser.absoluteurl import absoluteURL + +from zope.site import hooks +from zope.pluggableauth.interfaces import ICredentialsPlugin + + +class ISessionCredentials(Interface): + """Interface for storing and accessing credentials in a session. + + We use a real class with interface here to prevent unauthorized + access to the credentials. + """ + + def __init__(login, password): + pass + + def getLogin(): + """Return login name.""" + + def getPassword(): + """Return password.""" + + +class SessionCredentials(object): + """Credentials class for use with sessions. + + A session credential is created with a login and a password: + + >>> cred = SessionCredentials('scott', 'tiger') + + Logins are read using getLogin: + >>> cred.getLogin() + 'scott' + + and passwords with getPassword: + + >>> cred.getPassword() + 'tiger' + + """ + implements(ISessionCredentials) + + def __init__(self, login, password): + self.login = login + self.password = password + + def getLogin(self): + return self.login + + def getPassword(self): + return self.password + + def __str__(self): + return self.getLogin() + ':' + self.getPassword() + + +class IBrowserFormChallenger(Interface): + """A challenger that uses a browser form to collect user credentials.""" + + loginpagename = TextLine( + title=u'Loginpagename', + description=u"""Name of the login form used by challenger. + + The form must provide 'login' and 'password' input fields. + """, + default=u'loginForm.html') + + loginfield = TextLine( + title=u'Loginfield', + description=u"Field of the login page in which is looked for the login user name.", + default=u"login") + + passwordfield = TextLine( + title=u'Passwordfield', + description=u"Field of the login page in which is looked for the password.", + default=u"password") + + +class SessionCredentialsPlugin(object): + """A credentials plugin that uses Zope sessions to get/store credentials. + + To illustrate how a session plugin works, we'll first setup some session + machinery: + + >>> from zope.session.session import RAMSessionDataContainer + >>> from zope.pluggableauth.tests import sessionSetUp + >>> sessionSetUp(RAMSessionDataContainer) + + This lets us retrieve the same session info from any test request, which + simulates what happens when a user submits a session ID as a cookie. + + We also need a session plugin: + + >>> plugin = SessionCredentialsPlugin() + + A session plugin uses an ISession component to store the last set of + credentials it gets from a request. Credentials can be retrieved from + subsequent requests using the session-stored credentials. + + Our test environment is initially configured without credentials: + + >>> from zope.pluggableauth.tests import sessionSetUp + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + >>> print plugin.extractCredentials(request) + None + + We must explicitly provide credentials once so the plugin can store + them in a session: + + >>> request = TestRequest(login='scott', password='tiger') + >>> plugin.extractCredentials(request) + {'login': 'scott', 'password': 'tiger'} + + Subsequent requests now have access to the credentials even if they're + not explicitly in the request: + + >>> plugin.extractCredentials(TestRequest()) + {'login': 'scott', 'password': 'tiger'} + + We can always provide new credentials explicitly in the request: + + >>> plugin.extractCredentials(TestRequest( + ... login='harry', password='hirsch')) + {'login': 'harry', 'password': 'hirsch'} + + and these will be used on subsequent requests: + + >>> plugin.extractCredentials(TestRequest()) + {'login': 'harry', 'password': 'hirsch'} + + We can also change the fields from which the credentials are extracted: + + >>> plugin.loginfield = "my_new_login_field" + >>> plugin.passwordfield = "my_new_password_field" + + Now we build a request that uses the new fields: + + >>> request = TestRequest(my_new_login_field='luke', my_new_password_field='the_force') + + The plugin now extracts the credentials information from these new fields: + + >>> plugin.extractCredentials(request) + {'login': 'luke', 'password': 'the_force'} + + Finally, we clear the session credentials using the logout method: + + >>> plugin.logout(TestRequest()) + True + >>> print plugin.extractCredentials(TestRequest()) + None + + """ + implements(ICredentialsPlugin, IBrowserFormChallenger) + + loginpagename = 'loginForm.html' + loginfield = 'login' + passwordfield = 'password' + + def extractCredentials(self, request): + """Extracts credentials from a session if they exist.""" + if not IHTTPRequest.providedBy(request): + return None + session = ISession(request) + sessionData = session.get( + 'zope.pluggableauth.browserplugins') + login = request.get(self.loginfield, None) + password = request.get(self.passwordfield, None) + credentials = None + + if login and password: + credentials = SessionCredentials(login, password) + elif not sessionData: + return None + sessionData = session[ + 'zope.pluggableauth.browserplugins'] + if credentials: + sessionData['credentials'] = credentials + else: + credentials = sessionData.get('credentials', None) + if not credentials: + return None + return {'login': credentials.getLogin(), + 'password': credentials.getPassword()} + + def challenge(self, request): + """Challenges by redirecting to a login form. + + To illustrate, we'll create a test request: + + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + + and confirm its response's initial status and 'location' header: + + >>> request.response.getStatus() + 599 + >>> request.response.getHeader('location') + + When we issue a challenge using a session plugin: + + >>> plugin = SessionCredentialsPlugin() + >>> plugin.challenge(request) + True + + we get a redirect: + + >>> request.response.getStatus() + 302 + >>> request.response.getHeader('location') + 'http://127.0.0.1/@@loginForm.html?camefrom=%2F' + + The plugin redirects to the page defined by the loginpagename + attribute: + + >>> plugin.loginpagename = 'mylogin.html' + >>> plugin.challenge(request) + True + >>> request.response.getHeader('location') + 'http://127.0.0.1/@@mylogin.html?camefrom=%2F' + + It also provides the request URL as a 'camefrom' GET style parameter. + To illustrate, we'll pretend we've traversed a couple names: + + >>> env = { + ... 'REQUEST_URI': '/foo/bar/folder/page%201.html?q=value', + ... 'QUERY_STRING': 'q=value' + ... } + >>> request = TestRequest(environ=env) + >>> request._traversed_names = [u'foo', u'bar'] + >>> request._traversal_stack = [u'page 1.html', u'folder'] + >>> request['REQUEST_URI'] + '/foo/bar/folder/page%201.html?q=value' + + When we challenge: + + >>> plugin.challenge(request) + True + + We see the 'camefrom' points to the requested URL: + + >>> request.response.getHeader('location') # doctest: +ELLIPSIS + '.../@@mylogin.html?camefrom=%2Ffoo%2Fbar%2Ffolder%2Fpage+1.html%3Fq%3Dvalue' + + This can be used by the login form to redirect the user back to the + originating URL upon successful authentication. + """ + if not IHTTPRequest.providedBy(request): + return False + + site = hooks.getSite() + # We need the traversal stack to complete the 'camefrom' parameter + stack = request.getTraversalStack() + stack.reverse() + # Better to add the query string, if present + query = request.get('QUERY_STRING') + + camefrom = '/'.join([request.getURL(path_only=True)] + stack) + if query: + camefrom = camefrom + '?' + query + url = '%s/@@%s?%s' % (absoluteURL(site, request), + self.loginpagename, + urlencode({'camefrom': camefrom})) + request.response.redirect(url) + return True + + def logout(self, request): + """Performs logout by clearing session data credentials.""" + if not IHTTPRequest.providedBy(request): + return False + + sessionData = ISession(request)[ + 'zope.pluggableauth.browserplugins'] + sessionData['credentials'] = None + transaction.commit() + return True diff -Nru zope3-3.4.0/src/zope/pluggableauth/plugins/session.zcml zope3-3.5~bzr18/src/zope/pluggableauth/plugins/session.zcml --- zope3-3.4.0/src/zope/pluggableauth/plugins/session.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/plugins/session.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/pluggableauth/principalfactories.zcml zope3-3.5~bzr18/src/zope/pluggableauth/principalfactories.zcml --- zope3-3.4.0/src/zope/pluggableauth/principalfactories.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/principalfactories.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/pluggableauth/README.txt zope3-3.5~bzr18/src/zope/pluggableauth/README.txt --- zope3-3.4.0/src/zope/pluggableauth/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,546 @@ +================================ +Pluggable-Authentication Utility +================================ + +The Pluggable-Authentication Utility (PAU) provides a framework for +authenticating principals and associating information with them. It uses +plugins and subscribers to get its work done. + +For a pluggable-authentication utility to be used, it should be +registered as a utility providing the +`zope.authentication.interfaces.IAuthentication` interface. + +Authentication +-------------- + +The primary job of PAU is to authenticate principals. It uses two types of +plug-ins in its work: + + - Credentials Plugins + + - Authenticator Plugins + +Credentials plugins are responsible for extracting user credentials from a +request. A credentials plugin may in some cases issue a 'challenge' to obtain +credentials. For example, a 'session' credentials plugin reads credentials +from a session (the "extraction"). If it cannot find credentials, it will +redirect the user to a login form in order to provide them (the "challenge"). + +Authenticator plugins are responsible for authenticating the credentials +extracted by a credentials plugin. They are also typically able to create +principal objects for credentials they successfully authenticate. + +Given a request object, the PAU returns a principal object, if it can. The PAU +does this by first iterateing through its credentials plugins to obtain a +set of credentials. If it gets credentials, it iterates through its +authenticator plugins to authenticate them. + +If an authenticator succeeds in authenticating a set of credentials, the PAU +uses the authenticator to create a principal corresponding to the credentials. +The authenticator notifies subscribers if an authenticated principal is +created. Subscribers are responsible for adding data, especially groups, to +the principal. Typically, if a subscriber adds data, it should also add +corresponding interface declarations. + +Simple Credentials Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To illustrate, we'll create a simple credentials plugin:: + + >>> from zope import interface + >>> from zope.pluggableauth.authentication import interfaces + + >>> class MyCredentialsPlugin(object): + ... + ... interface.implements(interfaces.ICredentialsPlugin) + ... + ... def extractCredentials(self, request): + ... return request.get('credentials') + ... + ... def challenge(self, request): + ... pass # challenge is a no-op for this plugin + ... + ... def logout(self, request): + ... pass # logout is a no-op for this plugin + +As a plugin, MyCredentialsPlugin needs to be registered as a named utility:: + + >>> myCredentialsPlugin = MyCredentialsPlugin() + >>> provideUtility(myCredentialsPlugin, name='My Credentials Plugin') + +Simple Authenticator Plugin +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Next we'll create a simple authenticator plugin. For our plugin, we'll need +an implementation of IPrincipalInfo:: + + >>> class PrincipalInfo(object): + ... + ... interface.implements(interfaces.IPrincipalInfo) + ... + ... def __init__(self, id, title, description): + ... self.id = id + ... self.title = title + ... self.description = description + ... + ... def __repr__(self): + ... return 'PrincipalInfo(%r)' % self.id + +Our authenticator uses this type when it creates a principal info:: + + >>> class MyAuthenticatorPlugin(object): + ... + ... interface.implements(interfaces.IAuthenticatorPlugin) + ... + ... def authenticateCredentials(self, credentials): + ... if credentials == 'secretcode': + ... return PrincipalInfo('bob', 'Bob', '') + ... + ... def principalInfo(self, id): + ... pass # plugin not currently supporting search + +As with the credentials plugin, the authenticator plugin must be registered +as a named utility:: + + >>> myAuthenticatorPlugin = MyAuthenticatorPlugin() + >>> provideUtility(myAuthenticatorPlugin, name='My Authenticator Plugin') + +Configuring a PAU +~~~~~~~~~~~~~~~~~ + +Finally, we'll create the PAU itself:: + + >>> from zope.pluggableauth import authentication + >>> pau = authentication.PluggableAuthentication('xyz_') + +and configure it with the two plugins:: + + >>> pau.credentialsPlugins = ('My Credentials Plugin', ) + >>> pau.authenticatorPlugins = ('My Authenticator Plugin', ) + +Using the PAU to Authenticate +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + >>> from zope.pluggableauth.factories import AuthenticatedPrincipalFactory + >>> provideAdapter(AuthenticatedPrincipalFactory) + +We can now use the PAU to authenticate a sample request:: + + >>> from zope.publisher.browser import TestRequest + >>> print pau.authenticate(TestRequest()) + None + +In this case, we cannot authenticate an empty request. In the same way, we +will not be able to authenticate a request with the wrong credentials:: + + >>> print pau.authenticate(TestRequest(credentials='let me in!')) + None + +However, if we provide the proper credentials:: + + >>> request = TestRequest(credentials='secretcode') + >>> principal = pau.authenticate(request) + >>> principal + Principal('xyz_bob') + +we get an authenticated principal. + +Multiple Authenticator Plugins +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The PAU works with multiple authenticator plugins. It uses each plugin, in the +order specified in the PAU's authenticatorPlugins attribute, to authenticate +a set of credentials. + +To illustrate, we'll create another authenticator:: + + >>> class MyAuthenticatorPlugin2(MyAuthenticatorPlugin): + ... + ... def authenticateCredentials(self, credentials): + ... if credentials == 'secretcode': + ... return PrincipalInfo('black', 'Black Spy', '') + ... elif credentials == 'hiddenkey': + ... return PrincipalInfo('white', 'White Spy', '') + + >>> provideUtility(MyAuthenticatorPlugin2(), name='My Authenticator Plugin 2') + +If we put it before the original authenticator:: + + >>> pau.authenticatorPlugins = ( + ... 'My Authenticator Plugin 2', + ... 'My Authenticator Plugin') + +Then it will be given the first opportunity to authenticate a request:: + + >>> pau.authenticate(TestRequest(credentials='secretcode')) + Principal('xyz_black') + +If neither plugins can authenticate, pau returns None:: + + >>> print pau.authenticate(TestRequest(credentials='let me in!!')) + None + +When we change the order of the authenticator plugins:: + + >>> pau.authenticatorPlugins = ( + ... 'My Authenticator Plugin', + ... 'My Authenticator Plugin 2') + +we see that our original plugin is now acting first:: + + >>> pau.authenticate(TestRequest(credentials='secretcode')) + Principal('xyz_bob') + +The second plugin, however, gets a chance to authenticate if first does not:: + + >>> pau.authenticate(TestRequest(credentials='hiddenkey')) + Principal('xyz_white') + +Multiple Credentials Plugins +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As with with authenticators, we can specify multiple credentials plugins. To +illustrate, we'll create a credentials plugin that extracts credentials from +a request form:: + + >>> class FormCredentialsPlugin: + ... + ... interface.implements(interfaces.ICredentialsPlugin) + ... + ... def extractCredentials(self, request): + ... return request.form.get('my_credentials') + ... + ... def challenge(self, request): + ... pass + ... + ... def logout(request): + ... pass + + >>> provideUtility(FormCredentialsPlugin(), + ... name='Form Credentials Plugin') + +and insert the new credentials plugin before the existing plugin:: + + >>> pau.credentialsPlugins = ( + ... 'Form Credentials Plugin', + ... 'My Credentials Plugin') + +The PAU will use each plugin in order to try and obtain credentials from a +request:: + + >>> pau.authenticate(TestRequest(credentials='secretcode', + ... form={'my_credentials': 'hiddenkey'})) + Principal('xyz_white') + +In this case, the first credentials plugin succeeded in getting credentials +from the form and the second authenticator was able to authenticate the +credentials. Specifically, the PAU went through these steps: + + - Get credentials using 'Form Credentials Plugin' + + - Got 'hiddenkey' credentials using 'Form Credentials Plugin', try to + authenticate using 'My Authenticator Plugin' + + - Failed to authenticate 'hiddenkey' with 'My Authenticator Plugin', try + 'My Authenticator Plugin 2' + + - Succeeded in authenticating with 'My Authenticator Plugin 2' + +Let's try a different scenario:: + + >>> pau.authenticate(TestRequest(credentials='secretcode')) + Principal('xyz_bob') + +In this case, the PAU went through these steps:: + + - Get credentials using 'Form Credentials Plugin' + + - Failed to get credentials using 'Form Credentials Plugin', try + 'My Credentials Plugin' + + - Got 'scecretcode' credentials using 'My Credentials Plugin', try to + authenticate using 'My Authenticator Plugin' + + - Succeeded in authenticating with 'My Authenticator Plugin' + +Let's try a slightly more complex scenario:: + + >>> pau.authenticate(TestRequest(credentials='hiddenkey', + ... form={'my_credentials': 'bogusvalue'})) + Principal('xyz_white') + +This highlights PAU's ability to use multiple plugins for authentication: + + - Get credentials using 'Form Credentials Plugin' + + - Got 'bogusvalue' credentials using 'Form Credentials Plugin', try to + authenticate using 'My Authenticator Plugin' + + - Failed to authenticate 'boguskey' with 'My Authenticator Plugin', try + 'My Authenticator Plugin 2' + + - Failed to authenticate 'boguskey' with 'My Authenticator Plugin 2' -- + there are no more authenticators to try, so lets try the next credentials + plugin for some new credentials + + - Get credentials using 'My Credentials Plugin' + + - Got 'hiddenkey' credentials using 'My Credentials Plugin', try to + authenticate using 'My Authenticator Plugin' + + - Failed to authenticate 'hiddenkey' using 'My Authenticator Plugin', try + 'My Authenticator Plugin 2' + + - Succeeded in authenticating with 'My Authenticator Plugin 2' (shouts and + cheers!) + +Multiple Authenticator Plugins +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As with the other operations we've seen, the PAU uses multiple plugins to +find a principal. If the first authenticator plugin can't find the requested +principal, the next plugin is used, and so on. + + >>> class AnotherAuthenticatorPlugin: + ... + ... interface.implements(interfaces.IAuthenticatorPlugin) + ... + ... def __init__(self): + ... self.infos = {} + ... self.ids = {} + ... + ... def principalInfo(self, id): + ... return self.infos.get(id) + ... + ... def authenticateCredentials(self, credentials): + ... id = self.ids.get(credentials) + ... if id is not None: + ... return self.infos[id] + ... + ... def add(self, id, title, description, credentials): + ... self.infos[id] = PrincipalInfo(id, title, description) + ... self.ids[credentials] = id + + +To illustrate, we'll create and register two authenticators:: + + >>> authenticator1 = AnotherAuthenticatorPlugin() + >>> provideUtility(authenticator1, name='Authentication Plugin 1') + + >>> authenticator2 = AnotherAuthenticatorPlugin() + >>> provideUtility(authenticator2, name='Authentication Plugin 2') + +and add a principal to them:: + + >>> authenticator1.add('bob', 'Bob', 'A nice guy', 'b0b') + >>> authenticator1.add('white', 'White Spy', 'Sneaky', 'deathtoblack') + >>> authenticator2.add('black', 'Black Spy', 'Also sneaky', 'deathtowhite') + +When we configure the PAU to use both searchable authenticators (note the +order):: + + >>> pau.authenticatorPlugins = ( + ... 'Authentication Plugin 2', + ... 'Authentication Plugin 1') + +we register the factories for our principals:: + + >>> from zope.pluggableauth.factories import FoundPrincipalFactory + >>> provideAdapter(FoundPrincipalFactory) + +we see how the PAU uses both plugins:: + + >>> pau.getPrincipal('xyz_white') + Principal('xyz_white') + + >>> pau.getPrincipal('xyz_black') + Principal('xyz_black') + +If more than one plugin know about the same principal ID, the first plugin is +used and the remaining are not delegated to. To illustrate, we'll add +another principal with the same ID as an existing principal:: + + >>> authenticator2.add('white', 'White Rider', '', 'r1der') + >>> pau.getPrincipal('xyz_white').title + 'White Rider' + +If we change the order of the plugins:: + + >>> pau.authenticatorPlugins = ( + ... 'Authentication Plugin 1', + ... 'Authentication Plugin 2') + +we get a different principal for ID 'white':: + + >>> pau.getPrincipal('xyz_white').title + 'White Spy' + + +Issuing a Challenge +------------------- + +Part of PAU's IAuthentication contract is to challenge the user for +credentials when its 'unauthorized' method is called. The need for this +functionality is driven by the following use case: + + - A user attempts to perform an operation he is not authorized to perform. + + - A handler responds to the unauthorized error by calling IAuthentication + 'unauthorized'. + + - The authentication component (in our case, a PAU) issues a challenge to + the user to collect new credentials (typically in the form of logging in + as a new user). + +The PAU handles the credentials challenge by delegating to its credentials +plugins. + +Currently, the PAU is configured with the credentials plugins that don't +perform any action when asked to challenge (see above the 'challenge' methods). + +To illustrate challenges, we'll subclass an existing credentials plugin and +do something in its 'challenge':: + + >>> class LoginFormCredentialsPlugin(FormCredentialsPlugin): + ... + ... def __init__(self, loginForm): + ... self.loginForm = loginForm + ... + ... def challenge(self, request): + ... request.response.redirect(self.loginForm) + ... return True + +This plugin handles a challenge by redirecting the response to a login form. +It returns True to signal to the PAU that it handled the challenge. + +We will now create and register a couple of these plugins:: + + >>> provideUtility(LoginFormCredentialsPlugin('simplelogin.html'), + ... name='Simple Login Form Plugin') + + >>> provideUtility(LoginFormCredentialsPlugin('advancedlogin.html'), + ... name='Advanced Login Form Plugin') + +and configure the PAU to use them:: + + >>> pau.credentialsPlugins = ( + ... 'Simple Login Form Plugin', + ... 'Advanced Login Form Plugin') + +Now when we call 'unauthorized' on the PAU:: + + >>> request = TestRequest() + >>> pau.unauthorized(id=None, request=request) + +we see that the user is redirected to the simple login form:: + + >>> request.response.getStatus() + 302 + >>> request.response.getHeader('location') + 'simplelogin.html' + +We can change the challenge policy by reordering the plugins:: + + >>> pau.credentialsPlugins = ( + ... 'Advanced Login Form Plugin', + ... 'Simple Login Form Plugin') + +Now when we call 'unauthorized':: + + >>> request = TestRequest() + >>> pau.unauthorized(id=None, request=request) + +the advanced plugin is used because it's first:: + + >>> request.response.getStatus() + 302 + >>> request.response.getHeader('location') + 'advancedlogin.html' + +Challenge Protocols +~~~~~~~~~~~~~~~~~~~ + +Sometimes, we want multiple challengers to work together. For example, the +HTTP specification allows multiple challenges to be issued in a response. A +challenge plugin can provide a `challengeProtocol` attribute that effectively +groups related plugins together for challenging. If a plugin returns `True` +from its challenge and provides a non-None challengeProtocol, subsequent +plugins in the credentialsPlugins list that have the same challenge protocol +will also be used to challenge. + +Without a challengeProtocol, only the first plugin to succeed in a challenge +will be used. + +Let's look at an example. We'll define a new plugin that specifies an +'X-Challenge' protocol:: + + >>> class XChallengeCredentialsPlugin(FormCredentialsPlugin): + ... + ... challengeProtocol = 'X-Challenge' + ... + ... def __init__(self, challengeValue): + ... self.challengeValue = challengeValue + ... + ... def challenge(self, request): + ... value = self.challengeValue + ... existing = request.response.getHeader('X-Challenge', '') + ... if existing: + ... value += ' ' + existing + ... request.response.setHeader('X-Challenge', value) + ... return True + +and register a couple instances as utilities:: + + >>> provideUtility(XChallengeCredentialsPlugin('basic'), + ... name='Basic X-Challenge Plugin') + + >>> provideUtility(XChallengeCredentialsPlugin('advanced'), + ... name='Advanced X-Challenge Plugin') + +When we use both plugins with the PAU:: + + >>> pau.credentialsPlugins = ( + ... 'Basic X-Challenge Plugin', + ... 'Advanced X-Challenge Plugin') + +and call 'unauthorized':: + + >>> request = TestRequest() + >>> pau.unauthorized(None, request) + +we see that both plugins participate in the challange, rather than just the +first plugin:: + + >>> request.response.getHeader('X-Challenge') + 'advanced basic' + + +Pluggable-Authentication Prefixes +--------------------------------- + +Principal ids are required to be unique system wide. Plugins will often provide +options for providing id prefixes, so that different sets of plugins provide +unique ids within a PAU. If there are multiple pluggable-authentication +utilities in a system, it's a good idea to give each PAU a unique prefix, so +that principal ids from different PAUs don't conflict. We can provide a prefix +when a PAU is created:: + + >>> pau = authentication.PluggableAuthentication('mypau_') + >>> pau.credentialsPlugins = ('My Credentials Plugin', ) + >>> pau.authenticatorPlugins = ('My Authenticator Plugin', ) + +When we create a request and try to authenticate:: + + >>> pau.authenticate(TestRequest(credentials='secretcode')) + Principal('mypau_bob') + +Note that now, our principal's id has the pluggable-authentication +utility prefix. + +We can still lookup a principal, as long as we supply the prefix:: + + >> pau.getPrincipal('mypas_42') + Principal('mypas_42', "{'domain': 42}") + + >> pau.getPrincipal('mypas_41') + OddPrincipal('mypas_41', "{'int': 41}") diff -Nru zope3-3.4.0/src/zope/pluggableauth/tests.py zope3-3.5~bzr18/src/zope/pluggableauth/tests.py --- zope3-3.4.0/src/zope/pluggableauth/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/pluggableauth/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,144 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Pluggable Authentication Service Tests + +$Id: tests.py 108524 2010-01-26 16:16:33Z trollfot $ +""" +__docformat__ = "reStructuredText" + +import doctest +import unittest +import zope.component +from zope.component.eventtesting import getEvents, clearEvents +from zope.component.interfaces import IComponentLookup +from zope.container.interfaces import ISimpleReadContainer +from zope.container.traversal import ContainerTraversable +from zope.interface import Interface +from zope.interface import implements +from zope.pluggableauth.plugins.session import SessionCredentialsPlugin +from zope.publisher import base +from zope.publisher.interfaces import IRequest +from zope.session.http import CookieClientIdManager +from zope.site.folder import rootFolder +from zope.site.site import LocalSiteManager, SiteManagerAdapter +from zope.traversing.interfaces import ITraversable +from zope.traversing.testing import setUp +from zope.session.interfaces import ( + IClientId, IClientIdManager, ISession, ISessionDataContainer) +from zope.session.session import ( + ClientId, Session, PersistentSessionDataContainer) + + +class TestClientId(object): + implements(IClientId) + + def __new__(cls, request): + return 'dummyclientidfortesting' + + +def siteSetUp(test): + zope.component.hooks.setHooks() + + # Set up site manager adapter + zope.component.provideAdapter( + SiteManagerAdapter, (Interface,), IComponentLookup) + + # Set up traversal + setUp() + zope.component.provideAdapter( + ContainerTraversable, (ISimpleReadContainer,), ITraversable) + + # Set up site + site = rootFolder() + site.setSiteManager(LocalSiteManager(site)) + zope.component.hooks.setSite(site) + + return site + + +def siteTearDown(test): + zope.component.hooks.resetHooks() + zope.component.hooks.setSite() + + +def sessionSetUp(container=PersistentSessionDataContainer): + zope.component.provideAdapter(TestClientId, [IRequest], IClientId) + zope.component.provideAdapter(Session, [IRequest], ISession) + zope.component.provideUtility(CookieClientIdManager(), IClientIdManager) + zope.component.provideUtility(container(), ISessionDataContainer, '') + + +def nonHTTPSessionTestCaseSetUp(container=PersistentSessionDataContainer): + # I am getting an error with ClientId and not TestClientId + zope.component.provideAdapter(ClientId, [IRequest], IClientId) + zope.component.provideAdapter(Session, [IRequest], ISession) + zope.component.provideUtility(CookieClientIdManager(), IClientIdManager) + zope.component.provideUtility(container(), ISessionDataContainer, '') + + +class NonHTTPSessionTestCase(unittest.TestCase): + """Small test suite to catch an error with non HTTP protocols, + like FTP and SessionCredentialsPlugin. + """ + + def setUp(self): + nonHTTPSessionTestCaseSetUp() + + def tearDown(self): + zope.component.hooks.resetHooks() + zope.component.hooks.setSite() + + def test_exeractCredentials(self): + plugin = SessionCredentialsPlugin() + self.assertEqual( + plugin.extractCredentials(base.TestRequest('/')), None) + + def test_challenge(self): + plugin = SessionCredentialsPlugin() + self.assertEqual( + plugin.challenge(base.TestRequest('/')), False) + + def test_logout(self): + plugin = SessionCredentialsPlugin() + self.assertEqual( + plugin.logout(base.TestRequest('/')), False) + + +def test_suite(): + suite = unittest.TestSuite(( + unittest.makeSuite(NonHTTPSessionTestCase), + doctest.DocTestSuite('zope.pluggableauth.interfaces'), + doctest.DocTestSuite('zope.pluggableauth.plugins.generic'), + doctest.DocTestSuite('zope.pluggableauth.plugins.ftpplugins'), + doctest.DocTestSuite('zope.pluggableauth.plugins.httpplugins'), + doctest.DocTestSuite( + 'zope.pluggableauth.plugins.session', + setUp=siteSetUp, + tearDown=siteTearDown), + doctest.DocFileSuite( + 'README.txt', + setUp=siteSetUp, + tearDown=siteTearDown, + globs={'provideUtility': zope.component.provideUtility, + 'provideAdapter': zope.component.provideAdapter, + 'provideHandler': zope.component.provideHandler, + 'getEvents': getEvents, + 'clearEvents': clearEvents, + }), + )) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/principalregistry/configure.zcml zope3-3.5~bzr18/src/zope/principalregistry/configure.zcml --- zope3-3.4.0/src/zope/principalregistry/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/principalregistry/__init__.py zope3-3.5~bzr18/src/zope/principalregistry/__init__.py --- zope3-3.4.0/src/zope/principalregistry/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# i am a package diff -Nru zope3-3.4.0/src/zope/principalregistry/metaconfigure.py zope3-3.5~bzr18/src/zope/principalregistry/metaconfigure.py --- zope3-3.4.0/src/zope/principalregistry/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,129 @@ +############################################################################## +# +# Copyright (c) 2001-2009 Zope Corporation 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. +# +############################################################################## +"""Directives for defining principals and groups + +$Id: metaconfigure.py 98046 2009-03-13 20:12:47Z nadako $ +""" +from zope import component +from zope.component.zcml import utility +from zope.authentication import interfaces + +from zope.principalregistry import principalregistry + + +def _principal(): + group = component.queryUtility(interfaces.IAuthenticatedGroup) + if group is not None: + _authenticatedGroup(group.id) + group = component.queryUtility(interfaces.IEveryoneGroup) + if group is not None: + _everybodyGroup(group.id) + +def principal(_context, id, title, login, + password, description='', password_manager="Plain Text"): + _context.action( + discriminator = ('principal', id), + callable = principalregistry.principalRegistry.definePrincipal, + args = (id, title, description, login, password, password_manager) ) + _context.action(discriminator = None, callable = _principal, args = ()) + + +def _unauthenticatedPrincipal(): + group = component.queryUtility(interfaces.IUnauthenticatedGroup) + if group is not None: + _unauthenticatedGroup(group.id) + group = component.queryUtility(interfaces.IEveryoneGroup) + if group is not None: + _everybodyGroup(group.id) + +def unauthenticatedPrincipal(_context, id, title, description=''): + principal = principalregistry.UnauthenticatedPrincipal( + id, title, description) + _context.action( + discriminator = 'unauthenticatedPrincipal', + callable = principalregistry.principalRegistry.defineDefaultPrincipal, + args = (id, title, description, principal) ) + utility(_context, interfaces.IUnauthenticatedPrincipal, principal) + _context.action( + discriminator = None, + callable = _unauthenticatedPrincipal, + args = (), + ) + +def _unauthenticatedGroup(group): + p = principalregistry.principalRegistry.unauthenticatedPrincipal() + if p is not None: + p.groups.append(group) + +def unauthenticatedGroup(_context, id, title, description=''): + principal = principalregistry.UnauthenticatedGroup( + id, title, description) + utility(_context, interfaces.IUnauthenticatedGroup, principal) + _context.action( + discriminator = None, + callable = _unauthenticatedGroup, + args = (principal.id, ), + ) + _context.action( + discriminator = None, + callable = principalregistry.principalRegistry.registerGroup, + args = (principal, ), + ) + +def _authenticatedGroup(group): + for p in principalregistry.principalRegistry.getPrincipals(''): + if not isinstance(p, principalregistry.Principal): + continue + if group not in p.groups: + p.groups.append(group) + +def authenticatedGroup(_context, id, title, description=''): + principal = principalregistry.AuthenticatedGroup( + id, title, description) + utility(_context, interfaces.IAuthenticatedGroup, principal) + _context.action( + discriminator = None, + callable = _authenticatedGroup, + args = (principal.id, ), + ) + _context.action( + discriminator = None, + callable = principalregistry.principalRegistry.registerGroup, + args = (principal, ), + ) + +def _everybodyGroup(group): + for p in principalregistry.principalRegistry.getPrincipals(''): + if not isinstance(p, principalregistry.Principal): + continue + if group not in p.groups: + p.groups.append(group) + p = principalregistry.principalRegistry.unauthenticatedPrincipal() + if p is not None: + p.groups.append(group) + +def everybodyGroup(_context, id, title, description=''): + principal = principalregistry.EverybodyGroup( + id, title, description) + utility(_context, interfaces.IEveryoneGroup, principal) + _context.action( + discriminator = None, + callable = _everybodyGroup, + args = (principal.id, ), + ) + _context.action( + discriminator = None, + callable = principalregistry.principalRegistry.registerGroup, + args = (principal, ), + ) diff -Nru zope3-3.4.0/src/zope/principalregistry/metadirectives.py zope3-3.5~bzr18/src/zope/principalregistry/metadirectives.py --- zope3-3.4.0/src/zope/principalregistry/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,70 @@ +############################################################################## +# +# Copyright (c) 2001-2009 Zope Corporation 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. +# +############################################################################## +"""Schemas for directives that define principals and groups + +$Id: metadirectives.py 98046 2009-03-13 20:12:47Z nadako $ +""" +from zope.interface import Interface +from zope.schema import Id, TextLine + + +class IBasePrincipalDirective(Interface): + """Base interface for principal definition directives.""" + + id = Id( + title=u"Id", + description=u"Id as which this object will be known and used.", + required=True) + + title = TextLine( + title=u"Title", + description=u"Provides a title for the object.", + required=True) + + description = TextLine( + title=u"Title", + description=u"Provides a description for the object.", + required=False) + +class IDefinePrincipalDirective(IBasePrincipalDirective): + """Define a new principal.""" + + login = TextLine( + title=u"Username/Login", + description=u"Specifies the Principal's Username/Login.", + required=True) + + password = TextLine( + title=u"Password", + description=u"Specifies the Principal's Password.", + required=True) + + password_manager = TextLine( + title=u"Password Manager Name", + description=(u"Name of the password manager will be used" + " for encode/check the password"), + default=u"Plain Text" + ) + +class IDefineUnauthenticatedPrincipalDirective(IBasePrincipalDirective): + """Define a new unauthenticated principal.""" + +class IDefineUnauthenticatedGroupDirective(IBasePrincipalDirective): + """Define the unauthenticated group.""" + +class IDefineAuthenticatedGroupDirective(IBasePrincipalDirective): + """Define the authenticated group.""" + +class IDefineEverybodyGroupDirective(IBasePrincipalDirective): + """Define the everybody group.""" diff -Nru zope3-3.4.0/src/zope/principalregistry/meta.zcml zope3-3.5~bzr18/src/zope/principalregistry/meta.zcml --- zope3-3.4.0/src/zope/principalregistry/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/principalregistry/principalregistry.py zope3-3.5~bzr18/src/zope/principalregistry/principalregistry.py --- zope3-3.4.0/src/zope/principalregistry/principalregistry.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/principalregistry.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,218 @@ +############################################################################## +# +# Copyright (c) 2001-2009 Zope Corporation 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. +# +############################################################################## +"""Global Authentication Utility or Principal Registry + +$Id: principalregistry.py 98046 2009-03-13 20:12:47Z nadako $ +""" +from zope.component import getUtility +from zope.interface import implements +import zope.security.management +from zope.security.interfaces import IGroupAwarePrincipal +from zope.password.interfaces import IPasswordManager + +from zope.authentication.interfaces import ( + IAuthentication, + PrincipalLookupError, + ILoginPassword, + ILogout, + IUnauthenticatedPrincipal, + IUnauthenticatedGroup, + IAuthenticatedGroup, + IEveryoneGroup, + ) + + +class DuplicateLogin(Exception): + pass + + +class DuplicateId(Exception): + pass + + +class PrincipalRegistry(object): + + implements(IAuthentication, ILogout) + + # Methods implementing IAuthentication + + def authenticate(self, request): + a = ILoginPassword(request, None) + if a is not None: + login = a.getLogin() + if login is not None: + p = self.__principalsByLogin.get(login, None) + if p is not None: + password = a.getPassword() + if p.validate(password): + return p + return None + + __defaultid = None + __defaultObject = None + + def defineDefaultPrincipal(self, id, title, description='', + principal=None): + if id in self.__principalsById: + raise DuplicateId(id) + self.__defaultid = id + if principal is None: + principal = UnauthenticatedPrincipal(id, title, description) + principal.__name__ = id + principal.__parent__ = self + self.__defaultObject = principal + return principal + + def unauthenticatedPrincipal(self): + return self.__defaultObject + + def unauthorized(self, id, request): + if id is None or id is self.__defaultid: + a = ILoginPassword(request) + a.needLogin(realm="Zope") + + def getPrincipal(self, id): + r = self.__principalsById.get(id) + if r is None: + if id == self.__defaultid: + return self.__defaultObject + if id == zope.security.management.system_user.id: + return zope.security.management.system_user + raise PrincipalLookupError(id) + return r + + def getPrincipalByLogin(self, login): + return self.__principalsByLogin[login] + + def getPrincipals(self, name): + name = name.lower() + return [p for p in self.__principalsById.itervalues() + if p.title.lower().startswith(name) or + p.getLogin().lower().startswith(name)] + + def logout(self, request): + # not supporting basic auth logout -- no such thing + pass + + # Management methods + + def __init__(self): + self.__principalsById = {} + self.__principalsByLogin = {} + + def definePrincipal(self, principal, title, description='', + login='', password='', passwordManagerName='Plain Text'): + id=principal + if login in self.__principalsByLogin: + raise DuplicateLogin(login) + + if id in self.__principalsById or id == self.__defaultid: + raise DuplicateId(id) + + p = Principal(id, title, description, + login, password, passwordManagerName) + p.__name__ = id + p.__parent__ = self + + self.__principalsByLogin[login] = p + self.__principalsById[id] = p + + return p + + def registerGroup(self, group): + id = group.id + if id in self.__principalsById or id == self.__defaultid: + raise DuplicateId(id) + + self.__principalsById[group.id] = group + + def _clear(self): + self.__init__() + self.__defaultid = None + self.__defaultObject = None + +principalRegistry = PrincipalRegistry() + +# Register our cleanup with Testing.CleanUp to make writing unit tests +# simpler. +try: + from zope.testing.cleanup import addCleanUp +except ImportError: + pass +else: + addCleanUp(principalRegistry._clear) + del addCleanUp + +class PrincipalBase(object): + + __name__ = __parent__ = None + + def __init__(self, id, title, description): + self.id = id + self.title = title + self.description = description + self.groups = [] + +class Group(PrincipalBase): + + def getLogin(self): + return '' # to make registry search happy + +class Principal(PrincipalBase): + + implements(IGroupAwarePrincipal) + + def __init__(self, id, title, description, login, + pw, pwManagerName="Plain Text"): + super(Principal, self).__init__(id, title, description) + self.__login = login + self.__pwManagerName = pwManagerName + self.__pw = pw + + def __getPasswordManager(self): + return getUtility(IPasswordManager, self.__pwManagerName) + + def getLogin(self): + return self.__login + + def validate(self, pw): + pwManager = self.__getPasswordManager() + return pwManager.checkPassword(self.__pw, pw) + + +class UnauthenticatedPrincipal(PrincipalBase): + + implements(IUnauthenticatedPrincipal) + + +fallback_unauthenticated_principal = ( + UnauthenticatedPrincipal( + __name__+'.fallback_unauthenticated_principal', + 'Fallback unauthenticated principal', + 'The default unauthenticated principal. Used as a fallback to ' + 'allow challenging for a user even if the IAuthentication returned ' + 'None as the unauthenticated principal.')) + + +class UnauthenticatedGroup(Group): + + implements(IUnauthenticatedGroup) + +class AuthenticatedGroup(Group): + + implements(IAuthenticatedGroup) + +class EverybodyGroup(Group): + + implements(IEveryoneGroup) diff -Nru zope3-3.4.0/src/zope/principalregistry/README.txt zope3-3.5~bzr18/src/zope/principalregistry/README.txt --- zope3-3.4.0/src/zope/principalregistry/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,286 @@ +=========================== +Global principal definition +=========================== + +Global principals are defined via ZCML. There are several kinds of +principals that can be defined. + +Authenticated Users +------------------- + +There are principals that can log in: + + >>> zcml(""" + ... + ... + ... + ... + ... + ... """) + + >>> import pprint + >>> from zope.principalregistry.principalregistry import principalRegistry + >>> [p] = principalRegistry.getPrincipals('') + >>> p.id, p.title, p.description, p.getLogin(), p.validate('123') + ('zope.manager', u'Manager', u'System Manager', u'admin', True) + +The unauthenticated principal +----------------------------- + +There is the unauthenticated principal: + + >>> zcml(""" + ... + ... + ... + ... + ... + ... """) + + >>> p = principalRegistry.unauthenticatedPrincipal() + >>> p.id, p.title, p.description + ('zope.unknown', u'Anonymous user', u"A person we don't know") + +The unauthenticated principal will also be registered as a utility. +This is to provide easy access to the data defined for the principal so +that other (more featureful) principal objects can be created for the +same principal. + + >>> from zope import component + >>> from zope.authentication import interfaces + >>> p = component.getUtility(interfaces.IUnauthenticatedPrincipal) + >>> p.id, p.title, p.description + ('zope.unknown', u'Anonymous user', u"A person we don't know") + +The unauthenticated group +------------------------- + +An unauthenticated group can also be defined in ZCML: + + >>> zcml(""" + ... + ... + ... + ... + ... + ... """) + +This directive creates a group and registers it as a utility providing +IUnauthenticatedGroup: + + >>> g = component.getUtility(interfaces.IUnauthenticatedGroup) + >>> g.id, g.title, g.description + ('zope.unknowngroup', u'Anonymous users', u"People we don't know") + +The unauthenticatedGroup directive also updates the group of the +unauthenticated principal: + + >>> p = principalRegistry.unauthenticatedPrincipal() + >>> g.id in p.groups + True + >>> p = component.getUtility(interfaces.IUnauthenticatedPrincipal) + >>> g.id in p.groups + True + +If the unauthenticated principal is defined after the unauthenticated +group, it will likewise have the group added to it: + + >>> reset() + >>> zcml(""" + ... + ... + ... + ... + ... + ... + ... """) + + >>> g = component.getUtility(interfaces.IUnauthenticatedGroup) + >>> g.id, g.title, g.description + ('zope.unknowngroup2', u'Anonymous users', u"People we don't know") + >>> p = principalRegistry.unauthenticatedPrincipal() + >>> p.id, g.id in p.groups + ('zope.unknown2', True) + >>> p = component.getUtility(interfaces.IUnauthenticatedPrincipal) + >>> p.id, g.id in p.groups + ('zope.unknown2', True) + +The unauthenticated group shows up as a principal in the principal +registry: + + >>> principalRegistry.getPrincipal(g.id) == g + True + + >>> list(principalRegistry.getPrincipals("Anonymous")) == [g] + True + +The authenticated group +----------------------- + +There is an authenticated group: + + >>> reset() + >>> zcml(""" + ... + ... + ... + ... + ... + ... + ... + ... + ... """) + +It defines an IAuthenticatedGroup utility: + + >>> g = component.getUtility(interfaces.IAuthenticatedGroup) + >>> g.id, g.title, g.description + ('zope.authenticated', u'Authenticated users', u'People we know') + +It also adds it self to the groups of any non-group principals already +defined, and, when non-group principals are defined, they put +themselves in the group if it's defined: + + >>> principals = list(principalRegistry.getPrincipals('')) + >>> principals.sort(lambda p1, p2: cmp(p1.id, p2.id)) + >>> for p in principals: + ... print p.id, p.groups == [g.id] + zope.authenticated False + zope.manager2 True + zope.manager3 True + +Excluding unauthenticated principals, of course: + + >>> p = principalRegistry.unauthenticatedPrincipal() + >>> p.id, g.id in p.groups + ('zope.unknown3', False) + >>> p = component.getUtility(interfaces.IUnauthenticatedPrincipal) + >>> p.id, g.id in p.groups + ('zope.unknown3', False) + + +The everybody group +------------------- + +Finally, there is an everybody group: + + >>> reset() + >>> zcml(""" + ... + ... + ... + ... + ... + ... + ... + ... + ... """) + +The everybodyGroup directive defines an IEveryoneGroup utility: + + >>> g = component.getUtility(interfaces.IEveryoneGroup) + >>> g.id, g.title, g.description + ('zope.everybody', u'Everybody', u'All People') + +It also adds it self to the groups of any non-group principals already +defined, and, when non-group principals are defined, they put +themselves in the group if it's defined: + + >>> principals = list(principalRegistry.getPrincipals('')) + >>> principals.sort(lambda p1, p2: cmp(p1.id, p2.id)) + >>> for p in principals: + ... print p.id, p.groups == [g.id] + zope.everybody False + zope.manager4 True + zope.manager5 True + +Including unauthenticated principals, of course: + + >>> p = principalRegistry.unauthenticatedPrincipal() + >>> p.id, g.id in p.groups + ('zope.unknown4', True) + >>> p = component.getUtility(interfaces.IUnauthenticatedPrincipal) + >>> p.id, g.id in p.groups + ('zope.unknown4', True) + +Note that it is up to IAuthentication implementations to associate +these groups with their principals, as appropriate. + + +The system_user +--------------- + +There is also a system_user that is defined in the code. It will be returned +from the getPrincipal method of the registry. + + >>> import zope.security.management + >>> import zope.principalregistry.principalregistry + >>> auth = zope.principalregistry.principalregistry.PrincipalRegistry() + >>> system_user = auth.getPrincipal(u'zope.security.management.system_user') + >>> system_user is zope.security.management.system_user + True diff -Nru zope3-3.4.0/src/zope/principalregistry/tests/__init__.py zope3-3.5~bzr18/src/zope/principalregistry/tests/__init__.py --- zope3-3.4.0/src/zope/principalregistry/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# i am a package \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/principalregistry/tests/principal.zcml zope3-3.5~bzr18/src/zope/principalregistry/tests/principal.zcml --- zope3-3.4.0/src/zope/principalregistry/tests/principal.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/tests/principal.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/principalregistry/tests/test_doc.py zope3-3.5~bzr18/src/zope/principalregistry/tests/test_doc.py --- zope3-3.4.0/src/zope/principalregistry/tests/test_doc.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/tests/test_doc.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,45 @@ +############################################################################## +# +# Copyright (c) 2001-2009 Zope Corporation 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 principal registry ZCML directives + +$Id: test_doc.py 98046 2009-03-13 20:12:47Z nadako $ +""" +import unittest +from zope.testing import doctest +from zope.component.testing import setUp as setUpComponent +from zope.component.testing import tearDown as tearDownComponent +from zope.configuration import xmlconfig +from zope.password.testing import setUpPasswordManagers + +def setUp(test=None): + setUpComponent() + setUpPasswordManagers() + +def tearDown(test=None): + tearDownComponent() + +def zcml(s): + import zope.principalregistry + context = xmlconfig.file('meta.zcml', zope.principalregistry) + xmlconfig.string(s, context) + +def reset(): + tearDown() + setUp() + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('../README.txt', + setUp=setUp, globs={'zcml': zcml, 'reset': reset}), + )) diff -Nru zope3-3.4.0/src/zope/principalregistry/tests/test_principalregistry.py zope3-3.5~bzr18/src/zope/principalregistry/tests/test_principalregistry.py --- zope3-3.4.0/src/zope/principalregistry/tests/test_principalregistry.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/principalregistry/tests/test_principalregistry.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,145 @@ +############################################################################## +# +# Copyright (c) 2001-2009 Zope Corporation 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 principal registry + +$Id: test_principalregistry.py 98046 2009-03-13 20:12:47Z nadako $ +""" +import unittest +from zope.authentication.interfaces import PrincipalLookupError +from zope.authentication.loginpassword import LoginPassword +from zope.component import provideAdapter +from zope.interface import implements +from zope.password.testing import setUpPasswordManagers + +from zope.principalregistry.principalregistry import PrincipalRegistry +from zope.principalregistry.principalregistry import DuplicateLogin, DuplicateId + + +class Request(LoginPassword): + + challenge = None + + def __init__(self, lpw): + if lpw is not None: + l, p = lpw + else: + l = p = None + super(Request, self).__init__(l, p) + + def needLogin(self, realm): + self.challenge = 'basic realm="%s"' % realm + + +class Test(unittest.TestCase): + + def setUp(self): + setUpPasswordManagers() + self.reg = PrincipalRegistry() + self.reg.definePrincipal('1', 'Tim Peters', 'Sir Tim Peters', + 'tim', '123') + self.reg.definePrincipal('2', 'Jim Fulton', 'Sir Jim Fulton', + 'jim', '456') + + def testRegistered(self): + p = self.reg.getPrincipal('1') + self.assertEqual(p.id, '1') + self.assertEqual(p.title, 'Tim Peters') + self.assertEqual(p.description, 'Sir Tim Peters') + p = self.reg.getPrincipal('2') + self.assertEqual(p.id, '2') + self.assertEqual(p.title, 'Jim Fulton') + self.assertEqual(p.description, 'Sir Jim Fulton') + + self.assertEqual(len(self.reg.getPrincipals('')), 2) + + def testUnRegistered(self): + self.assertRaises(PrincipalLookupError, self.reg.getPrincipal, '3') + + def testDup(self): + self.assertRaises(DuplicateId, + self.reg.definePrincipal, + '1', 'Tim Peters', 'Sir Tim Peters', + 'tim2', '123') + self.assertRaises(DuplicateLogin, + self.reg.definePrincipal, + '3', 'Tim Peters', 'Sir Tim Peters', + 'tim', '123') + self.assertRaises(PrincipalLookupError, self.reg.getPrincipal, '3') + self.assertEqual(len(self.reg.getPrincipals('')), 2) + + def testSearch(self): + r = self.reg.getPrincipals('J') + self.assertEquals(len(r), 1) + self.failUnless(r[0] is self.reg.getPrincipal('2')) + + def testByLogin(self): + tim = self.reg.getPrincipalByLogin('tim') + self.assertEquals(tim.getLogin(), 'tim') + jim = self.reg.getPrincipalByLogin('jim') + self.assertEquals(jim.getLogin(), 'jim') + self.assertRaises(KeyError, + self.reg.getPrincipalByLogin, 'kim') + + def testValidation(self): + tim = self.reg.getPrincipalByLogin('tim') + self.assert_(tim.validate('123')) + self.failIf(tim.validate('456')) + self.failIf(tim.validate('')) + self.failIf(tim.validate('1234')) + self.failIf(tim.validate('12')) + + def testAuthenticate(self): + req = Request(('tim', '123')) + pid = self.reg.authenticate(req).id + self.assertEquals(pid, '1') + req = Request(('tim', '1234')) + p = self.reg.authenticate(req) + self.assertEquals(p, None) + req = Request(('kim', '123')) + p = self.reg.authenticate(req) + self.assertEquals(p, None) + + def testUnauthorized(self): + request = Request(None) + self.reg.unauthorized(self.reg.unauthenticatedPrincipal(), request) + self.assertEquals(request.challenge, 'basic realm="Zope"') + request = Request(None) + self.reg.unauthorized(None, request) + self.assertEquals(request.challenge, 'basic realm="Zope"') + request = Request(None) + self.reg.unauthorized("1", request) + self.assertEquals(request.challenge, None) + + def testDefaultPrincipal(self): + self.assertEquals(self.reg.unauthenticatedPrincipal(), None) + self.assertRaises(DuplicateId, self.reg.defineDefaultPrincipal, + "1", "tim") + self.reg.defineDefaultPrincipal("everybody", "Default Principal") + self.assertEquals(self.reg.unauthenticatedPrincipal().id, "everybody") + self.reg.defineDefaultPrincipal("anybody", "Default Principal", + "This is the default headmaster") + self.assertEquals(self.reg.unauthenticatedPrincipal().id, "anybody") + self.assertRaises(PrincipalLookupError, + self.reg.getPrincipal, "everybody") + p = self.reg.getPrincipal("anybody") + self.assertEquals(p.id, "anybody") + self.assertEquals(p.title, "Default Principal") + self.assertRaises(DuplicateId, self.reg.definePrincipal, + "anybody", "title") + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(Test), + )) diff -Nru zope3-3.4.0/src/zope/processlifetime/__init__.py zope3-3.5~bzr18/src/zope/processlifetime/__init__.py --- zope3-3.4.0/src/zope/processlifetime/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/processlifetime/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# Copyright (c) 2004-2009 Zope Corporation 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. +# +############################################################################## +"""Events in the lifetime of a server process. +""" +from zope.interface import Attribute +from zope.interface import Interface +from zope.interface import implements + +class IDatabaseOpened(Interface): + """The main database has been opened. + """ + database = Attribute("The main database.") + +class DatabaseOpened(object): + implements(IDatabaseOpened) + + def __init__(self, database): + self.database = database + +class IDatabaseOpenedWithRoot(Interface): + """The main database has been opened. + """ + database = Attribute("The main database.") + +class DatabaseOpenedWithRoot(object): + implements(IDatabaseOpenedWithRoot) + + def __init__(self, database): + self.database = database + +class IProcessStarting(Interface): + """The application server process is starting. + """ + +class ProcessStarting(object): + implements(IProcessStarting) diff -Nru zope3-3.4.0/src/zope/processlifetime/tests.py zope3-3.5~bzr18/src/zope/processlifetime/tests.py --- zope3-3.4.0/src/zope/processlifetime/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/processlifetime/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,66 @@ +############################################################################## +# +# Copyright (c) 2009 Zope Corporation 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 unittest + +class DatabaseOpenedTests(unittest.TestCase): + def _getTargetClass(self): + from zope.processlifetime import DatabaseOpened + return DatabaseOpened + + def test_class_conforms_to_IDatabaseOpened(self): + from zope.interface.verify import verifyClass + from zope.processlifetime import IDatabaseOpened + verifyClass(IDatabaseOpened, self._getTargetClass()) + + def test_instance_conforms_to_IDatabaseOpened(self): + from zope.interface.verify import verifyObject + from zope.processlifetime import IDatabaseOpened + verifyObject(IDatabaseOpened, self._getTargetClass()(object())) + +class DatabaseOpenedWithRootTests(unittest.TestCase): + def _getTargetClass(self): + from zope.processlifetime import DatabaseOpenedWithRoot + return DatabaseOpenedWithRoot + + def test_class_conforms_to_IDatabaseOpenedWithRoot(self): + from zope.interface.verify import verifyClass + from zope.processlifetime import IDatabaseOpenedWithRoot + verifyClass(IDatabaseOpenedWithRoot, self._getTargetClass()) + + def test_instance_conforms_to_IDatabaseOpenedWithRoot(self): + from zope.interface.verify import verifyObject + from zope.processlifetime import IDatabaseOpenedWithRoot + verifyObject(IDatabaseOpenedWithRoot, self._getTargetClass()(object())) + +class ProcessStartingTests(unittest.TestCase): + def _getTargetClass(self): + from zope.processlifetime import ProcessStarting + return ProcessStarting + + def test_class_conforms_to_IProcessStarting(self): + from zope.interface.verify import verifyClass + from zope.processlifetime import IProcessStarting + verifyClass(IProcessStarting, self._getTargetClass()) + + def test_instance_conforms_to_IProcessStarting(self): + from zope.interface.verify import verifyObject + from zope.processlifetime import IProcessStarting + verifyObject(IProcessStarting, self._getTargetClass()()) + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(DatabaseOpenedTests), + unittest.makeSuite(DatabaseOpenedWithRootTests), + unittest.makeSuite(ProcessStartingTests), + )) diff -Nru zope3-3.4.0/src/zope/proxy/decorator.py zope3-3.5~bzr18/src/zope/proxy/decorator.py --- zope3-3.4.0/src/zope/proxy/decorator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/decorator.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,106 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Decorator support + +Decorators are proxies that are mostly transparent but that may provide +additional features. + +$Id: decorator.py 70826 2006-10-20 03:41:16Z baijum $ +""" +__docformat__ = "reStructuredText" + +from zope.proxy import getProxiedObject, ProxyBase +from zope.interface.declarations import ObjectSpecificationDescriptor +from zope.interface.declarations import getObjectSpecification +from zope.interface.declarations import ObjectSpecification +from zope.interface import providedBy + +class DecoratorSpecificationDescriptor(ObjectSpecificationDescriptor): + """Support for interface declarations on decorators + + >>> from zope.interface import * + >>> class I1(Interface): + ... pass + >>> class I2(Interface): + ... pass + >>> class I3(Interface): + ... pass + >>> class I4(Interface): + ... pass + + >>> class D1(SpecificationDecoratorBase): + ... implements(I1) + + + >>> class D2(SpecificationDecoratorBase): + ... implements(I2) + + >>> class X(object): + ... implements(I3) + + >>> x = X() + >>> directlyProvides(x, I4) + + Interfaces of X are ordered with the directly-provided interfaces first + + >>> [interface.getName() for interface in list(providedBy(x))] + ['I4', 'I3'] + + When we decorate objects, what order should the interfaces come + in? One could argue that decorators are less specific, so they + should come last. + + >>> [interface.getName() for interface in list(providedBy(D1(x)))] + ['I4', 'I3', 'I1'] + + >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))] + ['I4', 'I3', 'I1', 'I2'] + + SpecificationDecorators also work with old-style classes: + + >>> class X: + ... implements(I3) + + >>> x = X() + >>> directlyProvides(x, I4) + + >>> [interface.getName() for interface in list(providedBy(x))] + ['I4', 'I3'] + + >>> [interface.getName() for interface in list(providedBy(D1(x)))] + ['I4', 'I3', 'I1'] + + >>> [interface.getName() for interface in list(providedBy(D2(D1(x))))] + ['I4', 'I3', 'I1', 'I2'] + """ + def __get__(self, inst, cls=None): + if inst is None: + return getObjectSpecification(cls) + else: + provided = providedBy(getProxiedObject(inst)) + + # Use type rather than __class__ because inst is a proxy and + # will return the proxied object's class. + cls = type(inst) + return ObjectSpecification(provided, cls) + + def __set__(self, inst, value): + raise TypeError("Can't set __providedBy__ on a decorated object") + + +class SpecificationDecoratorBase(ProxyBase): + """Base class for a proxy that provides additional interfaces.""" + + __providedBy__ = DecoratorSpecificationDescriptor() + diff -Nru zope3-3.4.0/src/zope/proxy/__init__.py zope3-3.5~bzr18/src/zope/proxy/__init__.py --- zope3-3.4.0/src/zope/proxy/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,33 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""More convenience functions for dealing with proxies. + +$Id: __init__.py 75661 2007-05-10 05:31:51Z baijum $ +""" +from zope.interface import moduleProvides +from zope.proxy.interfaces import IProxyIntrospection +from zope.proxy._zope_proxy_proxy import * +from zope.proxy._zope_proxy_proxy import _CAPI + +moduleProvides(IProxyIntrospection) +__all__ = tuple(IProxyIntrospection) + +def ProxyIterator(p): + yield p + while isProxy(p): + p = getProxiedObject(p) + yield p + +def non_overridable(func): + return property(lambda self: func.__get__(self)) diff -Nru zope3-3.4.0/src/zope/proxy/interfaces.py zope3-3.5~bzr18/src/zope/proxy/interfaces.py --- zope3-3.4.0/src/zope/proxy/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,68 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 +# +############################################################################## +"""Proxy-related interfaces. + +$Id: interfaces.py 69377 2006-08-08 16:33:57Z fdrake $ +""" + +from zope.interface import Interface + +class IProxyIntrospection(Interface): + """Provides methods for indentifying proxies and extracting proxied objects + """ + + def isProxy(obj, proxytype=None): + """Check whether the given object is a proxy + + If proxytype is not None, checkes whether the object is + proxied by the given proxytype. + """ + + def sameProxiedObjects(ob1, ob2): + """Check whether ob1 and ob2 are the same or proxies of the same object + """ + + def getProxiedObject(obj): + """Get the proxied Object + + If the object isn't proxied, then just return the object. + """ + + def setProxiedObject(ob1, ob2): + """Set the underlying object for ob1 to ob2, returning the old object. + + Raises TypeError if ob1 is not a proxy. + """ + + def removeAllProxies(obj): + """Get the proxied object with no proxies + + If obj is not a proxied object, return obj. + + The returned object has no proxies. + """ + + def queryProxy(obj, proxytype, default=None): + """Look for a proxy of the given type around the object + + If no such proxy can be found, return the default. + """ + + def queryInnerProxy(obj, proxytype, default=None): + """Look for the inner-most proxy of the given type around the object + + If no such proxy can be found, return the default. + + If there is such a proxy, return the inner-most one. + """ diff -Nru zope3-3.4.0/src/zope/proxy/proxy.h zope3-3.5~bzr18/src/zope/proxy/proxy.h --- zope3-3.4.0/src/zope/proxy/proxy.h 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/proxy.h 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,65 @@ +#ifndef _proxy_H_ +#define _proxy_H_ 1 + +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +typedef Py_ssize_t (*lenfunc)(PyObject *); +typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t); +typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t); +typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *); +typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *); +#endif + +typedef struct { + PyObject_HEAD + PyObject *proxy_object; +} ProxyObject; + +#define Proxy_GET_OBJECT(ob) (((ProxyObject *)(ob))->proxy_object) + +typedef struct { + PyTypeObject *proxytype; + int (*check)(PyObject *obj); + PyObject *(*create)(PyObject *obj); + PyObject *(*getobject)(PyObject *proxy); +} ProxyInterface; + + +#ifndef PROXY_MODULE + +/* These are only defined in the public interface, and are not + * available within the module implementation. There we use the + * classic Python/C API only. + */ + +static ProxyInterface *_proxy_api = NULL; + +static int +Proxy_Import(void) +{ + if (_proxy_api == NULL) { + PyObject *m = PyImport_ImportModule("zope.proxy"); + if (m != NULL) { + PyObject *tmp = PyObject_GetAttrString(m, "_CAPI"); + if (tmp != NULL) { + if (PyCObject_Check(tmp)) + _proxy_api = (ProxyInterface *) + PyCObject_AsVoidPtr(tmp); + Py_DECREF(tmp); + } + } + } + return (_proxy_api == NULL) ? -1 : 0; +} + +#define ProxyType (*_proxy_api->proxytype) +#define Proxy_Check(obj) (_proxy_api->check((obj))) +#define Proxy_CheckExact(obj) ((obj)->ob_type == ProxyType) +#define Proxy_New(obj) (_proxy_api->create((obj))) +#define Proxy_GetObject(proxy) (_proxy_api->getobject((proxy))) + +#endif /* PROXY_MODULE */ + +#endif /* _proxy_H_ */ diff -Nru zope3-3.4.0/src/zope/proxy/tests/__init__.py zope3-3.5~bzr18/src/zope/proxy/tests/__init__.py --- zope3-3.4.0/src/zope/proxy/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/proxy/tests/test_decorator.py zope3-3.5~bzr18/src/zope/proxy/tests/test_decorator.py --- zope3-3.4.0/src/zope/proxy/tests/test_decorator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/tests/test_decorator.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,24 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 Harness + +$Id: test_decorator.py 110992 2010-04-16 18:47:14Z tseaver $ +""" +from doctest import DocTestSuite + + +def test_suite(): + suite = DocTestSuite() + suite.addTest(DocTestSuite('zope.proxy.decorator')) + return suite diff -Nru zope3-3.4.0/src/zope/proxy/tests/test_proxy.py zope3-3.5~bzr18/src/zope/proxy/tests/test_proxy.py --- zope3-3.4.0/src/zope/proxy/tests/test_proxy.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/tests/test_proxy.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,724 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 base proxy class. + +$Id: test_proxy.py 110992 2010-04-16 18:47:14Z tseaver $ +""" +from doctest import DocTestSuite +import pickle +import sys +import unittest + +from zope.proxy import ProxyBase +import zope.proxy + +class Thing: + """This class is expected to be a classic class.""" + +class Comparable(object): + def __init__(self, value): + self.value = value + + def __eq__(self, other): + if hasattr(other, "value"): + other = other.value + return self.value == other + + def __ne__(self, other): + return not self.__eq__(other) + + def __lt__(self, other): + if hasattr(other, "value"): + other = other.value + return self.value < other + + def __ge__(self, other): + return not self.__lt__(other) + + def __le__(self, other): + if hasattr(other, "value"): + other = other.value + return self.value <= other + + def __gt__(self, other): + return not self.__le__(other) + + def __repr__(self): + return "" % self.value + + +class ProxyTestCase(unittest.TestCase): + + proxy_class = ProxyBase + + def setUp(self): + self.x = Thing() + self.p = self.new_proxy(self.x) + + def new_proxy(self, o): + return self.proxy_class(o) + + def test_constructor(self): + o = object() + self.assertRaises(TypeError, self.proxy_class, o, o) + self.assertRaises(TypeError, self.proxy_class, o, key='value') + self.assertRaises(TypeError, self.proxy_class, key='value') + + def test_subclass_constructor(self): + class MyProxy(self.proxy_class): + def __new__(cls, *args, **kwds): + return super(MyProxy, cls).__new__(cls, *args, **kwds) + def __init__(self, *args, **kwds): + super(MyProxy, self).__init__(*args, **kwds) + o1 = object() + o2 = object() + o = MyProxy((o1, o2)) + + self.assertEquals(o1, o[0]) + self.assertEquals(o2, o[1]) + + self.assertRaises(TypeError, MyProxy, o1, o2) + self.assertRaises(TypeError, MyProxy, o1, key='value') + self.assertRaises(TypeError, MyProxy, key='value') + + # Check that are passed to __init__() overrides what's passed + # to __new__(). + class MyProxy2(self.proxy_class): + def __new__(cls, *args, **kwds): + return super(MyProxy2, cls).__new__(cls, 'value') + + p = MyProxy2('splat!') + self.assertEquals(list(p), list('splat!')) + + class MyProxy3(MyProxy2): + def __init__(self, arg): + if list(self) != list('value'): + raise AssertionError("list(self) != list('value')") + super(MyProxy3, self).__init__('another') + + p = MyProxy3('notused') + self.assertEquals(list(p), list('another')) + + def test_proxy_attributes(self): + o = Thing() + o.foo = 1 + w = self.new_proxy(o) + self.assert_(w.foo == 1) + + def test___class__(self): + o = object() + w = self.new_proxy(o) + self.assert_(w.__class__ is o.__class__) + + def test_pickle_prevention(self): + w = self.new_proxy(Thing()) + self.assertRaises(pickle.PicklingError, + pickle.dumps, w) + + def test_proxy_equality(self): + w = self.new_proxy('foo') + self.assertEquals(w, 'foo') + + o1 = Comparable(1) + o2 = Comparable(1.0) + o3 = Comparable("splat!") + + w1 = self.new_proxy(o1) + w2 = self.new_proxy(o2) + w3 = self.new_proxy(o3) + + self.assertEquals(o1, w1) + self.assertEquals(o1, w2) + self.assertEquals(o2, w1) + self.assertEquals(w1, o2) + self.assertEquals(w2, o1) + + self.assertNotEquals(o3, w1) + self.assertNotEquals(w1, o3) + self.assertNotEquals(w3, o1) + self.assertNotEquals(o1, w3) + + def test_proxy_ordering_lt(self): + o1 = Comparable(1) + o2 = Comparable(2.0) + + w1 = self.new_proxy(o1) + w2 = self.new_proxy(o2) + + self.assert_(w1 < w2) + self.assert_(w1 <= w2) + self.assert_(o1 < w2) + self.assert_(o1 <= w2) + self.assert_(w1 < o2) + self.assert_(w2 <= o2) + + def test_proxy_callable(self): + w = self.new_proxy({}.get) + self.assert_(callable(w)) + + def test_proxy_item_protocol(self): + w = self.new_proxy({}) + self.assertRaises(KeyError, lambda: w[1]) + w[1] = 'a' + self.assertEquals(w[1], 'a') + del w[1] + self.assertRaises(KeyError, lambda: w[1]) + def del_w_1(): + del w[1] + self.assertRaises(KeyError, del_w_1) + + def test_wrapped_iterable(self): + a = [1, 2, 3] + b = [] + for x in self.new_proxy(a): + b.append(x) + self.assertEquals(a, b) + + def test_iteration_over_proxy(self): + # Wrap an iterator before starting iteration. + # PyObject_GetIter() will still be called on the proxy. + a = [1, 2, 3] + b = [] + for x in self.new_proxy(iter(a)): + b.append(x) + self.assertEquals(a, b) + t = tuple(self.new_proxy(iter(a))) + self.assertEquals(t, (1, 2, 3)) + + def test_iteration_using_proxy(self): + # Wrap an iterator within the iteration protocol, expecting it + # still to work. PyObject_GetIter() will not be called on the + # proxy, so the tp_iter slot won't unwrap it. + + class Iterable(object): + def __init__(self, test, data): + self.test = test + self.data = data + def __iter__(self): + return self.test.new_proxy(iter(self.data)) + + a = [1, 2, 3] + b = [] + for x in Iterable(self, a): + b.append(x) + self.assertEquals(a, b) + + def test_bool_wrapped_None(self): + w = self.new_proxy(None) + self.assertEquals(not w, 1) + + # Numeric ops. + + unops = [ + "-x", "+x", "abs(x)", "~x", + "int(x)", "long(x)", "float(x)", + ] + + def test_unops(self): + P = self.new_proxy + for expr in self.unops: + x = 1 + y = eval(expr) + x = P(1) + z = eval(expr) + self.assertEqual(z, y, + "x=%r; expr=%r" % (x, expr)) + + def test_odd_unops(self): + # unops that don't return a proxy + P = self.new_proxy + for func in hex, oct, lambda x: not x: + self.assertEqual(func(P(100)), func(100)) + + binops = [ + "x+y", "x-y", "x*y", "x/y", "divmod(x, y)", "x**y", "x//y", + "x<>y", "x&y", "x|y", "x^y", + ] + + def test_binops(self): + P = self.new_proxy + for expr in self.binops: + first = 1 + for x in [1, P(1)]: + for y in [2, P(2)]: + if first: + z = eval(expr) + first = 0 + else: + self.assertEqual(eval(expr), z, + "x=%r; y=%r; expr=%r" % (x, y, expr)) + + def test_inplace(self): + # TODO: should test all inplace operators... + P = self.new_proxy + + pa = P(1) + pa += 2 + self.assertEqual(pa, 3) + + a = [1, 2, 3] + pa = qa = P(a) + pa += [4, 5, 6] + self.failUnless(pa is qa) + self.assertEqual(a, [1, 2, 3, 4, 5, 6]) + + pa = P(2) + pa **= 2 + self.assertEqual(pa, 4) + + def test_coerce(self): + P = self.new_proxy + + # Before 2.3, coerce() of two proxies returns them unchanged + fixed_coerce = sys.version_info >= (2, 3, 0) + + x = P(1) + y = P(2) + a, b = coerce(x, y) + self.failUnless(a is x and b is y) + + x = P(1) + y = P(2.1) + a, b = coerce(x, y) + self.failUnless(a == 1.0) + self.failUnless(b is y) + if fixed_coerce: + self.failUnless(a.__class__ is float, a.__class__) + + x = P(1.1) + y = P(2) + a, b = coerce(x, y) + self.failUnless(a is x) + self.failUnless(b == 2.0) + if fixed_coerce: + self.failUnless(b.__class__ is float, b.__class__) + + x = P(1) + y = 2 + a, b = coerce(x, y) + self.failUnless(a is x) + self.failUnless(b is y) + + x = P(1) + y = 2.1 + a, b = coerce(x, y) + self.failUnless(a.__class__ is float, a.__class__) + self.failUnless(b is y) + + x = P(1.1) + y = 2 + a, b = coerce(x, y) + self.failUnless(a is x) + self.failUnless(b.__class__ is float, b.__class__) + + x = 1 + y = P(2) + a, b = coerce(x, y) + self.failUnless(a is x) + self.failUnless(b is y) + + x = 1.1 + y = P(2) + a, b = coerce(x, y) + self.failUnless(a is x) + self.failUnless(b.__class__ is float, b.__class__) + + x = 1 + y = P(2.1) + a, b = coerce(x, y) + self.failUnless(a.__class__ is float, a.__class__) + self.failUnless(b is y) + + def test_getslice(self): + # Lists have special slicing bahvior. + pList = self.new_proxy([1, 2]) + self.assertEqual(pList[-1:], [2]) + self.assertEqual(pList[-2:], [1, 2]) + self.assertEqual(pList[-3:], [1, 2]) + + # Tuples also have special slicing behavior. + pTuple = self.new_proxy((1, 2)) + self.assertEqual(pTuple[-1:], (2,)) + self.assertEqual(pTuple[-2:], (1, 2)) + self.assertEqual(pTuple[-3:], (1, 2)) + + # This behavior should be true for all list- and tuple-derived classes. + class DerivedList(list): + + def __getslice__(self, start, end, step=None): + return (start, end, step) + + pList = self.new_proxy(DerivedList([1, 2])) + self.assertEqual(pList[-1:], [2]) + self.assertEqual(pList[-2:], [1, 2]) + self.assertEqual(pList[-3:], [1, 2]) + + # Another sort of sequence has a different slicing interpretation. + class Slicer(object): + + def __len__(self): + return 2 + + def __getslice__(self, start, end, step=None): + return (start, end, step) + + pSlicer = self.new_proxy(Slicer()) + self.assertEqual(pSlicer[-1:][0], 1) + self.assertEqual(pSlicer[-2:][0], 0) + # Note that for non-lists and non-tuples the slice is computed + # differently + self.assertEqual(pSlicer[-3:][0], 1) + + def test_setslice(self): + # Lists have special slicing bahvior for assignment as well. + pList = self.new_proxy([1, 2]) + pList[-1:] = [3, 4] + self.assertEqual(pList, [1, 3, 4]) + pList = self.new_proxy([1, 2]) + pList[-2:] = [3, 4] + self.assertEqual(pList, [3, 4]) + pList = self.new_proxy([1, 2]) + pList[-3:] = [3, 4] + self.assertEqual(pList, [3, 4]) + + # This behavior should be true for all list-derived classes. + class DerivedList(list): + pass + + pList = self.new_proxy(DerivedList([1, 2])) + pList[-1:] = [3, 4] + self.assertEqual(pList, [1, 3, 4]) + pList = self.new_proxy(DerivedList([1, 2])) + pList[-2:] = [3, 4] + self.assertEqual(pList, [3, 4]) + pList = self.new_proxy(DerivedList([1, 2])) + pList[-3:] = [3, 4] + self.assertEqual(pList, [3, 4]) + + +def test_isProxy(): + """ + >>> from zope.proxy import ProxyBase, isProxy + >>> class P1(ProxyBase): + ... pass + >>> class P2(ProxyBase): + ... pass + >>> class C(object): + ... pass + >>> c = C() + >>> int(isProxy(c)) + 0 + >>> p = P1(c) + >>> int(isProxy(p)) + 1 + >>> int(isProxy(p, P1)) + 1 + >>> int(isProxy(p, P2)) + 0 + >>> p = P2(p) + >>> int(isProxy(p, P1)) + 1 + >>> int(isProxy(p, P2)) + 1 + + """ + +def test_getProxiedObject(): + """ + >>> from zope.proxy import ProxyBase, getProxiedObject + >>> class C(object): + ... pass + >>> c = C() + >>> int(getProxiedObject(c) is c) + 1 + >>> p = ProxyBase(c) + >>> int(getProxiedObject(p) is c) + 1 + >>> p2 = ProxyBase(p) + >>> int(getProxiedObject(p2) is p) + 1 + + """ + +def test_ProxyIterator(): + """ + >>> from zope.proxy import ProxyBase, ProxyIterator + >>> class C(object): + ... pass + >>> c = C() + >>> p1 = ProxyBase(c) + >>> class P(ProxyBase): + ... pass + >>> p2 = P(p1) + >>> p3 = ProxyBase(p2) + >>> list(ProxyIterator(p3)) == [p3, p2, p1, c] + 1 + """ + +def test_removeAllProxies(): + """ + >>> from zope.proxy import ProxyBase, removeAllProxies + >>> class C(object): + ... pass + >>> c = C() + >>> int(removeAllProxies(c) is c) + 1 + >>> p = ProxyBase(c) + >>> int(removeAllProxies(p) is c) + 1 + >>> p2 = ProxyBase(p) + >>> int(removeAllProxies(p2) is c) + 1 + + """ + +def test_queryProxy(): + """ + >>> from zope.proxy import ProxyBase, queryProxy + >>> class P1(ProxyBase): + ... pass + >>> class P2(ProxyBase): + ... pass + >>> class C(object): + ... pass + >>> c = C() + >>> queryProxy(c, P1) + >>> queryProxy(c, P1, 42) + 42 + >>> p1 = P1(c) + >>> int(queryProxy(p1, P1) is p1) + 1 + >>> queryProxy(c, P2) + >>> queryProxy(c, P2, 42) + 42 + >>> p2 = P2(p1) + >>> int(queryProxy(p2, P1) is p1) + 1 + >>> int(queryProxy(p2, P2) is p2) + 1 + >>> int(queryProxy(p2, ProxyBase) is p2) + 1 + + """ + +def test_queryInnerProxy(): + """ + >>> from zope.proxy import ProxyBase, queryProxy, queryInnerProxy + >>> class P1(ProxyBase): + ... pass + >>> class P2(ProxyBase): + ... pass + >>> class C(object): + ... pass + >>> c = C() + >>> queryInnerProxy(c, P1) + >>> queryInnerProxy(c, P1, 42) + 42 + >>> p1 = P1(c) + >>> int(queryProxy(p1, P1) is p1) + 1 + >>> queryInnerProxy(c, P2) + >>> queryInnerProxy(c, P2, 42) + 42 + >>> p2 = P2(p1) + >>> int(queryInnerProxy(p2, P1) is p1) + 1 + >>> int(queryInnerProxy(p2, P2) is p2) + 1 + >>> int(queryInnerProxy(p2, ProxyBase) is p1) + 1 + + >>> p3 = P1(p2) + >>> int(queryProxy(p3, P1) is p3) + 1 + >>> int(queryInnerProxy(p3, P1) is p1) + 1 + >>> int(queryInnerProxy(p3, P2) is p2) + 1 + + """ + +def test_sameProxiedObjects(): + """ + >>> from zope.proxy import ProxyBase, sameProxiedObjects + >>> class C(object): + ... pass + >>> c1 = C() + >>> c2 = C() + >>> int(sameProxiedObjects(c1, c1)) + 1 + >>> int(sameProxiedObjects(ProxyBase(c1), c1)) + 1 + >>> int(sameProxiedObjects(ProxyBase(c1), ProxyBase(c1))) + 1 + >>> int(sameProxiedObjects(ProxyBase(ProxyBase(c1)), c1)) + 1 + >>> int(sameProxiedObjects(c1, ProxyBase(c1))) + 1 + >>> int(sameProxiedObjects(c1, ProxyBase(ProxyBase(c1)))) + 1 + >>> int(sameProxiedObjects(c1, c2)) + 0 + >>> int(sameProxiedObjects(ProxyBase(c1), c2)) + 0 + >>> int(sameProxiedObjects(ProxyBase(c1), ProxyBase(c2))) + 0 + >>> int(sameProxiedObjects(ProxyBase(ProxyBase(c1)), c2)) + 0 + >>> int(sameProxiedObjects(c1, ProxyBase(c2))) + 0 + >>> int(sameProxiedObjects(c1, ProxyBase(ProxyBase(c2)))) + 0 + """ + +def test_subclassing_proxies(): + """You can subclass ProxyBase + + If you subclass a proxy, instances of the subclass have access to + data defined in the class, including descriptors. + + Your subclass instances don't get instance dictionaries, but they + can have slots. + + >>> class MyProxy(ProxyBase): + ... __slots__ = 'x', 'y' + ... + ... def f(self): + ... return self.x + + >>> l = [1, 2, 3] + >>> p = MyProxy(l) + + I can use attributes defined by the class, including slots: + + >>> p.x = 'x' + >>> p.x + 'x' + >>> p.f() + 'x' + + I can also use attributes of the proxied object: + + >>> p + [1, 2, 3] + >>> p.pop() + 3 + >>> p + [1, 2] + + """ + +def test_get_descriptors_in_proxy_class(): + """ + A non-data descriptor in a proxy class doesn't hide an attribute on + a proxied object or prevent writing the attribute. + + >>> class ReadDescr(object): + ... def __get__(self, i, c): + ... return 'read' + + >>> class MyProxy(ProxyBase): + ... __slots__ = () + ... + ... z = ReadDescr() + ... q = ReadDescr() + + >>> class MyOb: + ... q = 1 + + >>> o = MyOb() + >>> p = MyProxy(o) + >>> p.q + 1 + + >>> p.z + 'read' + + >>> p.z = 1 + >>> o.z, p.z + (1, 1) + + """ + +def test_non_overridable(): + """ + Normally, methods defined in proxies are overridden by + methods of proxied objects. This applies to all non-data + descriptors. The non_overridable function can be used to + convert a non-data descriptor to a data descriptor that disallows + writes. This function can be used as a decorator to make functions + defined in proxy classes take precedence over functions defined + in proxied objects. + + + >>> class MyProxy(ProxyBase): + ... __slots__ = () + ... + ... @zope.proxy.non_overridable + ... def foo(self): + ... return 'MyProxy foo' + + >>> class MyOb: + ... def foo(self): + ... return 'MyOb foo' + + >>> o = MyOb() + >>> p = MyProxy(o) + >>> p.foo() + 'MyProxy foo' + + """ + +def test_setProxiedObject(): + """ + >>> from zope.proxy import ProxyBase + >>> from zope.proxy import setProxiedObject, getProxiedObject + + >>> class C(object): + ... pass + + >>> c1 = C() + >>> c2 = C() + + >>> p = ProxyBase(c1) + + `setProxiedObject()` allows us to change the object a proxy refers to, + returning the previous referent: + + >>> old = setProxiedObject(p, c2) + >>> old is c1 + True + + >>> getProxiedObject(p) is c2 + True + + The first argument to `setProxiedObject()` must be a proxy; other objects + cause it to raise an exception: + + >>> setProxiedObject(c1, None) + Traceback (most recent call last): + TypeError: setProxiedObject() argument 1 must be zope.proxy.ProxyBase, not C + + """ + +def test_suite(): + suite = unittest.makeSuite(ProxyTestCase) + suite.addTest(DocTestSuite()) + return suite + +if __name__ == "__main__": + runner = unittest.TextTestRunner(sys.stdout) + result = runner.run(test_suite()) + newerrs = len(result.errors) + len(result.failures) + sys.exit(newerrs and 1 or 0) diff -Nru zope3-3.4.0/src/zope/proxy/_zope_proxy_proxy.c zope3-3.5~bzr18/src/zope/proxy/_zope_proxy_proxy.c --- zope3-3.4.0/src/zope/proxy/_zope_proxy_proxy.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/_zope_proxy_proxy.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1144 @@ +/*############################################################################ +# +# Copyright (c) 2004 Zope Corporation 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 file is also used as a really extensive macro in + * ../container/_zope_container_contained.c. If you need to + * change this file, you need to "svn copy" it to ../container/. + * + * This approach is taken to allow the sources for the two packages + * to be compilable when the relative locations of these aren't + * related in the same way as they are in a checkout. + * + * This will be revisited in the future, but works for now. + */ + +#include "Python.h" +#include "modsupport.h" + +#define PROXY_MODULE +#include "proxy.h" + +static PyTypeObject ProxyType; + +#define Proxy_Check(wrapper) (PyObject_TypeCheck((wrapper), &ProxyType)) + +static PyObject * +empty_tuple = NULL; + + +/* + * Slot methods. + */ + +static PyObject * +wrap_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *result = NULL; + PyObject *object; + + if (PyArg_UnpackTuple(args, "__new__", 1, 1, &object)) { + if (kwds != NULL && PyDict_Size(kwds) != 0) { + PyErr_SetString(PyExc_TypeError, + "proxy.__new__ does not accept keyword args"); + return NULL; + } + result = PyType_GenericNew(type, args, kwds); + if (result != NULL) { + ProxyObject *wrapper = (ProxyObject *) result; + Py_INCREF(object); + wrapper->proxy_object = object; + } + } + return result; +} + +static int +wrap_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result = -1; + PyObject *object; + + if (PyArg_UnpackTuple(args, "__init__", 1, 1, &object)) { + ProxyObject *wrapper = (ProxyObject *)self; + if (kwds != NULL && PyDict_Size(kwds) != 0) { + PyErr_SetString(PyExc_TypeError, + "proxy.__init__ does not accept keyword args"); + return -1; + } + /* If the object in this proxy is not the one we + * received in args, replace it with the new one. + */ + if (wrapper->proxy_object != object) { + PyObject *temp = wrapper->proxy_object; + Py_INCREF(object); + wrapper->proxy_object = object; + Py_DECREF(temp); + } + result = 0; + } + return result; +} + +static int +wrap_traverse(PyObject *self, visitproc visit, void *arg) +{ + PyObject *ob = Proxy_GET_OBJECT(self); + if (ob != NULL) + return visit(ob, arg); + else + return 0; +} + +static int +wrap_clear(PyObject *self) +{ + ProxyObject *proxy = (ProxyObject *)self; + PyObject *temp = proxy->proxy_object; + + if (temp != NULL) { + proxy->proxy_object = NULL; + Py_DECREF(temp); + } + return 0; +} + +static PyObject * +wrap_richcompare(PyObject* self, PyObject* other, int op) +{ + if (Proxy_Check(self)) { + self = Proxy_GET_OBJECT(self); + } + else { + other = Proxy_GET_OBJECT(other); + } + return PyObject_RichCompare(self, other, op); +} + +static PyObject * +wrap_iter(PyObject *self) +{ + return PyObject_GetIter(Proxy_GET_OBJECT(self)); +} + +static PyObject * +wrap_iternext(PyObject *self) +{ + return PyIter_Next(Proxy_GET_OBJECT(self)); +} + +static void +wrap_dealloc(PyObject *self) +{ + (void) wrap_clear(self); + self->ob_type->tp_free(self); +} + +/* A variant of _PyType_Lookup that doesn't look in ProxyType. + * + * If argument search_wrappertype is nonzero, we can look in WrapperType. + */ +PyObject * +WrapperType_Lookup(PyTypeObject *type, PyObject *name) +{ + int i, n; + PyObject *mro, *res, *base, *dict; + + /* Look in tp_dict of types in MRO */ + mro = type->tp_mro; + + /* If mro is NULL, the type is either not yet initialized + by PyType_Ready(), or already cleared by type_clear(). + Either way the safest thing to do is to return NULL. */ + if (mro == NULL) + return NULL; + + assert(PyTuple_Check(mro)); + + n = PyTuple_GET_SIZE(mro) + - 1; /* We don't want to look at the last item, which is object. */ + + for (i = 0; i < n; i++) { + base = PyTuple_GET_ITEM(mro, i); + + if (((PyTypeObject *)base) != &ProxyType) { + if (PyClass_Check(base)) + dict = ((PyClassObject *)base)->cl_dict; + else { + assert(PyType_Check(base)); + dict = ((PyTypeObject *)base)->tp_dict; + } + assert(dict && PyDict_Check(dict)); + res = PyDict_GetItem(dict, name); + if (res != NULL) + return res; + } + } + return NULL; +} + + +static PyObject * +wrap_getattro(PyObject *self, PyObject *name) +{ + PyObject *wrapped; + PyObject *descriptor; + PyObject *res = NULL; + char *name_as_string; + int maybe_special_name; + +#ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_getattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return NULL; + } + else +#endif + if (!PyString_Check(name)){ + PyErr_SetString(PyExc_TypeError, "attribute name must be string"); + return NULL; + } + else + Py_INCREF(name); + + name_as_string = PyString_AS_STRING(name); + wrapped = Proxy_GET_OBJECT(self); + if (wrapped == NULL) { + PyErr_Format(PyExc_RuntimeError, + "object is NULL; requested to get attribute '%s'", + name_as_string); + goto finally; + } + + maybe_special_name = name_as_string[0] == '_' && name_as_string[1] == '_'; + + if (!(maybe_special_name && strcmp(name_as_string, "__class__") == 0)) { + + descriptor = WrapperType_Lookup(self->ob_type, name); + + if (descriptor != NULL) { + if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) + && descriptor->ob_type->tp_descr_get != NULL) { + + if (descriptor->ob_type->tp_descr_set == NULL) + { + res = PyObject_GetAttr(wrapped, name); + if (res != NULL) + goto finally; + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + goto finally; + } + + res = descriptor->ob_type->tp_descr_get( + descriptor, + self, + (PyObject *)self->ob_type); + } else { + Py_INCREF(descriptor); + res = descriptor; + } + goto finally; + } + } + res = PyObject_GetAttr(wrapped, name); + +finally: + Py_DECREF(name); + return res; +} + +static int +wrap_setattro(PyObject *self, PyObject *name, PyObject *value) +{ + PyObject *wrapped; + PyObject *descriptor; + int res = -1; + +#ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_setattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return -1; + } + else +#endif + if (!PyString_Check(name)){ + PyErr_SetString(PyExc_TypeError, "attribute name must be string"); + return -1; + } + else + Py_INCREF(name); + + descriptor = WrapperType_Lookup(self->ob_type, name); + if (descriptor != NULL + && PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) + && descriptor->ob_type->tp_descr_set != NULL) + { + res = descriptor->ob_type->tp_descr_set(descriptor, self, value); + goto finally; + } + + wrapped = Proxy_GET_OBJECT(self); + if (wrapped == NULL) { + PyErr_Format(PyExc_RuntimeError, + "object is NULL; requested to set attribute '%s'", + PyString_AS_STRING(name)); + goto finally; + } + res = PyObject_SetAttr(wrapped, name, value); + +finally: + Py_DECREF(name); + return res; +} + +static int +wrap_print(PyObject *wrapper, FILE *fp, int flags) +{ + return PyObject_Print(Proxy_GET_OBJECT(wrapper), fp, flags); +} + +static PyObject * +wrap_str(PyObject *wrapper) { + return PyObject_Str(Proxy_GET_OBJECT(wrapper)); +} + +static PyObject * +wrap_repr(PyObject *wrapper) +{ + return PyObject_Repr(Proxy_GET_OBJECT(wrapper)); +} + + +static int +wrap_compare(PyObject *wrapper, PyObject *v) +{ + return PyObject_Compare(Proxy_GET_OBJECT(wrapper), v); +} + +static long +wrap_hash(PyObject *self) +{ + return PyObject_Hash(Proxy_GET_OBJECT(self)); +} + +static PyObject * +wrap_call(PyObject *self, PyObject *args, PyObject *kw) +{ + if (kw) + return PyEval_CallObjectWithKeywords(Proxy_GET_OBJECT(self), + args, kw); + else + return PyObject_CallObject(Proxy_GET_OBJECT(self), args); +} + +/* + * Number methods + */ + +/* + * Number methods. + */ + +static PyObject * +call_int(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_int == NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to int"); + return NULL; + } + return nb->nb_int(self); +} + +static PyObject * +call_long(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_long == NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to long"); + return NULL; + } + return nb->nb_long(self); +} + +static PyObject * +call_float(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_float== NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to float"); + return NULL; + } + return nb->nb_float(self); +} + +static PyObject * +call_oct(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_oct== NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to oct"); + return NULL; + } + return nb->nb_oct(self); +} + +static PyObject * +call_hex(PyObject *self) +{ + PyNumberMethods *nb = self->ob_type->tp_as_number; + if (nb == NULL || nb->nb_hex == NULL) { + PyErr_SetString(PyExc_TypeError, + "object can't be converted to hex"); + return NULL; + } + return nb->nb_hex(self); +} + +static PyObject * +call_ipow(PyObject *self, PyObject *other) +{ + /* PyNumber_InPlacePower has three args. How silly. :-) */ + return PyNumber_InPlacePower(self, other, Py_None); +} + +typedef PyObject *(*function1)(PyObject *); + +static PyObject * +check1(ProxyObject *self, char *opname, function1 operation) +{ + PyObject *result = NULL; + + result = operation(Proxy_GET_OBJECT(self)); +#if 0 + if (result != NULL) + /* ??? create proxy for result? */ + ; +#endif + return result; +} + +static PyObject * +check2(PyObject *self, PyObject *other, + char *opname, char *ropname, binaryfunc operation) +{ + PyObject *result = NULL; + PyObject *object; + + if (Proxy_Check(self)) { + object = Proxy_GET_OBJECT(self); + result = operation(object, other); + } + else if (Proxy_Check(other)) { + object = Proxy_GET_OBJECT(other); + result = operation(self, object); + } + else { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } +#if 0 + if (result != NULL) + /* ??? create proxy for result? */ + ; +#endif + return result; +} + +static PyObject * +check2i(ProxyObject *self, PyObject *other, + char *opname, binaryfunc operation) +{ + PyObject *result = NULL; + PyObject *object = Proxy_GET_OBJECT(self); + + result = operation(object, other); + if (result == object) { + /* If the operation was really carried out inplace, + don't create a new proxy, but use the old one. */ + Py_INCREF(self); + Py_DECREF(object); + result = (PyObject *)self; + } +#if 0 + else if (result != NULL) + /* ??? create proxy for result? */ + ; +#endif + return result; +} + +#define UNOP(NAME, CALL) \ + static PyObject *wrap_##NAME(PyObject *self) \ + { return check1((ProxyObject *)self, "__"#NAME"__", CALL); } + +#define BINOP(NAME, CALL) \ + static PyObject *wrap_##NAME(PyObject *self, PyObject *other) \ + { return check2(self, other, "__"#NAME"__", "__r"#NAME"__", CALL); } + +#define INPLACE(NAME, CALL) \ + static PyObject *wrap_i##NAME(PyObject *self, PyObject *other) \ + { return check2i((ProxyObject *)self, other, "__i"#NAME"__", CALL); } + +BINOP(add, PyNumber_Add) +BINOP(sub, PyNumber_Subtract) +BINOP(mul, PyNumber_Multiply) +BINOP(div, PyNumber_Divide) +BINOP(mod, PyNumber_Remainder) +BINOP(divmod, PyNumber_Divmod) + +static PyObject * +wrap_pow(PyObject *self, PyObject *other, PyObject *modulus) +{ + PyObject *result = NULL; + PyObject *object; + + if (Proxy_Check(self)) { + object = Proxy_GET_OBJECT(self); + result = PyNumber_Power(object, other, modulus); + } + else if (Proxy_Check(other)) { + object = Proxy_GET_OBJECT(other); + result = PyNumber_Power(self, object, modulus); + } + else if (modulus != NULL && Proxy_Check(modulus)) { + object = Proxy_GET_OBJECT(modulus); + result = PyNumber_Power(self, other, modulus); + } + else { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return result; +} + +BINOP(lshift, PyNumber_Lshift) +BINOP(rshift, PyNumber_Rshift) +BINOP(and, PyNumber_And) +BINOP(xor, PyNumber_Xor) +BINOP(or, PyNumber_Or) + +static int +wrap_coerce(PyObject **p_self, PyObject **p_other) +{ + PyObject *self = *p_self; + PyObject *other = *p_other; + PyObject *object; + PyObject *left; + PyObject *right; + int r; + + assert(Proxy_Check(self)); + object = Proxy_GET_OBJECT(self); + + left = object; + right = other; + r = PyNumber_CoerceEx(&left, &right); + if (r != 0) + return r; + /* Now left and right have been INCREF'ed. Any new value that + comes out is proxied; any unchanged value is left unchanged. */ + if (left == object) { + /* Keep the old proxy */ + Py_INCREF(self); + Py_DECREF(left); + left = self; + } +#if 0 + else { + /* ??? create proxy for left? */ + } + if (right != other) { + /* ??? create proxy for right? */ + } +#endif + *p_self = left; + *p_other = right; + return 0; +} + +UNOP(neg, PyNumber_Negative) +UNOP(pos, PyNumber_Positive) +UNOP(abs, PyNumber_Absolute) +UNOP(invert, PyNumber_Invert) + +UNOP(int, call_int) +UNOP(long, call_long) +UNOP(float, call_float) +UNOP(oct, call_oct) +UNOP(hex, call_hex) + +INPLACE(add, PyNumber_InPlaceAdd) +INPLACE(sub, PyNumber_InPlaceSubtract) +INPLACE(mul, PyNumber_InPlaceMultiply) +INPLACE(div, PyNumber_InPlaceDivide) +INPLACE(mod, PyNumber_InPlaceRemainder) +INPLACE(pow, call_ipow) +INPLACE(lshift, PyNumber_InPlaceLshift) +INPLACE(rshift, PyNumber_InPlaceRshift) +INPLACE(and, PyNumber_InPlaceAnd) +INPLACE(xor, PyNumber_InPlaceXor) +INPLACE(or, PyNumber_InPlaceOr) + +BINOP(floordiv, PyNumber_FloorDivide) +BINOP(truediv, PyNumber_TrueDivide) +INPLACE(floordiv, PyNumber_InPlaceFloorDivide) +INPLACE(truediv, PyNumber_InPlaceTrueDivide) + +static int +wrap_nonzero(PyObject *self) +{ + return PyObject_IsTrue(Proxy_GET_OBJECT(self)); +} + +/* + * Sequence methods + */ + +static Py_ssize_t +wrap_length(PyObject *self) +{ + return PyObject_Length(Proxy_GET_OBJECT(self)); +} + +static PyObject * +wrap_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end) +{ + PyObject *obj = Proxy_GET_OBJECT(self); + if (PyList_Check(obj)) { + return PyList_GetSlice(obj, start, end); + } + else if (PyTuple_Check(obj)) { + return PyTuple_GetSlice(obj, start, end); + } + else { + return PySequence_GetSlice(obj, start, end); + } +} + +static int +wrap_ass_slice(PyObject *self, Py_ssize_t i, Py_ssize_t j, PyObject *value) +{ + PyObject *obj = Proxy_GET_OBJECT(self); + if (PyList_Check(obj)) { + return PyList_SetSlice(obj, i, j, value); + } + else { + return PySequence_SetSlice(obj, i, j, value); + } +} + +static int +wrap_contains(PyObject *self, PyObject *value) +{ + return PySequence_Contains(Proxy_GET_OBJECT(self), value); +} + +/* + * Mapping methods + */ + +static PyObject * +wrap_getitem(PyObject *wrapper, PyObject *v) { + return PyObject_GetItem(Proxy_GET_OBJECT(wrapper), v); +} + +static int +wrap_setitem(PyObject *self, PyObject *key, PyObject *value) +{ + if (value == NULL) + return PyObject_DelItem(Proxy_GET_OBJECT(self), key); + else + return PyObject_SetItem(Proxy_GET_OBJECT(self), key, value); +} + +/* + * Normal methods + */ + +static char +reduce__doc__[] = +"__reduce__()\n" +"Raise an exception; this prevents proxies from being picklable by\n" +"default, even if the underlying object is picklable."; + +static PyObject * +wrap_reduce(PyObject *self) +{ + PyObject *pickle_error = NULL; + PyObject *pickle = PyImport_ImportModule("pickle"); + + if (pickle == NULL) + PyErr_Clear(); + else { + pickle_error = PyObject_GetAttrString(pickle, "PicklingError"); + if (pickle_error == NULL) + PyErr_Clear(); + } + if (pickle_error == NULL) { + pickle_error = PyExc_RuntimeError; + Py_INCREF(pickle_error); + } + PyErr_SetString(pickle_error, + "proxy instances cannot be pickled"); + Py_DECREF(pickle_error); + return NULL; +} + +static PyNumberMethods +wrap_as_number = { + wrap_add, /* nb_add */ + wrap_sub, /* nb_subtract */ + wrap_mul, /* nb_multiply */ + wrap_div, /* nb_divide */ + wrap_mod, /* nb_remainder */ + wrap_divmod, /* nb_divmod */ + wrap_pow, /* nb_power */ + wrap_neg, /* nb_negative */ + wrap_pos, /* nb_positive */ + wrap_abs, /* nb_absolute */ + wrap_nonzero, /* nb_nonzero */ + wrap_invert, /* nb_invert */ + wrap_lshift, /* nb_lshift */ + wrap_rshift, /* nb_rshift */ + wrap_and, /* nb_and */ + wrap_xor, /* nb_xor */ + wrap_or, /* nb_or */ + wrap_coerce, /* nb_coerce */ + wrap_int, /* nb_int */ + wrap_long, /* nb_long */ + wrap_float, /* nb_float */ + wrap_oct, /* nb_oct */ + wrap_hex, /* nb_hex */ + + /* Added in release 2.0 */ + /* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */ + wrap_iadd, /* nb_inplace_add */ + wrap_isub, /* nb_inplace_subtract */ + wrap_imul, /* nb_inplace_multiply */ + wrap_idiv, /* nb_inplace_divide */ + wrap_imod, /* nb_inplace_remainder */ + (ternaryfunc)wrap_ipow, /* nb_inplace_power */ + wrap_ilshift, /* nb_inplace_lshift */ + wrap_irshift, /* nb_inplace_rshift */ + wrap_iand, /* nb_inplace_and */ + wrap_ixor, /* nb_inplace_xor */ + wrap_ior, /* nb_inplace_or */ + + /* Added in release 2.2 */ + /* These require the Py_TPFLAGS_HAVE_CLASS flag */ + wrap_floordiv, /* nb_floor_divide */ + wrap_truediv, /* nb_true_divide */ + wrap_ifloordiv, /* nb_inplace_floor_divide */ + wrap_itruediv, /* nb_inplace_true_divide */ +}; + +static PySequenceMethods +wrap_as_sequence = { + wrap_length, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + wrap_slice, /* sq_slice */ + 0, /* sq_ass_item */ + wrap_ass_slice, /* sq_ass_slice */ + wrap_contains, /* sq_contains */ +}; + +static PyMappingMethods +wrap_as_mapping = { + wrap_length, /* mp_length */ + wrap_getitem, /* mp_subscript */ + wrap_setitem, /* mp_ass_subscript */ +}; + +static PyMethodDef +wrap_methods[] = { + {"__reduce__", (PyCFunction)wrap_reduce, METH_NOARGS, reduce__doc__}, + {NULL, NULL}, +}; + +/* + * Note that the numeric methods are not supported. This is primarily + * because of the way coercion-less operations are performed with + * new-style numbers; since we can't tell which side of the operation + * is 'self', we can't ensure we'd unwrap the right thing to perform + * the actual operation. We also can't afford to just unwrap both + * sides the way weakrefs do, since we don't know what semantics will + * be associated with the wrapper itself. + */ + +statichere PyTypeObject +ProxyType = { + PyObject_HEAD_INIT(NULL) /* PyObject_HEAD_INIT(&PyType_Type) */ + 0, + "zope.proxy.ProxyBase", + sizeof(ProxyObject), + 0, + wrap_dealloc, /* tp_dealloc */ + wrap_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + wrap_compare, /* tp_compare */ + wrap_repr, /* tp_repr */ + &wrap_as_number, /* tp_as_number */ + &wrap_as_sequence, /* tp_as_sequence */ + &wrap_as_mapping, /* tp_as_mapping */ + wrap_hash, /* tp_hash */ + wrap_call, /* tp_call */ + wrap_str, /* tp_str */ + wrap_getattro, /* tp_getattro */ + wrap_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + wrap_traverse, /* tp_traverse */ + wrap_clear, /* tp_clear */ + wrap_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + wrap_iter, /* tp_iter */ + wrap_iternext, /* tp_iternext */ + wrap_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + wrap_init, /* tp_init */ + 0, /* tp_alloc */ + wrap_new, /* tp_new */ + 0, /*_PyObject_GC_Del,*/ /* tp_free */ +}; + +static PyObject * +create_proxy(PyObject *object) +{ + PyObject *result = NULL; + PyObject *args; + + args = PyTuple_New(1); + if (args != NULL) { + Py_INCREF(object); + PyTuple_SET_ITEM(args, 0, object); + result = PyObject_CallObject((PyObject *)&ProxyType, args); + Py_DECREF(args); + } + return result; +} + +static int +api_check(PyObject *obj) +{ + return obj ? Proxy_Check(obj) : 0; +} + +static PyObject * +api_create(PyObject *object) +{ + if (object == NULL) { + PyErr_SetString(PyExc_ValueError, + "cannot create proxy around NULL"); + return NULL; + } + return create_proxy(object); +} + +static PyObject * +api_getobject(PyObject *proxy) +{ + if (proxy == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "cannot pass NULL to ProxyAPI.getobject()"); + return NULL; + } + if (Proxy_Check(proxy)) + return Proxy_GET_OBJECT(proxy); + else { + PyErr_Format(PyExc_TypeError, "expected proxy object, got %s", + proxy->ob_type->tp_name); + return NULL; + } +} + +static ProxyInterface +wrapper_capi = { + &ProxyType, + api_check, + api_create, + api_getobject, +}; + +static PyObject *api_object = NULL; + + +static char +getobject__doc__[] = +"getProxiedObject(proxy) --> object\n" +"\n" +"Get the underlying object for proxy, or the object itself, if it is\n" +"not a proxy."; + +static PyObject * +wrapper_getobject(PyObject *unused, PyObject *obj) +{ + if (Proxy_Check(obj)) + obj = Proxy_GET_OBJECT(obj); + + if (obj == NULL) + obj = Py_None; + + Py_INCREF(obj); + return obj; +} + +static char +setobject__doc__[] = +"setProxiedObject(proxy, object) --> object\n" +"\n" +"Set the underlying object for proxy, returning the old proxied object.\n" +"Raises TypeError if proxy is not a proxy.\n"; + +static PyObject * +wrapper_setobject(PyObject *unused, PyObject *args) +{ + PyObject *proxy; + PyObject *object; + PyObject *result = NULL; + if (PyArg_ParseTuple(args, "O!O:setProxiedObject", + &ProxyType, &proxy, &object)) { + result = Proxy_GET_OBJECT(proxy); + Py_INCREF(object); + ((ProxyObject *) proxy)->proxy_object = object; + } + return result; +} + +static char +isProxy__doc__[] = +"Check whether the given object is a proxy\n" +"\n" +"If proxytype is not None, checkes whether the object is\n" +"proxied by the given proxytype.\n" +; + +static PyObject * +wrapper_isProxy(PyObject *unused, PyObject *args) +{ + PyObject *obj, *result; + PyTypeObject *proxytype=&ProxyType; + + if (! PyArg_ParseTuple(args, "O|O!:isProxy", + &obj, &PyType_Type, &proxytype) + ) + return NULL; + + while (obj && Proxy_Check(obj)) + { + if (PyObject_TypeCheck(obj, proxytype)) + { + result = Py_True; + Py_INCREF(result); + return result; + } + obj = Proxy_GET_OBJECT(obj); + } + result = Py_False; + Py_INCREF(result); + return result; +} + +static char +removeAllProxies__doc__[] = +"removeAllProxies(proxy) --> object\n" +"\n" +"Get the proxied object with no proxies\n" +"\n" +"If obj is not a proxied object, return obj.\n" +"\n" +"The returned object has no proxies.\n" +; + +static PyObject * +wrapper_removeAllProxies(PyObject *unused, PyObject *obj) +{ + while (obj && Proxy_Check(obj)) + obj = Proxy_GET_OBJECT(obj); + + if (obj == NULL) + obj = Py_None; + + Py_INCREF(obj); + return obj; +} + +static char +sameProxiedObjects__doc__[] = +"Check whether two objects are the same or proxies of the same object"; + +static PyObject * +wrapper_sameProxiedObjects(PyObject *unused, PyObject *args) +{ + PyObject *ob1, *ob2; + + if (! PyArg_ParseTuple(args, "OO:sameProxiedObjects", &ob1, &ob2)) + return NULL; + + while (ob1 && Proxy_Check(ob1)) + ob1 = Proxy_GET_OBJECT(ob1); + + while (ob2 && Proxy_Check(ob2)) + ob2 = Proxy_GET_OBJECT(ob2); + + if (ob1 == ob2) + ob1 = Py_True; + else + ob1 = Py_False; + + Py_INCREF(ob1); + return ob1; +} + + +static char +queryProxy__doc__[] = +"Look for a proxy of the given type around the object\n" +"\n" +"If no such proxy can be found, return the default.\n" +; + +static PyObject * +wrapper_queryProxy(PyObject *unused, PyObject *args) +{ + PyObject *obj, *result=Py_None; + PyTypeObject *proxytype=&ProxyType; + + if (! PyArg_ParseTuple(args, "O|O!O:queryProxy", + &obj, &PyType_Type, &proxytype, &result) + ) + return NULL; + + while (obj && Proxy_Check(obj)) + { + if (PyObject_TypeCheck(obj, proxytype)) + { + Py_INCREF(obj); + return obj; + } + obj = Proxy_GET_OBJECT(obj); + } + + Py_INCREF(result); + return result; +} + +static char +queryInnerProxy__doc__[] = +"Look for the inner-most proxy of the given type around the object\n" +"\n" +"If no such proxy can be found, return the default.\n" +"\n" +"If there is such a proxy, return the inner-most one.\n" +; + +static PyObject * +wrapper_queryInnerProxy(PyObject *unused, PyObject *args) +{ + PyObject *obj, *result=Py_None; + PyTypeObject *proxytype=&ProxyType; + + if (! PyArg_ParseTuple(args, "O|O!O:queryInnerProxy", + &obj, &PyType_Type, &proxytype, &result) + ) + return NULL; + + while (obj && Proxy_Check(obj)) + { + if (PyObject_TypeCheck(obj, proxytype)) + result = obj; + obj = Proxy_GET_OBJECT(obj); + } + + Py_INCREF(result); + return result; +} + +static char +module___doc__[] = +"Association between an object, a context object, and a dictionary.\n\ +\n\ +The context object and dictionary give additional context information\n\ +associated with a reference to the basic object. The wrapper objects\n\ +act as proxies for the original object."; + + +static PyMethodDef +module_functions[] = { + {"getProxiedObject", wrapper_getobject, METH_O, getobject__doc__}, + {"setProxiedObject", wrapper_setobject, METH_VARARGS, setobject__doc__}, + {"isProxy", wrapper_isProxy, METH_VARARGS, isProxy__doc__}, + {"sameProxiedObjects", wrapper_sameProxiedObjects, METH_VARARGS, + sameProxiedObjects__doc__}, + {"queryProxy", wrapper_queryProxy, METH_VARARGS, queryProxy__doc__}, + {"queryInnerProxy", wrapper_queryInnerProxy, METH_VARARGS, + queryInnerProxy__doc__}, + {"removeAllProxies", wrapper_removeAllProxies, METH_O, + removeAllProxies__doc__}, + {NULL} +}; + +void +init_zope_proxy_proxy(void) +{ + PyObject *m = Py_InitModule3("_zope_proxy_proxy", + module_functions, module___doc__); + + if (m == NULL) + return; + + if (empty_tuple == NULL) + empty_tuple = PyTuple_New(0); + + ProxyType.tp_free = _PyObject_GC_Del; + + if (PyType_Ready(&ProxyType) < 0) + return; + + Py_INCREF(&ProxyType); + PyModule_AddObject(m, "ProxyBase", (PyObject *)&ProxyType); + + if (api_object == NULL) { + api_object = PyCObject_FromVoidPtr(&wrapper_capi, NULL); + if (api_object == NULL) + return; + } + Py_INCREF(api_object); + PyModule_AddObject(m, "_CAPI", api_object); +} diff -Nru zope3-3.4.0/src/zope/proxy/_zope_proxy_proxy.py zope3-3.5~bzr18/src/zope/proxy/_zope_proxy_proxy.py --- zope3-3.4.0/src/zope/proxy/_zope_proxy_proxy.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/proxy/_zope_proxy_proxy.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_zope_proxy_proxy.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/zope/ptresource/configure.zcml zope3-3.5~bzr18/src/zope/ptresource/configure.zcml --- zope3-3.4.0/src/zope/ptresource/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/ptresource/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/ptresource/ptresource.py zope3-3.5~bzr18/src/zope/ptresource/ptresource.py --- zope3-3.4.0/src/zope/ptresource/ptresource.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/ptresource/ptresource.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,94 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Page Template Resource + +$Id: ptresource.py 103139 2009-08-24 11:58:22Z nadako $ +""" + +from zope.interface import implements, classProvides +from zope.pagetemplate.engine import TrustedAppPT +from zope.pagetemplate.pagetemplatefile import PageTemplateFile +from zope.publisher.browser import BrowserView +from zope.publisher.interfaces import NotFound +from zope.publisher.interfaces.browser import IBrowserPublisher + +from zope.browserresource.resource import Resource +from zope.browserresource.interfaces import IResourceFactory +from zope.browserresource.interfaces import IResourceFactoryFactory + +class PageTemplate(TrustedAppPT, PageTemplateFile): + """ + Resource that is a page template + """ + + def __init__(self, filename, _prefix=None, content_type=None): + _prefix = self.get_path_from_prefix(_prefix) + super(PageTemplate, self).__init__(filename, _prefix) + if content_type is not None: + self.content_type = content_type + + def pt_getContext(self, request, **kw): + namespace = super(PageTemplate, self).pt_getContext(**kw) + namespace['context'] = None + namespace['request'] = request + return namespace + + def __call__(self, request, **keywords): + namespace = self.pt_getContext( + request=request, + options=keywords + ) + return self.pt_render(namespace) + +class PageTemplateResource(BrowserView, Resource): + + implements(IBrowserPublisher) + + def publishTraverse(self, request, name): + '''See interface IBrowserPublisher''' + raise NotFound(None, name) + + def browserDefault(self, request): + '''See interface IBrowserPublisher''' + return getattr(self, request.method), () + + def HEAD(self): + pt = self.context + response = self.request.response + if not response.getHeader("Content-Type"): + response.setHeader("Content-Type", pt.content_type) + return '' + + def GET(self): + pt = self.context + response = self.request.response + if not response.getHeader("Content-Type"): + response.setHeader("Content-Type", pt.content_type) + return pt(self.request) + +class PageTemplateResourceFactory(object): + + implements(IResourceFactory) + classProvides(IResourceFactoryFactory) + + def __init__(self, path, checker, name): + self.__pt = PageTemplate(path) + self.__checker = checker + self.__name = name + + def __call__(self, request): + resource = PageTemplateResource(self.__pt, request) + resource.__Security_checker__ = self.__checker + resource.__name__ = self.__name + return resource diff -Nru zope3-3.4.0/src/zope/ptresource/tests.py zope3-3.5~bzr18/src/zope/ptresource/tests.py --- zope3-3.4.0/src/zope/ptresource/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/ptresource/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,83 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Page Template based Resources Test + +$Id: tests.py 103139 2009-08-24 11:58:22Z nadako $ +""" +import os +import tempfile +import unittest + +from zope.component import provideAdapter +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces import NotFound +from zope.security.checker import NamesChecker +from zope.testing import cleanup +from zope.traversing.adapters import DefaultTraversable +from zope.traversing.interfaces import ITraversable + +from zope.ptresource.ptresource import PageTemplateResourceFactory + + +checker = NamesChecker(('__call__', 'request', 'publishTraverse')) + + +class Test(cleanup.CleanUp, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + provideAdapter(DefaultTraversable, (None,), ITraversable) + + def createTestFile(self, contents): + fd, path = tempfile.mkstemp() + os.close(fd) + open(path, 'w').write(contents) + return path + + def testNoTraversal(self): + path = self.createTestFile('

test

') + request = TestRequest() + factory = PageTemplateResourceFactory(path, checker, 'test.pt') + resource = factory(request) + self.assertRaises(NotFound, resource.publishTraverse, + resource.request, ()) + os.unlink(path) + + def testBrowserDefault(self): + path = self.createTestFile( + '') + test_data = "Foobar" + request = TestRequest(test_data=test_data) + factory = PageTemplateResourceFactory(path, checker, 'testresource.pt') + resource = factory(request) + view, next = resource.browserDefault(request) + self.assertEquals(view(), + '%s' % test_data) + self.assertEquals('text/html', + request.response.getHeader('Content-Type')) + self.assertEquals(next, ()) + + request = TestRequest(test_data=test_data, REQUEST_METHOD='HEAD') + resource = factory(request) + view, next = resource.browserDefault(request) + self.assertEquals(view(), '') + self.assertEquals('text/html', + request.response.getHeader('Content-Type')) + self.assertEquals(next, ()) + + os.unlink(path) + + +def test_suite(): + return unittest.makeSuite(Test) diff -Nru zope3-3.4.0/src/zope/publisher/base.py zope3-3.5~bzr18/src/zope/publisher/base.py --- zope3-3.4.0/src/zope/publisher/base.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/publisher/base.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,487 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Base implementations of the Publisher objects + +Specifically, 'BaseRequest', 'BaseResponse', and 'DefaultPublication' are +specified here. + +$Id: base.py 109239 2010-02-21 23:53:32Z sidnei $ +""" +from cStringIO import StringIO + +from zope.interface import implements, providedBy +from zope.interface.common.mapping import IReadMapping, IEnumerableMapping +from zope.exceptions.exceptionformatter import print_exception +from zope.security.proxy import removeSecurityProxy + +from zope.publisher.interfaces import IPublication, IHeld +from zope.publisher.interfaces import NotFound, DebugError, Unauthorized +from zope.publisher.interfaces import IRequest, IResponse, IDebugFlags +from zope.publisher.publish import mapply + +_marker = object() + +class BaseResponse(object): + """Base Response Class + """ + + __slots__ = ( + '_result', # The result of the application call + '_request', # The associated request (if any) + ) + + implements(IResponse) + + def __init__(self): + self._request = None + + def setResult(self, result): + 'See IPublisherResponse' + self._result = result + + def handleException(self, exc_info): + 'See IPublisherResponse' + f = StringIO() + print_exception( + exc_info[0], exc_info[1], exc_info[2], 100, f) + self.setResult(f.getvalue()) + + def internalError(self): + 'See IPublisherResponse' + pass + + def reset(self): + 'See IPublisherResponse' + pass + + def retry(self): + 'See IPublisherResponse' + return self.__class__() + +class RequestDataGetter(object): + + implements(IReadMapping) + + def __init__(self, request): + self.__get = getattr(request, self._gettrname) + + def __getitem__(self, name): + return self.__get(name) + + def get(self, name, default=None): + return self.__get(name, default) + + def __contains__(self, key): + lookup = self.get(key, self) + return lookup is not self + + has_key = __contains__ + +class RequestDataMapper(object): + + implements(IEnumerableMapping) + + def __init__(self, request): + self.__map = getattr(request, self._mapname) + + def __getitem__(self, name): + return self.__map[name] + + def get(self, name, default=None): + return self.__map.get(name, default) + + def __contains__(self, key): + lookup = self.get(key, self) + return lookup is not self + + has_key = __contains__ + + def keys(self): + return self.__map.keys() + + def __iter__(self): + return iter(self.keys()) + + def items(self): + return self.__map.items() + + def values(self): + return self.__map.values() + + def __len__(self): + return len(self.__map) + +class RequestDataProperty(object): + + def __init__(self, gettr_class): + self.__gettr_class = gettr_class + + def __get__(self, request, rclass=None): + if request is not None: + return self.__gettr_class(request) + + def __set__(*args): + raise AttributeError('Unassignable attribute') + + +class RequestEnvironment(RequestDataMapper): + _mapname = '_environ' + + +class DebugFlags(object): + """Debugging flags.""" + + implements(IDebugFlags) + + sourceAnnotations = False + showTAL = False + + +class BaseRequest(object): + """Represents a publishing request. + + This object provides access to request data. Request data may + vary depending on the protocol used. + + Request objects are created by the object publisher and will be + passed to published objects through the argument name, REQUEST. + + The request object is a mapping object that represents a + collection of variable to value mappings. + """ + + implements(IRequest) + + __slots__ = ( + '__provides__', # Allow request to directly provide interfaces + '_held', # Objects held until the request is closed + '_traversed_names', # The names that have been traversed + '_last_obj_traversed', # Object that was traversed last + '_traversal_stack', # Names to be traversed, in reverse order + '_environ', # The request environment variables + '_response', # The response + '_args', # positional arguments + '_body_instream', # input stream + '_body', # The request body as a string + '_publication', # publication object + '_principal', # request principal, set by publication + 'interaction', # interaction, set by interaction + 'debug', # debug flags + 'annotations', # per-package annotations + ) + + environment = RequestDataProperty(RequestEnvironment) + + def __init__(self, body_instream, environ, response=None, + positional=None): + self._traversal_stack = [] + self._last_obj_traversed = None + self._traversed_names = [] + self._environ = environ + + self._args = positional or () + + if response is None: + self._response = self._createResponse() + else: + self._response = response + + self._response._request = self + + self._body_instream = body_instream + self._held = () + self._principal = None + self.debug = DebugFlags() + self.interaction = None + self.annotations = {} + + def setPrincipal(self, principal): + self._principal = principal + + principal = property(lambda self: self._principal) + + def _getPublication(self): + 'See IPublisherRequest' + return getattr(self, '_publication', None) + + publication = property(_getPublication) + + def processInputs(self): + 'See IPublisherRequest' + # Nothing to do here + + def retry(self): + 'See IPublisherRequest' + raise TypeError('Retry is not supported') + + def setPublication(self, pub): + 'See IPublisherRequest' + self._publication = pub + + def supportsRetry(self): + 'See IPublisherRequest' + return 0 + + def traverse(self, obj): + 'See IPublisherRequest' + + publication = self.publication + + traversal_stack = self._traversal_stack + traversed_names = self._traversed_names + + prev_object = None + while True: + + self._last_obj_traversed = obj + + if removeSecurityProxy(obj) is not removeSecurityProxy(prev_object): + # Invoke hooks (but not more than once). + publication.callTraversalHooks(self, obj) + + if not traversal_stack: + # Finished traversal. + break + + prev_object = obj + + # Traverse to the next step. + entry_name = traversal_stack.pop() + traversed_names.append(entry_name) + obj = publication.traverseName(self, obj, entry_name) + + return obj + + def close(self): + 'See IPublicationRequest' + + for held in self._held: + if IHeld.providedBy(held): + held.release() + + self._held = None + self._body_instream = None + self._publication = None + + def getPositionalArguments(self): + 'See IPublicationRequest' + return self._args + + def _getResponse(self): + return self._response + + response = property(_getResponse) + + def getTraversalStack(self): + 'See IPublicationRequest' + return list(self._traversal_stack) # Return a copy + + def hold(self, object): + 'See IPublicationRequest' + self._held = self._held + (object,) + + def setTraversalStack(self, stack): + 'See IPublicationRequest' + self._traversal_stack[:] = list(stack) + + def _getBodyStream(self): + 'See zope.publisher.interfaces.IApplicationRequest' + return self._body_instream + + bodyStream = property(_getBodyStream) + + def __len__(self): + 'See Interface.Common.Mapping.IEnumerableMapping' + return len(self.keys()) + + def items(self): + 'See Interface.Common.Mapping.IEnumerableMapping' + result = [] + get = self.get + for k in self.keys(): + result.append((k, get(k))) + return result + + def keys(self): + 'See Interface.Common.Mapping.IEnumerableMapping' + return self._environ.keys() + + def __iter__(self): + return iter(self.keys()) + + def values(self): + 'See Interface.Common.Mapping.IEnumerableMapping' + result = [] + get = self.get + for k in self.keys(): + result.append(get(k)) + return result + + def __getitem__(self, key): + 'See Interface.Common.Mapping.IReadMapping' + result = self.get(key, _marker) + if result is _marker: + raise KeyError(key) + else: + return result + + def get(self, key, default=None): + 'See Interface.Common.Mapping.IReadMapping' + result = self._environ.get(key, _marker) + if result is not _marker: + return result + + return default + + def __contains__(self, key): + 'See Interface.Common.Mapping.IReadMapping' + lookup = self.get(key, self) + return lookup is not self + + has_key = __contains__ + + def _createResponse(self): + # Should be overridden by subclasses + return BaseResponse() + + def __nonzero__(self): + # This is here to avoid calling __len__ for boolean tests + return 1 + + def __str__(self): + L1 = self.items() + L1.sort() + return "\n".join(map(lambda item: "%s:\t%s" % item, L1)) + + def _setupPath_helper(self, attr): + path = self.get(attr, "/") + if path.endswith('/'): + # Remove trailing backslash, so that we will not get an empty + # last entry when splitting the path. + path = path[:-1] + self._endswithslash = True + else: + self._endswithslash = False + + clean = [] + for item in path.split('/'): + if not item or item == '.': + continue + elif item == '..': + # try to remove the last name + try: + del clean[-1] + except IndexError: + # the list of names was empty, so do nothing and let the + # string '..' be placed on the list + pass + clean.append(item) + + clean.reverse() + self.setTraversalStack(clean) + + self._path_suffix = None + +class TestRequest(BaseRequest): + + __slots__ = ('_presentation_type', ) + + def __init__(self, path, body_instream=None, environ=None): + + if environ is None: + environ = {} + + environ['PATH_INFO'] = path + if body_instream is None: + body_instream = StringIO('') + + super(TestRequest, self).__init__(body_instream, environ) + +class DefaultPublication(object): + """A stub publication. + + This works just like Zope2's ZPublisher. It rejects any name + starting with an underscore and any objects (specifically: method) + that doesn't have a docstring. + """ + implements(IPublication) + + require_docstrings = True + + def __init__(self, app): + self.app = app + + def beforeTraversal(self, request): + # Lop off leading and trailing empty names + stack = request.getTraversalStack() + while stack and not stack[-1]: + stack.pop() # toss a trailing empty name + while stack and not stack[0]: + stack.pop(0) # toss a leading empty name + request.setTraversalStack(stack) + + def getApplication(self, request): + return self.app + + def callTraversalHooks(self, request, ob): + pass + + def traverseName(self, request, ob, name, check_auth=1): + if name.startswith('_'): + raise Unauthorized(name) + if hasattr(ob, name): + subob = getattr(ob, name) + else: + try: + subob = ob[name] + except (KeyError, IndexError, + TypeError, AttributeError): + raise NotFound(ob, name, request) + if self.require_docstrings and not getattr(subob, '__doc__', None): + raise DebugError(subob, 'Missing or empty doc string') + return subob + + def getDefaultTraversal(self, request, ob): + return ob, () + + def afterTraversal(self, request, ob): + pass + + def callObject(self, request, ob): + return mapply(ob, request.getPositionalArguments(), request) + + def afterCall(self, request, ob): + pass + + def endRequest(self, request, ob): + pass + + def handleException(self, object, request, exc_info, retry_allowed=1): + # Let the response handle it as best it can. + request.response.reset() + request.response.handleException(exc_info) + + +class TestPublication(DefaultPublication): + + def traverseName(self, request, ob, name, check_auth=1): + if hasattr(ob, name): + subob = getattr(ob, name) + else: + try: + subob = ob[name] + except (KeyError, IndexError, + TypeError, AttributeError): + raise NotFound(ob, name, request) + return subob diff -Nru zope3-3.4.0/src/zope/publisher/browser.py zope3-3.5~bzr18/src/zope/publisher/browser.py --- zope3-3.4.0/src/zope/publisher/browser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/publisher/browser.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,971 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Browser-specific Publisher classes + +Here we define the specific 'BrowserRequest' and 'BrowserResponse' class. The +big improvement of the 'BrowserRequest' to 'HTTPRequest' is that is can handle +HTML form data and convert them into a Python-native format. Even file data is +packaged into a nice, Python-friendly 'FileUpload' object. + +$Id: browser.py 106689 2009-12-17 08:26:28Z ctheune $ +""" +__docformat__ = 'restructuredtext' + +import re +from types import ListType, TupleType, StringType +from cgi import FieldStorage +import tempfile + +import zope.component +import zope.interface +from zope.interface import implements, directlyProvides +from zope.i18n.interfaces import IUserPreferredLanguages +from zope.i18n.interfaces import IUserPreferredCharsets +from zope.i18n.interfaces import IModifiableUserPreferredLanguages +from zope.location import Location + +from zope.publisher.interfaces import NotFound +from zope.publisher.interfaces import IDefaultSkin +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.publisher.interfaces.browser import IBrowserApplicationRequest +from zope.publisher.interfaces.browser import IBrowserView +from zope.publisher.interfaces.browser import IBrowserPage +from zope.publisher.interfaces.browser import IBrowserSkinType +from zope.publisher.interfaces.http import IHTTPRequest +from zope.publisher.http import HTTPRequest, HTTPResponse + +# BBB imports, this compoennts get moved from this module +from zope.publisher.interfaces import ISkinType #BBB import +from zope.publisher.interfaces import ISkinChangedEvent #BBB import +from zope.publisher.skinnable import getDefaultSkin #BBB import +from zope.publisher.skinnable import setDefaultSkin #BBB import +from zope.publisher.skinnable import applySkin #BBB import +from zope.publisher.skinnable import SkinChangedEvent #BBB import + + +__ArrayTypes = (ListType, TupleType) + +start_of_header_search=re.compile('(]*>)', re.I).search +base_re_search=re.compile('()',re.I).search +isRelative = re.compile("[-_.!~*a-zA-z0-9'()@&=+$,]+(/|$)").match +newlines = re.compile('\r\n|\n\r|\r') + +def is_text_html(content_type): + return content_type.startswith('text/html') + +# Flag Constants +SEQUENCE = 1 +DEFAULT = 2 +RECORD = 4 +RECORDS = 8 +REC = RECORD | RECORDS +CONVERTED = 32 +DEFAULTABLE_METHODS = 'GET', 'POST', 'HEAD' + + +def field2string(v): + if hasattr(v, 'read'): + return v.read() + return str(v) + +def field2text(v, nl=newlines): + return nl.sub("\n", field2string(v)) + +def field2required(v): + v = field2string(v) + if not v.strip(): + raise ValueError('No input for required field

') + return v + +def field2int(v): + if isinstance(v, __ArrayTypes): + return map(field2int, v) + v = field2string(v) + if not v: + raise ValueError('Empty entry when integer expected') + try: + return int(v) + except ValueError: + raise ValueError("An integer was expected in the value '%s'" % v) + +def field2float(v): + if isinstance(v, __ArrayTypes): + return map(field2float, v) + v = field2string(v) + if not v: + raise ValueError( + 'Empty entry when floating-point number expected') + try: + return float(v) + except ValueError: + raise ValueError( + "A floating-point number was expected in the value '%s'" % v) + +def field2long(v): + if isinstance(v, __ArrayTypes): + return map(field2long, v) + v = field2string(v) + + # handle trailing 'L' if present. + if v and v[-1].upper() == 'L': + v = v[:-1] + if not v: + raise ValueError('Empty entry when integer expected') + try: + return long(v) + except ValueError: + raise ValueError("A long integer was expected in the value '%s'" % v) + +def field2tokens(v): + return field2string(v).split() + +def field2lines(v): + if isinstance(v, __ArrayTypes): + return [str(item) for item in v] + return field2text(v).splitlines() + +def field2boolean(v): + return bool(v) + +type_converters = { + 'float': field2float, + 'int': field2int, + 'long': field2long, + 'string': field2string, + 'required': field2required, + 'tokens': field2tokens, + 'lines': field2lines, + 'text': field2text, + 'boolean': field2boolean, + } + +get_converter = type_converters.get + +def registerTypeConverter(field_type, converter, replace=False): + """Add a custom type converter to the registry. + + o If 'replace' is not true, raise a KeyError if a converter is + already registered for 'field_type'. + """ + existing = type_converters.get(field_type) + + if existing is not None and not replace: + raise KeyError('Existing converter for field_type: %s' % field_type) + + type_converters[field_type] = converter + + +isCGI_NAME = { + # These fields are placed in request.environ instead of request.form. + 'SERVER_SOFTWARE' : 1, + 'SERVER_NAME' : 1, + 'GATEWAY_INTERFACE' : 1, + 'SERVER_PROTOCOL' : 1, + 'SERVER_PORT' : 1, + 'REQUEST_METHOD' : 1, + 'PATH_INFO' : 1, + 'PATH_TRANSLATED' : 1, + 'SCRIPT_NAME' : 1, + 'QUERY_STRING' : 1, + 'REMOTE_HOST' : 1, + 'REMOTE_ADDR' : 1, + 'AUTH_TYPE' : 1, + 'REMOTE_USER' : 1, + 'REMOTE_IDENT' : 1, + 'CONTENT_TYPE' : 1, + 'CONTENT_LENGTH' : 1, + 'SERVER_URL': 1, + }.has_key + +hide_key={ + 'HTTP_AUTHORIZATION':1, + 'HTTP_CGI_AUTHORIZATION': 1, + }.has_key + +class Record(object): + + _attrs = frozenset(('get', 'keys', 'items', 'values', 'copy', + 'has_key', '__contains__')) + + def __getattr__(self, key, default=None): + if key in self._attrs: + return getattr(self.__dict__, key) + raise AttributeError(key) + + def __getitem__(self, key): + return self.__dict__[key] + + def __str__(self): + items = self.__dict__.items() + items.sort() + return "{" + ", ".join(["%s: %s" % item for item in items]) + "}" + + def __repr__(self): + items = self.__dict__.items() + items.sort() + return ("{" + + ", ".join(["%s: %s" % (key, repr(value)) + for key, value in items]) + "}") + +_get_or_head = 'GET', 'HEAD' +class BrowserRequest(HTTPRequest): + implements(IBrowserRequest, IBrowserApplicationRequest) + + __slots__ = ( + '__provides__', # Allow request to directly provide interfaces + 'form', # Form data + 'charsets', # helper attribute + '__meth', + '__tuple_items', + '__defaults', + '__annotations__', + ) + + # Set this to True in a subclass to redirect GET requests when the + # effective and actual URLs differ. + use_redirect = False + + def __init__(self, body_instream, environ, response=None): + self.form = {} + self.charsets = None + super(BrowserRequest, self).__init__(body_instream, environ, response) + + + def _createResponse(self): + return BrowserResponse() + + def _decode(self, text): + """Try to decode the text using one of the available charsets.""" + if self.charsets is None: + envadapter = IUserPreferredCharsets(self) + self.charsets = envadapter.getPreferredCharsets() or ['utf-8'] + for charset in self.charsets: + try: + text = unicode(text, charset) + break + except UnicodeError: + pass + return text + + def processInputs(self): + 'See IPublisherRequest' + + if self.method not in _get_or_head: + # Process self.form if not a GET request. + fp = self._body_instream + if self.method == 'POST': + content_type = self._environ.get('CONTENT_TYPE') + if content_type and not ( + content_type.startswith('application/x-www-form-urlencoded') + or + content_type.startswith('multipart/') + ): + # for non-multi and non-form content types, FieldStorage + # consumes the body and we have no good place to put it. + # So we just won't call FieldStorage. :) + return + else: + fp = None + + # If 'QUERY_STRING' is not present in self._environ + # FieldStorage will try to get it from sys.argv[1] + # which is not what we need. + if 'QUERY_STRING' not in self._environ: + self._environ['QUERY_STRING'] = '' + + # The Python 2.6 cgi module mixes the query string and POST values + # together. We do not want this. + env = self._environ + if self.method == 'POST' and self._environ['QUERY_STRING']: + env = env.copy() + del env['QUERY_STRING'] + + + fs = ZopeFieldStorage(fp=fp, environ=env, + keep_blank_values=1) + + fslist = getattr(fs, 'list', None) + if fslist is not None: + self.__meth = None + self.__tuple_items = {} + self.__defaults = {} + + # process all entries in the field storage (form) + for item in fslist: + self.__processItem(item) + + if self.__defaults: + self.__insertDefaults() + + if self.__tuple_items: + self.__convertToTuples() + + if self.__meth: + self.setPathSuffix((self.__meth,)) + + _typeFormat = re.compile('([a-zA-Z][a-zA-Z0-9_]+|\\.[xy])$') + + def __processItem(self, item): + """Process item in the field storage.""" + + # Check whether this field is a file upload object + # Note: A field exists for files, even if no filename was + # passed in and no data was uploaded. Therefore we can only + # tell by the empty filename that no upload was made. + key = item.name + if (hasattr(item, 'file') and hasattr(item, 'filename') + and hasattr(item,'headers')): + if (item.file and + (item.filename is not None and item.filename != '' + # RFC 1867 says that all fields get a content-type. + # or 'content-type' in map(lower, item.headers.keys()) + )): + item = FileUpload(item) + else: + item = item.value + + flags = 0 + converter = None + + # Loop through the different types and set + # the appropriate flags + # Syntax: var_name:type_name + + # We'll search from the back to the front. + # We'll do the search in two steps. First, we'll + # do a string search, and then we'll check it with + # a re search. + + while key: + pos = key.rfind(":") + if pos < 0: + break + match = self._typeFormat.match(key, pos + 1) + if match is None: + break + + key, type_name = key[:pos], key[pos + 1:] + + # find the right type converter + c = get_converter(type_name, None) + + if c is not None: + converter = c + flags |= CONVERTED + elif type_name == 'list': + flags |= SEQUENCE + elif type_name == 'tuple': + self.__tuple_items[key] = 1 + flags |= SEQUENCE + elif (type_name == 'method' or type_name == 'action'): + if key: + self.__meth = key + else: + self.__meth = item + elif (type_name == 'default_method' + or type_name == 'default_action') and not self.__meth: + if key: + self.__meth = key + else: + self.__meth = item + elif type_name == 'default': + flags |= DEFAULT + elif type_name == 'record': + flags |= RECORD + elif type_name == 'records': + flags |= RECORDS + elif type_name == 'ignore_empty' and not item: + # skip over empty fields + return + + # Make it unicode if not None + if key is not None: + key = self._decode(key) + + if type(item) == StringType: + item = self._decode(item) + + if flags: + self.__setItemWithType(key, item, flags, converter) + else: + self.__setItemWithoutType(key, item) + + def __setItemWithoutType(self, key, item): + """Set item value without explicit type.""" + form = self.form + if key not in form: + form[key] = item + else: + found = form[key] + if isinstance(found, list): + found.append(item) + else: + form[key] = [found, item] + + def __setItemWithType(self, key, item, flags, converter): + """Set item value with explicit type.""" + #Split the key and its attribute + if flags & REC: + key, attr = self.__splitKey(key) + + # defer conversion + if flags & CONVERTED: + try: + item = converter(item) + except: + if item or flags & DEFAULT or key not in self.__defaults: + raise + item = self.__defaults[key] + if flags & RECORD: + item = getattr(item, attr) + elif flags & RECORDS: + item = getattr(item[-1], attr) + + # Determine which dictionary to use + if flags & DEFAULT: + form = self.__defaults + else: + form = self.form + + # Insert in dictionary + if key not in form: + if flags & SEQUENCE: + item = [item] + if flags & RECORD: + r = form[key] = Record() + setattr(r, attr, item) + elif flags & RECORDS: + r = Record() + setattr(r, attr, item) + form[key] = [r] + else: + form[key] = item + else: + r = form[key] + if flags & RECORD: + if not flags & SEQUENCE: + setattr(r, attr, item) + else: + if not hasattr(r, attr): + setattr(r, attr, [item]) + else: + getattr(r, attr).append(item) + elif flags & RECORDS: + last = r[-1] + if not hasattr(last, attr): + if flags & SEQUENCE: + item = [item] + setattr(last, attr, item) + else: + if flags & SEQUENCE: + getattr(last, attr).append(item) + else: + new = Record() + setattr(new, attr, item) + r.append(new) + else: + if isinstance(r, list): + r.append(item) + else: + form[key] = [r, item] + + def __splitKey(self, key): + """Split the key and its attribute.""" + i = key.rfind(".") + if i >= 0: + return key[:i], key[i + 1:] + return key, "" + + def __convertToTuples(self): + """Convert form values to tuples.""" + form = self.form + + for key in self.__tuple_items: + if key in form: + form[key] = tuple(form[key]) + else: + k, attr = self.__splitKey(key) + + # remove any type_names in the attr + i = attr.find(":") + if i >= 0: + attr = attr[:i] + + if k in form: + item = form[k] + if isinstance(item, Record): + if hasattr(item, attr): + setattr(item, attr, tuple(getattr(item, attr))) + else: + for v in item: + if hasattr(v, attr): + setattr(v, attr, tuple(getattr(v, attr))) + + def __insertDefaults(self): + """Insert defaults into form dictionary.""" + form = self.form + + for keys, values in self.__defaults.iteritems(): + if not keys in form: + form[keys] = values + else: + item = form[keys] + if isinstance(values, Record): + for k, v in values.items(): + if not hasattr(item, k): + setattr(item, k, v) + elif isinstance(values, list): + for val in values: + if isinstance(val, Record): + for k, v in val.items(): + for r in item: + if not hasattr(r, k): + setattr(r, k, v) + elif not val in item: + item.append(val) + + def traverse(self, obj): + 'See IPublisherRequest' + + ob = super(BrowserRequest, self).traverse(obj) + method = self.method + + base_needed = 0 + if self._path_suffix: + # We had a :method variable, so we need to set the base, + # but we don't look for default documents any more. + base_needed = 1 + redirect = 0 + elif method in DEFAULTABLE_METHODS: + # We need to check for default documents + publication = self.publication + + nsteps = 0 + ob, add_steps = publication.getDefaultTraversal(self, ob) + while add_steps: + nsteps += len(add_steps) + add_steps = list(add_steps) + add_steps.reverse() + self.setTraversalStack(add_steps) + ob = super(BrowserRequest, self).traverse(ob) + ob, add_steps = publication.getDefaultTraversal(self, ob) + + if nsteps != self._endswithslash: + base_needed = 1 + redirect = self.use_redirect and method == 'GET' + + + if base_needed: + url = self.getURL() + response = self.response + if redirect: + response.redirect(url) + return '' + elif not response.getBase(): + response.setBase(url) + + return ob + + def keys(self): + 'See Interface.Common.Mapping.IEnumerableMapping' + d = {} + d.update(self._environ) + d.update(self._cookies) + d.update(self.form) + return d.keys() + + + def get(self, key, default=None): + 'See Interface.Common.Mapping.IReadMapping' + marker = object() + result = self.form.get(key, marker) + if result is not marker: + return result + + return super(BrowserRequest, self).get(key, default) + +class ZopeFieldStorage(FieldStorage): + + def make_file(self, binary=None): + return tempfile.NamedTemporaryFile('w+b') + + +class FileUpload(object): + '''File upload objects + + File upload objects are used to represent file-uploaded data. + + File upload objects can be used just like files. + + In addition, they have a 'headers' attribute that is a dictionary + containing the file-upload headers, and a 'filename' attribute + containing the name of the uploaded file. + ''' + + def __init__(self, aFieldStorage): + + file = aFieldStorage.file + if hasattr(file, '__methods__'): + methods = file.__methods__ + else: + methods = ['close', 'fileno', 'flush', 'isatty', + 'read', 'readline', 'readlines', 'seek', + 'tell', 'truncate', 'write', 'writelines', + 'name'] + + d = self.__dict__ + for m in methods: + if hasattr(file,m): + d[m] = getattr(file,m) + + self.headers = aFieldStorage.headers + filename = unicode(aFieldStorage.filename, 'UTF-8') + # fix for IE full paths + filename = filename[filename.rfind('\\')+1:].strip() + self.filename = filename + +class RedirectingBrowserRequest(BrowserRequest): + """Browser requests that redirect when the actual and effective URLs differ + """ + + use_redirect = True + +class TestRequest(BrowserRequest): + """Browser request with a constructor convenient for testing + """ + + def __init__(self, body_instream=None, environ=None, form=None, + skin=None, **kw): + + _testEnv = { + 'SERVER_URL': 'http://127.0.0.1', + 'HTTP_HOST': '127.0.0.1', + 'CONTENT_LENGTH': '0', + 'GATEWAY_INTERFACE': 'TestFooInterface/1.0', + } + + if environ is not None: + _testEnv.update(environ) + + if kw: + _testEnv.update(kw) + if body_instream is None: + from StringIO import StringIO + body_instream = StringIO('') + + super(TestRequest, self).__init__(body_instream, _testEnv) + if form: + self.form.update(form) + + # Setup locale object + langs = BrowserLanguages(self).getPreferredLanguages() + from zope.i18n.locales import locales + if not langs or langs[0] == '': + self._locale = locales.getLocale(None, None, None) + else: + parts = (langs[0].split('-') + [None, None])[:3] + self._locale = locales.getLocale(*parts) + + if skin is not None: + directlyProvides(self, skin) + else: + directlyProvides(self, IDefaultBrowserLayer) + + + +class BrowserResponse(HTTPResponse): + """Browser response + """ + + __slots__ = ( + '_base', # The base href + ) + + def _implicitResult(self, body): + content_type = self.getHeader('content-type') + if content_type is None: + if isHTML(body): + content_type = 'text/html' + else: + content_type = 'text/plain' + self.setHeader('x-content-type-warning', 'guessed from content') + self.setHeader('content-type', content_type) + + body, headers = super(BrowserResponse, self)._implicitResult(body) + body = self.__insertBase(body) + # Update the Content-Length header to account for the inserted + # tag. + headers = [ + (name, value) for name, value in headers + if name != 'content-length' + ] + headers.append(('content-length', str(len(body)))) + return body, headers + + + def __insertBase(self, body): + # Only insert a base tag if content appears to be html. + content_type = self.getHeader('content-type', '') + if content_type and not is_text_html(content_type): + return body + + if self.getBase(): + if body: + match = start_of_header_search(body) + if match is not None: + index = match.start(0) + len(match.group(0)) + ibase = base_re_search(body) + if ibase is None: + # Make sure the base URL is not a unicode string. + base = str(self.getBase()) + body = ('%s\n\n%s' % + (body[:index], base, body[index:])) + return body + + def getBase(self): + return getattr(self, '_base', '') + + def setBase(self, base): + self._base = base + + def redirect(self, location, status=None, trusted=False): + base = getattr(self, '_base', '') + if base and isRelative(str(location)): + l = base.rfind('/') + if l >= 0: + base = base[:l+1] + else: + base += '/' + location = base + location + + # TODO: HTTP redirects must provide an absolute location, see + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30 + # So, what if location is relative and base is unknown? Uncomment + # the following and you'll see that it actually happens. + # + # if isRelative(str(location)): + # raise AssertionError('Cannot determine absolute location') + + return super(BrowserResponse, self).redirect(location, status, trusted) + + def reset(self): + super(BrowserResponse, self).reset() + self._base = '' + +def isHTML(str): + """Try to determine whether str is HTML or not.""" + s = str.lstrip().lower() + if s.startswith(''): + return True + if s.startswith(' + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/security/protectclass.py zope3-3.5~bzr18/src/zope/security/protectclass.py --- zope3-3.4.0/src/zope/security/protectclass.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/protectclass.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,89 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Make assertions about permissions needed to access class instances +attributes + +$Id: protectclass.py 95308 2009-01-28 13:03:16Z brandon_rhodes $ +""" +from zope.security.checker import defineChecker, getCheckerForInstancesOf +from zope.security.checker import Checker, CheckerPublic + + +def protectName(class_, name, permission): + """Set a permission on a particular name.""" + + checker = getCheckerForInstancesOf(class_) + if checker is None: + checker = Checker({}, {}) + defineChecker(class_, checker) + + if permission == 'zope.Public': + # Translate public permission to CheckerPublic + permission = CheckerPublic + + # We know a dictionary was used because we set it + protections = checker.get_permissions + protections[name] = permission + +def protectSetAttribute(class_, name, permission): + """Set a permission on a particular name.""" + + checker = getCheckerForInstancesOf(class_) + if checker is None: + checker = Checker({}, {}) + defineChecker(class_, checker) + + if permission == 'zope.Public': + # Translate public permission to CheckerPublic + permission = CheckerPublic + + # We know a dictionary was used because we set it + # Note however, that if a checker was created manually + # and the caller used say NamesChecker or MultiChecker, + # then set_permissions may be None here as Checker + # defaults a missing set_permissions parameter to None. + # Jim says this doensn't happens with the C version of the + # checkers because they use a 'shared dummy dict'. + protections = checker.set_permissions + protections[name] = permission + +def protectLikeUnto(class_, like_unto): + """Use the protections from like_unto for class_""" + + unto_checker = getCheckerForInstancesOf(like_unto) + if unto_checker is None: + return + + # We know a dictionary was used because we set it + # Note however, that if a checker was created manually + # and the caller used say NamesChecker or MultiChecker, + # then set_permissions may be None here as Checker + # defaults a missing set_permissions parameter to None. + # Jim says this doensn't happens with the C version of the + # checkers because they use a 'shared dummy dict'. + unto_get_protections = unto_checker.get_permissions + unto_set_protections = unto_checker.set_permissions + + checker = getCheckerForInstancesOf(class_) + if checker is None: + checker = Checker({}, {}) + defineChecker(class_, checker) + + get_protections = checker.get_permissions + for name in unto_get_protections: + get_protections[name] = unto_get_protections[name] + + set_protections = checker.set_permissions + for name in unto_set_protections: + set_protections[name] = unto_set_protections[name] diff -Nru zope3-3.4.0/src/zope/security/_proxy.c zope3-3.5~bzr18/src/zope/security/_proxy.c --- zope3-3.4.0/src/zope/security/_proxy.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/_proxy.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,1008 @@ +/***************************************************************************** +* +* Copyright (c) 2003, 2004 Zope Corporation 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. +* +****************************************************************************** +Security Proxy Implementation + +$Id: _proxy.c 102691 2009-08-11 21:32:45Z gary $ +*/ + +#include +#include "zope.proxy/proxy.h" + +static PyObject *__class__str = 0, *__name__str = 0, *__module__str = 0; + +#define DECLARE_STRING(N) static PyObject *str_##N + +DECLARE_STRING(__3pow__); +DECLARE_STRING(__call__); +DECLARE_STRING(check); +DECLARE_STRING(check_getattr); +DECLARE_STRING(check_setattr); +DECLARE_STRING(__cmp__); +DECLARE_STRING(__coerce__); +DECLARE_STRING(__contains__); +DECLARE_STRING(__delitem__); +DECLARE_STRING(__getitem__); +DECLARE_STRING(__getslice__); +DECLARE_STRING(__hash__); +DECLARE_STRING(__iter__); +DECLARE_STRING(__len__); +DECLARE_STRING(next); +DECLARE_STRING(__nonzero__); +DECLARE_STRING(op_abs); +DECLARE_STRING(op_add); +DECLARE_STRING(op_and); +DECLARE_STRING(op_div); +DECLARE_STRING(op_divmod); +DECLARE_STRING(op_float); +DECLARE_STRING(op_floordiv); +DECLARE_STRING(op_hex); +DECLARE_STRING(op_iadd); +DECLARE_STRING(op_iand); +DECLARE_STRING(op_idiv); +DECLARE_STRING(op_ifloordiv); +DECLARE_STRING(op_ilshift); +DECLARE_STRING(op_imod); +DECLARE_STRING(op_imul); +DECLARE_STRING(op_int); +DECLARE_STRING(op_invert); +DECLARE_STRING(op_ior); +DECLARE_STRING(op_ipow); +DECLARE_STRING(op_irshift); +DECLARE_STRING(op_isub); +DECLARE_STRING(op_itruediv); +DECLARE_STRING(op_ixor); +DECLARE_STRING(op_long); +DECLARE_STRING(op_lshift); +DECLARE_STRING(op_mod); +DECLARE_STRING(op_mul); +DECLARE_STRING(op_neg); +DECLARE_STRING(op_oct); +DECLARE_STRING(op_or); +DECLARE_STRING(op_pos); +DECLARE_STRING(op_radd); +DECLARE_STRING(op_rand); +DECLARE_STRING(op_rdiv); +DECLARE_STRING(op_rdivmod); +DECLARE_STRING(op_rfloordiv); +DECLARE_STRING(op_rlshift); +DECLARE_STRING(op_rmod); +DECLARE_STRING(op_rmul); +DECLARE_STRING(op_ror); +DECLARE_STRING(op_rrshift); +DECLARE_STRING(op_rshift); +DECLARE_STRING(op_rsub); +DECLARE_STRING(op_rtruediv); +DECLARE_STRING(op_rxor); +DECLARE_STRING(op_sub); +DECLARE_STRING(op_truediv); +DECLARE_STRING(op_xor); +DECLARE_STRING(__pow__); +DECLARE_STRING(proxy); +DECLARE_STRING(__repr__); +DECLARE_STRING(__rpow__); +DECLARE_STRING(__setitem__); +DECLARE_STRING(__setslice__); +DECLARE_STRING(__str__); + +typedef struct { + ProxyObject proxy; + PyObject *proxy_checker; +} SecurityProxy; + +#define CLEAR(O) if (O) {PyObject *t = O; O = 0; Py_DECREF(t); } + +#undef Proxy_Check +#define Proxy_Check(proxy) \ + PyObject_TypeCheck(proxy, &SecurityProxyType) + +static PyTypeObject SecurityProxyType; + +/* + * Machinery to call the checker. + */ + +static int +check(SecurityProxy *self, PyObject *meth, PyObject *name) +{ + PyObject *r; + + /* If the checker has __setitem__, we call it's slot rather than + calling check or check_getattr. Why? Because calling operator slots + is much faster than calling methods and security checks are done so + often that speed matters. So we have this hack of using + almost-arbitrary operations to represent methods that we call + alot. */ + if (self->proxy_checker->ob_type->tp_as_mapping != NULL + && self->proxy_checker->ob_type->tp_as_mapping->mp_ass_subscript != NULL + && meth != str_check_setattr) + return self->proxy_checker->ob_type->tp_as_mapping-> + mp_ass_subscript(self->proxy_checker, self->proxy.proxy_object, name); + + r = PyObject_CallMethodObjArgs(self->proxy_checker, meth, + self->proxy.proxy_object, name, + NULL); + if (r == NULL) + return -1; + + Py_DECREF(r); + return 0; +} + +/* If the checker has __getitem__, we call it's slot rather than + calling proxy. Why? Because calling operator slots + is much faster than calling methods and security checks are done so + often that speed matters. So we have this hack of using + almost-arbitrary operations to represent methods that we call + alot. */ +#define PROXY_RESULT(self, result) \ +if (result != NULL) { \ + PyObject *tmp; \ + if (self->proxy_checker->ob_type->tp_as_mapping != NULL \ + && self->proxy_checker->ob_type->tp_as_mapping->mp_subscript != NULL) \ + tmp = self->proxy_checker->ob_type->tp_as_mapping-> \ + mp_subscript(self->proxy_checker, result); \ + else \ + tmp = PyObject_CallMethodObjArgs(self->proxy_checker, str_proxy, \ + result, NULL); \ + Py_DECREF(result); \ + result = tmp; \ +} + +typedef PyObject *(*function1)(PyObject *); + +static PyObject * +check1(SecurityProxy *self, PyObject *opname, function1 operation) +{ + PyObject *result = NULL; + + if (check(self, str_check, opname) >= 0) { + result = operation(self->proxy.proxy_object); + PROXY_RESULT(self, result); + } + return result; +} + +static PyObject * +check2(PyObject *self, PyObject *other, + PyObject *opname, PyObject *ropname, binaryfunc operation) +{ + PyObject *result = NULL; + + if (Proxy_Check(self)) + { + if (check((SecurityProxy*)self, str_check, opname) >= 0) + { + result = operation(((SecurityProxy*)self)->proxy.proxy_object, + other); + PROXY_RESULT(((SecurityProxy*)self), result); + } + } + else if (Proxy_Check(other)) + { + if (check((SecurityProxy*)other, str_check, ropname) >= 0) + { + result = operation(self, + ((SecurityProxy*)other)->proxy.proxy_object); + + PROXY_RESULT(((SecurityProxy*)other), result); + } + } + else + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + return result; +} + +static PyObject * +check2i(SecurityProxy *self, PyObject *other, + PyObject *opname, binaryfunc operation) +{ + PyObject *result = NULL; + + if (check(self, str_check, opname) >= 0) + { + result = operation(self->proxy.proxy_object, other); + if (result == self->proxy.proxy_object) + { + /* If the operation was really carried out inplace, + don't create a new proxy, but use the old one. */ + Py_DECREF(result); + Py_INCREF((PyObject *)self); + result = (PyObject *)self; + } + else + PROXY_RESULT(self, result); + } + return result; +} + +#define UNOP(NAME, CALL) \ + static PyObject *proxy_##NAME(PyObject *self) \ + { return check1((SecurityProxy *)self, str_op_##NAME, CALL); } + +#define BINOP(NAME, CALL) \ + static PyObject *proxy_##NAME(PyObject *self, PyObject *other) \ + { return check2(self, other, str_op_##NAME, str_op_r##NAME, CALL); } + +#define INPLACE(NAME, CALL) \ + static PyObject *proxy_i##NAME(PyObject *self, PyObject *other) \ + { return check2i((SecurityProxy *)self, other, str_op_i##NAME, CALL); } + + +/* + * Slot methods. + */ + +static PyObject * +proxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"object", "checker", 0}; + SecurityProxy *self; + PyObject *object; + PyObject *checker; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "OO:_Proxy.__new__", kwlist, + &object, &checker)) + return NULL; + + if (checker == Py_None) + { + PyErr_SetString(PyExc_ValueError, "None passed as proxy checker"); + return NULL; + } + + self = (SecurityProxy *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + Py_INCREF(object); + Py_INCREF(checker); + self->proxy.proxy_object = object; + self->proxy_checker = checker; + return (PyObject *)self; +} + +/* This is needed to avoid calling the base class tp_init, which we + don't need. */ +static int +proxy_init(PyObject *self, PyObject *args, PyObject *kw) +{ + return 0; +} + +static int +proxy_clear(SecurityProxy *self) +{ + CLEAR(self->proxy_checker); + SecurityProxyType.tp_base->tp_clear((PyObject*)self); + return 0; +} + +static void +proxy_dealloc(SecurityProxy *self) +{ + proxy_clear(self); + SecurityProxyType.tp_base->tp_dealloc((PyObject*)self); +} + +static int +proxy_traverse(SecurityProxy *self, visitproc visit, void *arg) +{ + Py_VISIT(self->proxy.proxy_object); + Py_VISIT(self->proxy_checker); + return 0; +} + +static PyObject * +proxy_richcompare(SecurityProxy* self, PyObject* other, int op) +{ + PyObject *result = NULL; + + result = PyObject_RichCompare(self->proxy.proxy_object, other, op); + if (result == Py_True || result == Py_False) + return result; + PROXY_RESULT(self, result); + return result; +} + +static PyObject * +proxy_iter(SecurityProxy *self) +{ + PyObject *result = NULL; + + if (check(self, str_check, str___iter__) >= 0) + { + result = PyObject_GetIter(self->proxy.proxy_object); + PROXY_RESULT(self, result); + } + return result; +} + +static PyObject * +proxy_iternext(SecurityProxy *self) +{ + PyObject *result = NULL; + + if (check(self, str_check_getattr, str_next) >= 0) + { + result = PyIter_Next(self->proxy.proxy_object); + PROXY_RESULT(self, result); + } + return result; +} + +static PyObject * +proxy_getattro(SecurityProxy *self, PyObject *name) +{ + PyObject *result = NULL; + + if (check(self, str_check_getattr, name) >= 0) + { + result = PyObject_GetAttr(self->proxy.proxy_object, name); + PROXY_RESULT(self, result); + } + return result; +} + +static int +proxy_setattro(SecurityProxy *self, PyObject *name, PyObject *value) +{ + if (check(self, str_check_setattr, name) >= 0) + return PyObject_SetAttr(self->proxy.proxy_object, name, value); + return -1; +} + +static PyObject * +default_repr(PyObject *object) +{ + PyObject *klass, *name = 0, *module = 0, *result = 0; + char *sname, *smodule; + + klass = PyObject_GetAttr(object, __class__str); + if (klass == NULL) + return NULL; + + name = PyObject_GetAttr(klass, __name__str); + if (name == NULL) + goto err; + sname = PyString_AsString(name); + if (sname == NULL) + goto err; + + module = PyObject_GetAttr(klass, __module__str); + if (module != NULL) { + smodule = PyString_AsString(module); + if (smodule == NULL) + goto err; + result = PyString_FromFormat("", + smodule, sname, object); + } + else { + PyErr_Clear(); + result = PyString_FromFormat("", + sname, object); + } + + err: + Py_DECREF(klass); + Py_XDECREF(name); + Py_XDECREF(module); + + return result; +} + +static PyObject * +proxy_str(SecurityProxy *self) +{ + PyObject *result = NULL; + + if (check(self, str_check, str___str__) >= 0) + { + result = PyObject_Str(self->proxy.proxy_object); + } + else + { + PyErr_Clear(); + result = default_repr(self->proxy.proxy_object); + } + return result; +} + +static PyObject * +proxy_repr(SecurityProxy *self) +{ + PyObject *result = NULL; + + if (check(self, str_check, str___repr__) >= 0) { + result = PyObject_Repr(self->proxy.proxy_object); + } + else { + PyErr_Clear(); + result = default_repr(self->proxy.proxy_object); + } + return result; +} + +static int +proxy_compare(SecurityProxy *self, PyObject *other) +{ + return PyObject_Compare(self->proxy.proxy_object, other); +} + +static long +proxy_hash(SecurityProxy *self) +{ + return PyObject_Hash(self->proxy.proxy_object); +} + +static PyObject * +proxy_call(SecurityProxy *self, PyObject *args, PyObject *kwds) +{ + PyObject *result = NULL; + + if (check(self, str_check, str___call__) >= 0) + { + result = PyObject_Call(self->proxy.proxy_object, args, kwds); + PROXY_RESULT(self, result); + } + return result; +} + +/* + * Number methods. + */ + +#define NUMBER_METHOD(M) \ +static PyObject * \ +call_##M(PyObject *self) \ +{ \ + PyNumberMethods *nb = self->ob_type->tp_as_number; \ + if (nb == NULL || nb->nb_##M == NULL) { \ + PyErr_SetString(PyExc_TypeError, \ + "object can't be converted to " #M); \ + return NULL; \ + } \ + return nb->nb_##M(self); \ +} + +NUMBER_METHOD(int) +NUMBER_METHOD(long) +NUMBER_METHOD(float) +NUMBER_METHOD(oct) +NUMBER_METHOD(hex) + +static PyObject * +call_ipow(PyObject *self, PyObject *other) +{ + /* PyNumber_InPlacePower has three args. How silly. :-) */ + return PyNumber_InPlacePower(self, other, Py_None); +} + +BINOP(add, PyNumber_Add) +BINOP(sub, PyNumber_Subtract) +BINOP(mul, PyNumber_Multiply) +BINOP(div, PyNumber_Divide) +BINOP(mod, PyNumber_Remainder) +BINOP(divmod, PyNumber_Divmod) + +static PyObject * +proxy_pow(PyObject *self, PyObject *other, PyObject *modulus) +{ + PyObject *result = NULL; + + if (Proxy_Check(self)) + { + if (check((SecurityProxy*)self, str_check, str___pow__) >= 0) + { + result = PyNumber_Power(((SecurityProxy*)self)->proxy.proxy_object, + other, modulus); + PROXY_RESULT(((SecurityProxy*)self), result); + } + } + else if (Proxy_Check(other)) + { + if (check((SecurityProxy*)other, str_check, str___rpow__) >= 0) + { + result = PyNumber_Power(self, + ((SecurityProxy*)other)->proxy.proxy_object, + modulus); + PROXY_RESULT(((SecurityProxy*)other), result); + } + } + else if (modulus != NULL && Proxy_Check(modulus)) + { + if (check((SecurityProxy*)modulus, str_check, str___3pow__) >= 0) + { + result = PyNumber_Power(self, other, + ((SecurityProxy*)modulus)->proxy.proxy_object); + PROXY_RESULT(((SecurityProxy*)modulus), result); + } + } + else { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return result; +} + +BINOP(lshift, PyNumber_Lshift) +BINOP(rshift, PyNumber_Rshift) +BINOP(and, PyNumber_And) +BINOP(xor, PyNumber_Xor) +BINOP(or, PyNumber_Or) + +static int +proxy_coerce(PyObject **p_self, PyObject **p_other) +{ + PyObject *self = *p_self; + PyObject *other = *p_other; + + assert(Proxy_Check(self)); + + if (check((SecurityProxy*)self, str_check, str___coerce__) >= 0) + { + PyObject *left = ((SecurityProxy*)self)->proxy.proxy_object; + PyObject *right = other; + int r; + r = PyNumber_CoerceEx(&left, &right); + if (r != 0) + return r; + /* Now left and right have been INCREF'ed. + Any new value that comes out is proxied; + any unchanged value is left unchanged. */ + if (left == ((SecurityProxy*)self)->proxy.proxy_object) { + /* Keep the old proxy */ + Py_DECREF(left); + Py_INCREF(self); + left = self; + } + else { + PROXY_RESULT(((SecurityProxy*)self), left); + if (left == NULL) { + Py_DECREF(right); + return -1; + } + } + if (right != other) { + PROXY_RESULT(((SecurityProxy*)self), right); + if (right == NULL) { + Py_DECREF(left); + return -1; + } + } + *p_self = left; + *p_other = right; + return 0; + } + return -1; +} + +UNOP(neg, PyNumber_Negative) +UNOP(pos, PyNumber_Positive) +UNOP(abs, PyNumber_Absolute) + +static int +proxy_nonzero(PyObject *self) +{ + return PyObject_IsTrue(((SecurityProxy*)self)->proxy.proxy_object); +} + +UNOP(invert, PyNumber_Invert) +UNOP(int, call_int) +UNOP(long, call_long) +UNOP(float, call_float) +UNOP(oct, call_oct) +UNOP(hex, call_hex) + +INPLACE(add, PyNumber_InPlaceAdd) +INPLACE(sub, PyNumber_InPlaceSubtract) +INPLACE(mul, PyNumber_InPlaceMultiply) +INPLACE(div, PyNumber_InPlaceDivide) +INPLACE(mod, PyNumber_InPlaceRemainder) +INPLACE(pow, call_ipow) +INPLACE(lshift, PyNumber_InPlaceLshift) +INPLACE(rshift, PyNumber_InPlaceRshift) +INPLACE(and, PyNumber_InPlaceAnd) +INPLACE(xor, PyNumber_InPlaceXor) +INPLACE(or, PyNumber_InPlaceOr) + +BINOP(floordiv, PyNumber_FloorDivide) +BINOP(truediv, PyNumber_TrueDivide) +INPLACE(floordiv, PyNumber_InPlaceFloorDivide) +INPLACE(truediv, PyNumber_InPlaceTrueDivide) + +/* + * Sequence methods. + */ + +static Py_ssize_t +proxy_length(SecurityProxy *self) +{ + if (check(self, str_check, str___len__) >= 0) + return PyObject_Length(self->proxy.proxy_object); + return -1; +} + +/* sq_item and sq_ass_item may be called by PySequece_{Get,Set}Item(). */ +static PyObject *proxy_getitem(SecurityProxy *, PyObject *); +static int proxy_setitem(SecurityProxy *, PyObject *, PyObject *); + +static PyObject * +proxy_igetitem(SecurityProxy *self, Py_ssize_t i) +{ + PyObject *key = PyInt_FromLong(i); + PyObject *res = NULL; + + if (key != NULL) { + res = proxy_getitem(self, key); + Py_DECREF(key); + } + return res; +} + + +static int +proxy_isetitem(SecurityProxy *self, Py_ssize_t i, PyObject *value) +{ + PyObject *key = PyInt_FromLong(i); + int res = -1; + + if (key != NULL) { + res = proxy_setitem(self, key, value); + Py_DECREF(key); + } + return res; +} + +static PyObject * +proxy_slice(SecurityProxy *self, Py_ssize_t start, Py_ssize_t end) +{ + PyObject *result = NULL; + + if (check(self, str_check, str___getslice__) >= 0) { + result = PySequence_GetSlice(self->proxy.proxy_object, start, end); + PROXY_RESULT(self, result); + } + return result; +} + +static int +proxy_ass_slice(SecurityProxy *self, Py_ssize_t i, Py_ssize_t j, PyObject *value) +{ + if (check(self, str_check, str___setslice__) >= 0) + return PySequence_SetSlice(self->proxy.proxy_object, i, j, value); + return -1; +} + +static int +proxy_contains(SecurityProxy *self, PyObject *value) +{ + if (check(self, str_check, str___contains__) >= 0) + return PySequence_Contains(self->proxy.proxy_object, value); + return -1; +} + +/* + * Mapping methods. + */ + +static PyObject * +proxy_getitem(SecurityProxy *self, PyObject *key) +{ + PyObject *result = NULL; + + if (check(self, str_check, str___getitem__) >= 0) + { + result = PyObject_GetItem(self->proxy.proxy_object, key); + PROXY_RESULT(self, result); + } + return result; +} + +static int +proxy_setitem(SecurityProxy *self, PyObject *key, PyObject *value) +{ + if (value == NULL) { + if (check(self, str_check, str___delitem__) >= 0) + return PyObject_DelItem(self->proxy.proxy_object, key); + } + else { + if (check(self, str_check, str___setitem__) >= 0) + return PyObject_SetItem(self->proxy.proxy_object, key, value); + } + return -1; +} + +/* + * Normal methods. + */ + +static PyNumberMethods +proxy_as_number = { + proxy_add, /* nb_add */ + proxy_sub, /* nb_subtract */ + proxy_mul, /* nb_multiply */ + proxy_div, /* nb_divide */ + proxy_mod, /* nb_remainder */ + proxy_divmod, /* nb_divmod */ + proxy_pow, /* nb_power */ + proxy_neg, /* nb_negative */ + proxy_pos, /* nb_positive */ + proxy_abs, /* nb_absolute */ + proxy_nonzero, /* nb_nonzero */ + proxy_invert, /* nb_invert */ + proxy_lshift, /* nb_lshift */ + proxy_rshift, /* nb_rshift */ + proxy_and, /* nb_and */ + proxy_xor, /* nb_xor */ + proxy_or, /* nb_or */ + proxy_coerce, /* nb_coerce */ + proxy_int, /* nb_int */ + proxy_long, /* nb_long */ + proxy_float, /* nb_float */ + proxy_oct, /* nb_oct */ + proxy_hex, /* nb_hex */ + + /* Added in release 2.0 */ + /* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */ + proxy_iadd, /* nb_inplace_add */ + proxy_isub, /* nb_inplace_subtract */ + proxy_imul, /* nb_inplace_multiply */ + proxy_idiv, /* nb_inplace_divide */ + proxy_imod, /* nb_inplace_remainder */ + (ternaryfunc)proxy_ipow, /* nb_inplace_power */ + proxy_ilshift, /* nb_inplace_lshift */ + proxy_irshift, /* nb_inplace_rshift */ + proxy_iand, /* nb_inplace_and */ + proxy_ixor, /* nb_inplace_xor */ + proxy_ior, /* nb_inplace_or */ + + /* Added in release 2.2 */ + /* These require the Py_TPFLAGS_HAVE_CLASS flag */ + proxy_floordiv, /* nb_floor_divide */ + proxy_truediv, /* nb_true_divide */ + proxy_ifloordiv, /* nb_inplace_floor_divide */ + proxy_itruediv, /* nb_inplace_true_divide */ +}; + +static PySequenceMethods +proxy_as_sequence = { + (lenfunc)proxy_length, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (ssizeargfunc)proxy_igetitem, /* sq_item */ + (ssizessizeargfunc)proxy_slice, /* sq_slice */ + (ssizeobjargproc)proxy_isetitem, /* sq_ass_item */ + (ssizessizeobjargproc)proxy_ass_slice, /* sq_ass_slice */ + (objobjproc)proxy_contains, /* sq_contains */ +}; + +static PyMappingMethods +proxy_as_mapping = { + (lenfunc)proxy_length, /* mp_length */ + (binaryfunc)proxy_getitem, /* mp_subscript */ + (objobjargproc)proxy_setitem, /* mp_ass_subscript */ +}; + +static char proxy_doc[] = "\ +Security proxy class. Constructor: _Proxy(object, checker)\n\ +where 'object' is an arbitrary object, and 'checker' is an object\n\ +whose signature is described by the IChecker interface.\n\ +A checker should have the following methods:\n\ + check(object, operation) # operation is e.g. '__add__' or '__hash__'\n\ + check_getattr(object, name)\n\ + check_setattr(object, name)\n\ + proxy(object)\n\ +The check methods should raise an exception if the operation is\n\ +disallowed. The proxy method should return a proxy for the object\n\ +if one is needed, otherwise the object itself.\n\ +"; + +statichere PyTypeObject +SecurityProxyType = { + PyObject_HEAD_INIT(NULL) + 0, + "zope.security._proxy._Proxy", + sizeof(SecurityProxy), + 0, + (destructor)proxy_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)proxy_compare, /* tp_compare */ + (reprfunc)proxy_repr, /* tp_repr */ + &proxy_as_number, /* tp_as_number */ + &proxy_as_sequence, /* tp_as_sequence */ + &proxy_as_mapping, /* tp_as_mapping */ + (hashfunc)proxy_hash, /* tp_hash */ + (ternaryfunc)proxy_call, /* tp_call */ + (reprfunc)proxy_str, /* tp_str */ + (getattrofunc)proxy_getattro, /* tp_getattro */ + (setattrofunc)proxy_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + proxy_doc, /* tp_doc */ + (traverseproc)proxy_traverse, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)proxy_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)proxy_iter, /* tp_iter */ + (iternextfunc)proxy_iternext, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + proxy_init, /* tp_init */ + 0, /*PyType_GenericAlloc,*/ /* tp_alloc */ + proxy_new, /* tp_new */ + 0, /*_PyObject_GC_Del,*/ /* tp_free */ +}; + +static PyObject * +module_getChecker(PyObject *self, PyObject *arg) +{ + PyObject *result; + + if (!Proxy_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "getChecker argument must be a _Proxy"); + return NULL; + } + result = ((SecurityProxy*)arg)->proxy_checker; + Py_INCREF(result); + return result; +} + +static PyObject * +module_getObject(PyObject *self, PyObject *arg) +{ + PyObject *result; + + if (!Proxy_Check(arg)) + result = arg; + else + result = ((SecurityProxy*)arg)->proxy.proxy_object; + + Py_INCREF(result); + return result; +} + +static PyMethodDef +module_functions[] = { + {"getChecker", module_getChecker, METH_O, "get checker from proxy"}, + {"getObject", module_getObject, METH_O, + "Get the proxied object\n\nReturn the original object if not proxied."}, + {NULL} +}; + +static char +module___doc__[] = "Security proxy implementation."; + +void +init_proxy(void) +{ + PyObject *m; + + if (Proxy_Import() < 0) + return; + +#define INIT_STRING(S) \ +if((str_##S = PyString_InternFromString(#S)) == NULL) return +#define INIT_STRING_OP(S) \ +if((str_op_##S = PyString_InternFromString("__" #S "__")) == NULL) return + + INIT_STRING(__3pow__); + INIT_STRING(__call__); + INIT_STRING(check); + INIT_STRING(check_getattr); + INIT_STRING(check_setattr); + INIT_STRING(__cmp__); + INIT_STRING(__coerce__); + INIT_STRING(__contains__); + INIT_STRING(__delitem__); + INIT_STRING(__getitem__); + INIT_STRING(__getslice__); + INIT_STRING(__hash__); + INIT_STRING(__iter__); + INIT_STRING(__len__); + INIT_STRING(next); + INIT_STRING(__nonzero__); + INIT_STRING_OP(abs); + INIT_STRING_OP(add); + INIT_STRING_OP(and); + INIT_STRING_OP(div); + INIT_STRING_OP(divmod); + INIT_STRING_OP(float); + INIT_STRING_OP(floordiv); + INIT_STRING_OP(hex); + INIT_STRING_OP(iadd); + INIT_STRING_OP(iand); + INIT_STRING_OP(idiv); + INIT_STRING_OP(ifloordiv); + INIT_STRING_OP(ilshift); + INIT_STRING_OP(imod); + INIT_STRING_OP(imul); + INIT_STRING_OP(int); + INIT_STRING_OP(invert); + INIT_STRING_OP(ior); + INIT_STRING_OP(ipow); + INIT_STRING_OP(irshift); + INIT_STRING_OP(isub); + INIT_STRING_OP(itruediv); + INIT_STRING_OP(ixor); + INIT_STRING_OP(long); + INIT_STRING_OP(lshift); + INIT_STRING_OP(mod); + INIT_STRING_OP(mul); + INIT_STRING_OP(neg); + INIT_STRING_OP(oct); + INIT_STRING_OP(or); + INIT_STRING_OP(pos); + INIT_STRING_OP(radd); + INIT_STRING_OP(rand); + INIT_STRING_OP(rdiv); + INIT_STRING_OP(rdivmod); + INIT_STRING_OP(rfloordiv); + INIT_STRING_OP(rlshift); + INIT_STRING_OP(rmod); + INIT_STRING_OP(rmul); + INIT_STRING_OP(ror); + INIT_STRING_OP(rrshift); + INIT_STRING_OP(rshift); + INIT_STRING_OP(rsub); + INIT_STRING_OP(rtruediv); + INIT_STRING_OP(rxor); + INIT_STRING_OP(sub); + INIT_STRING_OP(truediv); + INIT_STRING_OP(xor); + INIT_STRING(__pow__); + INIT_STRING(proxy); + INIT_STRING(__repr__); + INIT_STRING(__rpow__); + INIT_STRING(__setitem__); + INIT_STRING(__setslice__); + INIT_STRING(__str__); + + + __class__str = PyString_FromString("__class__"); + if (! __class__str) return; + + __name__str = PyString_FromString("__name__"); + if (! __name__str) return; + + __module__str = PyString_FromString("__module__"); + if (! __module__str) return; + + SecurityProxyType.ob_type = &PyType_Type; + SecurityProxyType.tp_alloc = PyType_GenericAlloc; + SecurityProxyType.tp_free = _PyObject_GC_Del; + SecurityProxyType.tp_base = &ProxyType; + if (PyType_Ready(&SecurityProxyType) < 0) + return; + + m = Py_InitModule3("_proxy", module_functions, module___doc__); + if (m == NULL) + return; + + Py_INCREF(&SecurityProxyType); + PyModule_AddObject(m, "_Proxy", (PyObject *)&SecurityProxyType); +} diff -Nru zope3-3.4.0/src/zope/security/_proxy.py zope3-3.5~bzr18/src/zope/security/_proxy.py --- zope3-3.4.0/src/zope/security/_proxy.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/_proxy.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_proxy.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/zope/security/proxy.py zope3-3.5~bzr18/src/zope/security/proxy.py --- zope3-3.4.0/src/zope/security/proxy.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/proxy.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,84 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 functions for Proxies. + +$Id: proxy.py 97709 2009-03-09 16:19:10Z nadako $ +""" +__docformat__ = 'restructuredtext' + +from zope.security._proxy import getChecker, getObject +from zope.security._proxy import _Proxy as Proxy + +removeSecurityProxy = getObject + +# This import represents part of the API for this module +from zope.security.checker import ProxyFactory + +def getTestProxyItems(proxy): + """Try to get checker names and permissions for testing + + If this succeeds, a sorted sequence of items is returned, + otherwise, None is returned. + """ + checker = getChecker(proxy) + items = checker.get_permissions.items() + items.sort() + return items + + +builtin_isinstance = isinstance +def isinstance(object, cls): + """Test whether an object is an instance of a type. + + This works even if the object is security proxied: + + >>> class C1(object): + ... pass + + >>> c = C1() + >>> isinstance(c, C1) + True + + >>> from zope.security.checker import ProxyFactory + >>> isinstance(ProxyFactory(c), C1) + True + + >>> class C2(C1): + ... pass + + >>> c = C2() + >>> isinstance(c, C1) + True + + >>> from zope.security.checker import ProxyFactory + >>> isinstance(ProxyFactory(c), C1) + True + + """ + + # The removeSecurityProxy call is OK here because it is *only* + # being used for isinstance + + return builtin_isinstance(removeSecurityProxy(object), cls) + + +# zope.location was made independent of security. To work together with +# security, we re-inject the DecoratedSecurityCheckerDescriptor onto the +# location proxy from here. +# This is the only sane place we found for doing it: it kicks in as soon +# as someone starts using security proxies. +import zope.location.location +from zope.security.decorator import DecoratedSecurityCheckerDescriptor +zope.location.location.LocationProxy.__Security_checker__ = ( + DecoratedSecurityCheckerDescriptor()) diff -Nru zope3-3.4.0/src/zope/security/README.txt zope3-3.5~bzr18/src/zope/security/README.txt --- zope3-3.4.0/src/zope/security/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,340 @@ +============== +Zope3 Security +============== + +Introduction +------------ + +The Security framework provides a generic mechanism to implement security +policies on Python objects. This introduction provides a tutorial of the +framework explaining concepts, design, and going through sample usage from the +perspective of a Python programmer using the framework outside of Zope. + +Definitions +----------- + +Principal +~~~~~~~~~ + +A generalization of a concept of a user. + +Permission +~~~~~~~~~~ + +A kind of access, i.e. permission to READ vs. permission to WRITE. +Fundamentally the whole security framework is organized around checking +permissions on objects. + +Purpose +------- + +The security framework's primary purpose is to guard and check access to +Python objects. It does this by providing mechanisms for explicit and +implicit security checks on attribute access for objects. Attribute names are +mapped onto permission names when checking access and the implementation of +the security check is defined by the security policy, which receives the +object, the permission name, and an interaction. + +Interactions are objects that represent the use of the system by one or more +principals. An interaction contains a list of participations, which +represents the way a single principal participates in the interaction. An +HTTP request is one example of a participation. + +Its important to keep in mind that the policy provided is just a default, and +it can be substituted with one which doesn't care about principals or +interactions at all. + +Framework Components +-------------------- + +Low Level Components +~~~~~~~~~~~~~~~~~~~~ + +These components provide the infrastructure for guarding attribute access and +providing hooks into the higher level security framework. + +Checkers +~~~~~~~~ + +A checker is associated with an object kind, and provides the hooks that map +attribute checks onto permissions deferring to the security manager (which in +turn defers to the policy) to perform the check. + +Additionally, checkers provide for creating proxies of objects associated with +the checker. + +There are several implementation variants of checkers, such as checkers that +grant access based on attribute names. + +Proxies +~~~~~~~ + +Wrappers around Python objects that implicitly guard access to their wrapped +contents by delegating to their associated checker. Proxies are also viral in +nature, in that values returned by proxies are also proxied. + +High Level Components +--------------------- + +Security Management +~~~~~~~~~~~~~~~~~~~ + +Provides accessors for setting up interactions and the global security policy. + +Interaction +~~~~~~~~~~~ + +Stores transient information on the list of participations. + +Participation +~~~~~~~~~~~~~ + +Stores information about a principal participating in the interaction. + +Security Policy +~~~~~~~~~~~~~~~ + +Provides a single method that accepts the object, the permission, and the +interaction of the access being checked and is used to implement the +application logic for the security framework. + +Narrative (agent sandbox) +------------------------- + +As an example we take a look at constructing a multi-agent distributed system, +and then adding a security layer using the Zope security model onto it. + +Scenario +~~~~~~~~ + +Our agent simulation consists of autonomous agents that live in various agent +homes/sandboxes and perform actions that access services available at their +current home. Agents carry around authentication tokens which signify their +level of access within any given home. Additionally agents attempt to migrate +from home to home randomly. + +The agent simulation was constructed separately from any security aspects. +Now we want to define and integrate a security model into the simulation. The +full code for the simulation and the security model is available separately; +we present only relevant code snippets here for illustration as we go through +the implementation process. + +For the agent simulation we want to add a security model such that we group +agents into two authentication groups, "norse legends", including the +principals thor, odin, and loki, and "greek men", including prometheus, +archimedes, and thucydides. + +We associate permissions with access to services and homes. We differentiate +the homes such that certain authentication groups only have access to services +or the home itself based on the local settings of the home in which they +reside. + +We define the homes/sandboxes + + - origin - all agents start here, and have access to all + services here. + + - valhalla - only agents in the authentication group 'norse + legend' can reside here. + + - jail - all agents can come here, but only 'norse legend's + can leave or access services. + + +Process +~~~~~~~ + +Loosely we define a process for implementing this security model + + - mapping permissions onto actions + + - mapping authentication tokens onto permissions + + - implementing checkers and security policies that use our + authentication tokens and permissions. + + - binding checkers to our simulation classes + + - inserting the hooks into the original simulation code to add + proxy wrappers to automatically check security. + + - inserting hooks into the original simulation to register the + agents as the active principal in an interaction. + + +Defining a Permission Model +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We define the following permissions:: + + NotAllowed = 'Not Allowed' + Public = Checker.CheckerPublic + TransportAgent = 'Transport Agent' + AccessServices = 'Access Services' + AccessAgents = 'Access Agents' + AccessTimeService = 'Access Time Services' + AccessAgentService = 'Access Agent Service' + AccessHomeService = 'Access Home Service' + +and create a dictionary database mapping homes to authentication groups which +are linked to associated permissions. + + +Defining and Binding Checkers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Checkers are the foundational unit for the security framework. They define +what attributes can be accessed or set on a given instance. They can be used +implicitly via Proxy objects, to guard all attribute access automatically or +explicitly to check a given access for an operation. + +Checker construction expects two functions or dictionaries, one is used to map +attribute names to permissions for attribute access and another to do the same +for setting attributes. + +We use the following checker factory function:: + + def PermissionMapChecker(permissions_map={}, + setattr_permission_func=NoSetAttr): + res = {} + for k,v in permissions_map.items(): + for iv in v: + res[iv]=k + return checker.Checker(res.get, setattr_permission_func) + + time_service_checker = PermissionMapChecker( + # permission : [methods] + {'AccessTimeService':['getTime']} + ) + +with the NoSetAttr function defined as a lambda which always return the +permission `NotAllowed`. + +To bind the checkers to the simulation classes we register our checkers with +the security model's global checker registry:: + + import sandbox_simulation + from zope.security.checker import defineChecker + defineChecker(sandbox_simulation.TimeService, time_service_checker) + + +Defining a Security Policy +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We implement our security policy such that it checks the current agent's +authentication token against the given permission in the home of the object +being accessed:: + + class SimulationSecurityPolicy: + + implements(ISecurityPolicy) + + createInteraction = staticmethod(simpleinteraction.createInteraction) + + def checkPermission(self, permission, object, interaction): + + home = object.getHome() + db = getattr(SimulationSecurityDatabase, home.getId(), None) + + if db is None: + return False + + allowed = db.get('any', ()) + if permission in allowed or ALL in allowed: + return True + + if interaction is None: + return False + if not interaction.participations: + return False + for participation in interaction.participations: + token = participation.principal.getAuthenticationToken() + allowed = db.get(token, ()) + if permission not in allowed: + return False + + return True + +There are no specific requirements for the interaction class, so we can just +use `zope.security.simpleinteraction.Interaction`. + +Since an interaction can have more than one principal, we check that *all* of +them are given the necessary permission. This is not really necessary since +we only create interactions with a single active principal. + +There is some additional code present to allow for shortcuts in defining the +permission database when defining permissions for all auth groups and all +permissions. + + +Integration +~~~~~~~~~~~ + +At this point we have implemented our security model, and we need to integrate +it with our simulation model. We do so in three separate steps. + +First we make it such that agents only access homes that are wrapped in a +security proxy. By doing this all access to homes and services (proxies have +proxied return values for their methods) is implicitly guarded by our security +policy. + +The second step is that we want to associate the active agent with the +security context so the security policy will know which agent's authentication +token to validate against. + +The third step is to set our security policy as the default policy for the +Zope security framework. It is possible to create custom security policies at +a finer grained than global, but such is left as an exercise for the reader. + + +Interaction Access +~~~~~~~~~~~~~~~~~~ + +The *default* implementation of the interaction management interfaces defines +interactions on a per thread basis with a function for an accessor. This +model is not appropriate for all systems, as it restricts one to a single +active interaction per thread at any given moment. Reimplementing the +interaction access methods though is easily doable and is noted here for +completeness. + + +Perspectives +~~~~~~~~~~~~ + +It's important to keep in mind that there is a lot more that is possible using +the security framework than what's been presented here. All of the +interactions are interface based, such that if you need to re-implement the +semantics to suite your application a new implementation of the interface will +be sufficient. Additional possibilities range from restricted interpreters +and dynamic loading of untrusted code to non Zope web application security +systems. Insert imagination here ;-). + + +Zope Perspective +~~~~~~~~~~~~~~~~ + +A Zope3 programmer will never commonly need to interact with the low level +security framework. Zope3 defines a second security package over top the low +level framework and authentication sources and checkers are handled via zcml +registration. Still those developing Zope3 will hopefully find this useful as +an introduction into the underpinnings of the security framework. + + +Code +~~~~ + +The complete code for this example is available. + +- sandbox.py - the agent framework + +- sandbox_security.py - the security implementation and binding to the agent + framework. + + +Authors +~~~~~~~ + +- Kapil Thangavelu +- Guido Wesdorp +- Marius Gedminas + diff -Nru zope3-3.4.0/src/zope/security/setup.py zope3-3.5~bzr18/src/zope/security/setup.py --- zope3-3.4.0/src/zope/security/setup.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/setup.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,22 @@ +#! /usr/bin/env python +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Security setup + +$Id: setup.py 70826 2006-10-20 03:41:16Z baijum $ +""" +from distutils.core import setup, Extension + +setup(name="_Proxy", version = "0.1", + ext_modules=[Extension("_Proxy", ["_Proxy.c"])]) diff -Nru zope3-3.4.0/src/zope/security/simplepolicies.py zope3-3.5~bzr18/src/zope/security/simplepolicies.py --- zope3-3.4.0/src/zope/security/simplepolicies.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/simplepolicies.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,61 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Simple 'ISecurityPolicy' implementations. + +$Id: simplepolicies.py 78815 2007-08-14 17:52:16Z jim $ +""" +import zope.interface +from zope.security.checker import CheckerPublic +from zope.security.interfaces import IInteraction, ISecurityPolicy +from zope.security._definitions import system_user + +class ParanoidSecurityPolicy(object): + zope.interface.implements(IInteraction) + zope.interface.classProvides(ISecurityPolicy) + + def __init__(self, *participations): + self.participations = [] + for participation in participations: + self.add(participation) + + def add(self, participation): + if participation.interaction is not None: + raise ValueError("%r already belongs to an interaction" + % participation) + participation.interaction = self + self.participations.append(participation) + + def remove(self, participation): + if participation.interaction is not self: + raise ValueError("%r does not belong to this interaction" + % participation) + self.participations.remove(participation) + participation.interaction = None + + def checkPermission(self, permission, object): + if permission is CheckerPublic: + return True + + users = [p.principal + for p in self.participations + if p.principal is not system_user] + + return not users + +class PermissiveSecurityPolicy(ParanoidSecurityPolicy): + """Allow all access.""" + zope.interface.classProvides(ISecurityPolicy) + + def checkPermission(self, permission, object): + return True diff -Nru zope3-3.4.0/src/zope/security/testing.py zope3-3.5~bzr18/src/zope/security/testing.py --- zope3-3.4.0/src/zope/security/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,57 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Testing support code + +This module provides some helper/stub objects for setting up interactions. + +$Id: testing.py 97951 2009-03-12 03:23:29Z nadako $ +""" + +from zope import interface, component +from zope.security import interfaces +from zope.security.permission import Permission + +class Principal: + + interface.implements(interfaces.IPrincipal) + + def __init__(self, id, title=None, description='', groups=None): + self.id = id + self.title = title or id + self.description = description + if groups is not None: + self.groups = groups + interface.directlyProvides(self, interfaces.IGroupAwarePrincipal) + +class Participation: + + interface.implements(interfaces.IParticipation) + + def __init__(self, principal): + self.principal = principal + self.interaction = None + + +def addCheckerPublic(): + """Add the CheckerPublic permission as 'zope.Public'""" + + perm = Permission('zope.Public', 'Public', + """Special permission used for resources that are always public + + The public permission is effectively an optimization, sine + it allows security computation to be bypassed. + """ + ) + gsm = component.getGlobalSiteManager() + gsm.registerUtility(perm, interfaces.IPermission, perm.id) diff -Nru zope3-3.4.0/src/zope/security/tests/adapter.py zope3-3.5~bzr18/src/zope/security/tests/adapter.py --- zope3-3.4.0/src/zope/security/tests/adapter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/adapter.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,63 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 adapter class for testing + +$Id: adapter.py 95326 2009-01-28 15:11:49Z brandon_rhodes $ +""" +import zope.interface +import zope.component +import components + +class I1(zope.interface.Interface): + pass + +class I2(zope.interface.Interface): + pass + +class I3(zope.interface.Interface): + def f1(): pass + def f2(): pass + def f3(): pass + +class IS(zope.interface.Interface): + pass + + +class Adapter(object): + def __init__(self, *args): + self.context = args + +class A1(Adapter): + zope.interface.implements(I1) + +class A2(Adapter): + zope.interface.implements(I2) + +class A3(Adapter): + zope.component.adapts(components.IContent, I1, I2) + zope.interface.implements(I3) + +class A4: + pass + +a4 = A4() + +class A5: + zope.interface.implements(I1, I2) + +a5 = A5() + +def Handler(content, *args): + # uninteresting handler + content.args = getattr(content, 'args', ()) + (args, ) diff -Nru zope3-3.4.0/src/zope/security/tests/components.py zope3-3.5~bzr18/src/zope/security/tests/components.py --- zope3-3.4.0/src/zope/security/tests/components.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/components.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,44 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Components for testing + +$Id: components.py 95326 2009-01-28 15:11:49Z brandon_rhodes $ +""" +from zope.interface import Interface, Attribute, implements +from zope.component import adapts + +class IAppb(Interface): + a = Attribute('test attribute') + def f(): "test func" + +class IApp(IAppb): + pass + +class IContent(Interface): pass + +class Content(object): + implements(IContent) + +class Comp(object): + adapts(IContent) + implements(IApp) + + def __init__(self, *args): + # Ignore arguments passed to constructor + pass + + a = 1 + def f(): pass + +comp = Comp() diff -Nru zope3-3.4.0/src/zope/security/tests/emptymodule.py zope3-3.5~bzr18/src/zope/security/tests/emptymodule.py --- zope3-3.4.0/src/zope/security/tests/emptymodule.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/emptymodule.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 empty module is for containing objects used in the course of tests. + +(There is a problem with the way the unit tests interact with the modules +being tests, so the objects can't be expected to show up in place.) + +$Id: emptymodule.py 95326 2009-01-28 15:11:49Z brandon_rhodes $ +""" diff -Nru zope3-3.4.0/src/zope/security/tests/exampleclass.py zope3-3.5~bzr18/src/zope/security/tests/exampleclass.py --- zope3-3.4.0/src/zope/security/tests/exampleclass.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/exampleclass.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,30 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Example test classes + +$Id: exampleclass.py 95326 2009-01-28 15:11:49Z brandon_rhodes $ +""" +from zope.interface import Interface + +class ExampleClass(object): + pass + +class IExample(Interface): + pass + +class IExample2(Interface): + pass + +class IExampleContainer(Interface): + pass diff -Nru zope3-3.4.0/src/zope/security/tests/__init__.py zope3-3.5~bzr18/src/zope/security/tests/__init__.py --- zope3-3.4.0/src/zope/security/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/security/tests/modulehookup.py zope3-3.5~bzr18/src/zope/security/tests/modulehookup.py --- zope3-3.4.0/src/zope/security/tests/modulehookup.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/modulehookup.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Preliminaries to hookup a test suite with the external TestModule. + +This is necessary because the test framework interferes with seeing changes in +the running modules via the module namespace. This enables having some +subject classes, instances, permissions, etc, that don't live in the test +modules, themselves. + +$Id: modulehookup.py 95326 2009-01-28 15:11:49Z brandon_rhodes $ +""" +from zope.interface import Interface + +from zope.security.tests import emptymodule as TestModule + +class I(Interface): + def m1(): + pass + def m2(): + pass + diff -Nru zope3-3.4.0/src/zope/security/tests/module.py zope3-3.5~bzr18/src/zope/security/tests/module.py --- zope3-3.4.0/src/zope/security/tests/module.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/module.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Preliminaries to hookup a test suite with the external TestModule. + +This is necessary because the test framework interferes with seeing changes in +the running modules via the module namespace. This enables having some +subject classes, instances, permissions, etc, that don't live in the test +modules, themselves. + +$Id: module.py 95326 2009-01-28 15:11:49Z brandon_rhodes $ +""" +from zope.interface import Interface +from zope.schema import Text + +class I(Interface): + def m1(): + pass + def m2(): + pass + +class I2(I): + def m4(): + pass + +class I3(Interface): + def m3(): + pass + +class I4(Interface): + def m2(): + pass + + +class S(Interface): + foo = Text() + bar = Text() + baro = Text(readonly=True) + +class S2(Interface): + foo2 = Text() + bar2 = Text() + + +template_bracket = """ + %s +""" diff -Nru zope3-3.4.0/src/zope/security/tests/redefineperms.zcml zope3-3.5~bzr18/src/zope/security/tests/redefineperms.zcml --- zope3-3.4.0/src/zope/security/tests/redefineperms.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/redefineperms.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/security/tests/test_adapter.py zope3-3.5~bzr18/src/zope/security/tests/test_adapter.py --- zope3-3.4.0/src/zope/security/tests/test_adapter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_adapter.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,24 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +""" +$Id: test_adapter.py 111761 2010-04-30 21:52:52Z hannosch $ +""" +import unittest +from doctest import DocTestSuite + + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite('zope.security.adapter'), + )) diff -Nru zope3-3.4.0/src/zope/security/tests/test_checker.py zope3-3.5~bzr18/src/zope/security/tests/test_checker.py --- zope3-3.4.0/src/zope/security/tests/test_checker.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_checker.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,631 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Security Checker tests + +$Id: test_checker.py 97949 2009-03-12 03:09:05Z nadako $ +""" +from unittest import TestCase, TestSuite, main, makeSuite +from zope.interface import implements +from zope.interface.verify import verifyObject +from zope.testing.cleanup import CleanUp +from zope.proxy import getProxiedObject +from zope.security.interfaces import ISecurityPolicy, Unauthorized +from zope.security.interfaces import Forbidden, ForbiddenAttribute +from zope.security.management import setSecurityPolicy, newInteraction +from zope.security.management import endInteraction, getInteraction +from zope.security.proxy import removeSecurityProxy, getChecker, Proxy +from zope.security.checker import defineChecker, undefineChecker, ProxyFactory +from zope.security.checker import canWrite, canAccess +from zope.security.checker import Checker, NamesChecker, CheckerPublic +from zope.security.checker import BasicTypes, _checkers, NoProxy, _clear +import types, pickle + +class SecurityPolicy(object): + implements(ISecurityPolicy) + + def checkPermission(self, permission, object): + 'See ISecurityPolicy' + return permission == 'test_allowed' + +class RecordedSecurityPolicy(object): + implements(ISecurityPolicy) + + def __init__(self): + self._checked = [] + self.permissions = {} + + def checkPermission(self, permission, object): + 'See ISecurityPolicy' + self._checked.append(permission) + return self.permissions.get(permission, True) + + def checkChecked(self, checked): + res = self._checked == checked + self._checked = [] + return res + +class TransparentProxy(object): + def __init__(self, ob): + self._ob = ob + + def __getattribute__(self, name): + ob = object.__getattribute__(self, '_ob') + return getattr(ob, name) + +class OldInst: + __metaclass__ = types.ClassType + + a = 1 + + def b(self): + pass + + c = 2 + + def gete(self): + return 3 + e = property(gete) + + def __getitem__(self, x): + return 5, x + + def __setitem__(self, x, v): + pass + +class NewInst(object, OldInst): + # This is not needed, but left in to show the change of metaclass + # __metaclass__ = type + + def gete(self): + return 3 + + def sete(self, v): + pass + + e = property(gete, sete) + + +class Test(TestCase, CleanUp): + + def setUp(self): + CleanUp.setUp(self) + self.__oldpolicy = setSecurityPolicy(SecurityPolicy) + newInteraction() + + def tearDown(self): + endInteraction() + setSecurityPolicy(self.__oldpolicy) + CleanUp.tearDown(self) + + def test_typesAcceptedByDefineChecker(self): + class ClassicClass: + __metaclass__ = types.ClassType + class NewStyleClass: + __metaclass__ = type + import zope.security + not_a_type = object() + defineChecker(ClassicClass, NamesChecker()) + defineChecker(NewStyleClass, NamesChecker()) + defineChecker(zope.security, NamesChecker()) + self.assertRaises(TypeError, + defineChecker, not_a_type, NamesChecker()) + + # check_getattr cases: + # + # - no attribute there + # - method + # - allow and disallow by permission + def test_check_getattr(self): + + oldinst = OldInst() + oldinst.d = OldInst() + + newinst = NewInst() + newinst.d = NewInst() + + for inst in oldinst, newinst: + checker = NamesChecker(['a', 'b', 'c', '__getitem__'], 'perm') + + self.assertRaises(Unauthorized, checker.check_getattr, inst, 'a') + self.assertRaises(Unauthorized, checker.check_getattr, inst, 'b') + self.assertRaises(Unauthorized, checker.check_getattr, inst, 'c') + self.assertRaises(Unauthorized, checker.check, inst, '__getitem__') + self.assertRaises(Forbidden, checker.check, inst, '__setitem__') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') + + checker = NamesChecker(['a', 'b', 'c', '__getitem__'], + 'test_allowed') + + checker.check_getattr(inst, 'a') + checker.check_getattr(inst, 'b') + checker.check_getattr(inst, 'c') + checker.check(inst, '__getitem__') + self.assertRaises(Forbidden, checker.check, inst, '__setitem__') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') + + checker = NamesChecker(['a', 'b', 'c', '__getitem__'], + CheckerPublic) + + checker.check_getattr(inst, 'a') + checker.check_getattr(inst, 'b') + checker.check_getattr(inst, 'c') + checker.check(inst, '__getitem__') + self.assertRaises(Forbidden, checker.check, inst, '__setitem__') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') + self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') + + def test_check_setattr(self): + + oldinst = OldInst() + oldinst.d = OldInst() + + newinst = NewInst() + newinst.d = NewInst() + + for inst in oldinst, newinst: + checker = Checker({}, {'a': 'perm', 'z': 'perm'}) + + self.assertRaises(Unauthorized, checker.check_setattr, inst, 'a') + self.assertRaises(Unauthorized, checker.check_setattr, inst, 'z') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'c') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') + + checker = Checker({}, {'a': 'test_allowed', 'z': 'test_allowed'}) + + checker.check_setattr(inst, 'a') + checker.check_setattr(inst, 'z') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') + + checker = Checker({}, {'a': CheckerPublic, 'z': CheckerPublic}) + + checker.check_setattr(inst, 'a') + checker.check_setattr(inst, 'z') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') + + def test_proxy(self): + checker = NamesChecker(()) + + from zope.security.checker import BasicTypes_examples + rocks = tuple(BasicTypes_examples.values()) + for rock in rocks: + proxy = checker.proxy(rock) + self.failUnless(proxy is rock, (rock, type(proxy))) + + for class_ in OldInst, NewInst: + inst = class_() + + for ob in inst, class_: + proxy = checker.proxy(ob) + self.failUnless(removeSecurityProxy(proxy) is ob) + checker = getChecker(proxy) + if ob is inst: + self.assertEqual(checker.permission_id('__str__'), + None) + else: + self.assertEqual(checker.permission_id('__str__'), + CheckerPublic) + + #No longer doing anything special for transparent proxies. + #A proxy needs to provide its own security checker. + # + #special = NamesChecker(['a', 'b'], 'test_allowed') + #defineChecker(class_, special) + # + #for ob in inst, TransparentProxy(inst): + # proxy = checker.proxy(ob) + # self.failUnless(removeSecurityProxy(proxy) is ob) + # + # checker = getChecker(proxy) + # self.failUnless(checker is special, + # checker.get_permissions) + # + # proxy2 = checker.proxy(proxy) + # self.failUnless(proxy2 is proxy, [proxy, proxy2]) + + def testLayeredProxies(self): + """Tests that a Proxy will not be re-proxied.""" + class Base: + __Security_checker__ = NamesChecker(['__Security_checker__']) + base = Base() + checker = Checker({}) + + # base is not proxied, so we expect a proxy + proxy1 = checker.proxy(base) + self.assert_(type(proxy1) is Proxy) + self.assert_(getProxiedObject(proxy1) is base) + + # proxy is a proxy, so we don't expect to get another + proxy2 = checker.proxy(proxy1) + self.assert_(proxy2 is proxy1) + self.assert_(getProxiedObject(proxy2) is base) + + + def testMultiChecker(self): + from zope.interface import Interface + + class I1(Interface): + def f1(): '' + def f2(): '' + + class I2(I1): + def f3(): '' + def f4(): '' + + class I3(Interface): + def g(): '' + + from zope.exceptions import DuplicationError + + from zope.security.checker import MultiChecker + + self.assertRaises(DuplicationError, + MultiChecker, + [(I1, 'p1'), (I2, 'p2')]) + + self.assertRaises(DuplicationError, + MultiChecker, + [(I1, 'p1'), {'f2': 'p2'}]) + + MultiChecker([(I1, 'p1'), (I2, 'p1')]) + + checker = MultiChecker([ + (I2, 'p1'), + {'a': 'p3'}, + (I3, 'p2'), + (('x','y','z'), 'p4'), + ]) + + self.assertEqual(checker.permission_id('f1'), 'p1') + self.assertEqual(checker.permission_id('f2'), 'p1') + self.assertEqual(checker.permission_id('f3'), 'p1') + self.assertEqual(checker.permission_id('f4'), 'p1') + self.assertEqual(checker.permission_id('g'), 'p2') + self.assertEqual(checker.permission_id('a'), 'p3') + self.assertEqual(checker.permission_id('x'), 'p4') + self.assertEqual(checker.permission_id('y'), 'p4') + self.assertEqual(checker.permission_id('z'), 'p4') + self.assertEqual(checker.permission_id('zzz'), None) + + def testAlwaysAvailable(self): + from zope.security.checker import NamesChecker + checker = NamesChecker(()) + class C(object): pass + self.assertEqual(checker.check(C, '__hash__'), None) + self.assertEqual(checker.check(C, '__nonzero__'), None) + self.assertEqual(checker.check(C, '__class__'), None) + self.assertEqual(checker.check(C, '__implements__'), None) + self.assertEqual(checker.check(C, '__lt__'), None) + self.assertEqual(checker.check(C, '__le__'), None) + self.assertEqual(checker.check(C, '__gt__'), None) + self.assertEqual(checker.check(C, '__ge__'), None) + self.assertEqual(checker.check(C, '__eq__'), None) + self.assertEqual(checker.check(C, '__ne__'), None) + self.assertEqual(checker.check(C, '__name__'), None) + self.assertEqual(checker.check(C, '__parent__'), None) + + def test_setattr(self): + checker = NamesChecker(['a', 'b', 'c', '__getitem__'], + 'test_allowed') + + for inst in NewInst(), OldInst(): + self.assertRaises(Forbidden, checker.check_setattr, inst, 'a') + self.assertRaises(Forbidden, checker.check_setattr, inst, 'z') + + # TODO: write a test to see that + # Checker.check/check_setattr handle permission + # values that evaluate to False + + def test_ProxyFactory(self): + class SomeClass(object): + pass + import zope.security + checker = NamesChecker() + specific_checker = NamesChecker() + checker_as_magic_attr = NamesChecker() + + obj = SomeClass() + + proxy = ProxyFactory(obj) + self.assert_(type(proxy) is Proxy) + from zope.security.checker import _defaultChecker + self.assert_(getChecker(proxy) is _defaultChecker) + + defineChecker(SomeClass, checker) + + proxy = ProxyFactory(obj) + self.assert_(type(proxy) is Proxy) + self.assert_(getChecker(proxy) is checker) + + obj.__Security_checker__ = checker_as_magic_attr + + proxy = ProxyFactory(obj) + self.assert_(type(proxy) is Proxy) + self.assert_(getChecker(proxy) is checker_as_magic_attr) + + proxy = ProxyFactory(obj, specific_checker) + self.assert_(type(proxy) is Proxy) + self.assert_(getChecker(proxy) is specific_checker) + + def test_define_and_undefineChecker(self): + class SomeClass(object): + pass + obj = SomeClass() + + checker = NamesChecker() + from zope.security.checker import _defaultChecker, selectChecker + self.assert_(selectChecker(obj) is _defaultChecker) + defineChecker(SomeClass, checker) + self.assert_(selectChecker(obj) is checker) + undefineChecker(SomeClass) + self.assert_(selectChecker(obj) is _defaultChecker) + + def test_ProxyFactory_using_proxy(self): + class SomeClass(object): + pass + obj = SomeClass() + checker = NamesChecker() + proxy1 = ProxyFactory(obj) + + proxy2 = ProxyFactory(proxy1) + self.assert_(proxy1 is proxy2) + + # Trying to change the checker on a proxy. + self.assertRaises(TypeError, ProxyFactory, proxy1, checker) + + # Setting exactly the same checker as the proxy already has. + proxy1 = ProxyFactory(obj, checker) + proxy2 = ProxyFactory(proxy1, checker) + self.assert_(proxy1 is proxy2) + + def test_canWrite_canAccess(self): + # the canWrite and canAccess functions are conveniences. Often code + # wants to check if a certain option is open to a user before + # presenting it. If the code relies on a certain permission, the + # Zope 3 goal of keeping knowledge of security assertions out of the + # code and only in the zcml assertions is broken. Instead, ask if the + # current user canAccess or canWrite some pertinent aspect of the + # object. canAccess is used for both read access on an attribute + # and call access to methods. + + # For example, consider this humble pair of class and object. + class SomeClass(object): + pass + obj = SomeClass() + + # We will establish a checker for the class. This is the standard + # name-based checker, and works by specifying two dicts, one for read + # and one for write. Each item in the dictionary should be an + # attribute name and the permission required to read or write it. + + # For these tests, the SecurityPolicy defined at the top of this file + # is in place. It is a stub. Normally, the security policy would + # have knowledge of interactions and participants, and would determine + # on the basis of the particpants and the object if a certain permission + # were authorized. This stub simply says that the 'test_allowed' + # permission is authorized and nothing else is, for any object you pass + # it. + + # Therefore, according to the checker created here, the current + # 'interaction' (as stubbed out in the security policy) will be allowed + # to access and write foo, and access bar. The interaction is + # unauthorized for accessing baz and writing bar. Any other access or + # write is not merely unauthorized but forbidden--including write access + # for baz. + checker = Checker( + {'foo':'test_allowed', # these are the read settings + 'bar':'test_allowed', + 'baz':'you_will_not_have_this_permission'}, + {'foo':'test_allowed', # these are the write settings + 'bar':'you_will_not_have_this_permission', + 'bing':'you_will_not_have_this_permission'}) + defineChecker(SomeClass, checker) + + # so, our hapless interaction may write and access foo... + self.assert_(canWrite(obj, 'foo')) + self.assert_(canAccess(obj, 'foo')) + + # ...may access, but not write, bar... + self.assert_(not canWrite(obj, 'bar')) + self.assert_(canAccess(obj, 'bar')) + + # ...and may access baz. + self.assert_(not canAccess(obj, 'baz')) + + # there are no security assertions for writing or reading shazam, so + # checking these actually raises Forbidden. The rationale behind + # exposing the Forbidden exception is primarily that it is usually + # indicative of programming or configuration errors. + self.assertRaises(Forbidden, canAccess, obj, 'shazam') + self.assertRaises(Forbidden, canWrite, obj, 'shazam') + + # However, we special-case canWrite when an attribute has a Read + # setting but no Write setting. Consider the 'baz' attribute from the + # checker above: it is readonly. All users are forbidden to write + # it. This is a very reasonable configuration. Therefore, canWrite + # will hide the Forbidden exception if and only if there is a + # setting for accessing the attribute. + self.assert_(not canWrite(obj, 'baz')) + + # The reverse is not true at the moment: an unusal case like the + # write-only 'bing' attribute will return a boolean for canWrite, + # but canRead will simply raise a Forbidden exception, without checking + # write settings. + self.assert_(not canWrite(obj, 'bing')) + self.assertRaises(Forbidden, canAccess, obj, 'bing') + +class TestCheckerPublic(TestCase): + + def test_that_pickling_CheckerPublic_retains_identity(self): + self.assert_(pickle.loads(pickle.dumps(CheckerPublic)) + is + CheckerPublic) + + def test_that_CheckerPublic_identity_works_even_when_proxied(self): + self.assert_(ProxyFactory(CheckerPublic) is CheckerPublic) + + +class TestMixinDecoratedChecker(TestCase): + + def decoratedSetUp(self): + self.policy = RecordedSecurityPolicy + self._oldpolicy = setSecurityPolicy(self.policy) + newInteraction() + self.interaction = getInteraction() + self.obj = object() + + def decoratedTearDown(self): + endInteraction() + setSecurityPolicy(self._oldpolicy) + + def check_checking_impl(self, checker): + o = self.obj + checker.check_getattr(o, 'both_get_set') + self.assert_(self.interaction.checkChecked(['dc_get_permission'])) + checker.check_getattr(o, 'c_only') + self.assert_(self.interaction.checkChecked(['get_permission'])) + checker.check_getattr(o, 'd_only') + self.assert_(self.interaction.checkChecked(['dc_get_permission'])) + self.assertRaises(ForbiddenAttribute, + checker.check_getattr, o, + 'completely_different_attr') + self.assert_(self.interaction.checkChecked([])) + checker.check(o, '__str__') + self.assert_(self.interaction.checkChecked(['get_permission'])) + + checker.check_setattr(o, 'both_get_set') + self.assert_(self.interaction.checkChecked(['dc_set_permission'])) + self.assertRaises(ForbiddenAttribute, + checker.check_setattr, o, 'c_only') + self.assert_(self.interaction.checkChecked([])) + self.assertRaises(ForbiddenAttribute, + checker.check_setattr, o, 'd_only') + self.assert_(self.interaction.checkChecked([])) + + originalChecker = NamesChecker(['both_get_set', 'c_only', '__str__'], + 'get_permission') + + decorationSetMap = {'both_get_set': 'dc_set_permission'} + + decorationGetMap = {'both_get_set': 'dc_get_permission', + 'd_only': 'dc_get_permission'} + + overridingChecker = Checker(decorationGetMap, decorationSetMap) + +class TestCombinedChecker(TestMixinDecoratedChecker, TestCase): + + def setUp(self): + TestCase.setUp(self) + self.decoratedSetUp() + + def tearDown(self): + self.decoratedTearDown() + TestCase.tearDown(self) + + def test_checking(self): + from zope.security.checker import CombinedChecker + cc = CombinedChecker(self.overridingChecker, self.originalChecker) + self.check_checking_impl(cc) + + # When a permission is not authorized by the security policy, + # the policy is queried twice per check_getattr -- once for each + # checker. + self.interaction.permissions['dc_get_permission'] = False + cc.check_getattr(self.obj, 'both_get_set') + self.assert_( + self.interaction.checkChecked(['dc_get_permission', + 'get_permission']) + ) + + # This should raise Unauthorized instead of ForbiddenAttribute, since + # access can be granted if you e.g. login with different credentials. + self.assertRaises(Unauthorized, cc.check_getattr, self.obj, 'd_only') + self.assertRaises(Unauthorized, cc.check, self.obj, 'd_only') + + def test_interface(self): + from zope.security.checker import CombinedChecker + from zope.security.interfaces import IChecker + dc = CombinedChecker(self.overridingChecker, self.originalChecker) + verifyObject(IChecker, dc) + + +class TestBasicTypes(TestCase): + + def test(self): + class MyType(object): pass + class MyType2(object): pass + + # When an item is added to the basic types, it should also be added to + # the list of checkers. + BasicTypes[MyType] = NoProxy + self.assert_(MyType in _checkers) + + # If we clear the checkers, the type should still be there + _clear() + self.assert_(MyType in BasicTypes) + self.assert_(MyType in _checkers) + + # Now delete the type from the dictionary, will also delete it from + # the checkers + del BasicTypes[MyType] + self.assert_(MyType not in BasicTypes) + self.assert_(MyType not in _checkers) + + # The quick way of adding new types is using update + BasicTypes.update({MyType: NoProxy, MyType2: NoProxy}) + self.assert_(MyType in BasicTypes) + self.assert_(MyType2 in BasicTypes) + self.assert_(MyType in _checkers) + self.assert_(MyType2 in _checkers) + + # Let's remove the two new types + del BasicTypes[MyType] + del BasicTypes[MyType2] + + # Of course, BasicTypes is a full dictionary. This dictionary is by + # default filled with several entries: + keys = BasicTypes.keys() + keys.sort() + self.assert_(bool in keys) + self.assert_(int in keys) + self.assert_(float in keys) + self.assert_(str in keys) + self.assert_(unicode in keys) + self.assert_(object in keys) + # ... + + # Finally, the ``clear()`` method has been deactivated to avoid + # unwanted deletions. + self.assertRaises(NotImplementedError, BasicTypes.clear) + +def test_suite(): + return TestSuite(( + makeSuite(Test), + makeSuite(TestCheckerPublic), + makeSuite(TestCombinedChecker), + makeSuite(TestBasicTypes), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/security/tests/test_contentdirective.py zope3-3.5~bzr18/src/zope/security/tests/test_contentdirective.py --- zope3-3.4.0/src/zope/security/tests/test_contentdirective.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_contentdirective.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,203 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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:class' directive. + +$Id: test_contentdirective.py 95326 2009-01-28 15:11:49Z brandon_rhodes $ +""" +import unittest +from StringIO import StringIO + +from zope.component.interfaces import IFactory +from zope.component.interfaces import ComponentLookupError +from zope.component.interface import queryInterface +from zope.configuration.xmlconfig import xmlconfig, XMLConfig + +import zope.component +import zope.security +from zope.component.testing import PlacelessSetup + +# explicitly import ExampleClass and IExample using full paths +# so that they are the same objects as resolve will get. +from zope.security.tests.exampleclass import ExampleClass +from zope.security.tests.exampleclass import IExample, IExample2 + + +class ParticipationStub(object): + + def __init__(self, principal): + self.principal = principal + self.interaction = None + + +def configfile(s): + return StringIO(""" + %s + + """ % s) + +class TestClassDirective(PlacelessSetup, unittest.TestCase): + def setUp(self): + super(TestClassDirective, self).setUp() + XMLConfig('meta.zcml', zope.security)() + + try: + del ExampleClass.__implements__ + except AttributeError: + pass + + def testEmptyDirective(self): + f = configfile(""" + + + """) + xmlconfig(f) + + + def testImplements(self): + self.assertEqual(queryInterface( + "zope.security.tests.exampleclass.IExample"), None) + + f = configfile(""" + + + + """) + xmlconfig(f) + self.failUnless(IExample.implementedBy(ExampleClass)) + + self.assertEqual(queryInterface( + "zope.security.tests.exampleclass.IExample"), IExample) + + + def testMulImplements(self): + self.assertEqual(queryInterface( + "zope.security.tests.exampleclass.IExample"), None) + self.assertEqual(queryInterface( + "zope.security.tests.exampleclass.IExample2"), None) + + f = configfile(""" + + + + """) + xmlconfig(f) + self.failUnless(IExample.implementedBy(ExampleClass)) + self.failUnless(IExample2.implementedBy(ExampleClass)) + + self.assertEqual(queryInterface( + "zope.security.tests.exampleclass.IExample"), IExample) + self.assertEqual(queryInterface( + "zope.security.tests.exampleclass.IExample2"), + IExample2) + + def testRequire(self): + f = configfile(""" + + + + + """) + xmlconfig(f) + + def testAllow(self): + f = configfile(""" + + + + """) + xmlconfig(f) + + def testMimic(self): + f = configfile(""" + + + + """) + xmlconfig(f) + + +class TestFactorySubdirective(PlacelessSetup, unittest.TestCase): + def setUp(self): + super(TestFactorySubdirective, self).setUp() + XMLConfig('meta.zcml', zope.security)() + + def testFactory(self): + f = configfile(""" + + + + + + """) + xmlconfig(f) + factory = zope.component.getUtility(IFactory, 'test.Example') + self.assertEquals(factory.title, "Example content") + self.assertEquals(factory.description, "Example description") + + def testFactoryNoId(self): + f = configfile(""" + + + + + + """) + xmlconfig(f) + self.assertRaises(ComponentLookupError, zope.component.getUtility, + IFactory, 'Example') + factory = zope.component.getUtility( + IFactory, 'zope.security.tests.exampleclass.ExampleClass') + self.assertEquals(factory.title, "Example content") + self.assertEquals(factory.description, "Example description") + + + def testFactoryPublicPermission(self): + + f = configfile(""" + + + + """) + xmlconfig(f) + factory = zope.component.getUtility(IFactory, 'test.Example') + self.assert_(hasattr(factory, '__Security_checker__')) + + +def test_suite(): + suite = unittest.TestSuite() + loader = unittest.TestLoader() + suite.addTest(loader.loadTestsFromTestCase(TestClassDirective)) + suite.addTest(loader.loadTestsFromTestCase(TestFactorySubdirective)) + return suite + + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/security/tests/test_decorator.py zope3-3.5~bzr18/src/zope/security/tests/test_decorator.py --- zope3-3.4.0/src/zope/security/tests/test_decorator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_decorator.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Context Tests + +$Id: test_decorator.py 111761 2010-04-30 21:52:52Z hannosch $ +""" + +import doctest + + +def test_suite(): + suite = doctest.DocTestSuite() + suite.addTest(doctest.DocTestSuite('zope.security.decorator')) + return suite diff -Nru zope3-3.4.0/src/zope/security/tests/test_directives.py zope3-3.5~bzr18/src/zope/security/tests/test_directives.py --- zope3-3.4.0/src/zope/security/tests/test_directives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_directives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,375 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Component Directives Tests + +$Id: test_directives.py 111761 2010-04-30 21:52:52Z hannosch $ +""" +import re +import unittest +import pprint +from cStringIO import StringIO +from doctest import DocTestSuite + +import zope.component +from zope.interface import implements +from zope.component.interface import queryInterface + +from zope.configuration.xmlconfig import xmlconfig, XMLConfig +from zope.configuration.xmlconfig import ZopeXMLConfigurationError +from zope.security.checker import selectChecker +from zope.security import proxy + +import zope.security +from zope.component.testing import PlacelessSetup + +from zope.security.tests import module, exampleclass + +# TODO: tests for other directives needed + +atre = re.compile(' at [0-9a-fA-Fx]+') + +class Context(object): + actions = () + + def action(self, discriminator, callable, args): + self.actions += ((discriminator, callable, args), ) + + def __repr__(self): + stream = StringIO() + pprinter = pprint.PrettyPrinter(stream=stream, width=60) + pprinter.pprint(self.actions) + r = stream.getvalue() + return (''.join(atre.split(r))).strip() + + +template = """ + %s + """ + +def definePermissions(): + XMLConfig('meta.zcml', zope.security)() + + +class ParticipationStub(object): + + def __init__(self, principal): + self.principal = principal + self.interaction = None + + +def configfile(s): + return StringIO(""" + %s + + """ % s) + +class TestFactoryDirective(PlacelessSetup, unittest.TestCase): + def setUp(self): + super(TestFactoryDirective, self).setUp() + XMLConfig('meta.zcml', zope.security)() + + def testFactory(self): + f = configfile(''' + + + +''') + xmlconfig(f) + obj = zope.component.createObject('test.Example') + self.failUnless(proxy.isinstance(obj, exampleclass.ExampleClass)) + + + +PREFIX = module.__name__ + '.' + +def defineDirectives(): + XMLConfig('meta.zcml', zope.security)() + xmlconfig(StringIO(""" + + + """)) + +NOTSET = [] + +P1 = "zope.Extravagant" +P2 = "zope.Paltry" + +class TestRequireDirective(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(TestRequireDirective, self).setUp() + defineDirectives() + + class B(object): + def m1(self): + return "m1" + def m2(self): + return "m2" + class C(B): + implements(module.I) + def m3(self): + return "m3" + def m4(self): + return "m4" + module.test_base = B + module.test_class = C + module.test_instance = C() + self.assertState() + + def tearDown(self): + PlacelessSetup.tearDown(self) + module.test_class = None + + def assertState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): + "Verify that class, instance, and methods have expected permissions." + + checker = selectChecker(module.test_instance) + self.assertEqual(checker.permission_id('m1'), (m1P or None)) + self.assertEqual(checker.permission_id('m2'), (m2P or None)) + self.assertEqual(checker.permission_id('m3'), (m3P or None)) + + def assertDeclaration(self, declaration, **state): + apply_declaration(module.template_bracket % declaration) + self.assertState(**state) + + # "testSimple*" exercises tags that do NOT have children. This mode + # inherently sets the instances as well as the class attributes. + + def testSimpleMethodsPlural(self): + declaration = (''' + + ''' + % (PREFIX+"test_class", P1)) + self.assertDeclaration(declaration, m1P=P1, m3P=P1) + + def assertSetattrState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): + "Verify that class, instance, and methods have expected permissions." + + from zope.security.checker import selectChecker + + checker = selectChecker(module.test_instance) + self.assertEqual(checker.setattr_permission_id('m1'), (m1P or None)) + self.assertEqual(checker.setattr_permission_id('m2'), (m2P or None)) + self.assertEqual(checker.setattr_permission_id('m3'), (m3P or None)) + + def assertSetattrDeclaration(self, declaration, **state): + self.assertSetattrState(**state) + + def test_set_attributes(self): + declaration = (''' + + ''' + % (PREFIX+"test_class", P1)) + apply_declaration(module.template_bracket % declaration) + checker = selectChecker(module.test_instance) + self.assertEqual(checker.setattr_permission_id('m1'), P1) + self.assertEqual(checker.setattr_permission_id('m2'), None) + self.assertEqual(checker.setattr_permission_id('m3'), P1) + + def test_set_schema(self): + + self.assertEqual(queryInterface(PREFIX+"S"), None) + + declaration = (''' + + ''' + % (PREFIX+"test_class", P1, PREFIX+"S")) + apply_declaration(module.template_bracket % declaration) + + self.assertEqual(queryInterface(PREFIX+"S"), module.S) + + + checker = selectChecker(module.test_instance) + self.assertEqual(checker.setattr_permission_id('m1'), None) + self.assertEqual(checker.setattr_permission_id('m2'), None) + self.assertEqual(checker.setattr_permission_id('m3'), None) + self.assertEqual(checker.setattr_permission_id('foo'), P1) + self.assertEqual(checker.setattr_permission_id('bar'), P1) + self.assertEqual(checker.setattr_permission_id('baro'), None) + + def test_multiple_set_schema(self): + + self.assertEqual(queryInterface(PREFIX+"S"), None) + self.assertEqual(queryInterface(PREFIX+"S2"), None) + + declaration = (''' + + ''' + % (PREFIX+"test_class", P1, PREFIX+"S", PREFIX+"S2")) + apply_declaration(module.template_bracket % declaration) + + self.assertEqual(queryInterface(PREFIX+"S"), module.S) + self.assertEqual(queryInterface(PREFIX+"S2"), module.S2) + + + checker = selectChecker(module.test_instance) + self.assertEqual(checker.setattr_permission_id('m1'), None) + self.assertEqual(checker.setattr_permission_id('m2'), None) + self.assertEqual(checker.setattr_permission_id('m3'), None) + self.assertEqual(checker.setattr_permission_id('foo'), P1) + self.assertEqual(checker.setattr_permission_id('bar'), P1) + self.assertEqual(checker.setattr_permission_id('foo2'), P1) + self.assertEqual(checker.setattr_permission_id('bar2'), P1) + self.assertEqual(checker.setattr_permission_id('baro'), None) + + def testSimpleInterface(self): + + self.assertEqual(queryInterface(PREFIX+"I"), None) + + declaration = (''' + + ''' + % (PREFIX+"test_class", P1, PREFIX+"I")) + # m1 and m2 are in the interface, so should be set, and m3 should not: + self.assertDeclaration(declaration, m1P=P1, m2P=P1) + + # Make sure we know about the interfaces + self.assertEqual(queryInterface(PREFIX+"I"), module.I) + + + def testMultipleInterface(self): + + self.assertEqual(queryInterface(PREFIX+"I3"), None) + self.assertEqual(queryInterface(PREFIX+"I4"), None) + + declaration = (''' + + ''' + % (PREFIX+"test_class", P1, PREFIX+"I3", PREFIX+"I4")) + self.assertDeclaration(declaration, m3P=P1, m2P=P1) + + # Make sure we know about the interfaces + self.assertEqual(queryInterface(PREFIX+"I3"), module.I3) + self.assertEqual(queryInterface(PREFIX+"I4"), module.I4) + + # "testComposite*" exercises tags that DO have children. + # "testComposite*TopPerm" exercises tags with permission in containing tag. + # "testComposite*ElementPerm" exercises tags w/permission in children. + + def testCompositeNoPerm(self): + # Establish rejection of declarations lacking a permission spec. + declaration = (''' + + ''' + % (PREFIX+"test_class")) + self.assertRaises(ZopeXMLConfigurationError, + self.assertDeclaration, + declaration) + + + + def testCompositeMethodsPluralElementPerm(self): + declaration = (''' + + ''' + % (PREFIX+"test_class", P1)) + self.assertDeclaration(declaration, + m1P=P1, m3P=P1) + + def testCompositeInterfaceTopPerm(self): + declaration = (''' + + ''' + % (PREFIX+"test_class", P1, PREFIX+"I")) + self.assertDeclaration(declaration, + m1P=P1, m2P=P1) + + + def testSubInterfaces(self): + declaration = (''' + + ''' + % (PREFIX+"test_class", P1, PREFIX+"I2")) + # m1 and m2 are in the interface, so should be set, and m3 should not: + self.assertDeclaration(declaration, m1P=P1, m2P=P1) + + + def testMimicOnly(self): + declaration = (''' + + + + + + ''' % (PREFIX+"test_base", P1, + PREFIX+"test_class", PREFIX+"test_base")) + # m1 and m2 are in the interface, so should be set, and m3 should not: + self.assertDeclaration(declaration, + m1P=P1, m2P=P1) + + + def testMimicAsDefault(self): + declaration = (''' + + + + + + + ''' % (PREFIX+"test_base", P1, + PREFIX+"test_class", PREFIX+"test_base", P2)) + + # m1 and m2 are in the interface, so should be set, and m3 should not: + self.assertDeclaration(declaration, + m1P=P1, m2P=P2, m3P=P2) + + +def apply_declaration(declaration): + '''Apply the xmlconfig machinery.''' + return xmlconfig(StringIO(declaration)) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(TestFactoryDirective), + unittest.makeSuite(TestRequireDirective), + DocTestSuite(), + )) diff -Nru zope3-3.4.0/src/zope/security/tests/test_location.py zope3-3.5~bzr18/src/zope/security/tests/test_location.py --- zope3-3.4.0/src/zope/security/tests/test_location.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_location.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,52 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Context Tests + +$Id: test_decorator.py 95518 2009-01-29 19:16:15Z ctheune $ +""" + +import doctest + + +def test_locationproxy_security(): + """We start with an unlocated class that will be wrapped by a + LocationProxy: + + >>> class Unlocated(object): + ... a = 'a' + + >>> unlocated = Unlocated() + + Now we create a location proxy around it: + + >>> from zope.location.location import LocationProxy + >>> located = LocationProxy(unlocated) + + We define a checker for the unlocated object, which will also be + used by the security proxy as the LocationProxy defines + __Security_checker__: + + >>> from zope.security.checker import NamesChecker, defineChecker + >>> unlocatedChecker = NamesChecker(['a']) + >>> defineChecker(Unlocated, unlocatedChecker) + + >>> from zope.security.proxy import ProxyFactory + >>> secure_located = ProxyFactory(located) + >>> secure_located.a + 'a' + """ + + +def test_suite(): + return doctest.DocTestSuite() diff -Nru zope3-3.4.0/src/zope/security/tests/test_management.py zope3-3.5~bzr18/src/zope/security/tests/test_management.py --- zope3-3.4.0/src/zope/security/tests/test_management.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_management.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,136 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +""" Unit tests for SecurityManagement + +$Id: test_management.py 97524 2009-03-05 11:36:32Z nadako $ +""" + +import unittest + +from zope.interface.verify import verifyObject +from zope.testing.cleanup import CleanUp + + +class Test(CleanUp, unittest.TestCase): + + def test_import(self): + from zope.security import management + from zope.security.interfaces import ISecurityManagement + from zope.security.interfaces import IInteractionManagement + + verifyObject(ISecurityManagement, management) + verifyObject(IInteractionManagement, management) + + def test_securityPolicy(self): + from zope.security.management import setSecurityPolicy + from zope.security.management import getSecurityPolicy + from zope.security.simplepolicies import PermissiveSecurityPolicy + + policy = PermissiveSecurityPolicy + setSecurityPolicy(policy) + self.assert_(getSecurityPolicy() is policy) + + def test_query_new_end_restore_Interaction(self): + from zope.security.management import queryInteraction + self.assertEquals(queryInteraction(), None) + + from zope.security.management import newInteraction + + newInteraction() + + interaction = queryInteraction() + self.assert_(interaction is not None) + self.assertRaises(AssertionError, newInteraction) + + from zope.security.management import endInteraction + endInteraction() + self.assertEquals(queryInteraction(), None) + + from zope.security.management import restoreInteraction + restoreInteraction() + self.assert_(interaction is queryInteraction()) + + endInteraction() + self.assertEquals(queryInteraction(), None) + + endInteraction() + self.assertEquals(queryInteraction(), None) + + newInteraction() + self.assert_(queryInteraction() is not None) + + restoreInteraction() # restore to no interaction + self.assert_(queryInteraction() is None) + + def test_checkPermission(self): + from zope.security import checkPermission + from zope.security.management import setSecurityPolicy + from zope.security.management import queryInteraction + from zope.security.management import newInteraction, endInteraction + from zope.security.interfaces import NoInteraction + + permission = 'zope.Test' + obj = object() + + class PolicyStub(object): + + def checkPermission(s, p, o,): + self.assert_(p is permission) + self.assert_(o is obj) + self.assert_(s is queryInteraction() or s is interaction) + return s is interaction + + setSecurityPolicy(PolicyStub) + newInteraction() + interaction = queryInteraction() + self.assertEquals(checkPermission(permission, obj), True) + + endInteraction() + self.assertRaises(NoInteraction, checkPermission, permission, obj) + + def test_checkPublicPermission(self): + from zope.security import checkPermission + from zope.security.checker import CheckerPublic + from zope.security.management import setSecurityPolicy + from zope.security.management import newInteraction + + obj = object() + + class ForbiddenPolicyStub(object): + + def checkPermission(s, p, o): + return False + + setSecurityPolicy(ForbiddenPolicyStub) + newInteraction() + self.assertEquals(checkPermission('zope.Test', obj), False) + self.assertEquals(checkPermission(None, obj), True) + self.assertEquals(checkPermission(CheckerPublic, obj), True) + + def test_system_user(self): + from zope.security.management import system_user + self.assertEquals(system_user.id, + u'zope.security.management.system_user') + + self.assertEquals(system_user.title, u'System') + + for name in 'id', 'title', 'description': + self.assert_(isinstance(getattr(system_user, name), unicode)) + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/security/tests/test_module_directives.py zope3-3.5~bzr18/src/zope/security/tests/test_module_directives.py --- zope3-3.4.0/src/zope/security/tests/test_module_directives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_module_directives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,237 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Directives Tests + +$Id: test_module_directives.py 111761 2010-04-30 21:52:52Z hannosch $ +""" + +import doctest +import unittest +from pprint import PrettyPrinter + +import zope.security.zcml +from zope.interface import Interface, Attribute +from zope.component.testing import setUp, tearDown, PlacelessSetup +from zope.configuration import xmlconfig + +from zope.security import metaconfigure + +def pprint(ob, width=70): + PrettyPrinter(width=width).pprint(ob) + +class I1(Interface): + def x(): pass + y = Attribute("Y") + +class I2(I1): + def a(): pass + b = Attribute("B") + +test_perm = 'zope.security.metaconfigure.test' +test_bad_perm = 'zope.security.metaconfigure.bad' + +def test_protectModule(): + """ + >>> from zope.security.tests import test_directives + >>> from zope.security.interfaces import IPermission + >>> from zope.security.permission import Permission + + >>> from zope.component import provideUtility + + Initially, there's no checker defined for the module: + + >>> from zope.security.checker import moduleChecker + >>> moduleChecker(test_directives) + + >>> perm = Permission(test_perm, '') + >>> provideUtility(perm, IPermission, test_perm) + >>> metaconfigure.protectModule(test_directives, 'foo', test_perm) + + Now, the checker should exist and have an access dictionary with the + name and permission: + + >>> checker = moduleChecker(test_directives) + >>> cdict = checker.get_permissions + >>> pprint(cdict) + {'foo': 'zope.security.metaconfigure.test'} + + If we define additional names, they will be added to the dict: + + >>> metaconfigure.protectModule(test_directives, 'bar', test_perm) + >>> metaconfigure.protectModule(test_directives, 'baz', test_perm) + >>> pprint(cdict) + {'bar': 'zope.security.metaconfigure.test', + 'baz': 'zope.security.metaconfigure.test', + 'foo': 'zope.security.metaconfigure.test'} + + """ + +def test_allow(): + """ + + The allow directive creates actions for each named defined + directly, or via interface: + + >>> class Context(object): + ... def __init__(self): + ... self.actions = [] + ... + ... def action(self, discriminator, callable, args): + ... self.actions.append( + ... {'discriminator': discriminator, + ... 'callable': int(callable is metaconfigure.protectModule), + ... 'args': args}) + ... + ... module='testmodule' + + >>> context = Context() + >>> metaconfigure.allow(context, attributes=['foo', 'bar'], + ... interface=[I1, I2]) + + >>> context.actions.sort( + ... lambda a, b: cmp(a['discriminator'], b['discriminator'])) + >>> pprint(context.actions) + [{'args': ('testmodule', 'a', 'zope.Public'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'a')}, + {'args': ('testmodule', 'b', 'zope.Public'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'b')}, + {'args': ('testmodule', 'bar', 'zope.Public'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'bar')}, + {'args': ('testmodule', 'foo', 'zope.Public'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'foo')}, + {'args': ('testmodule', 'x', 'zope.Public'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'x')}, + {'args': ('testmodule', 'y', 'zope.Public'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'y')}] + + """ + +def test_require(): + """ + + The allow directive creates actions for each named defined + directly, or via interface: + + >>> class Context(object): + ... def __init__(self): + ... self.actions = [] + ... + ... def action(self, discriminator, callable, args): + ... self.actions.append( + ... {'discriminator': discriminator, + ... 'callable': int(callable is metaconfigure.protectModule), + ... 'args': args}) + ... + ... module='testmodule' + + >>> context = Context() + >>> metaconfigure.require(context, attributes=['foo', 'bar'], + ... interface=[I1, I2], permission='p') + + >>> context.actions.sort( + ... lambda a, b: cmp(a['discriminator'], b['discriminator'])) + >>> pprint(context.actions) + [{'args': ('testmodule', 'a', 'p'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'a')}, + {'args': ('testmodule', 'b', 'p'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'b')}, + {'args': ('testmodule', 'bar', 'p'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'bar')}, + {'args': ('testmodule', 'foo', 'p'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'foo')}, + {'args': ('testmodule', 'x', 'p'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'x')}, + {'args': ('testmodule', 'y', 'p'), + 'callable': 1, + 'discriminator': ('http://namespaces.zope.org/zope:module', + 'testmodule', + 'y')}] + + """ + +class IDummy(Interface): + + perm = zope.security.zcml.Permission(title=u'') + +perms = [] + +def dummy(context_, perm): + global perms + perms.append(perm) + + +class DirectivesTest(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(DirectivesTest, self).setUp() + from zope.security import tests + self.context = xmlconfig.file("redefineperms.zcml", tests) + + def tearDown(self): + super(DirectivesTest, self).tearDown() + perms.remove('zope.Security') + + def testRedefinePermission(self): + self.assertEqual(perms, ['zope.Security']) + +def setUpAuth(test=None): + setUp(test) + +def zcml(s): + context = xmlconfig.file('meta.zcml', package=zope.security) + xmlconfig.string(s, context) + +def reset(): + tearDown() + setUpAuth() + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite(setUp=setUp, tearDown=tearDown), + doctest.DocTestSuite('zope.security.zcml'), + unittest.makeSuite(DirectivesTest), + )) diff -Nru zope3-3.4.0/src/zope/security/tests/test_permission.py zope3-3.5~bzr18/src/zope/security/tests/test_permission.py --- zope3-3.4.0/src/zope/security/tests/test_permission.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_permission.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 permissions + +$Id: test_permission.py 111761 2010-04-30 21:52:52Z hannosch $ +""" +import unittest +from doctest import DocTestSuite + +from zope.component.testing import setUp, tearDown + +__docformat__ = "reStructuredText" + +def test_suite(): + return unittest.TestSuite([ + DocTestSuite('zope.security.permission', + setUp=setUp, tearDown=tearDown), + ]) diff -Nru zope3-3.4.0/src/zope/security/tests/test_protectclass.py zope3-3.5~bzr18/src/zope/security/tests/test_protectclass.py --- zope3-3.4.0/src/zope/security/tests/test_protectclass.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_protectclass.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,128 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 handler for 'protectClass' directive + +$Id: test_protectclass.py 95308 2009-01-28 13:03:16Z brandon_rhodes $ +""" +import unittest +from zope.interface import implements +from zope.security.checker import selectChecker +from zope.security.permission import Permission +from zope import component +from zope.component.testing import PlacelessSetup + +from zope.security.interfaces import IPermission +from zope.security.protectclass import protectName, protectLikeUnto +from zope.security.protectclass import protectSetAttribute +from zope.security.tests.modulehookup import TestModule, I + +NOTSET = [] + +P1 = "extravagant" +P2 = "paltry" + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + + component.provideUtility(Permission(P1), IPermission, P1) + component.provideUtility(Permission(P2), IPermission, P2) + + class B(object): + def m1(self): + return "m1" + def m2(self): + return "m2" + + class C(B): + implements(I) + def m3(self): + return "m3" + def m4(self): + return "m4" + + TestModule.test_base = B + TestModule.test_class = C + TestModule.test_instance = C() + self.assertState() + + def tearDown(self): + super(Test, self).tearDown() + TestModule.test_class = None + + def assertState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): + "Verify that class, instance, and methods have expected permissions." + checker = selectChecker(TestModule.test_instance) + self.assertEqual(checker.permission_id('m1'), (m1P or None)) + self.assertEqual(checker.permission_id('m2'), (m2P or None)) + self.assertEqual(checker.permission_id('m3'), (m3P or None)) + + def assertSetattrState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): + "Verify that class, instance, and methods have expected permissions." + checker = selectChecker(TestModule.test_instance) + self.assertEqual(checker.setattr_permission_id('m1'), (m1P or None)) + self.assertEqual(checker.setattr_permission_id('m2'), (m2P or None)) + self.assertEqual(checker.setattr_permission_id('m3'), (m3P or None)) + + # "testSimple*" exercises tags that do NOT have children. This mode + # inherently sets the instances as well as the class attributes. + + def testSimpleMethodsPlural(self): + protectName(TestModule.test_class, 'm1', P1) + protectName(TestModule.test_class, 'm3', P1) + self.assertState(m1P=P1, m3P=P1) + + def testLikeUntoOnly(self): + protectName(TestModule.test_base, 'm1', P1) + protectName(TestModule.test_base, 'm2', P1) + protectSetAttribute(TestModule.test_base, 'm1', P1) + protectSetAttribute(TestModule.test_base, 'm2', P1) + protectLikeUnto(TestModule.test_class, TestModule.test_base) + # m1 and m2 are in the interface, so should be set, and m3 should not: + self.assertState(m1P=P1, m2P=P1) + self.assertSetattrState(m1P=P1, m2P=P1) + + def assertSetattrState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): + "Verify that class, instance, and methods have expected permissions." + checker = selectChecker(TestModule.test_instance) + self.assertEqual(checker.setattr_permission_id('m1'), (m1P or None)) + self.assertEqual(checker.setattr_permission_id('m2'), (m2P or None)) + self.assertEqual(checker.setattr_permission_id("m3"), (m3P or None)) + + def testSetattr(self): + protectSetAttribute(TestModule.test_class, 'm1', P1) + protectSetAttribute(TestModule.test_class, 'm3', P1) + self.assertSetattrState(m1P=P1, m3P=P1) + + def testLikeUntoAsDefault(self): + protectName(TestModule.test_base, 'm1', P1) + protectName(TestModule.test_base, 'm2', P1) + protectSetAttribute(TestModule.test_base, 'm1', P1) + protectSetAttribute(TestModule.test_base, 'm2', P1) + protectLikeUnto(TestModule.test_class, TestModule.test_base) + protectName(TestModule.test_class, 'm2', P2) + protectName(TestModule.test_class, 'm3', P2) + protectSetAttribute(TestModule.test_class, 'm2', P2) + protectSetAttribute(TestModule.test_class, 'm3', P2) + # m1 and m2 are in the interface, so should be set, and m3 should not: + self.assertState(m1P=P1, m2P=P2, m3P=P2) + self.assertSetattrState(m1P=P1, m2P=P2, m3P=P2) + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/security/tests/test_protectsubclass.py zope3-3.5~bzr18/src/zope/security/tests/test_protectsubclass.py --- zope3-3.4.0/src/zope/security/tests/test_protectsubclass.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_protectsubclass.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,59 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 proper protection of inherited methods + +$Id: test_protectsubclass.py 95308 2009-01-28 13:03:16Z brandon_rhodes $ +""" +import unittest +from zope.security.checker import selectChecker +from zope.security.permission import Permission +from zope import component +from zope.component.testing import PlacelessSetup + +from zope.security.interfaces import IPermission +from zope.security.protectclass import protectName + +class Test(PlacelessSetup, unittest.TestCase): + + def testInherited(self): + + class B1(object): + def g(self): return 'B1.g' + + class B2(object): + def h(self): return 'B2.h' + + class S(B1, B2): + pass + + component.provideUtility(Permission('B1', ''), IPermission, 'B1') + component.provideUtility(Permission('S', ''), IPermission, 'S') + protectName(B1, 'g', 'B1') + protectName(S, 'g', 'S') + protectName(S, 'h', 'S') + + self.assertEqual(selectChecker(B1()).permission_id('g'), 'B1') + self.assertEqual(selectChecker(B2()).permission_id('h'), None) + self.assertEqual(selectChecker(S()).permission_id('g'), 'S') + self.assertEqual(selectChecker(S()).permission_id('h'), 'S') + + self.assertEqual(S().g(), 'B1.g') + self.assertEqual(S().h(), 'B2.h') + + +def test_suite(): + return unittest.makeSuite(Test) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/security/tests/test_proxy.py zope3-3.5~bzr18/src/zope/security/tests/test_proxy.py --- zope3-3.4.0/src/zope/security/tests/test_proxy.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_proxy.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,443 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Security proxy tests + +$Id: test_proxy.py 111761 2010-04-30 21:52:52Z hannosch $ +""" + +import unittest +from doctest import DocTestSuite + +from zope.security.proxy import getChecker, ProxyFactory, removeSecurityProxy +from zope.proxy import ProxyBase as proxy + +class Checker(object): + + ok = 1 + + unproxied_types = str, + + def check_getattr(self, object, name): + if name not in ("foo", "next", "__class__", "__name__", "__module__"): + raise RuntimeError + + def check_setattr(self, object, name): + if name != "foo": + raise RuntimeError + + def check(self, object, opname): + if not self.ok: + raise RuntimeError + + def proxy(self, value): + if type(value) in self.unproxied_types: + return value + return ProxyFactory(value, self) + + +class Something: + def __init__(self): + self.foo = [1,2,3] + def __getitem__(self, key): + return self.foo[key] + def __setitem__(self, key, value): + self.foo[key] = value + def __delitem__(self, key): + del self.foo[key] + def __call__(self, arg): + return 42 + def __eq__(self, other): + return self is other + def __hash__(self): + return 42 + def __iter__(self): + return self + def next(self): + return 42 # Infinite sequence + def __len__(self): + return 42 + def __nonzero__(self): + return 1 + def __getslice__(self, i, j): + return [42] + def __setslice__(self, i, j, value): + if value != [42]: + raise ValueError + def __contains__(self, x): + return x == 42 + + +class ProxyTests(unittest.TestCase): + + def setUp(self): + self.x = Something() + self.c = Checker() + self.p = ProxyFactory(self.x, self.c) + + def shouldFail(self, *args): + self.c.ok = 0 + self.assertRaises(RuntimeError, *args) + self.c.ok = 1 + + def testDerivation(self): + self.assert_(isinstance(self.p, proxy)) + + def testStr(self): + self.assertEqual(str(self.p), str(self.x)) + + x = Something() + c = Checker() + c.ok = 0 + p = ProxyFactory(x, c) + s = str(p) + self.failUnless(s.startswith( + ">y", "x&y", "x|y", "x^y", + ] + + def test_binops(self): + P = self.c.proxy + for expr in self.binops: + first = 1 + for x in [1, P(1)]: + for y in [2, P(2)]: + if first: + z = eval(expr) + first = 0 + else: + self.assertEqual(removeSecurityProxy(eval(expr)), z, + "x=%r; y=%r; expr=%r" % (x, y, expr)) + self.shouldFail(lambda x, y: eval(expr), x, y) + + def test_inplace(self): + # TODO: should test all inplace operators... + P = self.c.proxy + + pa = P(1) + pa += 2 + self.assertEqual(removeSecurityProxy(pa), 3) + + a = [1, 2, 3] + pa = qa = P(a) + pa += [4, 5, 6] + self.failUnless(pa is qa) + self.assertEqual(a, [1, 2, 3, 4, 5, 6]) + + def doit(): + pa = P(1) + pa += 2 + self.shouldFail(doit) + + pa = P(2) + pa **= 2 + self.assertEqual(removeSecurityProxy(pa), 4) + + def doit(): + pa = P(2) + pa **= 2 + self.shouldFail(doit) + + def test_coerce(self): + P = self.c.proxy + + # Before 2.3, coerce() of two proxies returns them unchanged + import sys + fixed_coerce = sys.version_info >= (2, 3, 0) + + x = P(1) + y = P(2) + a, b = coerce(x, y) + self.failUnless(a is x and b is y) + + x = P(1) + y = P(2.1) + a, b = coerce(x, y) + self.failUnless(removeSecurityProxy(a) == 1.0 and b is y) + if fixed_coerce: + self.failUnless(type(removeSecurityProxy(a)) is float and b is y) + + x = P(1.1) + y = P(2) + a, b = coerce(x, y) + self.failUnless(a is x and removeSecurityProxy(b) == 2.0) + if fixed_coerce: + self.failUnless(a is x and type(removeSecurityProxy(b)) is float) + + x = P(1) + y = 2 + a, b = coerce(x, y) + self.failUnless(a is x and b is y) + + x = P(1) + y = 2.1 + a, b = coerce(x, y) + self.failUnless(type(removeSecurityProxy(a)) is float and b is y) + + x = P(1.1) + y = 2 + a, b = coerce(x, y) + self.failUnless(a is x and type(removeSecurityProxy(b)) is float) + + x = 1 + y = P(2) + a, b = coerce(x, y) + self.failUnless(a is x and b is y) + + x = 1.1 + y = P(2) + a, b = coerce(x, y) + self.failUnless(a is x and type(removeSecurityProxy(b)) is float) + + x = 1 + y = P(2.1) + a, b = coerce(x, y) + self.failUnless(type(removeSecurityProxy(a)) is float and b is y) + +def test_using_mapping_slots_hack(): + """The security proxy will use mapping slots, on the checker to go faster + + If a checker implements normally, a checkers's check and + check_getattr methods are used to check operator and attribute + access: + + >>> class Checker(object): + ... def check(self, object, name): + ... print 'check', name + ... def check_getattr(self, object, name): + ... print 'check_getattr', name + ... def proxy(self, object): + ... return 1 + >>> def f(): + ... pass + >>> p = ProxyFactory(f, Checker()) + >>> p.__name__ + check_getattr __name__ + 1 + >>> p() + check __call__ + 1 + + But, if the checker has a __setitem__ method: + + >>> def __setitem__(self, object, name): + ... print '__setitem__', name + >>> Checker.__setitem__ = __setitem__ + + It will be used rather than either check or check_getattr: + + >>> p.__name__ + __setitem__ __name__ + 1 + >>> p() + __setitem__ __call__ + 1 + + If a checker has a __getitem__ method: + + >>> def __getitem__(self, object): + ... return 2 + >>> Checker.__getitem__ = __getitem__ + + It will be used rather than it's proxy method: + + >>> p.__name__ + __setitem__ __name__ + 2 + >>> p() + __setitem__ __call__ + 2 + + """ + + +def test_suite(): + suite = unittest.makeSuite(ProxyTests) + suite.addTest(DocTestSuite()) + suite.addTest(DocTestSuite('zope.security.proxy')) + return suite diff -Nru zope3-3.4.0/src/zope/security/tests/test_set_checkers.py zope3-3.5~bzr18/src/zope/security/tests/test_set_checkers.py --- zope3-3.4.0/src/zope/security/tests/test_set_checkers.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_set_checkers.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,220 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 checkers for standard types + +This is a test of the assertions made in +zope.security.checkers._default_checkers. + +$Id: test_set_checkers.py 111761 2010-04-30 21:52:52Z hannosch $ +""" +import sys +import unittest +from doctest import DocTestSuite + +from zope.security.checker import ProxyFactory +from zope.security.interfaces import ForbiddenAttribute + +def check_forbidden_get(object, attr): + try: + return getattr(object, attr) + except ForbiddenAttribute, e: + return 'ForbiddenAttribute: %s' % e[0] + +def test_set(): + """Test that we can do everything we expect to be able to do + + with proxied sets. + + >>> us = set((1, 2)) + >>> s = ProxyFactory(us) + + >>> check_forbidden_get(s, 'add') # Verify that we are protected + 'ForbiddenAttribute: add' + >>> check_forbidden_get(s, 'remove') # Verify that we are protected + 'ForbiddenAttribute: remove' + >>> check_forbidden_get(s, 'discard') # Verify that we are protected + 'ForbiddenAttribute: discard' + >>> check_forbidden_get(s, 'pop') # Verify that we are protected + 'ForbiddenAttribute: pop' + >>> check_forbidden_get(s, 'clear') # Verify that we are protected + 'ForbiddenAttribute: clear' + + >>> len(s) + 2 + + >>> 1 in s + True + + >>> 1 not in s + False + + >>> s.issubset(set((1,2,3))) + True + + >>> s.issuperset(set((1,2,3))) + False + + >>> c = s.union(set((2, 3))) + >>> sorted(c) + [1, 2, 3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s | set((2, 3)) + >>> sorted(c) + [1, 2, 3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s | ProxyFactory(set((2, 3))) + >>> sorted(c) + [1, 2, 3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = set((2, 3)) | s + >>> sorted(c) + [1, 2, 3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s.intersection(set((2, 3))) + >>> sorted(c) + [2] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s & set((2, 3)) + >>> sorted(c) + [2] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s & ProxyFactory(set((2, 3))) + >>> sorted(c) + [2] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = set((2, 3)) & s + >>> sorted(c) + [2] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s.difference(set((2, 3))) + >>> sorted(c) + [1] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s - ProxyFactory(set((2, 3))) + >>> sorted(c) + [1] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s - set((2, 3)) + >>> sorted(c) + [1] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = set((2, 3)) - s + >>> sorted(c) + [3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s.symmetric_difference(set((2, 3))) + >>> sorted(c) + [1, 3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s ^ set((2, 3)) + >>> sorted(c) + [1, 3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s ^ ProxyFactory(set((2, 3))) + >>> sorted(c) + [1, 3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = set((2, 3)) ^ s + >>> sorted(c) + [1, 3] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> c = s.copy() + >>> sorted(c) + [1, 2] + >>> check_forbidden_get(c, 'add') + 'ForbiddenAttribute: add' + + >>> str(s) == str(us) + True + + >>> repr(s) == repr(us) + True + + Always available: + + >>> s < us + False + >>> s > us + False + >>> s <= us + True + >>> s >= us + True + >>> s == us + True + >>> s != us + False + + Note that you can't compare proxied sets with other proxied sets + due a limitaion in the set comparison functions which won't work + with any kind of proxy. + + >>> bool(s) + True + >>> s.__class__ == set + True + """ + +def setUpFrozenSet(test): + test.globs['set'] = frozenset + +def setUpSet(test): + import sets + test.globs['set'] = sets.Set + +def setUpImmutableSet(test): + import sets + test.globs['set'] = sets.ImmutableSet + +def test_suite(): + doctests = [ + DocTestSuite(), + DocTestSuite(setUp=setUpFrozenSet), + ] + if sys.version_info[:2] < (2, 6): + doctests.append(DocTestSuite(setUp=setUpSet)) + doctests.append(DocTestSuite(setUp=setUpImmutableSet)) + return unittest.TestSuite(doctests) diff -Nru zope3-3.4.0/src/zope/security/tests/test_simpleinteraction.py zope3-3.5~bzr18/src/zope/security/tests/test_simpleinteraction.py --- zope3-3.4.0/src/zope/security/tests/test_simpleinteraction.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_simpleinteraction.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,81 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Unit tests for zope.security.simpleinteraction. + +$Id: test_simpleinteraction.py 70826 2006-10-20 03:41:16Z baijum $ +""" +import unittest + +from zope.interface.verify import verifyObject +from zope.security.interfaces import IInteraction +from zope.security.simplepolicies import ParanoidSecurityPolicy + +class RequestStub(object): + + def __init__(self, principal=None): + self.principal = principal + self.interaction = None + + +class TestInteraction(unittest.TestCase): + + def test(self): + interaction = ParanoidSecurityPolicy() + verifyObject(IInteraction, interaction) + + def test_add(self): + rq = RequestStub() + interaction = ParanoidSecurityPolicy() + interaction.add(rq) + self.assert_(rq in interaction.participations) + self.assert_(rq.interaction is interaction) + + # rq already added + self.assertRaises(ValueError, interaction.add, rq) + + interaction2 = ParanoidSecurityPolicy() + self.assertRaises(ValueError, interaction2.add, rq) + + def test_remove(self): + rq = RequestStub() + interaction = ParanoidSecurityPolicy() + + self.assertRaises(ValueError, interaction.remove, rq) + + interaction.add(rq) + + interaction.remove(rq) + self.assert_(rq not in interaction.participations) + self.assert_(rq.interaction is None) + + def testCreateInteraction(self): + i1 = ParanoidSecurityPolicy() + verifyObject(IInteraction, i1) + self.assertEquals(list(i1.participations), []) + + user = object() + request = RequestStub(user) + i2 = ParanoidSecurityPolicy(request) + verifyObject(IInteraction, i2) + self.assertEquals(list(i2.participations), [request]) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestInteraction)) + return suite + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/security/tests/test_standard_checkers.py zope3-3.5~bzr18/src/zope/security/tests/test_standard_checkers.py --- zope3-3.4.0/src/zope/security/tests/test_standard_checkers.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/tests/test_standard_checkers.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,550 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 checkers for standard types + +This is a test of the assertions made in +zope.security.checkers._default_checkers. +""" +from zope.security.checker import ProxyFactory, NamesChecker +from zope.security.interfaces import ForbiddenAttribute + +import sys + +def check_forbidden_get(object, attr): + try: + return getattr(object, attr) + except ForbiddenAttribute, e: + return 'ForbiddenAttribute: %s' % e[0] + + +def check_forbidden_setitem(object, item, value): + try: + object[item] = value + except ForbiddenAttribute, e: + return 'ForbiddenAttribute: %s' % e[0] + + +def check_forbidden_delitem(object, item): + try: + del object[item] + except ForbiddenAttribute, e: + return 'ForbiddenAttribute: %s' % e[0] + + +def check_forbidden_call(callable, *args): + try: + return callable(*args) + except ForbiddenAttribute, e: + return 'ForbiddenAttribute: %s' % e[0] + + +def test_dict(): + """Test that we can do everything we expect to be able to do + + with proxied dicts. + + >>> d = ProxyFactory({'a': 1, 'b': 2}) + + >>> check_forbidden_get(d, 'clear') # Verify that we are protected + 'ForbiddenAttribute: clear' + >>> check_forbidden_setitem(d, 3, 4) # Verify that we are protected + 'ForbiddenAttribute: __setitem__' + + >>> d['a'] + 1 + >>> len(d) + 2 + >>> list(d) + ['a', 'b'] + >>> d.get('a') + 1 + >>> int(d.has_key('a')) + 1 + + >>> c = d.copy() + >>> check_forbidden_get(c, 'clear') + 'ForbiddenAttribute: clear' + >>> int(str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")) + 1 + + >>> int(`c` in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}")) + 1 + + + >>> def sorted(x): + ... x = list(x) + ... x.sort() + ... return x + + >>> sorted(d.keys()) + ['a', 'b'] + >>> sorted(d.values()) + [1, 2] + >>> sorted(d.items()) + [('a', 1), ('b', 2)] + + >>> sorted(d.iterkeys()) + ['a', 'b'] + >>> sorted(d.itervalues()) + [1, 2] + >>> sorted(d.iteritems()) + [('a', 1), ('b', 2)] + + Always available: + + >>> int(d < d) + 0 + >>> int(d > d) + 0 + >>> int(d <= d) + 1 + >>> int(d >= d) + 1 + >>> int(d == d) + 1 + >>> int(d != d) + 0 + >>> int(bool(d)) + 1 + >>> int(d.__class__ == dict) + 1 + + """ + +def test_list(): + """Test that we can do everything we expect to be able to do + + with proxied lists. + + >>> l = ProxyFactory([1, 2]) + >>> check_forbidden_delitem(l, 0) + 'ForbiddenAttribute: __delitem__' + >>> check_forbidden_setitem(l, 0, 3) + 'ForbiddenAttribute: __setitem__' + >>> l[0] + 1 + >>> l[0:1] + [1] + >>> check_forbidden_setitem(l[:1], 0, 2) + 'ForbiddenAttribute: __setitem__' + >>> len(l) + 2 + >>> tuple(l) + (1, 2) + >>> int(1 in l) + 1 + >>> l.index(2) + 1 + >>> l.count(2) + 1 + >>> str(l) + '[1, 2]' + >>> `l` + '[1, 2]' + >>> l + l + [1, 2, 1, 2] + + Always available: + + >>> int(l < l) + 0 + >>> int(l > l) + 0 + >>> int(l <= l) + 1 + >>> int(l >= l) + 1 + >>> int(l == l) + 1 + >>> int(l != l) + 0 + >>> int(bool(l)) + 1 + >>> int(l.__class__ == list) + 1 + + + """ + +def test_tuple(): + """Test that we can do everything we expect to be able to do + + with proxied lists. + + >>> l = ProxyFactory((1, 2)) + >>> l[0] + 1 + >>> l[0:1] + (1,) + >>> len(l) + 2 + >>> list(l) + [1, 2] + >>> int(1 in l) + 1 + >>> str(l) + '(1, 2)' + >>> `l` + '(1, 2)' + >>> l + l + (1, 2, 1, 2) + + Always available: + + >>> int(l < l) + 0 + >>> int(l > l) + 0 + >>> int(l <= l) + 1 + >>> int(l >= l) + 1 + >>> int(l == l) + 1 + >>> int(l != l) + 0 + >>> int(bool(l)) + 1 + >>> int(l.__class__ == tuple) + 1 + + """ + +def test_iter(): + """ + >>> list(ProxyFactory(iter([1, 2]))) + [1, 2] + >>> list(ProxyFactory(iter((1, 2)))) + [1, 2] + >>> list(ProxyFactory(iter({1:1, 2:2}))) + [1, 2] + >>> def f(): + ... for i in 1, 2: + ... yield i + ... + >>> list(ProxyFactory(f())) + [1, 2] + >>> list(ProxyFactory(f)()) + [1, 2] + """ + +def test_new_class(): + """ + + >>> class C(object): + ... x = 1 + >>> C = ProxyFactory(C) + >>> check_forbidden_call(C) + 'ForbiddenAttribute: __call__' + >>> check_forbidden_get(C, '__dict__') + 'ForbiddenAttribute: __dict__' + >>> s = str(C) + >>> s = `C` + >>> int(C.__module__ == __name__) + 1 + >>> len(C.__bases__) + 1 + >>> len(C.__mro__) + 2 + + Always available: + + >>> int(C < C) + 0 + >>> int(C > C) + 0 + >>> int(C <= C) + 1 + >>> int(C >= C) + 1 + >>> int(C == C) + 1 + >>> int(C != C) + 0 + >>> int(bool(C)) + 1 + >>> int(C.__class__ == type) + 1 + + """ + +def test_new_instance(): + """ + + >>> class C(object): + ... x, y = 1, 2 + >>> c = ProxyFactory(C(), NamesChecker(['x'])) + >>> check_forbidden_get(c, 'y') + 'ForbiddenAttribute: y' + >>> check_forbidden_get(c, 'z') + 'ForbiddenAttribute: z' + >>> c.x + 1 + >>> int(c.__class__ == C) + 1 + + Always available: + + >>> int(c < c) + 0 + >>> int(c > c) + 0 + >>> int(c <= c) + 1 + >>> int(c >= c) + 1 + >>> int(c == c) + 1 + >>> int(c != c) + 0 + >>> int(bool(c)) + 1 + >>> int(c.__class__ == C) + 1 + + """ + +def test_classic_class(): + """ + + >>> class C: + ... x = 1 + >>> C = ProxyFactory(C) + >>> check_forbidden_call(C) + 'ForbiddenAttribute: __call__' + >>> check_forbidden_get(C, '__dict__') + 'ForbiddenAttribute: __dict__' + >>> s = str(C) + >>> s = `C` + >>> int(C.__module__ == __name__) + 1 + >>> len(C.__bases__) + 0 + + Always available: + + >>> int(C < C) + 0 + >>> int(C > C) + 0 + >>> int(C <= C) + 1 + >>> int(C >= C) + 1 + >>> int(C == C) + 1 + >>> int(C != C) + 0 + >>> int(bool(C)) + 1 + + """ + +def test_classic_instance(): + """ + + >>> class C(object): + ... x, y = 1, 2 + >>> c = ProxyFactory(C(), NamesChecker(['x'])) + >>> check_forbidden_get(c, 'y') + 'ForbiddenAttribute: y' + >>> check_forbidden_get(c, 'z') + 'ForbiddenAttribute: z' + >>> c.x + 1 + >>> int(c.__class__ == C) + 1 + + Always available: + + >>> int(c < c) + 0 + >>> int(c > c) + 0 + >>> int(c <= c) + 1 + >>> int(c >= c) + 1 + >>> int(c == c) + 1 + >>> int(c != c) + 0 + >>> int(bool(c)) + 1 + >>> int(c.__class__ == C) + 1 + + """ + +def test_rocks(): + """ + >>> int(type(ProxyFactory( object() )) is object) + 1 + >>> int(type(ProxyFactory( 1 )) is int) + 1 + >>> int(type(ProxyFactory( 1.0 )) is float) + 1 + >>> int(type(ProxyFactory( 1l )) is long) + 1 + >>> int(type(ProxyFactory( 1j )) is complex) + 1 + >>> int(type(ProxyFactory( None )) is type(None)) + 1 + >>> int(type(ProxyFactory( 'xxx' )) is str) + 1 + >>> int(type(ProxyFactory( u'xxx' )) is unicode) + 1 + >>> int(type(ProxyFactory( True )) is type(True)) + 1 + + >>> from datetime import timedelta, datetime, date, time, tzinfo + >>> int(type(ProxyFactory( timedelta(1) )) is timedelta) + 1 + >>> int(type(ProxyFactory( datetime(2000, 1, 1) )) is datetime) + 1 + >>> int(type(ProxyFactory( date(2000, 1, 1) )) is date) + 1 + >>> int(type(ProxyFactory( time() )) is time) + 1 + >>> int(type(ProxyFactory( tzinfo() )) is tzinfo) + 1 + + >>> try: + ... from pytz import UTC + ... except ImportError: # pytz checker only if pytz is present. + ... UTC = None + >>> int(UTC is None or type(ProxyFactory( UTC )) is type(UTC)) + 1 + """ + +def test_iter_of_sequences(): + """ + >>> class X(object): + ... d = 1, 2, 3 + ... def __getitem__(self, i): + ... return self.d[i] + ... + >>> x = X() + + We can iterate over sequences + + >>> list(x) + [1, 2, 3] + >>> c = NamesChecker(['__getitem__']) + >>> p = ProxyFactory(x, c) + + Even if they are proxied + + >>> list(p) + [1, 2, 3] + + But if the class has an iter: + + >>> X.__iter__ = lambda self: iter(self.d) + >>> list(x) + [1, 2, 3] + + We shouldn't be able to iterate if we don't have an assertion: + + >>> check_forbidden_call(list, p) + 'ForbiddenAttribute: __iter__' + """ + +def test_interfaces_and_declarations(): + """Test that we can still use interfaces though proxies + + >>> import zope.interface + >>> class I(zope.interface.Interface): + ... pass + >>> class IN(zope.interface.Interface): + ... pass + >>> class II(zope.interface.Interface): + ... pass + >>> class N(object): + ... zope.interface.implements(I) + ... zope.interface.classProvides(IN) + >>> n = N() + >>> zope.interface.directlyProvides(n, II) + >>> from zope.security.checker import ProxyFactory + >>> N = ProxyFactory(N) + >>> n = ProxyFactory(n) + >>> I.implementedBy(N) + True + >>> IN.providedBy(N) + True + >>> I.providedBy(n) + True + >>> II.providedBy(n) + True + """ + +if sys.version_info >= (2, 6): + def test_ABCMeta(): + """ + Test that we work with the ABCMeta meta class + + >>> import abc + >>> class MyABC: + ... __metaclass__ = abc.ABCMeta + + >>> class Foo(MyABC): pass + + >>> class Bar(Foo): pass + + >>> PBar = ProxyFactory(Bar) + >>> [c.__name__ for c in PBar.__mro__] + ['Bar', 'Foo', 'MyABC', 'object'] + + >>> issubclass(PBar, Foo) + True + + >>> issubclass(Bar, Foo) + True + + >>> PBar = ProxyFactory(PBar) + >>> check_forbidden_call(PBar) + 'ForbiddenAttribute: __call__' + >>> check_forbidden_get(PBar, '__dict__') + 'ForbiddenAttribute: __dict__' + >>> s = str(PBar) + >>> s = `PBar` + >>> int(PBar.__module__ == __name__) + 1 + >>> len(PBar.__bases__) + 1 + + Always available: + + >>> int(PBar < PBar) + 0 + >>> int(PBar > PBar) + 0 + >>> int(PBar <= PBar) + 1 + >>> int(PBar >= PBar) + 1 + >>> int(PBar == PBar) + 1 + >>> int(PBar != PBar) + 0 + >>> int(bool(PBar)) + 1 + >>> int(PBar.__class__ == abc.ABCMeta) + 1 + """ + + +def test_suite(): + from doctest import DocTestSuite + return DocTestSuite() diff -Nru zope3-3.4.0/src/zope/security/untrustedinterpreter.txt zope3-3.5~bzr18/src/zope/security/untrustedinterpreter.txt --- zope3-3.4.0/src/zope/security/untrustedinterpreter.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedinterpreter.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,220 @@ +====================== +Untrusted interpreters +====================== + +Untrusted programs are executed by untrusted interpreters. Untrusted +interpreters make use of security proxies to prevent un-mediated +access to assets. An untrusted interpreter defines an environment for +running untrusted programs. All objects within the environment are +either: + +- "safe" objects created internally by the environment or created in + the course of executing the untrusted program, or + +- "basic" objects + +- security-proxied non-basic objects + +The environment includes proxied functions for accessing objects +outside of the environment. These proxied functions provide the only +way to access information outside the environment. Because these +functions are proxied, as described below, any access to objects +outside the environment is mediated by the target security functions. + +Safe objects are objects whose operations, except for attribute +retrieval, and methods access only information stored within the +objects or passed as arguments. Safe objects contained within the +interpreter environment can contain only information that is already +in the environment or computed directly from information that is +included in the environment. For this reason, safe objects created +within the environment cannot be used to directly access information +outside the environment. + +Safe objects have some attributes that could (very) indirectly be used +to access assets. For this reason, an untrusted interpreter always +proxies the results of attribute accesses on a safe objects. + +Basic objects are safe objects that are used to represent elemental +data values such as strings and numbers. Basic objects require a +lower level of protection than non-basic objects, as will be described +detail in a later section. + +Security proxies mediate all object operations. Any operation +access is checked to see whether a subject is authorized to perform +the operation. All operation results other than basic objects are, in +turn, security proxied. Security proxies will be described in greater +detail in a later section. Any operation on a security proxy that +results in a non-basic object is also security proxied. + +All external resources needed to perform an operation are security +proxied. + +Let's consider the trusted interpreter for evaluating URLs. In +operation 1 of the example, the interpreter uses a proxied method for +getting the system root object. Because the method is proxied, the +result of calling the method and the operation is also proxied. + +The interpreter has a function for traversing objects. This function +is proxied. When traversing an object, the function is passed an +object and a name. In operation 2, the function is passed the result +of operation 1, which is the proxied root object and the name 'A'. We +may traverse an object by invoking an operation on it. For example, +we may use an operation to get a sub-object. Because any operation on a +proxied object returns a proxied object or a basic object, the result +is either a proxied object or a basic object. Traversal may also look +up a component. For example, in operation 1, we might look up a +presentation component named "A" for the root object. In this case, +the external object is not proxied, but, when it is returned from the +traversal function, it is proxied (unless it is a a basic object) +because the traversal function is proxied, and the result of calling a +proxied function is proxied (unless the result is a basic object). +Operation 3 proceeds in the same way. + +When we get to operation 4, we use a function for computing the +default presentation of the result of operation 3. As with traversal, +the result of getting the default presentation is either a proxied +object or a basic object because the function for getting the default +presentation is proxied. + +When we get to the last operation, we have either a proxied object or a +basic object. If the result of operation 4 is a basic object, we +simply convert it to a string and return it as the result page. If +the result of operation 4 is a non-basic object, we invoke a render +operation on it and return the result as a string. + +Note that an untrusted interpreter may or may not provide protection +against excessive resource usage. Different interpreters will provide +different levels of service with respect to limitations on resource +usage. + +If an untrusted interpreter performs an attribute access, the trusted +interpreter must proxy the result unless the result is a basic object. + +In summary, an untrusted interpreter assures that any access to assets +is mediated through security proxies by creating an environment to run +untrusted code and making sure that: + +- The only way to access anything from outside of the environment is + to call functions that are proxied in the environment. + +- Results of any attribute access in the environment are proxied + unless the results are basic objects. + +Security proxies +---------------- + +Security proxies are objects that wrap and mediate access to objects. + +The Python programming language used by Zope defines a set of specific +named low-level operations. In addition to operations, Python objects +can have attributes, used to represent data and methods. Attributes +are accessed using a dot notation. Applications can, and usually do, +define methods to provide extended object behaviors. Methods are +accessed as attributes through the low-level operation named +"__getattribute__". The Python code:: + + a.b() + +invokes 2 operations: + + 1. Use the low-level `__getattribute__` operation with the name "b". + + 2. Use the low-level '__call__' operation on the result of the first + operation. + +For all operations except the `__getattribute__` and +`__setattribute__` operations, security proxies have a permission +value defined by the permission-declaration subsystem. Two special +permission values indicate that access is either forbidden (never +allowed) or public (always allowed). For all other permission values, +the authorization subsystem is used to decide whether the subject has +the permission for the proxied object. If the subject has the +permission, then access to the operation is allowed. Otherwise, access +is denied. + +For getting or setting attributes, a proxy has permissions for getting +and a permission for setting attribute values for a given attribute +name. As described above, these permissions may be one of the two +special permission values indicating forbidden or public access, or +another permission value that must be checked with the authorization +system. + +For all objects, Zope defines the following operations to be always public: + + comparison + "__lt__", "__le__", "__eq__", "__gt__", "__ge__", "__ne__" + + hash + "__hash__" + + boolean value + "__nonzero__" + + class introspection + "__class__" + + interface introspection + "__providedBy__", "__implements__" + + adaptation + "__conform__" + + low-level string representation + "__repr__" + +The result of an operation on a proxied object is a security proxy +unless the result is a basic value. + +Basic objects +------------- + +Basic objects are safe immutable objects that contain only immutable +subobjects. Examples of basic objects include: + +- Strings, + +- Integers (long and normal), + +- Floating-point objects, + +- Date-time objects, + +- Boolean objects (True and False), and + +- The special (nil) object, None. + +Basic objects are safe, so, as described earlier, operations on basic +objects, other than attribute access, use only information contained +within the objects or information passed to them. For this reason, +basic objects cannot be used to access information outside of the +untrusted interpreter environment. + +The decision not to proxy basic objects is largely an optimization. +It allows low-level safe computation to be performed without +unnecessary overhead, + +Note that a basic object could contain sensitive information, but such +a basic object would need to be obtained by making a call on a proxied +object. Therefore, the access to the basic object in the first place +is mediated by the security functions. + +Rationale for mutable safe objects +---------------------------------- + +Some safe objects are not basic. For these objects, we proxy the +objects if they originate from outside of the environment. We do this +for two reasons: + +1. Non-basic objects from outside the environment need to be proxied + to prevent unauthorized access to information. + +2. We need to prevent un-mediated change of information from outside of + the environment. + +We don't proxy safe objects created within the environment. This is +safe to do because such safe objects can contain and provide access to +information already in the environment. Sometimes the interpreter or +the interpreted program needs to be able to create simple data +containers to hold information computed in the course of the program +execution. Several safe container types are provided for this +purpose. diff -Nru zope3-3.4.0/src/zope/security/untrustedpython/builtins.py zope3-3.5~bzr18/src/zope/security/untrustedpython/builtins.py --- zope3-3.4.0/src/zope/security/untrustedpython/builtins.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedpython/builtins.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,126 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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 +# +############################################################################## +"""Protection of builtin objects. + +$Id: builtins.py 87149 2008-06-05 01:01:01Z srichter $ +""" +from zope.security.proxy import ProxyFactory +import new + +def SafeBuiltins(): + + builtins = {} + + from zope.security.checker import NamesChecker + import __builtin__ + + _builtinTypeChecker = NamesChecker( + ['__str__', '__repr__', '__name__', '__module__', + '__bases__', '__call__']) + + # It's better to say what is safe than it say what is not safe + for name in [ + + # Names of safe objects. See untrustedinterpreter.txt for a + # definition of safe objects. + + 'ArithmeticError', 'AssertionError', 'AttributeError', + 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', + 'Exception', 'FloatingPointError', 'IOError', 'ImportError', + 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', + 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', + 'NotImplementedError', 'OSError', 'OverflowError', 'OverflowWarning', + 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', + 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', + 'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError', + 'UnicodeError', 'UserWarning', 'ValueError', 'Warning', + 'ZeroDivisionError', + '__debug__', '__name__', '__doc__', 'abs', 'apply', 'bool', + 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', + 'complex', 'copyright', 'credits', 'delattr', + 'dict', 'divmod', 'filter', 'float', 'frozenset', 'getattr', + 'hasattr', 'hash', 'hex', 'id', 'int', 'isinstance', + 'issubclass', 'iter', 'len', 'license', 'list', + 'long', 'map', 'max', 'min', 'object', 'oct', 'ord', 'pow', + 'property', 'quit', 'range', 'reduce', 'repr', 'reversed', 'round', + 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'super', + 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip', + 'True', 'False', + + # TODO: dir segfaults with a seg fault due to a bad tuple + # check in merge_class_dict in object.c. The assert macro + # seems to be doing the wrong think. Basically, if an object + # has bases, then bases is assumed to be a tuple. + #dir, + ]: + + try: + value = getattr(__builtin__, name) + except AttributeError: + pass + else: + if isinstance(value, type): + value = ProxyFactory(value, _builtinTypeChecker) + else: + value = ProxyFactory(value) + builtins[name] = value + + from sys import modules + + def _imp(name, fromlist, prefix=''): + module = modules.get(prefix+name) + if module is not None: + if fromlist or ('.' not in name): + return module + return modules[prefix+name.split('.')[0]] + + def __import__(name, globals=None, locals=None, fromlist=()): + # Waaa, we have to emulate __import__'s weird semantics. + + if globals: + __name__ = globals.get('__name__') + if __name__: + # Maybe do a relative import + if '__path__' not in globals: + # We have an ordinary module, not a package, + # so remove last name segment: + __name__ = '.'.join(__name__.split('.')[:-1]) + if __name__: + module = _imp(name, fromlist, __name__+'.') + if module is not None: + return module + + module = _imp(name, fromlist) + if module is not None: + return module + + raise ImportError(name) + + builtins['__import__'] = ProxyFactory(__import__) + + return builtins + +class ImmutableModule(new.module): + def __init__(self, name='__builtins__', **kw): + new.module.__init__(self, name) + self.__dict__.update(kw) + + def __setattr__(self, name, v): + raise AttributeError(name) + + def __delattr__(self, name): + raise AttributeError(name) + + +SafeBuiltins = ImmutableModule(**SafeBuiltins()) diff -Nru zope3-3.4.0/src/zope/security/untrustedpython/builtins.txt zope3-3.5~bzr18/src/zope/security/untrustedpython/builtins.txt --- zope3-3.4.0/src/zope/security/untrustedpython/builtins.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedpython/builtins.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,114 @@ +Safe Builtins +============= + +When executing untrusted Python code, we need to make sure that we +only give the code access to safe, basic or proxied objects. This +included the builtin objects provided to Python code through a special +__builtins__ module in globals. The `builtins` module provides a +suitable module object: + + >>> from zope.security.untrustedpython.builtins import SafeBuiltins + >>> d = {'__builtins__': SafeBuiltins} + >>> exec 'x = str(1)' in d + >>> d['x'] + '1' + +The object is immutable: + + >>> SafeBuiltins.foo = 1 + Traceback (most recent call last): + ... + AttributeError: foo + + >>> del SafeBuiltins['getattr'] + Traceback (most recent call last): + ... + TypeError: object does not support item deletion + + + + Exception raised: + ... + TypeError: object does not support item deletion + +(Note that you can mutate it through its `__dict__` attribute, + however, when combined with the untrusted code compiler, getting the + `__dict__` attribute will return a proxied object that will prevent + mutation.) + +It contains items with keys that are all strings and values that are +either proxied or are basic types: + + >>> from zope.security.proxy import Proxy + >>> for key, value in SafeBuiltins.__dict__.items(): + ... if not isinstance(key, str): + ... raise TypeError(key) + ... if value is not None and not isinstance(value, (Proxy, int, str)): + ... raise TypeError(value, key) + +It doesn't contain unsafe items, such as eval, globals, etc: + + >>> SafeBuiltins.eval + Traceback (most recent call last): + ... + AttributeError: 'ImmutableModule' object has no attribute 'eval' + >>> SafeBuiltins.globals + Traceback (most recent call last): + ... + AttributeError: 'ImmutableModule' object has no attribute 'globals' + +The safe builtins also contains a custom __import__ function. + + >>> imp = SafeBuiltins.__import__ + +As with regular import, it only returns the top-level package if no +fromlist is specified: + + >>> import zope.security + >>> imp('zope.security') == zope + True + >>> imp('zope.security', {}, {}, ['*']) == zope.security + True + +Note that the values returned are proxied: + + >>> type(imp('zope.security')) is Proxy + True + +This means that, having imported a module, you will only be able to +access attributes for which you are authorized. + +Unlike regular __import__, you can only import modules that have been +previously imported. This is to prevent unauthorized execution of +module-initialization code: + + >>> security = zope.security + >>> import sys + >>> del sys.modules['zope.security'] + >>> imp('zope.security') + Traceback (most recent call last): + ... + ImportError: zope.security + + >>> sys.modules['zope.security'] = security + +Package-relative imports are supported (for now): + + >>> imp('security', {'__name__': 'zope', '__path__': []}) == security + True + >>> imp('security', {'__name__': 'zope.foo'}) == zope.security + True + + >>> imp('security.untrustedpython', {'__name__': 'zope.foo'}) == security + True + >>> from zope.security import untrustedpython + >>> imp('security.untrustedpython', {'__name__': 'zope.foo'}, {}, ['*'] + ... ) == untrustedpython + True + + + + + + + diff -Nru zope3-3.4.0/src/zope/security/untrustedpython/__init__.py zope3-3.5~bzr18/src/zope/security/untrustedpython/__init__.py --- zope3-3.4.0/src/zope/security/untrustedpython/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedpython/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# diff -Nru zope3-3.4.0/src/zope/security/untrustedpython/interpreter.py zope3-3.5~bzr18/src/zope/security/untrustedpython/interpreter.py --- zope3-3.4.0/src/zope/security/untrustedpython/interpreter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedpython/interpreter.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,78 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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 +# +############################################################################## +"""Restricted interpreter. + +TODO: This code needs a serious security review!!! + +$Id: interpreter.py 26819 2004-07-28 19:37:35Z jim $ +""" +from zope.security.untrustedpython.builtins import SafeBuiltins +from zope.security.untrustedpython.rcompile import compile +import warnings + +class RestrictedInterpreter(object): + + def __init__(self): + warnings.warn("RestrictedInterpreter was deprecated 2004/7/27", + DeprecationWarning, 2) + self.globals = {} + self.locals = {} + + def ri_exec(self, code): + """Execute Python code in a restricted environment. + + The value of code can be either source or binary code.""" + if isinstance(code, basestring): + code = compile(code, '', 'exec') + self.globals['__builtins__'] = SafeBuiltins + exec code in self.globals, self.locals + +def exec_code(code, globals, locals=None): + globals['__builtins__'] = SafeBuiltins + exec code in globals, locals + +def exec_src(source, globals, locals=None): + globals['__builtins__'] = SafeBuiltins + code = compile(source, '', 'exec') + exec code in globals, locals + + +class CompiledExpression(object): + """A compiled expression + """ + + def __init__(self, source, filename=''): + self.source = source + self.code = compile(source, filename, 'eval') + + def eval(self, globals, locals=None): + globals['__builtins__'] = SafeBuiltins + if locals is None: + return eval(self.code, globals) + else: + return eval(self.code, globals) + +class CompiledProgram(object): + """A compiled expression + """ + + def __init__(self, source, filename=''): + self.source = source + self.code = compile(source, filename, 'exec') + + def exec_(self, globals, locals=None, output=None): + globals['__builtins__'] = SafeBuiltins + if output is not None: + globals['untrusted_output'] = output + exec self.code in globals, locals diff -Nru zope3-3.4.0/src/zope/security/untrustedpython/interpreter.txt zope3-3.5~bzr18/src/zope/security/untrustedpython/interpreter.txt --- zope3-3.4.0/src/zope/security/untrustedpython/interpreter.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedpython/interpreter.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,112 @@ +Untrusted Python interpreter +============================ + +The interpreter module provides very basic Python interpreter +support. It combined untrusted code compilation with safe builtins +and an exec-like API. The exec_src function can be used to execute +Python source: + + >>> from zope.security.untrustedpython.interpreter import exec_src + >>> d = {} + >>> exec_src("x=1", d) + >>> d['x'] + 1 + + >>> exec_src("x=getattr", d) + + +Note that the safe builtins dictionary is inserted into the +dictionary: + + >>> from zope.security.untrustedpython.builtins import SafeBuiltins + >>> d['__builtins__'] == SafeBuiltins + True + +All of the non-basic items in the safe builtins are proxied: + + >>> exec_src('str=str', d) + >>> from zope.security.proxy import Proxy + >>> type(d['str']) is Proxy + True + +Note that, while you can get to the safe `__builtins__`'s dictionary, +you can't use the dictionary to mutate it: + + >>> from zope.security.interfaces import ForbiddenAttribute + + >>> try: exec_src('__builtins__.__dict__["x"] = 1', d) + ... except ForbiddenAttribute: print 'Forbidden!' + Forbidden! + + >>> try: exec_src('del __builtins__.__dict__["str"]', d) + ... except ForbiddenAttribute: print 'Forbidden!' + Forbidden! + + >>> try: exec_src('__builtins__.__dict__.update({"x": 1})', d) + ... except ForbiddenAttribute: print 'Forbidden!' + Forbidden! + +Because the untrusted code compiler is used, you can't use exec, +raise, or try/except statements: + + >>> exec_src("exec 'x=1'", d) + Traceback (most recent call last): + ... + SyntaxError: Line 1: exec statements are not supported + +Any attribute-access results will be proxied: + + >>> exec_src("data = {}\nupdate = data.update\nupdate({'x': 'y'})", d) + >>> type(d['update']) is Proxy + True + +In this case, we were able to get to and use the update method because +the data dictionary itself was created by the untrusted code and was, +thus, unproxied. + +You can compile code yourself and call exec_code instead: + + >>> from zope.security.untrustedpython.rcompile import compile + >>> code = compile('x=2', '', 'exec') + >>> d = {} + >>> from zope.security.untrustedpython.interpreter import exec_code + >>> exec_code(code, d) + >>> d['x'] + 2 + +This is useful if you are going to be executing the same expression +many times, as you can avoid the cost of repeated comilation. + +Compiled Programs +----------------- + +A slightly higher-level interface is provided by compiled programs. +These make it easier to safetly safe the results of compilation: + + >>> from zope.security.untrustedpython.interpreter import CompiledProgram + >>> p = CompiledProgram('x=2') + >>> d = {} + >>> p.exec_(d) + >>> d['x'] + 2 + +When you execute a compiled program, you can supply an object with a +write method to get print output: + + >>> p = CompiledProgram('print "Hello world!"') + >>> import cStringIO + >>> f = cStringIO.StringIO() + >>> p.exec_({}, output=f) + >>> f.getvalue() + 'Hello world!\n' + + +Compiled Expressions +-------------------- + +You can also precompile expressions: + + >>> from zope.security.untrustedpython.interpreter import CompiledExpression + >>> p = CompiledExpression('x*2') + >>> p.eval({'x': 2}) + 4 diff -Nru zope3-3.4.0/src/zope/security/untrustedpython/rcompile.py zope3-3.5~bzr18/src/zope/security/untrustedpython/rcompile.py --- zope3-3.4.0/src/zope/security/untrustedpython/rcompile.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedpython/rcompile.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,97 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""compile() equivalent that produces restricted code. + +Only 'eval' is supported at this time. + +$Id: rcompile.py 70826 2006-10-20 03:41:16Z baijum $ +""" + +import compiler.pycodegen + +import RestrictedPython.RCompile +from RestrictedPython.SelectCompiler import ast, OP_ASSIGN, OP_DELETE, OP_APPLY + +def compile(text, filename, mode): + if not isinstance(text, basestring): + raise TypeError("Compiled source must be string") + gen = RExpression(text, str(filename), mode) + gen.compile() + return gen.getCode() + +class RExpression(RestrictedPython.RCompile.RestrictedCompileMode): + + CodeGeneratorClass = compiler.pycodegen.ExpressionCodeGenerator + + def __init__(self, source, filename, mode = "eval"): + self.mode = mode + RestrictedPython.RCompile.RestrictedCompileMode.__init__( + self, source, filename) + self.rm = RestrictionMutator() + + +# The security checks are performed by a set of six functions that +# must be provided by the restricted environment. + +_getattr_name = ast.Name("getattr") + + +class RestrictionMutator: + + def __init__(self): + self.errors = [] + self.warnings = [] + self.used_names = {} + + def error(self, node, info): + """Records a security error discovered during compilation.""" + lineno = getattr(node, 'lineno', None) + if lineno is not None and lineno > 0: + self.errors.append('Line %d: %s' % (lineno, info)) + else: + self.errors.append(info) + + def visitGetattr(self, node, walker): + """Converts attribute access to a function call. + + 'foo.bar' becomes 'getattr(foo, "bar")'. + + Also prevents augmented assignment of attributes, which would + be difficult to support correctly. + """ + node = walker.defaultVisitNode(node) + return ast.CallFunc(_getattr_name, + [node.expr, ast.Const(node.attrname)]) + + def visitExec(self, node, walker): + self.error(node, "exec statements are not supported") + + def visitPrint(self, node, walker): + """Make sure prints always have a destination + + If we get a print without a destination, make the default destination + untrusted_output. + """ + node = walker.defaultVisitNode(node) + if node.dest is None: + node.dest = ast.Name('untrusted_output') + return node + visitPrintnl = visitPrint + + def visitRaise(self, node, walker): + self.error(node, "raise statements are not supported") + + def visitTryExcept(self, node, walker): + self.error(node, "try/except statements are not supported") + diff -Nru zope3-3.4.0/src/zope/security/untrustedpython/rcompile.txt zope3-3.5~bzr18/src/zope/security/untrustedpython/rcompile.txt --- zope3-3.4.0/src/zope/security/untrustedpython/rcompile.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedpython/rcompile.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,128 @@ +================================== +Support for Restricted Python Code +================================== + +This package provides a way to compile +untrusted Python code so that it can be executed safely. + +This form of restricted Python assumes that security proxies will be +used to protect assets. Given this, the only thing that actually +needs to be done differently by the generated code is to: + +- Ensure that all attribute lookups go through a safe version of the getattr() + function that's been provided in the built-in functions used in the + execution environment. + +- Prevent exec statements. (Later, we could possibly make exec safe.) + +- Print statements always go to an output that is provided as a + global, rather than having an implicit sys.output. + +- Prevent try/except and raise statements. This is mainly because they + don't work properly in the presense of security proxies. Try/except + statements will be made to work in the future. + +No other special treatment is needed to support safe expression +evaluation. + +The implementation makes use of the `RestrictedPython` package, +originally written for Zope 2. There is a new AST re-writer in +`zope.security.untrustedpython.rcompile` which performs the +tree-transformation, and a top-level `compile()` function in +`zope.security.untrustedpython.rcompile`; the later is what client +applications are expected to use. + +The signature of the `compile()` function is very similar to that of +Python's built-in `compile()` function:: + + compile(source, filename, mode) + +Using it is equally simple:: + + >>> from zope.security.untrustedpython.rcompile import compile + + >>> code = compile("21 * 2", "", "eval") + >>> eval(code) + 42 + +What's interesting about the restricted code is that all attribute +lookups go through the `getattr()` function. This is generally +provided as a built-in function in the restricted environment:: + + >>> def mygetattr(object, name, default="Yahoo!"): + ... marker = [] + ... print "Looking up", name + ... if getattr(object, name, marker) is marker: + ... return default + ... else: + ... return "Yeehaw!" + + >>> import __builtin__ + >>> builtins = __builtin__.__dict__.copy() + >>> builtins["getattr"] = mygetattr + + >>> def reval(source): + ... code = compile(source, "README.txt", "eval") + ... globals = {"__builtins__": builtins} + ... return eval(code, globals, {}) + + >>> reval("(42).__class__") + Looking up __class__ + 'Yeehaw!' + >>> reval("(42).not_really_there") + Looking up not_really_there + 'Yahoo!' + >>> reval("(42).foo.not_really_there") + Looking up foo + Looking up not_really_there + 'Yahoo!' + +This allows a `getattr()` to be used that ensures the result of +evaluation is a security proxy. + +To compile code with statements, use exec or single: + + >>> exec compile("x = 1", "", "exec") + >>> x + 1 + +Trying to compile exec, raise or try/except sattements gives +syntax errors: + + >>> compile("exec 'x = 2'", "", "exec") + Traceback (most recent call last): + ... + SyntaxError: Line 1: exec statements are not supported + + >>> compile("raise KeyError('x')", "", "exec") + Traceback (most recent call last): + ... + SyntaxError: Line 1: raise statements are not supported + + >>> compile("try: pass\nexcept: pass", "", "exec") + Traceback (most recent call last): + ... + SyntaxError: Line 1: try/except statements are not supported + +Printing to an explicit writable is allowed: + + >>> import StringIO + >>> f = StringIO.StringIO() + >>> code = compile("print >> f, 'hi',\nprint >> f, 'world'", '', 'exec') + >>> exec code in {'f': f} + >>> f.getvalue() + 'hi world\n' + +But if no output is specified, then output will be send to +`untrusted_output`: + + >>> code = compile("print 'hi',\nprint 'world'", '', 'exec') + >>> exec code in {} + Traceback (most recent call last): + ... + NameError: name 'untrusted_output' is not defined + + >>> f = StringIO.StringIO() + >>> exec code in {'untrusted_output': f} + + diff -Nru zope3-3.4.0/src/zope/security/untrustedpython/tests.py zope3-3.5~bzr18/src/zope/security/untrustedpython/tests.py --- zope3-3.4.0/src/zope/security/untrustedpython/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/untrustedpython/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,36 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""Untrusted python tests + +$Id: tests.py 111761 2010-04-30 21:52:52Z hannosch $ +""" + +import doctest +import unittest +import re + +from zope.testing import renormalizing + + +def test_suite(): + checker = renormalizing.RENormalizing([ + (re.compile(r"'ImmutableModule' object"), r'object'), + ]) + return unittest.TestSuite(( + doctest.DocFileSuite('builtins.txt', + 'rcompile.txt', + 'interpreter.txt', + checker=checker, + ), + )) diff -Nru zope3-3.4.0/src/zope/security/zcml.py zope3-3.5~bzr18/src/zope/security/zcml.py --- zope3-3.4.0/src/zope/security/zcml.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/zcml.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,146 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Security related configuration fields. + +$Id: zcml.py 70539 2006-10-05 08:45:29Z flox $ +""" +__docformat__ = 'restructuredtext' + +import zope.schema +from zope.interface import Interface, implements +from zope.schema.interfaces import IFromUnicode +from zope.security.permission import checkPermission +from zope.security.management import setSecurityPolicy +from zope.configuration.fields import MessageID, GlobalObject + +class Permission(zope.schema.Id): + r"""This field describes a permission. + + Let's look at an example: + + >>> class FauxContext(object): + ... permission_mapping = {'zope.ManageCode':'zope.private'} + ... _actions = [] + ... def action(self, **kws): + ... self._actions.append(kws) + >>> context = FauxContext() + >>> field = Permission().bind(context) + + Let's test the fromUnicode method: + + >>> field.fromUnicode(u'zope.foo') + 'zope.foo' + >>> field.fromUnicode(u'zope.ManageCode') + 'zope.private' + + Now let's see whether validation works alright + + >>> field._validate('zope.ManageCode') + >>> context._actions[0]['args'] + (None, 'zope.foo') + >>> field._validate('3 foo') + Traceback (most recent call last): + ... + InvalidId: 3 foo + + zope.Public is always valid + >>> field._validate('zope.Public') + """ + implements(IFromUnicode) + + def fromUnicode(self, u): + u = super(Permission, self).fromUnicode(u) + + map = getattr(self.context, 'permission_mapping', {}) + return map.get(u, u) + + def _validate(self, value): + super(Permission, self)._validate(value) + + if value != 'zope.Public': + self.context.action( + discriminator = None, + callable = checkPermission, + args = (None, value), + + # Delay execution till end. This is an + # optimization. We don't want to intersperse utility + # lookup, done when checking permissions, with utility + # definitions. Utility lookup is expensive after + # utility definition, as extensive caches have to be + # rebuilt. + order=9999999, + ) + + +class ISecurityPolicyDirective(Interface): + """Defines the security policy that will be used for Zope.""" + + component = GlobalObject( + title=u"Component", + description=u"Pointer to the object that will handle the security.", + required=True) + +def securityPolicy(_context, component): + _context.action( + discriminator = 'defaultPolicy', + callable = setSecurityPolicy, + args = (component,) ) + +class IPermissionDirective(Interface): + """Define a new security object.""" + + id = zope.schema.Id( + title=u"Id", + description=u"Id as which this object will be known and used.", + required=True) + + title = MessageID( + title=u"Title", + description=u"Provides a title for the object.", + required=True) + + description = MessageID( + title=u"Description", + description=u"Provides a description for the object.", + required=False) + +def permission(_context, id, title, description=''): + from zope.security.interfaces import IPermission + from zope.security.permission import Permission + from zope.component.zcml import utility + permission = Permission(id, title, description) + utility(_context, IPermission, permission, name=id) + +class IRedefinePermission(Interface): + """Define a permission to replace another permission.""" + + from_ = Permission( + title=u"Original permission", + description=u"Original permission id to redefine.", + required=True) + + to = Permission( + title=u"Substituted permission", + description=u"Substituted permission id.", + required=True) + +def redefinePermission(_context, from_, to): + _context = _context.context + + # check if context has any permission mappings yet + if not hasattr(_context, 'permission_mapping'): + _context.permission_mapping={} + + _context.permission_mapping[from_] = to diff -Nru zope3-3.4.0/src/zope/security/_zope_security_checker.c zope3-3.5~bzr18/src/zope/security/_zope_security_checker.c --- zope3-3.4.0/src/zope/security/_zope_security_checker.c 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/_zope_security_checker.c 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,605 @@ +/* + + Copyright (c) 2004 Zope Corporation and Contributors. + All Rights Reserved. + + This software is subject to the provisions of the Zope Public License, + Version 2.0 (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. + +*/ +#include + +static PyObject *_checkers, *_defaultChecker, *_available_by_default, *NoProxy; +static PyObject *Proxy, *thread_local, *CheckerPublic; +static PyObject *ForbiddenAttribute, *Unauthorized; + +#define DECLARE_STRING(N) static PyObject *str_##N + +DECLARE_STRING(checkPermission); +DECLARE_STRING(__Security_checker__); +DECLARE_STRING(interaction); + +#define CLEAR(O) if (O) {PyObject *t = O; O = 0; Py_DECREF(t); } + +typedef struct { + PyObject_HEAD + PyObject *getperms, *setperms; +} Checker; + +/* def permission_id(self, name): */ +static PyObject * +Checker_permission_id(Checker *self, PyObject *name) +{ +/* return self._permission_func(name) */ + PyObject *result; + + if (self->getperms) + { + result = PyDict_GetItem(self->getperms, name); + if (result == NULL) + result = Py_None; + } + else + result = Py_None; + + Py_INCREF(result); + return result; +} + +/* def setattr_permission_id(self, name): */ +static PyObject * +Checker_setattr_permission_id(Checker *self, PyObject *name) +{ +/* return self._setattr_permission_func(name) */ + PyObject *result; + + if (self->setperms) + { + result = PyDict_GetItem(self->setperms, name); + if (result == NULL) + result = Py_None; + } + else + result = Py_None; + + Py_INCREF(result); + return result; +} + +static int +checkPermission(PyObject *permission, PyObject *object, PyObject *name) +{ + PyObject *interaction, *r; + int i; + +/* if thread_local.interaction.checkPermission(permission, object): */ +/* return */ + interaction = PyObject_GetAttr(thread_local, str_interaction); + if (interaction == NULL) + return -1; + r = PyObject_CallMethodObjArgs(interaction, str_checkPermission, + permission, object, NULL); + Py_DECREF(interaction); + if (r == NULL) + return -1; + i = PyObject_IsTrue(r); + Py_DECREF(r); + if (i < 0) + return -1; + if (i) + return 0; +/* else: */ +/* __traceback_supplement__ = (TracebackSupplement, object) */ +/* raise Unauthorized(object, name, permission) */ + r = Py_BuildValue("OOO", object, name, permission); + if (r == NULL) + return -1; + PyErr_SetObject(Unauthorized, r); + Py_DECREF(r); + return -1; +} + + +/* def check(self, object, name): */ + +/* Note that we have an int version here because we will use it for + __setitem__, as described below */ + +static int +Checker_check_int(Checker *self, PyObject *object, PyObject *name) +{ + PyObject *permission=NULL; + int operator; + +/* permission = self._permission_func(name) */ + if (self->getperms) + permission = PyDict_GetItem(self->getperms, name); + +/* if permission is not None: */ + if (permission != NULL) + { +/* if permission is CheckerPublic: */ +/* return # Public */ + if (permission == CheckerPublic) + return 0; + + if (checkPermission(permission, object, name) < 0) + return -1; + return 0; + } + + + operator = (PyString_Check(name) + && PyString_AS_STRING(name)[0] == '_' + && PyString_AS_STRING(name)[1] == '_'); + + if (operator) + { +/* elif name in _available_by_default: */ +/* return */ + int ic = PySequence_Contains(_available_by_default, name); + if (ic < 0) + return -1; + if (ic) + return 0; + +/* if name != '__iter__' or hasattr(object, name): */ +/* __traceback_supplement__ = (TracebackSupplement, object) */ +/* raise ForbiddenAttribute, (name, object) */ + + if (strcmp("__iter__", PyString_AS_STRING(name)) == 0 + && ! PyObject_HasAttr(object, name)) + /* We want an attr error if we're asked for __iter__ and we don't + have it. We'll get one by allowing the access. */ + return 0; + } + + { + PyObject *args; + args = Py_BuildValue("OO", name, object); + if (args != NULL) + { + PyErr_SetObject(ForbiddenAttribute, args); + Py_DECREF(args); + } + return -1; + } +} + +/* Here we have the non-int version, implemented using the int + version, which is exposed as a method */ + +static PyObject * +Checker_check(Checker *self, PyObject *args) +{ + PyObject *object, *name; + + if (!PyArg_ParseTuple(args, "OO", &object, &name)) + return NULL; + + if (Checker_check_int(self, object, name) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +/* def check_setattr(self, object, name): */ +static PyObject * +Checker_check_setattr(Checker *self, PyObject *args) +{ + PyObject *object, *name, *permission=NULL; + + if (!PyArg_ParseTuple(args, "OO", &object, &name)) + return NULL; + +/* permission = self._permission_func(name) */ + if (self->setperms) + permission = PyDict_GetItem(self->setperms, name); + +/* if permission is not None: */ + if (permission != NULL) + { +/* if permission is CheckerPublic: */ +/* return # Public */ + if (permission != CheckerPublic + && checkPermission(permission, object, name) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; + } + +/* __traceback_supplement__ = (TracebackSupplement, object) */ +/* raise ForbiddenAttribute, (name, object) */ + args = Py_BuildValue("OO", name, object); + if (args != NULL) + { + PyErr_SetObject(ForbiddenAttribute, args); + Py_DECREF(args); + } + return NULL; +} + + +static PyObject * +selectChecker(PyObject *ignored, PyObject *object); + +/* def proxy(self, value): */ +static PyObject * +Checker_proxy(Checker *self, PyObject *value) +{ + PyObject *checker, *r; + +/* if type(value) is Proxy: */ +/* return value */ + if ((PyObject*)(value->ob_type) == Proxy) + { + Py_INCREF(value); + return value; + } + +/* checker = getattr(value, '__Security_checker__', None) */ + checker = PyObject_GetAttr(value, str___Security_checker__); +/* if checker is None: */ + if (checker == NULL) + { + PyErr_Clear(); + +/* checker = selectChecker(value) */ + checker = selectChecker(NULL, value); + if (checker == NULL) + return NULL; + +/* if checker is None: */ +/* return value */ + if (checker == Py_None) + { + Py_DECREF(checker); + Py_INCREF(value); + return value; + } + } + else if (checker == Py_None) + { + PyObject *errv = Py_BuildValue("sO", + "Invalid value, None. " + "for security checker", + value); + if (errv != NULL) + { + PyErr_SetObject(PyExc_ValueError, errv); + Py_DECREF(errv); + } + + return NULL; + } + + r = PyObject_CallFunctionObjArgs(Proxy, value, checker, NULL); + Py_DECREF(checker); + return r; +} + +/* return Proxy(value, checker) */ + + +static struct PyMethodDef Checker_methods[] = { + {"permission_id", (PyCFunction)Checker_permission_id, METH_O, + "permission_id(name) -- Return the permission neded to get the name"}, + {"setattr_permission_id", (PyCFunction)Checker_setattr_permission_id, + METH_O, + "setattr_permission_id(name) -- Return the permission neded to set the name" + }, + {"check_getattr", (PyCFunction)Checker_check, METH_VARARGS, + "check_getattr(object, name) -- Check whether a getattr is allowes"}, + {"check_setattr", (PyCFunction)Checker_check_setattr, METH_VARARGS, + "check_setattr(object, name) -- Check whether a setattr is allowes"}, + {"check", (PyCFunction)Checker_check, METH_VARARGS, + "check(object, opname) -- Check whether an operation is allowes"}, + {"proxy", (PyCFunction)Checker_proxy, METH_O, + "proxy(object) -- Security-proxy an object"}, + + {NULL, NULL} /* sentinel */ +}; + +static int +Checker_clear(Checker *self) +{ + CLEAR(self->getperms); + CLEAR(self->setperms); + return 0; +} + +static void +Checker_dealloc(Checker *self) +{ + Checker_clear(self); + self->ob_type->tp_free((PyObject*)self); +} + +static int +Checker_traverse(Checker *self, visitproc visit, void *arg) +{ + if (self->getperms != NULL && visit(self->getperms, arg) < 0) + return -1; + if (self->setperms != NULL && visit(self->setperms, arg) < 0) + return -1; + + return 0; +} + +static int +Checker_init(Checker *self, PyObject *args, PyObject *kwds) +{ + PyObject *getperms, *setperms=NULL; + static char *kwlist[] = {"get_permissions", "set_permissions", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!:Checker", kwlist, + &PyDict_Type, &getperms, + &PyDict_Type, &setperms)) + return -1; + + Py_INCREF(getperms); + self->getperms = getperms; + Py_XINCREF(setperms); + self->setperms = setperms; + + return 0; +} + +static PyObject * +Checker_get_get_permissions(Checker *self, void *closure) +{ + if (self->getperms == NULL) + { + self->getperms = PyDict_New(); + if (self->getperms == NULL) + return NULL; + } + + Py_INCREF(self->getperms); + return self->getperms; +} + +static PyObject * +Checker_get_set_permissions(Checker *self, void *closure) +{ + if (self->setperms == NULL) + { + self->setperms = PyDict_New(); + if (self->setperms == NULL) + return NULL; + } + + Py_INCREF(self->setperms); + return self->setperms; +} + +static PyGetSetDef Checker_getset[] = { + {"get_permissions", + (getter)Checker_get_get_permissions, NULL, + "getattr name to permission dictionary", + NULL}, + {"set_permissions", + (getter)Checker_get_set_permissions, NULL, + "setattr name to permission dictionary", + NULL}, + {NULL} /* Sentinel */ +}; + +/* We create operator aliases for check and proxy. Why? Because + calling operator slots is much faster than calling methods and + security checks are done so often that speed matters. So we have + this hack of using almost-arbitrary operations to represent methods + that we call alot. The security proxy implementation participates + in the same hack. */ + +static PyMappingMethods Checker_as_mapping = { + /* mp_length */ NULL, + /* mp_subscript */ (binaryfunc)Checker_proxy, + /* mp_ass_subscript */ (objobjargproc)Checker_check_int, +}; + + + +static PyTypeObject CheckerType = { + PyObject_HEAD_INIT(NULL) + /* ob_size */ 0, + /* tp_name */ "zope.security.checker." + "Checker", + /* tp_basicsize */ sizeof(Checker), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)&Checker_dealloc, + /* tp_print */ (printfunc)0, + /* tp_getattr */ (getattrfunc)0, + /* tp_setattr */ (setattrfunc)0, + /* tp_compare */ (cmpfunc)0, + /* tp_repr */ (reprfunc)0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ &Checker_as_mapping, + /* tp_hash */ (hashfunc)0, + /* tp_call */ (ternaryfunc)0, + /* tp_str */ (reprfunc)0, + /* tp_getattro */ (getattrofunc)0, + /* tp_setattro */ (setattrofunc)0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, + /* tp_doc */ "Security checker", + /* tp_traverse */ (traverseproc)Checker_traverse, + /* tp_clear */ (inquiry)Checker_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ Checker_methods, + /* tp_members */ 0, + /* tp_getset */ Checker_getset, + /* tp_base */ 0, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ (descrgetfunc)0, + /* tp_descr_set */ (descrsetfunc)0, + /* tp_dictoffset */ 0, + /* tp_init */ (initproc)Checker_init, + /* tp_alloc */ (allocfunc)0, + /* tp_new */ (newfunc)0, + /* tp_free */ 0, /* Low-level free-mem routine */ + /* tp_is_gc */ (inquiry)0, /* For PyObject_IS_GC */ +}; + + + + + +/* def selectChecker(object): */ +/* """Get a checker for the given object */ +/* The appropriate checker is returned or None is returned. If the */ +/* return value is None, then object should not be wrapped in a proxy. */ +/* """ */ + +static char selectChecker_doc[] = +"Get a checker for the given object\n" +"\n" +"The appropriate checker is returned or None is returned. If the\n" +"return value is None, then object should not be wrapped in a proxy.\n" +; + +static PyObject * +selectChecker(PyObject *ignored, PyObject *object) +{ + PyObject *checker; + +/* checker = _getChecker(type(object), _defaultChecker) */ + + checker = PyDict_GetItem(_checkers, (PyObject*)(object->ob_type)); + if (checker == NULL) + checker = _defaultChecker; + +/* if checker is NoProxy: */ +/* return None */ + + if (checker == NoProxy) + { + Py_INCREF(Py_None); + return Py_None; + } + +/* if checker is _defaultChecker and isinstance(object, Exception): */ +/* return None */ + + if (checker == _defaultChecker + && PyObject_IsInstance(object, PyExc_Exception)) + { + Py_INCREF(Py_None); + return Py_None; + } + +/* while not isinstance(checker, Checker): */ +/* checker = checker(object) */ +/* if checker is NoProxy or checker is None: */ +/* return None */ + + Py_INCREF(checker); + while (! PyObject_TypeCheck(checker, &CheckerType)) + { + PyObject *newchecker; + newchecker = PyObject_CallFunctionObjArgs(checker, object, NULL); + Py_DECREF(checker); + if (newchecker == NULL) + return NULL; + checker = newchecker; + if (checker == NoProxy || checker == Py_None) + { + Py_DECREF(checker); + Py_INCREF(Py_None); + return Py_None; + } + } + +/* return checker */ + + return checker; +} + + +static PyMethodDef module_methods[] = { + {"selectChecker", (PyCFunction)selectChecker, METH_O, selectChecker_doc}, + {NULL} /* Sentinel */ +}; + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +init_zope_security_checker(void) +{ + PyObject* m; + + CheckerType.tp_new = PyType_GenericNew; + if (PyType_Ready(&CheckerType) < 0) + return; + + _defaultChecker = PyObject_CallFunction((PyObject*)&CheckerType, "{}"); + if (_defaultChecker == NULL) + return; + +#define INIT_STRING(S) \ +if((str_##S = PyString_InternFromString(#S)) == NULL) return + + INIT_STRING(checkPermission); + INIT_STRING(__Security_checker__); + INIT_STRING(interaction); + + if ((_checkers = PyDict_New()) == NULL) + return; + + NoProxy = PyObject_CallObject((PyObject*)&PyBaseObject_Type, NULL); + if (NoProxy == NULL) + return; + + if ((m = PyImport_ImportModule("zope.security._proxy")) == NULL) return; + if ((Proxy = PyObject_GetAttrString(m, "_Proxy")) == NULL) return; + Py_DECREF(m); + + if ((m = PyImport_ImportModule("zope.security._definitions")) == NULL) return; + thread_local = PyObject_GetAttrString(m, "thread_local"); + if (thread_local == NULL) return; + Py_DECREF(m); + + if ((m = PyImport_ImportModule("zope.security.interfaces")) == NULL) return; + ForbiddenAttribute = PyObject_GetAttrString(m, "ForbiddenAttribute"); + if (ForbiddenAttribute == NULL) return; + Unauthorized = PyObject_GetAttrString(m, "Unauthorized"); + if (Unauthorized == NULL) return; + Py_DECREF(m); + + if ((m = PyImport_ImportModule("zope.security.checker")) == NULL) return; + CheckerPublic = PyObject_GetAttrString(m, "CheckerPublic"); + if (CheckerPublic == NULL) return; + Py_DECREF(m); + + if ((_available_by_default = PyList_New(0)) == NULL) return; + + m = Py_InitModule3("_zope_security_checker", module_methods, + "C optimizations for zope.security.checker"); + + if (m == NULL) + return; + +#define EXPORT(N) Py_INCREF(N); PyModule_AddObject(m, #N, N) + + EXPORT(_checkers); + EXPORT(NoProxy); + EXPORT(_defaultChecker); + EXPORT(_available_by_default); + + Py_INCREF(&CheckerType); + PyModule_AddObject(m, "Checker", (PyObject *)&CheckerType); +} diff -Nru zope3-3.4.0/src/zope/security/_zope_security_checker.py zope3-3.5~bzr18/src/zope/security/_zope_security_checker.py --- zope3-3.4.0/src/zope/security/_zope_security_checker.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/security/_zope_security_checker.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,7 @@ +def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__,'_zope_security_checker.so') + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) +__bootstrap__() diff -Nru zope3-3.4.0/src/zope/securitypolicy/configure.zcml zope3-3.5~bzr18/src/zope/securitypolicy/configure.zcml --- zope3-3.4.0/src/zope/securitypolicy/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/securitypolicy/grantinfo.py zope3-3.5~bzr18/src/zope/securitypolicy/grantinfo.py --- zope3-3.4.0/src/zope/securitypolicy/grantinfo.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/grantinfo.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,77 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""Grant info + +$Id: grantinfo.py 80108 2007-09-26 15:02:36Z rogerineichen $ +""" +from zope.annotation.interfaces import IAnnotations +from zope.securitypolicy.interfaces import Unset +from zope.securitypolicy.interfaces import IGrantInfo + +from zope.securitypolicy.principalpermission import \ + AnnotationPrincipalPermissionManager +prinperkey = AnnotationPrincipalPermissionManager.key +del AnnotationPrincipalPermissionManager + +from zope.securitypolicy.principalrole import \ + AnnotationPrincipalRoleManager +prinrolekey = AnnotationPrincipalRoleManager.key +del AnnotationPrincipalRoleManager + +from zope.securitypolicy.rolepermission import \ + AnnotationRolePermissionManager +rolepermkey = AnnotationRolePermissionManager.key +del AnnotationRolePermissionManager + +class AnnotationGrantInfo(object): + + prinper = prinrole = permrole = {} + + def __init__(self, context): + self._context = context + annotations = IAnnotations(context, None) + if annotations is not None: + + prinper = annotations.get(prinperkey) + if prinper is not None: + self.prinper = prinper._bycol # by principals + + prinrole = annotations.get(prinrolekey) + if prinrole is not None: + self.prinrole = prinrole._bycol # by principals + + roleper = annotations.get(rolepermkey) + if roleper is not None: + self.permrole = roleper._byrow # by permission + + def __nonzero__(self): + return bool(self.prinper or self.prinrole or self.permrole) + + def principalPermissionGrant(self, principal, permission): + prinper = self.prinper.get(principal) + if prinper: + return prinper.get(permission, Unset) + return Unset + + def getRolesForPermission(self, permission): + permrole = self.permrole.get(permission) + if permrole: + return permrole.items() + return () + + def getRolesForPrincipal(self, principal): + prinrole = self.prinrole.get(principal) + if prinrole: + return prinrole.items() + return () diff -Nru zope3-3.4.0/src/zope/securitypolicy/__init__.py zope3-3.5~bzr18/src/zope/securitypolicy/__init__.py --- zope3-3.4.0/src/zope/securitypolicy/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,17 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Securitypolicy + +$Id: __init__.py 29365 2005-03-01 18:32:59Z sidnei $ +""" diff -Nru zope3-3.4.0/src/zope/securitypolicy/interfaces.py zope3-3.5~bzr18/src/zope/securitypolicy/interfaces.py --- zope3-3.4.0/src/zope/securitypolicy/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,237 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Security map to hold matrix-like relationships. + +In all cases, 'setting' values are one of the defined constants +`Allow`, `Deny`, or `Unset`. + +$Id: interfaces.py 97747 2009-03-10 00:02:10Z nadako $ +""" +from zope.interface import Interface +from zope.schema import TextLine, Text + +# These are the "setting" values returned by several methods defined +# in these interfaces. The implementation may move to another +# location in the future, so this should be the preferred module to +# import these from. +from zope.securitypolicy.settings import Allow, Deny, Unset + + +class IRole(Interface): + """A role object.""" + + id = TextLine( + title=u"Id", + description=u"Id as which this role will be known and used.", + readonly=True, + required=True) + + title = TextLine( + title=u"Title", + description=u"Provides a title for the role.", + required=True) + + description = Text( + title=u"Description", + description=u"Provides a description for the role.", + required=False) + + + +class IPrincipalRoleMap(Interface): + """Mappings between principals and roles.""" + + def getPrincipalsForRole(role_id): + """Get the principals that have been granted a role. + + Return the list of (principal id, setting) who have been assigned or + removed from a role. + + If no principals have been assigned this role, + then the empty list is returned. + """ + + def getRolesForPrincipal(principal_id): + """Get the roles granted to a principal. + + Return the list of (role id, setting) assigned or removed from + this principal. + + If no roles have been assigned to + this principal, then the empty list is returned. + """ + + def getSetting(role_id, principal_id): + """Return the setting for this principal, role combination + """ + + def getPrincipalsAndRoles(): + """Get all settings. + + Return all the principal/role combinations along with the + setting for each combination as a sequence of tuples with the + role id, principal id, and setting, in that order. + """ + + +class IPrincipalRoleManager(IPrincipalRoleMap): + """Management interface for mappings between principals and roles.""" + + def assignRoleToPrincipal(role_id, principal_id): + """Assign the role to the principal.""" + + def removeRoleFromPrincipal(role_id, principal_id): + """Remove a role from the principal.""" + + def unsetRoleForPrincipal(role_id, principal_id): + """Unset the role for the principal.""" + + +class IRolePermissionMap(Interface): + """Mappings between roles and permissions.""" + + def getPermissionsForRole(role_id): + """Get the premissions granted to a role. + + Return a sequence of (permission id, setting) tuples for the given + role. + + If no permissions have been granted to this + role, then the empty list is returned. + """ + + def getRolesForPermission(permission_id): + """Get the roles that have a permission. + + Return a sequence of (role id, setting) tuples for the given + permission. + + If no roles have been granted this permission, then the empty list is + returned. + """ + + def getSetting(permission_id, role_id): + """Return the setting for the given permission id and role id + + If there is no setting, Unset is returned + """ + + def getRolesAndPermissions(): + """Return a sequence of (permission_id, role_id, setting) here. + + The settings are returned as a sequence of permission, role, + setting tuples. + + If no principal/role assertions have been made here, then the empty + list is returned. + """ + + +class IRolePermissionManager(IRolePermissionMap): + """Management interface for mappings between roles and permissions.""" + + def grantPermissionToRole(permission_id, role_id): + """Bind the permission to the role. + """ + + def denyPermissionToRole(permission_id, role_id): + """Deny the permission to the role + """ + + def unsetPermissionFromRole(permission_id, role_id): + """Clear the setting of the permission to the role. + """ + + +class IPrincipalPermissionMap(Interface): + """Mappings between principals and permissions.""" + + def getPrincipalsForPermission(permission_id): + """Get the principas that have a permission. + + Return the list of (principal_id, setting) tuples that describe + security assertions for this permission. + + If no principals have been set for this permission, then the empty + list is returned. + """ + + def getPermissionsForPrincipal(principal_id): + """Get the permissions granted to a principal. + + Return the list of (permission, setting) tuples that describe + security assertions for this principal. + + If no permissions have been set for this principal, then the empty + list is returned. + """ + + def getSetting(permission_id, principal_id): + """Get the setting for a permission and principal. + + Get the setting (Allow/Deny/Unset) for a given permission and + principal. + """ + + def getPrincipalsAndPermissions(): + """Get all principal permission settings. + + Get the principal security assertions here in the form + of a list of three tuple containing + (permission id, principal id, setting) + """ + + +class IPrincipalPermissionManager(IPrincipalPermissionMap): + """Management interface for mappings between principals and permissions.""" + + def grantPermissionToPrincipal(permission_id, principal_id): + """Assert that the permission is allowed for the principal. + """ + + def denyPermissionToPrincipal(permission_id, principal_id): + """Assert that the permission is denied to the principal. + """ + + def unsetPermissionForPrincipal(permission_id, principal_id): + """Remove the permission (either denied or allowed) from the + principal. + """ + +class IGrantInfo(Interface): + """Get grant info needed for checking access + """ + + def principalPermissionGrant(principal, permission): + """Return the principal-permission grant if any + + The return value is one of Allow, Deny, or Unset + """ + + def getRolesForPermission(permission): + """Return the role grants for the permission + + The role grants are an iterable of role, setting tuples, where + setting is either Allow or Deny. + """ + + def getRolesForPrincipal(principal): + """Return the role grants for the principal + + The role grants are an iterable of role, setting tuples, where + setting is either Allow or Deny. + """ + +class IGrantVocabulary(Interface): + """Marker interface for register the RadioWidget.""" diff -Nru zope3-3.4.0/src/zope/securitypolicy/metaconfigure.py zope3-3.5~bzr18/src/zope/securitypolicy/metaconfigure.py --- zope3-3.4.0/src/zope/securitypolicy/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,95 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +""" Register security related configuration directives. + +$Id: metaconfigure.py 80108 2007-09-26 15:02:36Z rogerineichen $ +""" +from zope.configuration.exceptions import ConfigurationError +from zope.component.zcml import utility + +from zope.securitypolicy.interfaces import IRole +from zope.securitypolicy.role import Role +from zope.securitypolicy.rolepermission import \ + rolePermissionManager as role_perm_mgr +from zope.securitypolicy.principalpermission import \ + principalPermissionManager as principal_perm_mgr +from zope.securitypolicy.principalrole import \ + principalRoleManager as principal_role_mgr + + +def grant(_context, principal=None, role=None, permission=None): + nspecified = ((principal is not None) + + (role is not None) + + (permission is not None) + ) + + if nspecified != 2: + raise ConfigurationError( + "Exactly two of the principal, role, and permission attributes " + "must be specified") + + if principal: + if role: + _context.action( + discriminator = ('grantRoleToPrincipal', role, principal), + callable = principal_role_mgr.assignRoleToPrincipal, + args = (role, principal) + ) + else: + _context.action( + discriminator = ('grantPermissionToPrincipal', + permission, + principal), + callable = principal_perm_mgr.grantPermissionToPrincipal, + args = (permission, principal) + ) + else: + _context.action( + discriminator = ('grantPermissionToRole', permission, role), + callable = role_perm_mgr.grantPermissionToRole, + args = (permission, role) + ) + +def grantAll(_context, principal=None, role=None): + """Grant all permissions to a role or principal + """ + nspecified = ((principal is not None) + + (role is not None) + ) + + if nspecified != 1: + raise ConfigurationError( + "Exactly one of the principal and role attributes " + "must be specified") + + if principal: + _context.action( + discriminator = ('grantAllPermissionsToPrincipal', + principal), + callable = + principal_perm_mgr.grantAllPermissionsToPrincipal, + args = (principal, ) + ) + else: + _context.action( + discriminator = ('grantAllPermissionsToRole', role), + callable = role_perm_mgr.grantAllPermissionsToRole, + args = (role, ) + ) + + +def defineRole(_context, id, title, description=''): + role = Role(id, title, description) + utility(_context, IRole, role, name=id) + diff -Nru zope3-3.4.0/src/zope/securitypolicy/metadirectives.py zope3-3.5~bzr18/src/zope/securitypolicy/metadirectives.py --- zope3-3.4.0/src/zope/securitypolicy/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,45 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Grant Directive Schema + +$Id: metadirectives.py 67630 2006-04-27 00:54:03Z jim $ +""" +from zope.interface import Interface +from zope.schema import Id +from zope.security.zcml import Permission, IPermissionDirective + +class IGrantAllDirective(Interface): + """Grant Permissions to roles and principals and roles to principals.""" + + principal = Id( + title=u"Principal", + description=u"Specifies the Principal to be mapped.", + required=False) + + role = Id( + title=u"Role", + description=u"Specifies the Role to be mapped.", + required=False) + +class IGrantDirective(IGrantAllDirective): + """Grant Permissions to roles and principals and roles to principals.""" + + permission = Permission( + title=u"Permission", + description=u"Specifies the Permission to be mapped.", + required=False) + +class IDefineRoleDirective(IPermissionDirective): + """Define a new role.""" + diff -Nru zope3-3.4.0/src/zope/securitypolicy/meta.zcml zope3-3.5~bzr18/src/zope/securitypolicy/meta.zcml --- zope3-3.4.0/src/zope/securitypolicy/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,27 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/securitypolicy/principalpermission.py zope3-3.5~bzr18/src/zope/securitypolicy/principalpermission.py --- zope3-3.4.0/src/zope/securitypolicy/principalpermission.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/principalpermission.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,123 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Mappings between principals and permissions, stored in an object locally. + +$Id: principalpermission.py 97975 2009-03-12 09:48:57Z nadako $ +""" + +from zope.interface import implements +from zope.security.permission import allPermissions + +from zope.authentication.principal import checkPrincipal +from zope.securitypolicy.interfaces import Allow, Deny, Unset +from zope.securitypolicy.interfaces import IPrincipalPermissionManager +from zope.securitypolicy.securitymap import SecurityMap +from zope.securitypolicy.securitymap import AnnotationSecurityMap + + +class AnnotationPrincipalPermissionManager(AnnotationSecurityMap): + """Mappings between principals and permissions.""" + + # the annotation key is a holdover from this module's old + # location, but cannot change without breaking existing databases + # It is also is misspelled, but that's OK. It just has to be unique. + # we'll keep it as is, to prevent breaking old data: + key = 'zopel.app.security.AnnotationPrincipalPermissionManager' + + implements(IPrincipalPermissionManager) + + def grantPermissionToPrincipal(self, permission_id, principal_id): + AnnotationSecurityMap.addCell(self, permission_id, principal_id, Allow) + + def denyPermissionToPrincipal(self, permission_id, principal_id): + AnnotationSecurityMap.addCell(self, permission_id, principal_id, Deny) + + unsetPermissionForPrincipal = AnnotationSecurityMap.delCell + getPrincipalsForPermission = AnnotationSecurityMap.getRow + getPermissionsForPrincipal = AnnotationSecurityMap.getCol + + def getSetting(self, permission_id, principal_id, default=Unset): + return AnnotationSecurityMap.queryCell( + self, permission_id, principal_id, default) + + getPrincipalsAndPermissions = AnnotationSecurityMap.getAllCells + + +class PrincipalPermissionManager(SecurityMap): + """Mappings between principals and permissions.""" + + implements(IPrincipalPermissionManager) + + def grantPermissionToPrincipal(self, permission_id, principal_id, + check=True): + ''' See the interface IPrincipalPermissionManager ''' + + if check: + checkPrincipal(None, principal_id) + + self.addCell(permission_id, principal_id, Allow) + + def grantAllPermissionsToPrincipal(self, principal_id): + ''' See the interface IPrincipalPermissionManager ''' + + for permission_id in allPermissions(None): + self.grantPermissionToPrincipal(permission_id, principal_id, False) + + def denyPermissionToPrincipal(self, permission_id, principal_id, + check=True): + ''' See the interface IPrincipalPermissionManager ''' + + if check: + checkPrincipal(None, principal_id) + + self.addCell(permission_id, principal_id, Deny) + + def unsetPermissionForPrincipal(self, permission_id, principal_id): + ''' See the interface IPrincipalPermissionManager ''' + + # Don't check validity intentionally. + # After all, we certianly want to unset invalid ids. + + self.delCell(permission_id, principal_id) + + def getPrincipalsForPermission(self, permission_id): + ''' See the interface IPrincipalPermissionManager ''' + return self.getRow(permission_id) + + def getPermissionsForPrincipal(self, principal_id): + ''' See the interface IPrincipalPermissionManager ''' + return self.getCol(principal_id) + + def getSetting(self, permission_id, principal_id, default=Unset): + ''' See the interface IPrincipalPermissionManager ''' + return self.queryCell(permission_id, principal_id, default) + + def getPrincipalsAndPermissions(self): + ''' See the interface IPrincipalPermissionManager ''' + return self.getAllCells() + + +# Permissions are our rows, and principals are our columns +principalPermissionManager = PrincipalPermissionManager() + + +# Register our cleanup with Testing.CleanUp to make writing unit tests +# simpler. +try: + from zope.testing.cleanup import addCleanUp +except ImportError: + pass +else: + addCleanUp(principalPermissionManager._clear) + del addCleanUp diff -Nru zope3-3.4.0/src/zope/securitypolicy/principalrole.py zope3-3.5~bzr18/src/zope/securitypolicy/principalrole.py --- zope3-3.4.0/src/zope/securitypolicy/principalrole.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/principalrole.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,112 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Mappings between principals and roles, stored in an object locally. + +$Id: principalrole.py 97975 2009-03-12 09:48:57Z nadako $ +""" +from zope.interface import implements + +from zope.authentication.principal import checkPrincipal +from zope.securitypolicy.interfaces import Allow, Deny, Unset +from zope.securitypolicy.interfaces import IPrincipalRoleManager +from zope.securitypolicy.securitymap import SecurityMap +from zope.securitypolicy.securitymap import AnnotationSecurityMap +from zope.securitypolicy.role import checkRole + + +class AnnotationPrincipalRoleManager(AnnotationSecurityMap): + """Mappings between principals and roles.""" + + # the annotation key is a holdover from this module's old + # location, but cannot change without breaking existing databases + key = 'zope.app.security.AnnotationPrincipalRoleManager' + + implements(IPrincipalRoleManager) + + def assignRoleToPrincipal(self, role_id, principal_id): + AnnotationSecurityMap.addCell(self, role_id, principal_id, Allow) + + def removeRoleFromPrincipal(self, role_id, principal_id): + AnnotationSecurityMap.addCell(self, role_id, principal_id, Deny) + + unsetRoleForPrincipal = AnnotationSecurityMap.delCell + getPrincipalsForRole = AnnotationSecurityMap.getRow + getRolesForPrincipal = AnnotationSecurityMap.getCol + + def getSetting(self, role_id, principal_id): + return AnnotationSecurityMap.queryCell( + self, role_id, principal_id, default=Unset) + + getPrincipalsAndRoles = AnnotationSecurityMap.getAllCells + + +class PrincipalRoleManager(SecurityMap): + """Mappings between principals and roles.""" + + implements(IPrincipalRoleManager) + + def assignRoleToPrincipal(self, role_id, principal_id, check=True): + ''' See the interface IPrincipalRoleManager ''' + + if check: + checkPrincipal(None, principal_id) + checkRole(None, role_id) + + self.addCell(role_id, principal_id, Allow) + + def removeRoleFromPrincipal(self, role_id, principal_id, check=True): + ''' See the interface IPrincipalRoleManager ''' + + if check: + checkPrincipal(None, principal_id) + checkRole(None, role_id) + + self.addCell(role_id, principal_id, Deny) + + def unsetRoleForPrincipal(self, role_id, principal_id): + ''' See the interface IPrincipalRoleManager ''' + + # Don't check validity intentionally. + # After all, we certainly want to unset invalid ids. + + self.delCell(role_id, principal_id) + + def getPrincipalsForRole(self, role_id): + ''' See the interface IPrincipalRoleMap ''' + return self.getRow(role_id) + + def getRolesForPrincipal(self, principal_id): + ''' See the interface IPrincipalRoleMap ''' + return self.getCol(principal_id) + + def getSetting(self, role_id, principal_id): + ''' See the interface IPrincipalRoleMap ''' + return self.queryCell(role_id, principal_id, default=Unset) + + def getPrincipalsAndRoles(self): + ''' See the interface IPrincipalRoleMap ''' + return self.getAllCells() + +# Roles are our rows, and principals are our columns +principalRoleManager = PrincipalRoleManager() + +# Register our cleanup with Testing.CleanUp to make writing unit tests +# simpler. +try: + from zope.testing.cleanup import addCleanUp +except ImportError: + pass +else: + addCleanUp(principalRoleManager._clear) + del addCleanUp diff -Nru zope3-3.4.0/src/zope/securitypolicy/rolepermission.py zope3-3.5~bzr18/src/zope/securitypolicy/rolepermission.py --- zope3-3.4.0/src/zope/securitypolicy/rolepermission.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/rolepermission.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,116 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Permission to Roles Manager (Adapter) + +$Id: rolepermission.py 80108 2007-09-26 15:02:36Z rogerineichen $ +""" +from zope.interface import implements + +from zope.security.permission import allPermissions +from zope.securitypolicy.role import checkRole +from zope.securitypolicy.interfaces import Allow, Deny, Unset +from zope.securitypolicy.interfaces import IRolePermissionManager +from zope.securitypolicy.interfaces import IRolePermissionMap +from zope.securitypolicy.securitymap import AnnotationSecurityMap +from zope.securitypolicy.securitymap import SecurityMap + + +class AnnotationRolePermissionManager(AnnotationSecurityMap): + """Provide adapter that manages role permission data in an object attribute + """ + + # the annotation key is a holdover from this module's old + # location, but cannot change without breaking existing databases + key = 'zope.app.security.AnnotationRolePermissionManager' + + implements(IRolePermissionManager) + + def grantPermissionToRole(self, permission_id, role_id): + AnnotationSecurityMap.addCell(self, permission_id, role_id, Allow) + + def denyPermissionToRole(self, permission_id, role_id): + AnnotationSecurityMap.addCell(self, permission_id, role_id, Deny) + + unsetPermissionFromRole = AnnotationSecurityMap.delCell + getRolesForPermission = AnnotationSecurityMap.getRow + getPermissionsForRole = AnnotationSecurityMap.getCol + getRolesAndPermissions = AnnotationSecurityMap.getAllCells + + def getSetting(self, permission_id, role_id): + return AnnotationSecurityMap.queryCell( + self, permission_id, role_id, default=Unset) + + +class RolePermissionManager(SecurityMap): + """Mappings between roles and permissions.""" + + implements(IRolePermissionManager) + + def grantPermissionToRole(self, permission_id, role_id, check=True): + '''See interface IRolePermissionMap''' + + if check: + checkRole(None, role_id) + + self.addCell(permission_id, role_id, Allow) + + def grantAllPermissionsToRole(self, role_id): + for permission_id in allPermissions(None): + self.grantPermissionToRole(permission_id, role_id, False) + + def denyPermissionToRole(self, permission_id, role_id, check=True): + '''See interface IRolePermissionMap''' + + if check: + checkRole(None, role_id) + + self.addCell(permission_id, role_id, Deny) + + def unsetPermissionFromRole(self, permission_id, role_id): + '''See interface IRolePermissionMap''' + + # Don't check validity intentionally. + # After all, we certianly want to unset invalid ids. + + self.delCell(permission_id, role_id) + + def getRolesForPermission(self, permission_id): + '''See interface IRolePermissionMap''' + return self.getRow(permission_id) + + def getPermissionsForRole(self, role_id): + '''See interface IRolePermissionMap''' + return self.getCol(role_id) + + def getSetting(self, permission_id, role_id): + '''See interface IRolePermissionMap''' + return self.queryCell(permission_id, role_id) + + def getRolesAndPermissions(self): + '''See interface IRolePermissionMap''' + return self.getAllCells() + +# Permissions are our rows, and roles are our columns +rolePermissionManager = RolePermissionManager() + + +# Register our cleanup with Testing.CleanUp to make writing unit tests +# simpler. +try: + from zope.testing.cleanup import addCleanUp +except ImportError: + pass +else: + addCleanUp(rolePermissionManager._clear) + del addCleanUp diff -Nru zope3-3.4.0/src/zope/securitypolicy/role.py zope3-3.5~bzr18/src/zope/securitypolicy/role.py --- zope3-3.4.0/src/zope/securitypolicy/role.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/role.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,109 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Role implementation + +$Id: role.py 80108 2007-09-26 15:02:36Z rogerineichen $ +""" +__docformat__ = 'restructuredtext' + +from persistent import Persistent + +from zope.interface import implements +from zope.component import getUtilitiesFor +from zope.location import Location + +from zope.securitypolicy.interfaces import IRole + +from zope.i18nmessageid import ZopeMessageFactory as _ +NULL_ID = _('') + + +class Role(object): + implements(IRole) + + def __init__(self, id, title, description=""): + self.id = id + self.title = title + self.description = description + + +class LocalRole(Persistent, Location): + implements(IRole) + + def __init__(self, title, description=""): + self.id = NULL_ID + self.title = title + self.description = description + +def setIdOnActivation(role, event): + """Set the permission id upon registration activation. + + Let's see how this notifier can be used. First we need to create an event + using the permission instance and a registration stub: + + >>> class Registration: + ... def __init__(self, obj, name): + ... self.component = obj + ... self.name = name + + >>> role1 = LocalRole('Role 1', 'A first role') + >>> role1.id + u'' + >>> import zope.component.interfaces + >>> event = zope.component.interfaces.Registered( + ... Registration(role1, 'role1')) + + Now we pass the event into this function, and the id of the role should be + set to 'role1'. + + >>> setIdOnActivation(role1, event) + >>> role1.id + 'role1' + """ + role.id = event.object.name + + +def unsetIdOnDeactivation(role, event): + """Unset the permission id up registration deactivation. + + Let's see how this notifier can be used. First we need to create an event + using the permission instance and a registration stub: + + >>> class Registration: + ... def __init__(self, obj, name): + ... self.component = obj + ... self.name = name + + >>> role1 = LocalRole('Role 1', 'A first role') + >>> role1.id = 'role1' + + >>> import zope.component.interfaces + >>> event = zope.component.interfaces.Unregistered( + ... Registration(role1, 'role1')) + + Now we pass the event into this function, and the id of the role should be + set to NULL_ID. + + >>> unsetIdOnDeactivation(role1, event) + >>> role1.id + u'' + """ + role.id = NULL_ID + + + +def checkRole(context, role_id): + names = [name for name, util in getUtilitiesFor(IRole, context)] + if not role_id in names: + raise ValueError("Undefined role id", role_id) diff -Nru zope3-3.4.0/src/zope/securitypolicy/securitymap.py zope3-3.5~bzr18/src/zope/securitypolicy/securitymap.py --- zope3-3.4.0/src/zope/securitypolicy/securitymap.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/securitymap.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,161 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Generic two-dimensional array type (in context of security) + +$Id: securitymap.py 67630 2006-04-27 00:54:03Z jim $ +""" +from persistent import Persistent +from zope.annotation import IAnnotations +from zope.security.management import queryInteraction + +class SecurityMap(object): + + def __init__(self): + self._clear() + + def _clear(self): + self._byrow = {} + self._bycol = {} + + def __nonzero__(self): + return bool(self._byrow) + + def addCell(self, rowentry, colentry, value): + # setdefault may get expensive if an empty mapping is + # expensive to create, for PersistentDict for instance. + row = self._byrow.get(rowentry) + if row: + if row.get(colentry) is value: + return False + else: + row = self._byrow[rowentry] = {} + + col = self._bycol.get(colentry) + if not col: + col = self._bycol[colentry] = {} + + row[colentry] = value + col[rowentry] = value + + self._invalidated_interaction_cache() + + return True + + def _invalidated_interaction_cache(self): + # Invalidate this threads interaction cache + interaction = queryInteraction() + if interaction is not None: + try: + invalidate_cache = interaction.invalidate_cache + except AttributeError: + pass + else: + invalidate_cache() + + def delCell(self, rowentry, colentry): + row = self._byrow.get(rowentry) + if row and (colentry in row): + del row[colentry] + if not row: + del self._byrow[rowentry] + col = self._bycol[colentry] + del col[rowentry] + if not col: + del self._bycol[colentry] + + self._invalidated_interaction_cache() + + return True + + return False + + def queryCell(self, rowentry, colentry, default=None): + row = self._byrow.get(rowentry) + if row: + return row.get(colentry, default) + else: + return default + + def getCell(self, rowentry, colentry): + marker = object() + cell = self.queryCell(rowentry, colentry, marker) + if cell is marker: + raise KeyError('Not a valid row and column pair.') + return cell + + def getRow(self, rowentry): + row = self._byrow.get(rowentry) + if row: + return row.items() + else: + return [] + + def getCol(self, colentry): + col = self._bycol.get(colentry) + if col: + return col.items() + else: + return [] + + def getAllCells(self): + res = [] + for r in self._byrow.keys(): + for c in self._byrow[r].items(): + res.append((r,) + c) + return res + +class PersistentSecurityMap(SecurityMap, Persistent): + + def addCell(self, rowentry, colentry, value): + if SecurityMap.addCell(self, rowentry, colentry, value): + self._p_changed = 1 + + def delCell(self, rowentry, colentry): + if SecurityMap.delCell(self, rowentry, colentry): + self._p_changed = 1 + +class AnnotationSecurityMap(SecurityMap): + + def __init__(self, context): + self.__parent__ = context + self._context = context + annotations = IAnnotations(self._context) + map = annotations.get(self.key) + if map is None: + self._byrow = {} + self._bycol = {} + else: + self._byrow = map._byrow + self._bycol = map._bycol + self.map = map + + def _changed(self): + map = self.map + if isinstance(map, PersistentSecurityMap): + map._p_changed = 1 + else: + map = PersistentSecurityMap() + map._byrow = self._byrow + map._bycol = self._bycol + annotations = IAnnotations(self._context) + annotations[self.key] = map + + def addCell(self, rowentry, colentry, value): + if SecurityMap.addCell(self, rowentry, colentry, value): + self._changed() + + def delCell(self, rowentry, colentry): + if SecurityMap.delCell(self, rowentry, colentry): + self._changed() + diff -Nru zope3-3.4.0/src/zope/securitypolicy/securitypolicy.zcml zope3-3.5~bzr18/src/zope/securitypolicy/securitypolicy.zcml --- zope3-3.4.0/src/zope/securitypolicy/securitypolicy.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/securitypolicy.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/securitypolicy/settings.py zope3-3.5~bzr18/src/zope/securitypolicy/settings.py --- zope3-3.4.0/src/zope/securitypolicy/settings.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/settings.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,74 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Security setting constants. + +The `Allow`, `Deny`, and `Unset` constants are exposed by the +`zope.securitypolicy.interfaces` module, and should be imported +from there. + +$Id: settings.py 97748 2009-03-10 00:20:49Z nadako $ +""" + +class PermissionSetting(object): + """PermissionSettings should be considered as immutable. + They can be compared by identity. They are identified by + their name. + """ + + def __new__(cls, name, description=None): + """Keep a dict of PermissionSetting instances, indexed by + name. If the name already exists in the dict, return that + instance rather than creating a new one. + """ + instances = cls.__dict__.get('_z_instances') + if instances is None: + cls._z_instances = instances = {} + it = instances.get(name) + if it is None: + instances[name] = it = object.__new__(cls) + it._init(name, description) + return it + + def _init(self, name, description): + self.__name = name + self.__description = description + + def getDescription(self): + return self.__description + + def getName(self): + return self.__name + + def __str__(self): + return "PermissionSetting: %s" % self.__name + + __repr__ = __str__ + +# register PermissionSettings to be symbolic constants by identity, +# even when pickled and unpickled. +import copy_reg +copy_reg.constructor(PermissionSetting) +copy_reg.pickle(PermissionSetting, + PermissionSetting.getName, + PermissionSetting) + + +Allow = PermissionSetting('Allow', + 'Explicit allow setting for permissions') + +Deny = PermissionSetting('Deny', + 'Explicit deny setting for permissions') + +Unset = PermissionSetting('Unset', + 'Unset constant that denotes no setting for permission') diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/__init__.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/__init__.py --- zope3-3.4.0/src/zope/securitypolicy/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,50 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +""" +$Id: __init__.py 98094 2009-03-14 15:50:25Z nadako $ +""" +from zope.interface import implements +from zope.authentication.interfaces import IAuthentication, PrincipalLookupError +from zope.security.interfaces import IPrincipal + +class DummyPrincipalRegistry(object): + """Dummy principal registry that only implements getPrincipal + and definePrincipal method that are needed for securitypolicy tests.""" + + implements(IAuthentication) + + def __init__(self): + self._principals = {} + + def getPrincipal(self, id): + if id not in self._principals: + raise PrincipalLookupError(id) + return self._principals[id] + + def definePrincipal(self, id, title=u'', description=u''): + p = DummyPrincipal(id, title, description) + self._principals[id] = p + return p + +principalRegistry = DummyPrincipalRegistry() + +class DummyPrincipal(object): + """Very simple principal implementation""" + + implements(IPrincipal) + + def __init__(self, id, title=u'', description=u''): + self.id = id + self.title = title + self.description = description diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/mapping.zcml zope3-3.5~bzr18/src/zope/securitypolicy/tests/mapping.zcml --- zope3-3.4.0/src/zope/securitypolicy/tests/mapping.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/mapping.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/role_duplicate.zcml zope3-3.5~bzr18/src/zope/securitypolicy/tests/role_duplicate.zcml --- zope3-3.4.0/src/zope/securitypolicy/tests/role_duplicate.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/role_duplicate.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,20 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/role.zcml zope3-3.5~bzr18/src/zope/securitypolicy/tests/role.zcml --- zope3-3.4.0/src/zope/securitypolicy/tests/role.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/role.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_annotationprincipalpermissionmanager.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_annotationprincipalpermissionmanager.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_annotationprincipalpermissionmanager.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_annotationprincipalpermissionmanager.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,153 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 handler for Annotation Principal Permission Manager module. + +$Id: test_annotationprincipalpermissionmanager.py 98094 2009-03-14 15:50:25Z nadako $ +""" +import unittest + +from zope.component import provideAdapter, provideUtility +from zope.component.testing import PlacelessSetup +from zope.interface import implements +from zope.annotation.attribute import AttributeAnnotations +from zope.annotation.interfaces import IAttributeAnnotatable, IAnnotations +from zope.security.interfaces import IPermission +from zope.security.permission import Permission + +from zope.securitypolicy.interfaces import Allow, Deny, Unset +from zope.securitypolicy.principalpermission import \ + AnnotationPrincipalPermissionManager +from zope.securitypolicy.tests import principalRegistry + + +class Manageable(object): + implements(IAttributeAnnotatable) + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + provideAdapter(AttributeAnnotations) + + def _make_principal(self, id=None, title=None): + p = principalRegistry.definePrincipal( + id or 'APrincipal', + title or 'A Principal') + return p.id + + def testUnboundPrincipalPermission(self): + manager = AnnotationPrincipalPermissionManager(Manageable()) + provideUtility(Permission('APerm', 'title'), IPermission, 'APerm') + permission = 'APerm' + principal = self._make_principal() + self.assertEqual(manager.getPrincipalsForPermission(permission), []) + self.assertEqual(manager.getPermissionsForPrincipal(principal), []) + + def testPrincipalPermission(self): + manager = AnnotationPrincipalPermissionManager(Manageable()) + provideUtility(Permission('APerm', 'title'), IPermission, 'APerm') + permission = 'APerm' + principal = self._make_principal() + + # check that an allow permission is saved correctly + manager.grantPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), + [(principal, Allow)]) + self.assertEqual(manager.getPermissionsForPrincipal(principal), + [(permission, Allow)]) + + # check that the allow permission is removed. + manager.unsetPermissionForPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), []) + self.assertEqual(manager.getPermissionsForPrincipal(principal), []) + + # now put a deny in there, check it's set. + manager.denyPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), + [(principal, Deny)]) + self.assertEqual(manager.getPermissionsForPrincipal(principal), + [(permission, Deny)]) + + # test for deny followed by allow . The latter should override. + manager.grantPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), + [(principal, Allow)]) + self.assertEqual(manager.getPermissionsForPrincipal(principal), + [(permission, Allow)]) + + # check that allow followed by allow is just a single allow. + manager.grantPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), + [(principal, Allow)]) + self.assertEqual(manager.getPermissionsForPrincipal(principal), + [(permission, Allow)]) + + # check that two unsets in a row quietly ignores the second one. + manager.unsetPermissionForPrincipal(permission, principal) + manager.unsetPermissionForPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), []) + self.assertEqual(manager.getPermissionsForPrincipal(principal), []) + + # check the result of getSetting() when it's empty. + self.assertEqual(manager.getSetting(permission, principal), Unset) + + # check the result of getSetting() when it's allowed. + manager.grantPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getSetting(permission, principal), Allow) + + # check the result of getSetting() when it's denied. + manager.denyPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getSetting(permission, principal), Deny) + + def testManyPermissionsOnePrincipal(self): + manager = AnnotationPrincipalPermissionManager(Manageable()) + provideUtility(Permission('Perm One', 'title'), IPermission, + 'Perm One') + perm1 = 'Perm One' + provideUtility(Permission('Perm Two', 'title'), IPermission, + 'Perm Two') + perm2 = 'Perm Two' + prin1 = self._make_principal() + manager.grantPermissionToPrincipal(perm1, prin1) + manager.grantPermissionToPrincipal(perm2, prin1) + perms = manager.getPermissionsForPrincipal(prin1) + self.assertEqual(len(perms), 2) + self.failUnless((perm1, Allow) in perms) + self.failUnless((perm2, Allow) in perms) + manager.denyPermissionToPrincipal(perm2, prin1) + perms = manager.getPermissionsForPrincipal(prin1) + self.assertEqual(len(perms), 2) + self.failUnless((perm1, Allow) in perms) + self.failUnless((perm2, Deny) in perms) + + def testManyPrincipalsOnePermission(self): + manager = AnnotationPrincipalPermissionManager(Manageable()) + provideUtility(Permission('Perm One', 'title'), IPermission, + 'Perm One') + perm1 = 'Perm One' + prin1 = self._make_principal() + prin2 = self._make_principal('Principal 2', 'Principal Two') + manager.grantPermissionToPrincipal(perm1, prin1) + manager.denyPermissionToPrincipal(perm1, prin2) + principals = manager.getPrincipalsForPermission(perm1) + self.assertEqual(len(principals), 2) + self.failUnless((prin1, Allow) in principals) + self.failUnless((prin2, Deny) in principals) + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_annotationprincipalrolemanager.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_annotationprincipalrolemanager.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_annotationprincipalrolemanager.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_annotationprincipalrolemanager.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,143 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 handler for PrincipalRoleManager module. + +$Id: test_annotationprincipalrolemanager.py 98094 2009-03-14 15:50:25Z nadako $ +""" +import unittest +import zope.component +from zope.interface import implements +from zope.annotation.interfaces import IAttributeAnnotatable + +from zope.component.testing import PlacelessSetup +from zope.annotation.attribute import AttributeAnnotations + +from zope.securitypolicy.principalrole import AnnotationPrincipalRoleManager +from zope.securitypolicy.interfaces import Allow, Deny +from zope.securitypolicy.interfaces import IRole +from zope.securitypolicy.role import Role +from zope.securitypolicy.tests import principalRegistry + +class Manageable(object): + implements(IAttributeAnnotatable) + +def defineRole(id, title=None, description=None): + role = Role(id, title, description) + zope.component.provideUtility(role, IRole, name=role.id) + return role + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + PlacelessSetup.setUp(self) + zope.component.provideAdapter(AttributeAnnotations) + + def _make_principal(self, id=None, title=None): + p = principalRegistry.definePrincipal( + id or 'APrincipal', + title or 'A Principal') + return p.id + + def _make_roleManager(self, obj=None): + if obj is None: + obj = Manageable() + return AnnotationPrincipalRoleManager(obj) + + def testUnboundPrincipalRole(self): + principalRoleManager = self._make_roleManager() + role = defineRole('ARole', 'A Role').id + principal = self._make_principal() + self.assertEqual( + principalRoleManager.getPrincipalsForRole(role), []) + self.assertEqual( + principalRoleManager.getRolesForPrincipal(principal), []) + + def testPrincipalRoleAllow(self): + principalRoleManager = self._make_roleManager() + role = defineRole('ARole', 'A Role').id + principal = self._make_principal() + principalRoleManager.assignRoleToPrincipal(role, principal) + self.assertEqual(principalRoleManager.getPrincipalsForRole(role), + [(principal, Allow)]) + self.assertEqual(principalRoleManager.getRolesForPrincipal(principal), + [(role, Allow)]) + + def testPrincipalRoleDeny(self): + principalRoleManager = self._make_roleManager() + role = defineRole('ARole', 'A Role').id + principal = self._make_principal() + principalRoleManager.removeRoleFromPrincipal(role, principal) + self.assertEqual(principalRoleManager.getPrincipalsForRole(role), + [(principal, Deny)]) + self.assertEqual(principalRoleManager.getRolesForPrincipal(principal), + [(role, Deny)]) + + def testPrincipalRoleUnset(self): + principalRoleManager = self._make_roleManager() + role = defineRole('ARole', 'A Role').id + principal = self._make_principal() + principalRoleManager.removeRoleFromPrincipal(role, principal) + principalRoleManager.unsetRoleForPrincipal(role, principal) + self.assertEqual(principalRoleManager.getPrincipalsForRole(role), + []) + self.assertEqual(principalRoleManager.getRolesForPrincipal(principal), + []) + + def testManyRolesOnePrincipal(self): + principalRoleManager = self._make_roleManager() + role1 = defineRole('Role One', 'Role #1').id + role2 = defineRole('Role Two', 'Role #2').id + prin1 = self._make_principal() + principalRoleManager.assignRoleToPrincipal(role1, prin1) + principalRoleManager.assignRoleToPrincipal(role2, prin1) + roles = principalRoleManager.getRolesForPrincipal(prin1) + self.assertEqual(len(roles), 2) + self.failUnless((role1, Allow) in roles) + self.failUnless((role2, Allow) in roles) + + def testManyPrincipalsOneRole(self): + principalRoleManager = self._make_roleManager() + role1 = defineRole('Role One', 'Role #1').id + prin1 = self._make_principal() + prin2 = self._make_principal('Principal 2', 'Principal Two') + principalRoleManager.assignRoleToPrincipal(role1, prin1) + principalRoleManager.assignRoleToPrincipal(role1, prin2) + principals = principalRoleManager.getPrincipalsForRole(role1) + self.assertEqual(len(principals), 2) + self.failUnless((prin1, Allow) in principals) + self.failUnless((prin2, Allow) in principals) + + def testPrincipalsAndRoles(self): + principalRoleManager = self._make_roleManager() + principalsAndRoles = principalRoleManager.getPrincipalsAndRoles() + self.assertEqual(len(principalsAndRoles), 0) + role1 = defineRole('Role One', 'Role #1').id + role2 = defineRole('Role Two', 'Role #2').id + prin1 = self._make_principal() + prin2 = self._make_principal('Principal 2', 'Principal Two') + principalRoleManager.assignRoleToPrincipal(role1, prin1) + principalRoleManager.assignRoleToPrincipal(role1, prin2) + principalRoleManager.assignRoleToPrincipal(role2, prin1) + principalsAndRoles = principalRoleManager.getPrincipalsAndRoles() + self.assertEqual(len(principalsAndRoles), 3) + self.failUnless((role1, prin1, Allow) in principalsAndRoles) + self.failUnless((role1, prin2, Allow) in principalsAndRoles) + self.failUnless((role2, prin1, Allow) in principalsAndRoles) + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_annotationrolepermissionmanager.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_annotationrolepermissionmanager.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_annotationrolepermissionmanager.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_annotationrolepermissionmanager.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,96 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 handler for Annotation Role Permission Manager. + +$Id: test_annotationrolepermissionmanager.py 98091 2009-03-14 11:20:13Z nadako $ +""" +import unittest +from zope.component import provideUtility, provideAdapter +from zope.component.testing import PlacelessSetup +from zope.interface import implements +from zope.annotation.attribute import AttributeAnnotations +from zope.annotation.interfaces import IAttributeAnnotatable +from zope.security.interfaces import IPermission +from zope.security.permission import Permission + +from zope.securitypolicy.role import Role +from zope.securitypolicy.interfaces import Allow, Deny +from zope.securitypolicy.interfaces import IRole +from zope.securitypolicy.rolepermission import AnnotationRolePermissionManager + +class Manageable(object): + implements(IAttributeAnnotatable) + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + PlacelessSetup.setUp(self) + provideAdapter(AttributeAnnotations) + + + read = Permission('read', 'Read Something') + provideUtility(read, IPermission, read.id) + self.read = read.id + + write = Permission('write', 'Write Something') + provideUtility(write, IPermission, write.id) + self.write = write.id + + peon = Role('peon', 'Poor Slob') + provideUtility(peon, IRole, peon.id) + self.peon = peon.id + + manager = Role('manager', 'Supreme Being') + provideUtility(manager, IRole, manager.id) + self.manager = manager.id + + def testNormal(self): + obj = Manageable() + mgr = AnnotationRolePermissionManager(obj) + mgr.grantPermissionToRole(self.read,self.manager) + mgr.grantPermissionToRole(self.write,self.manager) + mgr.grantPermissionToRole(self.write,self.manager) + + mgr.grantPermissionToRole(self.read,self.peon) + + l = list(mgr.getPermissionsForRole(self.manager)) + self.failUnless((self.read, Allow) in l) + self.failUnless((self.write, Allow) in l) + + l = list(mgr.getPermissionsForRole(self.peon)) + self.failUnless([(self.read, Allow)] == l) + + l = list(mgr.getRolesForPermission(self.read)) + self.failUnless((self.manager, Allow) in l) + self.failUnless((self.peon, Allow) in l) + + l = list(mgr.getRolesForPermission(self.write)) + self.assertEqual(l, [(self.manager, Allow)]) + + mgr.denyPermissionToRole(self.read, self.peon) + l = list(mgr.getPermissionsForRole(self.peon)) + self.assertEqual(l, [(self.read, Deny)]) + + mgr.unsetPermissionFromRole(self.read, self.peon) + + l = list(mgr.getRolesForPermission(self.read)) + self.assertEqual(l, [(self.manager, Allow)]) + + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_principalpermissionmanager.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_principalpermissionmanager.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_principalpermissionmanager.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_principalpermissionmanager.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,155 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 handler for PrincipalPermissionManager module. + +$Id: test_principalpermissionmanager.py 98094 2009-03-14 15:50:25Z nadako $ +""" +import unittest + +from zope.component import provideUtility +from zope.component.testing import PlacelessSetup +from zope.security.interfaces import IPermission +from zope.security.permission import Permission + +from zope.authentication.interfaces import IAuthentication + +from zope.securitypolicy.interfaces import Allow, Deny, Unset +from zope.securitypolicy.principalpermission import \ + principalPermissionManager as manager +from zope.securitypolicy.tests import principalRegistry + + +def definePermission(id, title=None, description=None): + perm = Permission(id, title, description) + provideUtility(perm, IPermission, perm.id) + return perm + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + provideUtility(principalRegistry, IAuthentication) + + + def _make_principal(self, id=None, title=None): + p = principalRegistry.definePrincipal( + id or 'APrincipal', + title or 'A Principal') + return p.id + + def testUnboundPrincipalPermission(self): + permission = definePermission('APerm', 'title').id + principal = self._make_principal() + self.assertEqual(manager.getPrincipalsForPermission(permission), []) + self.assertEqual(manager.getPermissionsForPrincipal(principal), []) + + + def test_invalidPrincipal(self): + permission = definePermission('APerm', 'title').id + self.assertRaises(ValueError, + manager.grantPermissionToPrincipal, + permission, 'principal') + + + def testPrincipalPermission(self): + permission = definePermission('APerm', 'title').id + principal = self._make_principal() + # check that an allow permission is saved correctly + manager.grantPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), + [(principal, Allow)]) + self.assertEqual(manager.getPermissionsForPrincipal(principal), + [(permission, Allow)]) + # check that the allow permission is removed. + manager.unsetPermissionForPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), []) + self.assertEqual(manager.getPermissionsForPrincipal(principal), []) + # now put a deny in there, check it's set. + manager.denyPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), + [(principal, Deny)]) + self.assertEqual(manager.getPermissionsForPrincipal(principal), + [(permission, Deny)]) + # test for deny followed by allow . The latter should override. + manager.grantPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), + [(principal, Allow)]) + self.assertEqual(manager.getPermissionsForPrincipal(principal), + [(permission, Allow)]) + # check that allow followed by allow is just a single allow. + manager.grantPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), + [(principal, Allow)]) + self.assertEqual(manager.getPermissionsForPrincipal(principal), + [(permission, Allow)]) + # check that two unsets in a row quietly ignores the second one. + manager.unsetPermissionForPrincipal(permission, principal) + manager.unsetPermissionForPrincipal(permission, principal) + self.assertEqual(manager.getPrincipalsForPermission(permission), []) + self.assertEqual(manager.getPermissionsForPrincipal(principal), []) + # check the result of getSetting() when it's empty. + self.assertEqual(manager.getSetting(permission, principal), Unset) + # check the result of getSetting() when it's allowed. + manager.grantPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getSetting(permission, principal), Allow) + # check the result of getSetting() when it's denied. + manager.denyPermissionToPrincipal(permission, principal) + self.assertEqual(manager.getSetting(permission, principal), Deny) + + def testManyPermissionsOnePrincipal(self): + perm1 = definePermission('Perm One', 'title').id + perm2 = definePermission('Perm Two', 'title').id + prin1 = self._make_principal() + manager.grantPermissionToPrincipal(perm1, prin1) + manager.grantPermissionToPrincipal(perm2, prin1) + perms = manager.getPermissionsForPrincipal(prin1) + self.assertEqual(len(perms), 2) + self.failUnless((perm1,Allow) in perms) + self.failUnless((perm2,Allow) in perms) + manager.denyPermissionToPrincipal(perm2, prin1) + perms = manager.getPermissionsForPrincipal(prin1) + self.assertEqual(len(perms), 2) + self.failUnless((perm1,Allow) in perms) + self.failUnless((perm2,Deny) in perms) + perms = manager.getPrincipalsAndPermissions() + self.failUnless((perm1,prin1,Allow) in perms) + self.failUnless((perm2,prin1,Deny) in perms) + + def testAllPermissions(self): + perm1 = definePermission('Perm One', 'title').id + perm2 = definePermission('Perm Two', 'title').id + prin1 = self._make_principal() + manager.grantAllPermissionsToPrincipal(prin1) + perms = manager.getPermissionsForPrincipal(prin1) + self.assertEqual(len(perms), 2) + self.failUnless((perm1,Allow) in perms) + self.failUnless((perm2,Allow) in perms) + + def testManyPrincipalsOnePermission(self): + perm1 = definePermission('Perm One', 'title').id + prin1 = self._make_principal() + prin2 = self._make_principal('Principal 2', 'Principal Two') + manager.grantPermissionToPrincipal(perm1, prin1) + manager.denyPermissionToPrincipal(perm1, prin2) + principals = manager.getPrincipalsForPermission(perm1) + self.assertEqual(len(principals), 2) + self.failUnless((prin1,Allow) in principals) + self.failUnless((prin2,Deny) in principals) + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_principalrolemanager.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_principalrolemanager.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_principalrolemanager.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_principalrolemanager.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,142 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 handler for PrincipalRoleManager module. + +$Id: test_principalrolemanager.py 98094 2009-03-14 15:50:25Z nadako $ +""" +import unittest + +from zope.component import provideUtility +from zope.component.testing import PlacelessSetup + +from zope.authentication.interfaces import IAuthentication + +from zope.securitypolicy.role import Role +from zope.securitypolicy.interfaces import Allow, Deny +from zope.securitypolicy.interfaces import IRole +from zope.securitypolicy.principalrole import principalRoleManager +from zope.securitypolicy.tests import principalRegistry + +def defineRole(id, title=None, description=None): + role = Role(id, title, description) + provideUtility(role, IRole, role.id) + return role + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + provideUtility(principalRegistry, IAuthentication) + + def _make_principal(self, id=None, title=None): + p = principalRegistry.definePrincipal( + id or 'APrincipal', + title or 'A Principal') + return p.id + + def testUnboundPrincipalRole(self): + role = defineRole('ARole', 'A Role').id + principal = self._make_principal() + self.assertEqual(principalRoleManager.getPrincipalsForRole(role), []) + self.assertEqual(principalRoleManager.getRolesForPrincipal(principal), + []) + + def testPrincipalRoleAllow(self): + role = defineRole('ARole', 'A Role').id + principal = self._make_principal() + principalRoleManager.assignRoleToPrincipal(role, principal) + self.assertEqual(principalRoleManager.getPrincipalsForRole(role), + [(principal, Allow)]) + self.assertEqual(principalRoleManager.getRolesForPrincipal(principal), + [(role, Allow)]) + + def testPrincipalRoleDeny(self): + role = defineRole('ARole', 'A Role').id + principal = self._make_principal() + principalRoleManager.removeRoleFromPrincipal(role, principal) + self.assertEqual(principalRoleManager.getPrincipalsForRole(role), + [(principal, Deny)]) + self.assertEqual(principalRoleManager.getRolesForPrincipal(principal), + [(role, Deny)]) + + def testPrincipalRoleUnset(self): + role = defineRole('ARole', 'A Role').id + principal = self._make_principal() + principalRoleManager.removeRoleFromPrincipal(role, principal) + principalRoleManager.unsetRoleForPrincipal(role, principal) + self.assertEqual(principalRoleManager.getPrincipalsForRole(role), + []) + self.assertEqual(principalRoleManager.getRolesForPrincipal(principal), + []) + + + def test_invalidPrincipal(self): + self.assertRaises(ValueError, + principalRoleManager.assignRoleToPrincipal, + 'role1', 'prin1') + role1 = defineRole('Role One', 'Role #1').id + self.assertRaises(ValueError, + principalRoleManager.assignRoleToPrincipal, + role1, 'prin1') + + def test_invalidRole(self): + prin1 = self._make_principal() + self.assertRaises(ValueError, + principalRoleManager.assignRoleToPrincipal, + 'role1', prin1) + + + def testManyRolesOnePrincipal(self): + role1 = defineRole('Role One', 'Role #1').id + role2 = defineRole('Role Two', 'Role #2').id + prin1 = self._make_principal() + principalRoleManager.assignRoleToPrincipal(role1, prin1) + principalRoleManager.assignRoleToPrincipal(role2, prin1) + roles = principalRoleManager.getRolesForPrincipal(prin1) + self.assertEqual(len(roles), 2) + self.failUnless((role1, Allow) in roles) + self.failUnless((role2, Allow) in roles) + + def testManyPrincipalsOneRole(self): + role1 = defineRole('Role One', 'Role #1').id + prin1 = self._make_principal() + prin2 = self._make_principal('Principal 2', 'Principal Two') + principalRoleManager.assignRoleToPrincipal(role1, prin1) + principalRoleManager.assignRoleToPrincipal(role1, prin2) + principals = principalRoleManager.getPrincipalsForRole(role1) + self.assertEqual(len(principals), 2) + self.failUnless((prin1, Allow) in principals) + self.failUnless((prin2, Allow) in principals) + + def testPrincipalsAndRoles(self): + role1 = defineRole('Role One', 'Role #1').id + role2 = defineRole('Role Two', 'Role #2').id + prin1 = self._make_principal() + prin2 = self._make_principal('Principal 2', 'Principal Two') + principalRoleManager.assignRoleToPrincipal(role1, prin1) + principalRoleManager.assignRoleToPrincipal(role1, prin2) + principalRoleManager.assignRoleToPrincipal(role2, prin1) + principalsAndRoles = principalRoleManager.getPrincipalsAndRoles() + self.assertEqual(len(principalsAndRoles), 3) + self.failUnless((role1, prin1, Allow) in principalsAndRoles) + self.failUnless((role1, prin2, Allow) in principalsAndRoles) + self.failUnless((role2, prin1, Allow) in principalsAndRoles) + + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_rolepermissionmanager.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_rolepermissionmanager.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_rolepermissionmanager.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_rolepermissionmanager.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,128 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 handler for RolePermissionManager module. + +$Id: test_rolepermissionmanager.py 98089 2009-03-14 11:08:54Z nadako $ +""" +import unittest + +from zope.component import provideUtility +from zope.component.testing import PlacelessSetup +from zope.security.interfaces import IPermission +from zope.security.permission import Permission + +from zope.securitypolicy.role import Role +from zope.securitypolicy.interfaces import Allow, Deny +from zope.securitypolicy.interfaces import IRole +from zope.securitypolicy.rolepermission import \ + rolePermissionManager as manager + +def defineRole(id, title=None, description=None): + role = Role(id, title, description) + provideUtility(role, IRole, role.id) + return role + +def definePermission(id, title=None, description=None): + perm = Permission(id, title, description) + provideUtility(perm, IPermission, perm.id) + return perm + +class Test(PlacelessSetup, unittest.TestCase): + + def testUnboundRolePermission(self): + permission = definePermission('APerm', 'aPerm title').id + role = defineRole('ARole', 'A Role').id + self.assertEqual(manager.getRolesForPermission(permission), []) + self.assertEqual(manager.getPermissionsForRole(role), []) + + def testRolePermission(self): + permission = definePermission('APerm', 'aPerm title').id + role = defineRole('ARole', 'A Role').id + manager.grantPermissionToRole(permission, role) + self.assertEqual(manager.getRolesForPermission(permission), + [(role,Allow)]) + self.assertEqual(manager.getPermissionsForRole(role), + [(permission,Allow)]) + + def testManyPermissionsOneRole(self): + perm1 = definePermission('Perm One', 'P1').id + perm2 = definePermission('Perm Two', 'P2').id + perm3 = definePermission('Perm Three', 'P3').id + role1 = defineRole('Role One', 'Role #1').id + perms = manager.getPermissionsForRole(role1) + self.assertEqual(len(perms), 0) + manager.grantPermissionToRole(perm1, role1) + manager.grantPermissionToRole(perm2, role1) + manager.grantPermissionToRole(perm2, role1) + manager.denyPermissionToRole(perm3, role1) + perms = manager.getPermissionsForRole(role1) + self.assertEqual(len(perms), 3) + self.failUnless((perm1,Allow) in perms) + self.failUnless((perm2,Allow) in perms) + self.failUnless((perm3,Deny) in perms) + manager.unsetPermissionFromRole(perm1, role1) + perms = manager.getPermissionsForRole(role1) + self.assertEqual(len(perms), 2) + self.failUnless((perm2,Allow) in perms) + + def testAllPermissions(self): + perm1 = definePermission('Perm One', 'P1').id + perm2 = definePermission('Perm Two', 'P2').id + perm3 = definePermission('Perm Three', 'P3').id + role1 = defineRole('Role One', 'Role #1').id + perms = manager.getPermissionsForRole(role1) + self.assertEqual(len(perms), 0) + manager.grantAllPermissionsToRole(role1) + perms = manager.getPermissionsForRole(role1) + self.assertEqual(len(perms), 3) + self.failUnless((perm1, Allow) in perms) + self.failUnless((perm2, Allow) in perms) + self.failUnless((perm3, Allow) in perms) + + def testManyRolesOnePermission(self): + perm1 = definePermission('Perm One', 'title').id + role1 = defineRole('Role One', 'Role #1').id + role2 = defineRole('Role Two', 'Role #2').id + roles = manager.getRolesForPermission(perm1) + self.assertEqual(len(roles), 0) + manager.grantPermissionToRole(perm1, role1) + manager.grantPermissionToRole(perm1, role2) + manager.grantPermissionToRole(perm1, role2) + manager.denyPermissionToRole(perm1, role1) + roles = manager.getRolesForPermission(perm1) + self.assertEqual(len(roles), 2) + self.failIf((role1,Allow) in roles) + self.failUnless((role1,Deny) in roles) + self.failUnless((role2,Allow) in roles) + manager.unsetPermissionFromRole(perm1, role1) + roles = manager.getRolesForPermission(perm1) + self.assertEqual(len(roles), 1) + self.failUnless((role2,Allow) in roles) + + def test_invalidRole(self): + self.assertRaises(ValueError, + manager.grantPermissionToRole, 'perm1', 'role1' + ) + perm1 = definePermission('Perm One', 'title').id + self.assertRaises(ValueError, + manager.grantPermissionToRole, perm1, 'role1' + ) + + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_role.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_role.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_role.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_role.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,28 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Doctests for 'permission' module. + +$Id: test_role.py 98094 2009-03-14 15:50:25Z nadako $ +""" +import unittest +from zope.testing.doctest import DocTestSuite + + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite('zope.securitypolicy.role'), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_securitydirectives.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_securitydirectives.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_securitydirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_securitydirectives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,119 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Security Directives Tests + +$Id: test_securitydirectives.py 98094 2009-03-14 15:50:25Z nadako $ +""" +import unittest + +import zope.component +from zope.configuration import xmlconfig +from zope.configuration.config import ConfigurationConflictError +from zope.security.interfaces import IPermission +from zope.security.permission import Permission + +from zope.component.testing import PlacelessSetup +from zope.authentication.interfaces import IAuthentication + +from zope.securitypolicy.role import Role +from zope.securitypolicy.interfaces import Allow +from zope.securitypolicy.interfaces import IRole +from zope.securitypolicy.rolepermission import \ + rolePermissionManager as role_perm_mgr +from zope.securitypolicy.principalpermission import \ + principalPermissionManager as principal_perm_mgr +from zope.securitypolicy.principalrole import \ + principalRoleManager as principal_role_mgr +import zope.securitypolicy.tests +from zope.securitypolicy.tests import principalRegistry + + +def defineRole(id, title=None, description=None): + role = Role(id, title, description) + zope.component.provideUtility(role, IRole, role.id) + return role + + +class TestBase(PlacelessSetup): + + def setUp(self): + super(TestBase, self).setUp() + zope.component.provideUtility(principalRegistry, IAuthentication) + + +class TestRoleDirective(TestBase, unittest.TestCase): + + def testRegister(self): + context = xmlconfig.file("role.zcml", zope.securitypolicy.tests) + + role = zope.component.getUtility(IRole, "zope.Everyperson") + self.failUnless(role.id.endswith('Everyperson')) + self.assertEqual(role.title, 'Tout le monde') + self.assertEqual(role.description, + 'The common man, woman, person, or thing') + + def testDuplicationRegistration(self): + self.assertRaises(ConfigurationConflictError, xmlconfig.file, + "role_duplicate.zcml", zope.securitypolicy.tests) + + +class TestSecurityMapping(TestBase, unittest.TestCase): + + def setUp(self): + super(TestSecurityMapping, self).setUp() + zope.component.provideUtility(Permission('zope.Foo', ''), + IPermission, 'zope.Foo') + defineRole("zope.Bar", '', '') + principalRegistry.definePrincipal("zope.Blah", '', '') + self.context = xmlconfig.file("mapping.zcml", zope.securitypolicy.tests) + + def test_PermRoleMap(self): + roles = role_perm_mgr.getRolesForPermission("zope.Foo") + perms = role_perm_mgr.getPermissionsForRole("zope.Bar") + + self.assertEqual(len(roles), 1) + self.failUnless(("zope.Bar",Allow) in roles) + + self.assertEqual(len(perms), 1) + self.failUnless(("zope.Foo",Allow) in perms) + + def test_PermPrincipalMap(self): + principals = principal_perm_mgr.getPrincipalsForPermission("zope.Foo") + perms = principal_perm_mgr.getPermissionsForPrincipal("zope.Blah") + + self.assertEqual(len(principals), 1) + self.failUnless(("zope.Blah", Allow) in principals) + + self.assertEqual(len(perms), 1) + self.failUnless(("zope.Foo", Allow) in perms) + + def test_RolePrincipalMap(self): + principals = principal_role_mgr.getPrincipalsForRole("zope.Bar") + roles = principal_role_mgr.getRolesForPrincipal("zope.Blah") + + self.assertEqual(len(principals), 1) + self.failUnless(("zope.Blah", Allow) in principals) + + self.assertEqual(len(roles), 1) + self.failUnless(("zope.Bar", Allow) in roles) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(TestRoleDirective), + unittest.makeSuite(TestSecurityMapping), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_securitymap.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_securitymap.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_securitymap.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_securitymap.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,168 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 SecurityMap implementations + +$Id: test_securitymap.py 80107 2007-09-26 14:58:39Z rogerineichen $ +""" +import unittest +from zope.securitypolicy.securitymap import SecurityMap +from zope.securitypolicy.securitymap import PersistentSecurityMap +from zope.security.management import setSecurityPolicy, getInteraction +from zope.security.management import newInteraction, endInteraction + +class InteractionStub: + invalidated = 0 + def invalidate_cache(self): + self.invalidated += 1 + + +class TestSecurityMap(unittest.TestCase): + + def setUp(self): + self.oldpolicy = setSecurityPolicy(InteractionStub) + newInteraction() + + def tearDown(self): + endInteraction() + setSecurityPolicy(self.oldpolicy) + + def _getSecurityMap(self): + return SecurityMap() + + def test_addCell(self): + map = self._getSecurityMap() + self.assertEqual(getInteraction().invalidated, 0) + map.addCell(0, 0, 'aa') + self.assertEqual(getInteraction().invalidated, 1) + self.assertEqual(map._byrow[0][0], 'aa') + self.assertEqual(map._bycol[0][0], 'aa') + + map.addCell(1, 0, 'ba') + self.assertEqual(getInteraction().invalidated, 2) + self.assertEqual(map._byrow[1][0], 'ba') + self.assertEqual(map._bycol[0][1], 'ba') + + map.addCell(5, 3, 'fd') + self.assertEqual(getInteraction().invalidated, 3) + self.assertEqual(map._byrow[5][3], 'fd') + self.assertEqual(map._bycol[3][5], 'fd') + + def test_addCell_noninteger(self): + map = self._getSecurityMap() + map.addCell(0.3, 0.4, 'entry') + self.assertEqual(map._byrow[0.3][0.4], 'entry') + self.assertEqual(map._bycol[0.4][0.3], 'entry') + + marker = object() + map.addCell('a', 'b', marker) + self.assertEqual(map._byrow['a']['b'], marker) + self.assertEqual(map._bycol['b']['a'], marker) + + def test_delCell(self): + map = self._getSecurityMap() + self.assertEqual(getInteraction().invalidated, 0) + map._byrow[0] = {} + map._bycol[1] = {} + map._byrow[0][1] = 'aa' + map._bycol[1][0] = 'aa' + map.delCell(0, 1) + self.assertEqual(getInteraction().invalidated, 1) + self.assertEqual(map._byrow.get(0), None) + self.assertEqual(map._bycol.get(1), None) + + def test_queryCell(self): + map = self._getSecurityMap() + map._byrow[0] = {} + map._bycol[1] = {} + map._byrow[0][1] = 'aa' + map._bycol[1][0] = 'aa' + + marker = object() + self.assertEqual(map.queryCell(0, 1), 'aa') + self.assertEqual(map.queryCell(1, 0), None) + self.assertEqual(map.queryCell(1, 0, marker), marker) + + def test_getCell(self): + map = self._getSecurityMap() + map._byrow[0] = {} + map._bycol[1] = {} + map._byrow[0][1] = 'aa' + map._bycol[1][0] = 'aa' + + self.assertEqual(map.getCell(0, 1), 'aa') + self.assertRaises(KeyError, map.getCell, 1, 0) + + def test_getRow(self): + map = self._getSecurityMap() + map._byrow[0] = {} + map._byrow[0][1] = 'ab' + map._byrow[0][2] = 'ac' + map._byrow[1] = {} + map._byrow[1][1] = 'bb' + map._bycol[1] = {} + map._bycol[1][0] = 'ab' + map._bycol[1][1] = 'bb' + map._bycol[2] = {} + map._bycol[2][0] = 'ac' + + self.assertEqual(map.getRow(0), [(1, 'ab'), (2, 'ac')]) + self.assertEqual(map.getRow(1), [(1, 'bb')]) + self.assertEqual(map.getRow(2), []) + + def test_getCol(self): + map = self._getSecurityMap() + map._byrow[0] = {} + map._byrow[0][1] = 'ab' + map._byrow[0][2] = 'ac' + map._byrow[1] = {} + map._byrow[1][1] = 'bb' + map._bycol[1] = {} + map._bycol[1][0] = 'ab' + map._bycol[1][1] = 'bb' + map._bycol[2] = {} + map._bycol[2][0] = 'ac' + + self.assertEqual(map.getCol(1), [(0, 'ab'), (1, 'bb')]) + self.assertEqual(map.getCol(2), [(0, 'ac')]) + self.assertEqual(map.getCol(0), []) + + def test_getAllCells(self): + map = self._getSecurityMap() + map._byrow[0] = {} + map._byrow[0][1] = 'ab' + map._byrow[0][2] = 'ac' + map._byrow[1] = {} + map._byrow[1][1] = 'bb' + map._bycol[1] = {} + map._bycol[1][0] = 'ab' + map._bycol[1][1] = 'bb' + map._bycol[2] = {} + map._bycol[2][0] = 'ac' + + self.assertEqual(map.getCol(1), [(0, 'ab'), (1, 'bb')]) + self.assertEqual(map.getCol(2), [(0, 'ac')]) + self.assertEqual(map.getCol(0), []) + + +class TestPersistentSecurityMap(TestSecurityMap): + + def _getSecurityMap(self): + return PersistentSecurityMap() + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(TestSecurityMap), + unittest.makeSuite(TestPersistentSecurityMap), + )) diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_settings.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_settings.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_settings.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_settings.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,41 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Security Settings Tests + +$Id: test_settings.py 97975 2009-03-12 09:48:57Z nadako $ +""" +import unittest +from cPickle import Pickler, Unpickler +from StringIO import StringIO + +from zope.securitypolicy.interfaces import Allow + +class Test(unittest.TestCase): + + def testPickleUnpickle(self): + s = StringIO() + p = Pickler(s) + p.dump(Allow) + s.seek(0) + u = Unpickler(s) + newAllow = u.load() + + self.failUnless(newAllow is Allow) + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_vocabulary.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_vocabulary.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Role vocabluary doc tests. + +$Id: test_vocabulary.py 98094 2009-03-14 15:50:25Z nadako $ +""" +import unittest +from zope.testing.doctest import DocTestSuite + +def test_suite(): + return unittest.TestSuite(( + DocTestSuite('zope.securitypolicy.vocabulary'), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/securitypolicy/tests/test_zopepolicy.py zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_zopepolicy.py --- zope3-3.4.0/src/zope/securitypolicy/tests/test_zopepolicy.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/tests/test_zopepolicy.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,64 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 the zope policy. + +$Id: test_zopepolicy.py 98094 2009-03-14 15:50:25Z nadako $ +""" + +import unittest +from zope.component import provideAdapter +from zope.component.testing import setUp as componentSetUp +from zope.component.testing import tearDown as componentTearDown +from zope.testing.doctest import DocFileSuite +from zope.annotation.interfaces import IAnnotatable +from zope.annotation.interfaces import IAttributeAnnotatable +from zope.annotation.interfaces import IAnnotations +from zope.annotation.attribute import AttributeAnnotations +from zope.security.management import endInteraction + +from zope.securitypolicy.interfaces import IGrantInfo +from zope.securitypolicy.interfaces import IPrincipalRoleManager +from zope.securitypolicy.interfaces import IPrincipalPermissionManager +from zope.securitypolicy.interfaces import IRolePermissionManager +from zope.securitypolicy.principalpermission import \ + AnnotationPrincipalPermissionManager +from zope.securitypolicy.principalrole import \ + AnnotationPrincipalRoleManager +from zope.securitypolicy.rolepermission import \ + AnnotationRolePermissionManager +from zope.securitypolicy.grantinfo import \ + AnnotationGrantInfo + +def setUp(test): + componentSetUp() + endInteraction() + provideAdapter(AttributeAnnotations) + provideAdapter(AnnotationPrincipalPermissionManager, (IAnnotatable,), + IPrincipalPermissionManager) + provideAdapter(AnnotationPrincipalRoleManager, (IAnnotatable,), + IPrincipalRoleManager) + provideAdapter(AnnotationRolePermissionManager, (IAnnotatable,), + IRolePermissionManager) + provideAdapter(AnnotationGrantInfo, (IAnnotatable,), IGrantInfo) + + +def test_suite(): + return unittest.TestSuite(( + DocFileSuite('zopepolicy.txt', + package='zope.securitypolicy', + setUp=setUp, tearDown=componentTearDown), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/securitypolicy/vocabulary.py zope3-3.5~bzr18/src/zope/securitypolicy/vocabulary.py --- zope3-3.4.0/src/zope/securitypolicy/vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,80 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Role Id Vocabulary. + +This vocabulary provides role IDs. + +$Id: vocabulary.py 102198 2009-07-24 06:18:13Z srichter $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +from zope.interface import implements, classProvides +from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary +from zope.schema.interfaces import IVocabularyFactory + +from zope.securitypolicy.interfaces import IRole +from zope.securitypolicy.interfaces import IGrantVocabulary + + +class RoleIdsVocabulary(SimpleVocabulary): + """A vocabular of role IDs. + + Term values are the role ID strings + Term are stored by title + + To illustrate, we need to register the role IDs vocab: + + >>> from zope.schema import vocabulary + >>> from zope.component.testing import setUp, tearDown + >>> setUp() + >>> vocabulary.setVocabularyRegistry(None) + + >>> registry = vocabulary.getVocabularyRegistry() + >>> registry.register('Role Ids', RoleIdsVocabulary) + + Let's register some sample roles to test against them + + >>> from zope.securitypolicy.interfaces import IRole + >>> from zope.securitypolicy.role import Role + >>> from zope.component import provideUtility + >>> provideUtility(Role('a_id','a_title'), IRole, 'a_id') + >>> provideUtility(Role('b_id','b_title'), IRole, 'b_id') + + Let's lookup the roles using the vocabulary + + >>> vocab = registry.get(None, 'Role Ids') + + >>> vocab.getTermByToken('a_id').value + u'a_id' + >>> vocab.getTermByToken('b_id').value + u'b_id' + + >>> tearDown() + + """ + classProvides(IVocabularyFactory) + + def __init__(self, context): + terms = [] + roles = zope.component.getUtilitiesFor(IRole, context) + for name, role in roles: + terms.append(SimpleTerm(name, name, name)) + super(RoleIdsVocabulary, self).__init__(terms) + + +class GrantVocabulary(SimpleVocabulary): + """A vocabular for getting the RadioWidget via the Choice field.""" + classProvides(IVocabularyFactory) + implements(IGrantVocabulary) diff -Nru zope3-3.4.0/src/zope/securitypolicy/zopepolicy.py zope3-3.5~bzr18/src/zope/securitypolicy/zopepolicy.py --- zope3-3.4.0/src/zope/securitypolicy/zopepolicy.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/zopepolicy.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,374 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Define Zope's default security policy + +$Id: zopepolicy.py 97975 2009-03-12 09:48:57Z nadako $ +""" + +import zope.interface + +from zope.component import getUtility +from zope.security.checker import CheckerPublic +from zope.security.management import system_user +from zope.security.simplepolicies import ParanoidSecurityPolicy +from zope.security.interfaces import ISecurityPolicy +from zope.security.proxy import removeSecurityProxy + +from zope.authentication.interfaces import PrincipalLookupError, IAuthentication + +from zope.securitypolicy.principalpermission import principalPermissionManager +globalPrincipalPermissionSetting = principalPermissionManager.getSetting + +from zope.securitypolicy.rolepermission import rolePermissionManager +globalRolesForPermission = rolePermissionManager.getRolesForPermission + +from zope.securitypolicy.principalrole import principalRoleManager +globalRolesForPrincipal = principalRoleManager.getRolesForPrincipal + +from zope.securitypolicy.interfaces import Allow, Deny, Unset +from zope.securitypolicy.interfaces import IRolePermissionMap +from zope.securitypolicy.interfaces import IPrincipalPermissionMap +from zope.securitypolicy.interfaces import IPrincipalRoleMap +from zope.securitypolicy.interfaces import IGrantInfo + +SettingAsBoolean = {Allow: True, Deny: False, Unset: None, None: None} + +class CacheEntry: + pass + +class ZopeSecurityPolicy(ParanoidSecurityPolicy): + zope.interface.classProvides(ISecurityPolicy) + + def __init__(self, *args, **kw): + ParanoidSecurityPolicy.__init__(self, *args, **kw) + self._cache = {} + + def invalidate_cache(self): + self._cache = {} + + def cache(self, parent): + cache = self._cache.get(id(parent)) + if cache: + cache = cache[0] + else: + cache = CacheEntry() + self._cache[id(parent)] = cache, parent + return cache + + def cached_decision(self, parent, principal, groups, permission): + # Return the decision for a principal and permission + + cache = self.cache(parent) + try: + cache_decision = cache.decision + except AttributeError: + cache_decision = cache.decision = {} + + cache_decision_prin = cache_decision.get(principal) + if not cache_decision_prin: + cache_decision_prin = cache_decision[principal] = {} + + try: + return cache_decision_prin[permission] + except KeyError: + pass + + # cache_decision_prin[permission] is the cached decision for a + # principal and permission. + + decision = self.cached_prinper(parent, principal, groups, permission) + if (decision is None) and groups: + decision = self._group_based_cashed_prinper(parent, principal, + groups, permission) + if decision is not None: + cache_decision_prin[permission] = decision + return decision + + roles = self.cached_roles(parent, permission) + if roles: + prin_roles = self.cached_principal_roles(parent, principal) + if groups: + prin_roles = self.cached_principal_roles_w_groups( + parent, principal, groups, prin_roles) + for role, setting in prin_roles.items(): + if setting and (role in roles): + cache_decision_prin[permission] = decision = True + return decision + + cache_decision_prin[permission] = decision = False + return decision + + def cached_prinper(self, parent, principal, groups, permission): + # Compute the permission, if any, for the principal. + cache = self.cache(parent) + try: + cache_prin = cache.prin + except AttributeError: + cache_prin = cache.prin = {} + + cache_prin_per = cache_prin.get(principal) + if not cache_prin_per: + cache_prin_per = cache_prin[principal] = {} + + try: + return cache_prin_per[permission] + except KeyError: + pass + + if parent is None: + prinper = SettingAsBoolean[ + globalPrincipalPermissionSetting(permission, principal, None) + ] + cache_prin_per[permission] = prinper + return prinper + + prinper = IPrincipalPermissionMap(parent, None) + if prinper is not None: + prinper = SettingAsBoolean[ + prinper.getSetting(permission, principal, None) + ] + if prinper is not None: + cache_prin_per[permission] = prinper + return prinper + + parent = removeSecurityProxy(getattr(parent, '__parent__', None)) + prinper = self.cached_prinper(parent, principal, groups, permission) + cache_prin_per[permission] = prinper + return prinper + + def _group_based_cashed_prinper(self, parent, principal, groups, + permission): + denied = False + for group_id, ggroups in groups: + decision = self.cached_prinper(parent, group_id, ggroups, + permission) + if (decision is None) and ggroups: + decision = self._group_based_cashed_prinper( + parent, group_id, ggroups, permission) + + if decision is None: + continue + + if decision: + return decision + + denied = True + + if denied: + return False + + return None + + def cached_roles(self, parent, permission): + cache = self.cache(parent) + try: + cache_roles = cache.roles + except AttributeError: + cache_roles = cache.roles = {} + try: + return cache_roles[permission] + except KeyError: + pass + + if parent is None: + roles = dict( + [(role, 1) + for (role, setting) in globalRolesForPermission(permission) + if setting is Allow + ] + ) + cache_roles[permission] = roles + return roles + + roles = self.cached_roles( + removeSecurityProxy(getattr(parent, '__parent__', None)), + permission) + roleper = IRolePermissionMap(parent, None) + if roleper: + roles = roles.copy() + for role, setting in roleper.getRolesForPermission(permission): + if setting is Allow: + roles[role] = 1 + elif role in roles: + del roles[role] + + cache_roles[permission] = roles + return roles + + def cached_principal_roles_w_groups(self, parent, + principal, groups, prin_roles): + denied = {} + allowed = {} + for group_id, ggroups in groups: + group_roles = dict(self.cached_principal_roles(parent, group_id)) + if ggroups: + group_roles = self.cached_principal_roles_w_groups( + parent, group_id, ggroups, group_roles) + for role, setting in group_roles.items(): + if setting: + allowed[role] = setting + else: + denied[role] = setting + + denied.update(allowed) + denied.update(prin_roles) + return denied + + def cached_principal_roles(self, parent, principal): + cache = self.cache(parent) + try: + cache_principal_roles = cache.principal_roles + except AttributeError: + cache_principal_roles = cache.principal_roles = {} + try: + return cache_principal_roles[principal] + except KeyError: + pass + + if parent is None: + roles = dict( + [(role, SettingAsBoolean[setting]) + for (role, setting) in globalRolesForPrincipal(principal) + ] + ) + roles['zope.Anonymous'] = True # Everybody has Anonymous + cache_principal_roles[principal] = roles + return roles + + roles = self.cached_principal_roles( + removeSecurityProxy(getattr(parent, '__parent__', None)), + principal) + + prinrole = IPrincipalRoleMap(parent, None) + if prinrole: + roles = roles.copy() + for role, setting in prinrole.getRolesForPrincipal(principal): + roles[role] = SettingAsBoolean[setting] + + cache_principal_roles[principal] = roles + return roles + + def checkPermission(self, permission, object): + if permission is CheckerPublic: + return True + + object = removeSecurityProxy(object) + seen = {} + for participation in self.participations: + principal = participation.principal + if principal is system_user: + continue # always allow system_user + + if principal.id in seen: + continue + + if not self.cached_decision( + object, principal.id, self._groupsFor(principal), permission, + ): + return False + + seen[principal.id] = 1 + + return True + + def _findGroupsFor(self, principal, getPrincipal, seen): + result = [] + for group_id in getattr(principal, 'groups', ()): + if group_id in seen: + # Dang, we have a cycle. We don't want to + # raise an exception here (or do we), so we'll skip it + continue + seen.append(group_id) + + try: + group = getPrincipal(group_id) + except PrincipalLookupError: + # It's bad if we have an undefined principal, + # but we don't want to fail here. But we won't + # honor any grants for the group. We'll just skip it. + continue + + result.append((group_id, + self._findGroupsFor(group, getPrincipal, seen))) + seen.pop() + + return tuple(result) + + def _groupsFor(self, principal): + groups = self._cache.get(principal.id) + if groups is None: + groups = getattr(principal, 'groups', ()) + if groups: + getPrincipal = getUtility(IAuthentication).getPrincipal + groups = self._findGroupsFor(principal, getPrincipal, []) + else: + groups = () + + self._cache[principal.id] = groups + + return groups + +def settingsForObject(ob): + """Analysis tool to show all of the grants to a process + """ + result = [] + while ob is not None: + data = {} + result.append((getattr(ob, '__name__', '(no name)'), data)) + + principalPermissions = IPrincipalPermissionMap(ob, None) + if principalPermissions is not None: + settings = principalPermissions.getPrincipalsAndPermissions() + settings.sort() + data['principalPermissions'] = [ + {'principal': pr, 'permission': p, 'setting': s} + for (p, pr, s) in settings] + + principalRoles = IPrincipalRoleMap(ob, None) + if principalRoles is not None: + settings = principalRoles.getPrincipalsAndRoles() + data['principalRoles'] = [ + {'principal': p, 'role': r, 'setting': s} + for (r, p, s) in settings] + + rolePermissions = IRolePermissionMap(ob, None) + if rolePermissions is not None: + settings = rolePermissions.getRolesAndPermissions() + data['rolePermissions'] = [ + {'permission': p, 'role': r, 'setting': s} + for (p, r, s) in settings] + + ob = getattr(ob, '__parent__', None) + + data = {} + result.append(('global settings', data)) + + settings = principalPermissionManager.getPrincipalsAndPermissions() + settings.sort() + data['principalPermissions'] = [ + {'principal': pr, 'permission': p, 'setting': s} + for (p, pr, s) in settings] + + settings = principalRoleManager.getPrincipalsAndRoles() + data['principalRoles'] = [ + {'principal': p, 'role': r, 'setting': s} + for (r, p, s) in settings] + + settings = rolePermissionManager.getRolesAndPermissions() + data['rolePermissions'] = [ + {'permission': p, 'role': r, 'setting': s} + for (p, r, s) in settings] + + return result + diff -Nru zope3-3.4.0/src/zope/securitypolicy/zopepolicy.txt zope3-3.5~bzr18/src/zope/securitypolicy/zopepolicy.txt --- zope3-3.4.0/src/zope/securitypolicy/zopepolicy.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/securitypolicy/zopepolicy.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,657 @@ +============================ +Classic Zope Security Policy +============================ + +This package implements a role-based security policy similar to the +policy found in Zope 2. The security policy is responsible for +deciding whether an interaction has a permission on an object. This +security policy does this using grant and denial information. Managers +can grant or deny: + + - roles to principals, + + - permissions to principals, and + + - permissions to roles + +Grants and denials are stored as annotations on objects. To store +grants and denials, objects must be annotatable: + + >>> import zope.interface + >>> from zope.annotation.interfaces import IAttributeAnnotatable + >>> class Ob: + ... zope.interface.implements(IAttributeAnnotatable) + + >>> ob = Ob() + +We use objects to represent principals. These objects implement an +interface named `IPrincipal`, but the security policy only uses the `id` +and `groups` attributes: + + >>> class Principal: + ... def __init__(self, id): + ... self.id = id + ... self.groups = [] + + >>> principal = Principal('bob') + +Roles and permissions are also represented by objects, however, for +the purposes of the security policy, only string `ids` are used. + +The security policy provides a factory for creating interactions: + + >>> import zope.securitypolicy.zopepolicy + >>> interaction = zope.securitypolicy.zopepolicy.ZopeSecurityPolicy() + +An interaction represents a specific interaction between some +principals (normally users) and the system. Normally, we are only +concerned with the interaction of one principal with the system, although +we can have interactions of multiple principals. Multiple-principal +interactions normally occur when untrusted users store code on a +system for later execution. When untrusted code is executing, the +authors of the code participate in the interaction. An +interaction has a permission on an object only if all of the +principals participating in the interaction have access to the object. + +The `checkPermission` method on interactions is used to test whether +an interaction has a permission for an object. An interaction without +participants always has every permission: + + >>> interaction.checkPermission('P1', ob) + True + +In this example, 'P1' is a permission id. + +Normally, interactions have participants: + + >>> class Participation: + ... interaction = None + >>> participation = Participation() + >>> participation.principal = principal + >>> interaction.add(participation) + +If we have participants, then we don't have a permission unless there +are grants: + + >>> interaction.checkPermission('P1', ob) + False + +Note, however, that we always have the CheckerPublic permission: + + >>> from zope.security.checker import CheckerPublic + >>> interaction.checkPermission(CheckerPublic, ob) + True + +We make grants and denials on objects by adapting them to various +granting interfaces. The objects returned from the adaptation are +object-specific manager objects: + + >>> from zope.securitypolicy import interfaces + >>> roleper = interfaces.IRolePermissionManager(ob) + >>> prinrole = interfaces.IPrincipalRoleManager(ob) + >>> prinper = interfaces.IPrincipalPermissionManager(ob) + +The computations involved in checking permissions can be +significant. To reduce the computational cost, caching is used +extensively. We could invalidate the cache as we make grants, but the +adapters for making grants will automatically invalidate the cache of +the current interaction. They use the security-management apis to do +this. To take advantage of the cache invalidation, we'll need to let +the security-management system manage our interactions. First, we'll +set our security policy as the default: + + >>> import zope.security.management + >>> oldpolicy = zope.security.management.setSecurityPolicy( + ... zope.securitypolicy.zopepolicy.ZopeSecurityPolicy) + +and then we'll create a new interaction: + + >>> participation = Participation() + >>> participation.principal = principal + >>> zope.security.management.newInteraction(participation) + >>> interaction = zope.security.management.getInteraction() + +We normally provide access by granting permissions to roles for an object: + + >>> roleper.grantPermissionToRole('P1', 'R1') + +and then granting roles to principals for an object (local roles): + + >>> prinrole.assignRoleToPrincipal('R1', 'bob') + +The combination of these grants, which we call a role-based grant, +provides the permission: + + >>> interaction.checkPermission('P1', ob) + True + +We can also provide a permission directly: + + >>> prinper.grantPermissionToPrincipal('P2', 'bob') + >>> interaction.checkPermission('P2', ob) + True + +Permission grants or denials override role-based grant or denials. So +if we deny P1: + + >>> prinper.denyPermissionToPrincipal('P1', 'bob') + +we cause the interaction to lack the permission, despite the role +grants: + + >>> interaction.checkPermission('P1', ob) + False + +Similarly, even if we have a role-based denial of P2: + + >>> roleper.denyPermissionToRole('P2', 'R1') + +we still have access, because of the permission-based grant: + + >>> interaction.checkPermission('P2', ob) + True + +A role-based denial doesn't actually deny a permission; rather it +prevents the granting of a permission. So, if we have both grants and +denials based on roles, we have access: + + >>> roleper.grantPermissionToRole('P3', 'R1') + >>> roleper.grantPermissionToRole('P3', 'R2') + >>> roleper.denyPermissionToRole('P3', 'R3') + >>> prinrole.removeRoleFromPrincipal('R2', 'bob') + >>> prinrole.assignRoleToPrincipal('R3', 'bob') + + >>> interaction.checkPermission('P3', ob) + True + +Global grants +------------- + +Grants made to an object are said to be "local". We can also make +global grants: + + >>> from zope.securitypolicy.rolepermission import \ + ... rolePermissionManager as roleperG + >>> from zope.securitypolicy.principalpermission import \ + ... principalPermissionManager as prinperG + >>> from zope.securitypolicy.principalrole import \ + ... principalRoleManager as prinroleG + +And the same rules apply to global grants and denials. + + >>> roleperG.grantPermissionToRole('P1G', 'R1G', False) + +In these tests, we aren't bothering to define any roles, permissions, +or principals, so we pass an extra argument that tells the granting +routines not to check the validity of the values. + + >>> prinroleG.assignRoleToPrincipal('R1G', 'bob', False) + >>> interaction.checkPermission('P1G', ob) + True + + >>> prinperG.grantPermissionToPrincipal('P2G', 'bob', False) + >>> interaction.checkPermission('P2G', ob) + True + + >>> prinperG.denyPermissionToPrincipal('P1G', 'bob', False) + >>> interaction.checkPermission('P1G', ob) + False + + >>> roleperG.denyPermissionToRole('P2G', 'R1G', False) + >>> interaction.checkPermission('P2G', ob) + True + + >>> roleperG.grantPermissionToRole('P3G', 'R1G', False) + >>> roleperG.grantPermissionToRole('P3G', 'R2G', False) + >>> roleperG.denyPermissionToRole('P3G', 'R3G', False) + >>> prinroleG.removeRoleFromPrincipal('R2G', 'bob', False) + >>> prinroleG.assignRoleToPrincipal('R3G', 'bob', False) + >>> interaction.checkPermission('P3G', ob) + True + +Local versus global grants +-------------------------- + +We, of course, acquire global grants by default: + + >>> interaction.checkPermission('P1G', ob) + False + >>> interaction.checkPermission('P2G', ob) + True + >>> interaction.checkPermission('P3G', ob) + True + +Local role-based grants do not override global principal-specific denials: + + >>> roleper.grantPermissionToRole('P1G', 'R1G') + >>> prinrole.assignRoleToPrincipal('R1G', 'bob') + >>> interaction.checkPermission('P1G', ob) + False + +And local role-based denials don't override global +principal-grants: + + >>> roleper.denyPermissionToRole('P2G', 'R1G') + >>> interaction.checkPermission('P2G', ob) + True + +A local role-based deny can cancel a global role-based grant: + + >>> roleper.denyPermissionToRole('P3G', 'R1G') + >>> interaction.checkPermission('P3G', ob) + False + +and a local role-based grant can override a global role-based denial: + + >>> roleperG.denyPermissionToRole('P4G', 'R1G', False) + >>> prinroleG.assignRoleToPrincipal('R1G', "bob", False) + >>> interaction.checkPermission('P4G', ob) + False + >>> roleper.grantPermissionToRole('P4G', 'R1G') + >>> interaction.checkPermission('P4G', ob) + True + >>> prinroleG.removeRoleFromPrincipal('R1G', "bob", False) + >>> interaction.checkPermission('P4G', ob) + True + +Of course, a local permission-based grant or denial overrides any +global setting and overrides local role-based grants or denials: + + >>> prinper.grantPermissionToPrincipal('P3G', 'bob') + >>> interaction.checkPermission('P3G', ob) + True + + >>> prinper.denyPermissionToPrincipal('P2G', 'bob') + >>> interaction.checkPermission('P2G', ob) + False + +Sublocations +------------ + +We can have sub-locations. A sublocation of a location is an object +whose `__parent__` attribute is the location: + + >>> ob2 = Ob() + >>> ob2.__parent__ = ob + +By default, sublocations acquire grants from higher locations: + + >>> interaction.checkPermission('P1', ob2) + False + >>> interaction.checkPermission('P2', ob2) + True + >>> interaction.checkPermission('P3', ob2) + True + >>> interaction.checkPermission('P1G', ob2) + False + >>> interaction.checkPermission('P2G', ob2) + False + >>> interaction.checkPermission('P3G', ob2) + True + >>> interaction.checkPermission('P4G', ob2) + True + +Sublocation role-based grants do not override their parent +principal-specific denials: + + >>> roleper2 = interfaces.IRolePermissionManager(ob2) + >>> prinrole2 = interfaces.IPrincipalRoleManager(ob2) + >>> prinper2 = interfaces.IPrincipalPermissionManager(ob2) + + >>> roleper2.grantPermissionToRole('P1', 'R1') + >>> prinrole2.assignRoleToPrincipal('R1', 'bob') + >>> interaction.checkPermission('P1', ob2) + False + +And local role-based denials don't override their parents +principal-grant: + + >>> roleper2.denyPermissionToRole('P2', 'R1') + >>> interaction.checkPermission('P2', ob2) + True + +A local role-based deny can cancel a parent role-based grant: + + >>> roleper2.denyPermissionToRole('P3', 'R1') + >>> interaction.checkPermission('P3', ob2) + False + +and a local role-based grant can override a parent role-based denial: + + >>> roleper.denyPermissionToRole('P4', 'R1') + >>> prinrole.assignRoleToPrincipal('R1', 'bob') + >>> interaction.checkPermission('P4', ob2) + False + >>> roleper2.grantPermissionToRole('P4', 'R1') + >>> interaction.checkPermission('P4', ob2) + True + >>> prinrole.removeRoleFromPrincipal('R1', 'bob') + >>> interaction.checkPermission('P4', ob2) + True + + +Of course, a local permission-based grant or denial overrides any +global setting and overrides local role-based grants or denials: + + >>> prinper.grantPermissionToPrincipal('P3', 'bob') + >>> interaction.checkPermission('P3', ob2) + True + + >>> prinper.denyPermissionToPrincipal('P2', 'bob') + >>> interaction.checkPermission('P2', ob2) + False + +If an object is not annotatable, but does have a parent, it will get +its grants from its parent: + + >>> class C: + ... pass + + >>> ob3 = C() + >>> ob3.__parent__ = ob + + >>> interaction.checkPermission('P1', ob3) + False + >>> interaction.checkPermission('P2', ob3) + False + >>> interaction.checkPermission('P3', ob3) + True + >>> interaction.checkPermission('P1G', ob3) + False + >>> interaction.checkPermission('P2G', ob3) + False + >>> interaction.checkPermission('P3G', ob3) + True + >>> interaction.checkPermission('P4G', ob3) + True + +The same results will be had if there are multiple non-annotatable +objects: + + >>> ob3.__parent__ = C() + >>> ob3.__parent__.__parent__ = ob + + >>> interaction.checkPermission('P1', ob3) + False + >>> interaction.checkPermission('P2', ob3) + False + >>> interaction.checkPermission('P3', ob3) + True + >>> interaction.checkPermission('P1G', ob3) + False + >>> interaction.checkPermission('P2G', ob3) + False + >>> interaction.checkPermission('P3G', ob3) + True + >>> interaction.checkPermission('P4G', ob3) + True + +and if an object doesn't have a parent: + + >>> ob4 = C() + +it will have whatever grants were made globally: + + >>> interaction.checkPermission('P1', ob4) + False + >>> interaction.checkPermission('P2', ob4) + False + >>> interaction.checkPermission('P3', ob4) + False + >>> interaction.checkPermission('P1G', ob4) + False + >>> interaction.checkPermission('P2G', ob4) + True + >>> interaction.checkPermission('P3G', ob4) + False + >>> interaction.checkPermission('P4G', ob4) + False + + >>> prinroleG.assignRoleToPrincipal('R1G', "bob", False) + >>> interaction.checkPermission('P3G', ob4) + True + +We'll get the same result if we have a non-annotatable parent without a +parent: + + >>> ob3.__parent__ = C() + + >>> interaction.checkPermission('P1', ob3) + False + >>> interaction.checkPermission('P2', ob3) + False + >>> interaction.checkPermission('P3', ob3) + False + >>> interaction.checkPermission('P1G', ob3) + False + >>> interaction.checkPermission('P2G', ob3) + True + >>> interaction.checkPermission('P3G', ob3) + True + >>> interaction.checkPermission('P4G', ob3) + False + +The Anonymous role +------------------ + +The security policy defines a special role named "zope.Anonymous". All +principals have this role and the role cannot be taken away. + + >>> roleperG.grantPermissionToRole('P5', 'zope.Anonymous', False) + >>> interaction.checkPermission('P5', ob2) + True + +Proxies +------- + +Objects may be proxied: + + >>> from zope.security.checker import ProxyFactory + >>> ob = ProxyFactory(ob) + >>> interaction.checkPermission('P1', ob) + False + >>> interaction.checkPermission('P2', ob) + False + >>> interaction.checkPermission('P3', ob) + True + >>> interaction.checkPermission('P1G', ob) + False + >>> interaction.checkPermission('P2G', ob) + False + >>> interaction.checkPermission('P3G', ob) + True + >>> interaction.checkPermission('P4G', ob) + True + +as may their parents: + + >>> ob3 = C() + >>> ob3.__parent__ = ob + + >>> interaction.checkPermission('P1', ob3) + False + >>> interaction.checkPermission('P2', ob3) + False + >>> interaction.checkPermission('P3', ob3) + True + >>> interaction.checkPermission('P1G', ob3) + False + >>> interaction.checkPermission('P2G', ob3) + False + >>> interaction.checkPermission('P3G', ob3) + True + >>> interaction.checkPermission('P4G', ob3) + True + +Groups +------ + +Principals may have groups. Groups are also principals (and, thus, +may have groups). + +If a principal has groups, the groups are available as group ids in +the principal's `groups` attribute. The interaction has to convert +these group ids to group objects, so that it can tell whether the +groups have groups. It does this by calling the `getPrincipal` method +on the principal authentication service, which is responsible for, +among other things, converting a principal id to a principal. +For our examples here, we'll create and register a stub principal +authentication service: + + >>> from zope.authentication.interfaces import IAuthentication + >>> class FauxPrincipals(object): + ... zope.interface.implements(IAuthentication) + ... def __init__(self): + ... self.data = {} + ... def __setitem__(self, key, value): + ... self.data[key] = value + ... def __getitem__(self, key): + ... return self.data[key] + ... def getPrincipal(self, id): + ... return self.data[id] + + >>> auth = FauxPrincipals() + + >>> from zope.component import provideUtility + >>> provideUtility(auth, IAuthentication) + +Let's define a group: + + >>> auth['g1'] = Principal('g1') + +Let's put the principal in our group. We do that by adding the group id +to the new principals groups: + + >>> principal.groups.append('g1') + +Of course, the principal doesn't have permissions not granted: + + >>> interaction.checkPermission('gP1', ob) + False + +Now, if we grant a permission to the group: + + >>> prinper.grantPermissionToPrincipal('gP1', 'g1') + +We see that our principal has the permission: + + >>> interaction.checkPermission('gP1', ob) + True + +This works even if the group grant is global: + + >>> interaction.checkPermission('gP1G', ob) + False + + >>> prinperG.grantPermissionToPrincipal('gP1G', 'g1', True) + + >>> interaction.checkPermission('gP1G', ob) + True + +Grants are, of course, acquired: + + >>> interaction.checkPermission('gP1', ob2) + True + + >>> interaction.checkPermission('gP1G', ob2) + True + +Inner grants can override outer grants: + + >>> prinper2.denyPermissionToPrincipal('gP1', 'g1') + >>> interaction.checkPermission('gP1', ob2) + False + +But principal grants always trump group grants: + + >>> prinper2.grantPermissionToPrincipal('gP1', 'bob') + >>> interaction.checkPermission('gP1', ob2) + True + +Groups can have groups too: + + >>> auth['g2'] = Principal('g2') + >>> auth['g1'].groups.append('g2') + +If we grant to the new group: + + >>> prinper.grantPermissionToPrincipal('gP2', 'g2') + +Then we, of course have that permission too: + + >>> interaction.checkPermission('gP2', ob2) + True + +Just as principal grants override group grants, group grants can +override other group grants: + + >>> prinper.denyPermissionToPrincipal('gP2', 'g1') + >>> interaction.checkPermission('gP2', ob2) + False + +Principals can be in more than one group. Let's define a new group: + + >>> auth['g3'] = Principal('g3') + >>> principal.groups.append('g3') + >>> prinper.grantPermissionToPrincipal('gP2', 'g3') + +Now, the principal has two groups. In one group, the permission 'gP2' +is denied, but in the other, it is allowed. In a case like this, the +permission is allowed: + + >>> interaction.checkPermission('gP2', ob2) + True + +In a case where a principal has two or more groups, the group denies +prevent allows from their parents. They don't prevent the principal +from getting an allow from another principal. + +Grants can be inherited from ancestor groups through multiple paths. +Let's grant a permission to g2 and deny it to g1: + + >>> prinper.grantPermissionToPrincipal('gP3', 'g2') + >>> prinper.denyPermissionToPrincipal('gP3', 'g1') + +Now, as before, the deny in g1 blocks the grant in g2: + + >>> interaction.checkPermission('gP3', ob2) + False + +Let's make g2 a group of g3: + + >>> auth['g3'].groups.append('g2') + +Now, we get g2's grant through g3, and access is allowed: + + >>> interaction.invalidate_cache() + >>> interaction.checkPermission('gP3', ob2) + True + +We can assign roles to groups: + + >>> prinrole.assignRoleToPrincipal('gR1', 'g2') + +and get permissions through the roles: + + >>> roleper.grantPermissionToRole('gP4', 'gR1') + >>> interaction.checkPermission('gP4', ob2) + True + +we can override role assignments to groups through subgroups: + + >>> prinrole.removeRoleFromPrincipal('gR1', 'g1') + >>> prinrole.removeRoleFromPrincipal('gR1', 'g3') + >>> interaction.checkPermission('gP4', ob2) + False + +and through principals: + + >>> prinrole.assignRoleToPrincipal('gR1', 'bob') + >>> interaction.checkPermission('gP4', ob2) + True + + +We clean up the changes we made in these examples: + + >>> zope.security.management.endInteraction() + >>> ignore = zope.security.management.setSecurityPolicy(oldpolicy) diff -Nru zope3-3.4.0/src/zope/sendmail/configure.zcml zope3-3.5~bzr18/src/zope/sendmail/configure.zcml --- zope3-3.4.0/src/zope/sendmail/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,31 @@ + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/sendmail/delivery.py zope3-3.5~bzr18/src/zope/sendmail/delivery.py --- zope3-3.4.0/src/zope/sendmail/delivery.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/delivery.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,137 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Mail Delivery utility implementation + +This module contains various implementations of `MailDeliverys`. + +$Id: delivery.py 108107 2010-01-13 13:32:26Z kobold $ +""" +__docformat__ = 'restructuredtext' + +import os +import rfc822 +from cStringIO import StringIO +from random import randrange +from time import strftime +from socket import gethostname + +from zope.interface import implements +from zope.sendmail.interfaces import IDirectMailDelivery, IQueuedMailDelivery +from zope.sendmail.maildir import Maildir +from transaction.interfaces import IDataManager +import transaction + +# BBB: this import is needed for backward compatibility with older versions of +# zope.sendmail which defined QueueProcessorThread in this module +from zope.sendmail.queue import QueueProcessorThread + +class MailDataManager(object): + implements(IDataManager) + + def __init__(self, callable, args=(), onAbort=None): + self.callable = callable + self.args = args + self.onAbort = onAbort + # Use the default thread transaction manager. + self.transaction_manager = transaction.manager + + def commit(self, transaction): + pass + + def abort(self, transaction): + if self.onAbort: + self.onAbort() + + def sortKey(self): + return id(self) + + # No subtransaction support. + def abort_sub(self, transaction): + pass + + commit_sub = abort_sub + + def beforeCompletion(self, transaction): + pass + + afterCompletion = beforeCompletion + + def tpc_begin(self, transaction, subtransaction=False): + assert not subtransaction + + def tpc_vote(self, transaction): + pass + + def tpc_finish(self, transaction): + self.callable(*self.args) + + tpc_abort = abort + + +class AbstractMailDelivery(object): + + def newMessageId(self): + """Generates a new message ID according to RFC 2822 rules""" + randmax = 0x7fffffff + left_part = '%s.%d.%d' % (strftime('%Y%m%d%H%M%S'), + os.getpid(), + randrange(0, randmax)) + return "%s@%s" % (left_part, gethostname()) + + def send(self, fromaddr, toaddrs, message): + parser = rfc822.Message(StringIO(message)) + messageid = parser.getheader('Message-Id') + if messageid: + if not messageid.startswith('<') or not messageid.endswith('>'): + raise ValueError('Malformed Message-Id header') + messageid = messageid[1:-1] + else: + messageid = self.newMessageId() + message = 'Message-Id: <%s>\n%s' % (messageid, message) + transaction.get().join( + self.createDataManager(fromaddr, toaddrs, message)) + return messageid + + +class DirectMailDelivery(AbstractMailDelivery): + __doc__ = IDirectMailDelivery.__doc__ + + implements(IDirectMailDelivery) + + def __init__(self, mailer): + self.mailer = mailer + + def createDataManager(self, fromaddr, toaddrs, message): + return MailDataManager(self.mailer.send, + args=(fromaddr, toaddrs, message)) + + +class QueuedMailDelivery(AbstractMailDelivery): + __doc__ = IQueuedMailDelivery.__doc__ + + implements(IQueuedMailDelivery) + + def __init__(self, queuePath): + self._queuePath = queuePath + + queuePath = property(lambda self: self._queuePath) + + def createDataManager(self, fromaddr, toaddrs, message): + maildir = Maildir(self.queuePath, True) + msg = maildir.newMessage() + msg.write('X-Zope-From: %s\n' % fromaddr) + msg.write('X-Zope-To: %s\n' % ", ".join(toaddrs)) + msg.write(message) + msg.close() + return MailDataManager(msg.commit, onAbort=msg.abort) diff -Nru zope3-3.4.0/src/zope/sendmail/event.py zope3-3.5~bzr18/src/zope/sendmail/event.py --- zope3-3.4.0/src/zope/sendmail/event.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/event.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,41 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Collection of possible Mail Events. + +$Id: event.py 66922 2006-04-12 23:28:07Z jinty $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import implements + +from zope.sendmail.interfaces import IMailSentEvent, IMailErrorEvent + + +class MailSentEvent(object): + __doc__ = IMailSentEvent.__doc__ + + implements(IMailSentEvent) + + def __init__(self, messageId): + self.messageId = messageId + + +class MailErrorEvent(object): + __doc__ = IMailErrorEvent.__doc__ + + implements(IMailErrorEvent) + + def __init__(self, messageId, errorMessage): + self.messageId = messageId + self.errorMessage = errorMessage diff -Nru zope3-3.4.0/src/zope/sendmail/__init__.py zope3-3.5~bzr18/src/zope/sendmail/__init__.py --- zope3-3.4.0/src/zope/sendmail/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# make this directory a package diff -Nru zope3-3.4.0/src/zope/sendmail/interfaces.py zope3-3.5~bzr18/src/zope/sendmail/interfaces.py --- zope3-3.4.0/src/zope/sendmail/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,280 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Mailer interfaces + +Email sending from Zope 3 applications works as follows: + +- A Zope 3 application locates a mail delivery utility (`IMailDelivery`) and + feeds a message to it. It gets back a unique message ID so it can keep + track of the message by subscribing to `IMailEvent` events. + +- The utility registers with the transaction system to make sure the + message is only sent when the transaction commits successfully. (Among + other things this avoids duplicate messages on `ConflictErrors`.) + +- If the delivery utility is a `IQueuedMailDelivery`, it puts the message into + a queue (a Maildir mailbox in the file system). A separate process or thread + (`IMailQueueProcessor`) watches the queue and delivers messages + asynchronously. Since the queue is located in the file system, it survives + Zope restarts or crashes and the mail is not lost. The queue processor + can implement batching to keep the server load low. + +- If the delivery utility is a `IDirectMailDelivery`, it delivers messages + synchronously during the transaction commit. This is not a very good idea, + as it makes the user wait. Note that transaction commits must not fail, + but that is not a problem, because mail delivery problems dispatch an + event instead of raising an exception. + + However, there is a problem -- sending events causes unknown code to be + executed during the transaction commit phase. There should be a way to + start a new transaction for event processing after this one is commited. + +- An `IMailQueueProcessor` or `IDirectMailDelivery` actually delivers the + messages by using a mailer (`IMailer`) component that encapsulates the + delivery process. There currently is only one mailer: + + - `ISMTPMailer` sends all messages to a relay host using SMTP + +- If mail delivery succeeds, an `IMailSentEvent` is dispatched by the mailer. + If mail delivery fails, no exceptions are raised, but an `IMailErrorEvent` is + dispatched by the mailer. + +$Id: interfaces.py 79091 2007-08-21 18:35:51Z andreasjung $ +""" +__docformat__ = 'restructuredtext' + +from zope.interface import Interface, Attribute +from zope.schema import TextLine, Int, Password, Bool + +from zope.i18nmessageid import MessageFactory +_ = MessageFactory('zope') + + +class IMailDelivery(Interface): + """A mail delivery utility allows someone to send an email to a group of + people.""" + + def send(fromaddr, toaddrs, message): + """Send an email message. + + `fromaddr` is the sender address (byte string), + + `toaddrs` is a sequence of recipient addresses (byte strings). + + `message` is a byte string that contains both headers and body + formatted according to RFC 2822. If it does not contain a Message-Id + header, it will be generated and added automatically. + + Returns the message ID. + + You can subscribe to `IMailEvent` events for notification about + problems or successful delivery. + + Messages are actually sent during transaction commit. + """ + + +class IDirectMailDelivery(IMailDelivery): + """A mail delivery utility that delivers messages synchronously during + transaction commit. + + Not useful for production use, but simpler to set up and use. + """ + + mailer = Attribute("IMailer that is used for message delivery") + + +class IQueuedMailDelivery(IMailDelivery): + """A mail delivery utility that puts all messages into a queue in the + filesystem. + + Messages will be delivered asynchronously by a separate component. + """ + + queuePath = TextLine( + title=_(u"Queue path"), + description=_(u"Pathname of the directory used to queue mail.")) + + +class IMailQueueProcessor(Interface): + """A mail queue processor that delivers queueud messages asynchronously. + """ + + queuePath = TextLine( + title=_(u"Queue Path"), + description=_(u"Pathname of the directory used to queue mail.")) + + pollingInterval = Int( + title=_(u"Polling Interval"), + description=_(u"How often the queue is checked for new messages" + " (in milliseconds)"), + default=5000) + + mailer = Attribute("IMailer that is used for message delivery") + + +class IMailer(Interface): + """Mailer handles synchronous mail delivery.""" + + def send(fromaddr, toaddrs, message): + """Send an email message. + + `fromaddr` is the sender address (unicode string), + + `toaddrs` is a sequence of recipient addresses (unicode strings). + + `message` contains both headers and body formatted according to RFC + 2822. It should contain at least Date, From, To, and Message-Id + headers. + + Messages are sent immediatelly. + + Dispatches an `IMailSentEvent` on successful delivery, otherwise an + `IMailErrorEvent`. + """ + + +class ISMTPMailer(IMailer): + """A mailer that delivers mail to a relay host via SMTP.""" + + hostname = TextLine( + title=_(u"Hostname"), + description=_(u"Name of server to be used as SMTP server.")) + + port = Int( + title=_(u"Port"), + description=_(u"Port of SMTP service"), + default=25) + + username = TextLine( + title=_(u"Username"), + description=_(u"Username used for optional SMTP authentication.")) + + password = Password( + title=_(u"Password"), + description=_(u"Password used for optional SMTP authentication.")) + + no_tls = Bool( + title=_(u"No TLS"), + description=_(u"Never use TLS for sending email.")) + + force_tls = Bool( + title=_(u"Force TLS"), + description=_(u"Use TLS always for sending email.")) + + +class IMailEvent(Interface): + """Generic mail event.""" + + messageId = Attribute("Message id according to RFC 2822") + + +class IMailSentEvent(IMailEvent): + """Event that is fired when a message is succesfully sent. + + This does not mean that all the recipients have received it, it only + means that the message left this system successfully. It is possible + that a bounce message will arrive later from some remote mail server. + """ + + +class IMailErrorEvent(IMailEvent): + """Event that is fired when a message cannot be delivered.""" + + errorMessage = Attribute("Error message") + + + +class IMaildirFactory(Interface): + + def __call__(dirname, create=False): + """Opens a `Maildir` folder at a given filesystem path. + + If `create` is ``True``, the folder will be created when it does not + exist. If `create` is ``False`` and the folder does not exist, an + exception (``OSError``) will be raised. + + If path points to a file or an existing directory that is not a + valid `Maildir` folder, an exception is raised regardless of the + `create` argument. + """ + + +class IMaildir(Interface): + """Read/write access to `Maildir` folders. + + See http://www.qmail.org/man/man5/maildir.html for detailed format + description. + """ + + def __iter__(): + """Returns an iterator over the pathnames of messages in this folder. + """ + + def newMessage(): + """Creates a new message in the `maildir`. + + Returns a file-like object for a new file in the ``tmp`` subdirectory + of the `Maildir`. After writing message contents to it, call the + ``commit()`` or ``abort()`` method on it. + + The returned object implements `IMaildirMessageWriter`. + """ + + +class IMaildirMessageWriter(Interface): + """A file-like object to a new message in a `Maildir`.""" + + def write(str): + """Writes a string to the file. + + There is no return value. Due to buffering, the string may not actually + show up in the file until the ``commit()`` method is called. + """ + + def writelines(sequence): + """Writes a sequence of strings to the file. + + The sequence can be any iterable object producing strings, typically a + list of strings. There is no return value. ``writelines`` does not add + any line separators. + """ + + def close(): + """Closes the message file. + + No further writes are allowed. You can call ``close()`` before calling + ``commit()`` or ``abort()`` to avoid having too many open files. + + Calling ``close()`` more than once is allowed. + """ + + def commit(): + """Commits the new message using the `Maildir` protocol. + + First, the message file is flushed, closed, then it is moved from + ``tmp`` into ``new`` subdirectory of the maildir. + + Calling ``commit()`` more than once is allowed. + """ + + def abort(): + """Aborts the new message. + + The message file is closed and removed from the ``tmp`` subdirectory + of the `maildir`. + + Calling ``abort()`` more than once is allowed. + """ + diff -Nru zope3-3.4.0/src/zope/sendmail/maildir.py zope3-3.5~bzr18/src/zope/sendmail/maildir.py --- zope3-3.4.0/src/zope/sendmail/maildir.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/maildir.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,166 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Read/write access to `Maildir` folders. + +$Id: maildir.py 109588 2010-03-03 09:57:07Z kobold $ +""" +__docformat__ = 'restructuredtext' + +import os +import errno +import socket +import time +import random + +from zope.interface import implements, classProvides + +from zope.sendmail.interfaces import \ + IMaildirFactory, IMaildir, IMaildirMessageWriter + + +class Maildir(object): + """See `zope.sendmail.interfaces.IMaildir`""" + + classProvides(IMaildirFactory) + implements(IMaildir) + + def __init__(self, path, create=False): + "See `zope.sendmail.interfaces.IMaildirFactory`" + self.path = path + + def access(path): + return os.access(path, os.F_OK) + + subdir_cur = os.path.join(path, 'cur') + subdir_new = os.path.join(path, 'new') + subdir_tmp = os.path.join(path, 'tmp') + + if create and not access(path): + os.mkdir(path) + os.mkdir(subdir_cur) + os.mkdir(subdir_new) + os.mkdir(subdir_tmp) + maildir = True + else: + maildir = (os.path.isdir(subdir_cur) and os.path.isdir(subdir_new) + and os.path.isdir(subdir_tmp)) + if not maildir: + raise ValueError('%s is not a Maildir folder' % path) + + def __iter__(self): + "See `zope.sendmail.interfaces.IMaildir`" + join = os.path.join + subdir_cur = join(self.path, 'cur') + subdir_new = join(self.path, 'new') + # http://www.qmail.org/man/man5/maildir.html says: + # "It is a good idea for readers to skip all filenames in new + # and cur starting with a dot. Other than this, readers + # should not attempt to parse filenames." + new_messages = [join(subdir_new, x) for x in os.listdir(subdir_new) + if not x.startswith('.')] + cur_messages = [join(subdir_cur, x) for x in os.listdir(subdir_cur) + if not x.startswith('.')] + # Sort by modification time so earlier messages are sent before + # later messages during queue processing. + msgs_sorted = [(m, os.path.getmtime(m)) for m + in new_messages + cur_messages] + msgs_sorted.sort(key=lambda x: x[1]) + return iter([m[0] for m in msgs_sorted]) + + def newMessage(self): + "See `zope.sendmail.interfaces.IMaildir`" + # NOTE: http://www.qmail.org/man/man5/maildir.html says, that the first + # step of the delivery process should be a chdir. Chdirs and + # threading do not mix. Is that chdir really necessary? + join = os.path.join + subdir_tmp = join(self.path, 'tmp') + subdir_new = join(self.path, 'new') + pid = os.getpid() + host = socket.gethostname() + randmax = 0x7fffffff + counter = 0 + while True: + timestamp = int(time.time()) + unique = '%d.%d.%s.%d' % (timestamp, pid, host, + random.randrange(randmax)) + filename = join(subdir_tmp, unique) + try: + fd = os.open(filename, os.O_CREAT|os.O_EXCL|os.O_WRONLY, 0600) + except OSError, e: + if e.errno != errno.EEXIST: + raise + # File exists + counter += 1 + if counter >= 1000: + raise RuntimeError("Failed to create unique file name" + " in %s, are we under a DoS attack?" + % subdir_tmp) + # NOTE: maildir.html (see above) says I should sleep for 2 + time.sleep(0.1) + else: + break + return MaildirMessageWriter(os.fdopen(fd, 'w'), filename, + join(subdir_new, unique)) + + +def _encode_utf8(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + return s + + +class MaildirMessageWriter(object): + """See `zope.sendmail.interfaces.IMaildirMessageWriter`""" + + implements(IMaildirMessageWriter) + + def __init__(self, fd, filename, new_filename): + self._filename = filename + self._new_filename = new_filename + self._fd = fd + self._finished = False + self._aborted = False + + def write(self, data): + self._fd.write(_encode_utf8(data)) + + def writelines(self, lines): + lines = map(_encode_utf8, lines) + self._fd.writelines(lines) + + def close(self): + self._fd.close() + + def commit(self): + if self._aborted: + raise RuntimeError('Cannot commit, message already aborted') + elif not self._finished: + self._finished = True + self._fd.close() + os.rename(self._filename, self._new_filename) + # NOTE: the same maildir.html says it should be a link, followed by + # unlink. But Win32 does not necessarily have hardlinks! + + def abort(self): + # XXX mgedmin: I think it is dangerous to have an abort() that does + # nothing when commit() already succeeded. But the tests currently + # test that expectation. + if not self._finished: + self._finished = True + self._aborted = True + self._fd.close() + os.unlink(self._filename) + + # XXX: should there be a __del__ that calls abort()? + diff -Nru zope3-3.4.0/src/zope/sendmail/mailer.py zope3-3.5~bzr18/src/zope/sendmail/mailer.py --- zope3-3.4.0/src/zope/sendmail/mailer.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/mailer.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,77 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## + +"""These are classes which abstract different channels an email +message could be sent out by. + +$Id: mailer.py 92747 2008-11-01 19:51:58Z adamg $ +""" +__docformat__ = 'restructuredtext' + +import socket +from smtplib import SMTP + +from zope.interface import implements +from zope.sendmail.interfaces import ISMTPMailer + +have_ssl = hasattr(socket, 'ssl') + +class SMTPMailer(object): + + implements(ISMTPMailer) + + smtp = SMTP + + def __init__(self, hostname='localhost', port=25, + username=None, password=None, no_tls=False, force_tls=False): + self.hostname = hostname + self.port = port + self.username = username + self.password = password + self.force_tls = force_tls + self.no_tls = no_tls + + def send(self, fromaddr, toaddrs, message): + connection = self.smtp(self.hostname, str(self.port)) + + # send EHLO + code, response = connection.ehlo() + if code < 200 or code >= 300: + code, response = connection.helo() + if code < 200 or code >= 300: + raise RuntimeError('Error sending HELO to the SMTP server ' + '(code=%s, response=%s)' % (code, response)) + + # encryption support + have_tls = connection.has_extn('starttls') + if not have_tls and self.force_tls: + raise RuntimeError('TLS is not available but TLS is required') + + if have_tls and have_ssl and not self.no_tls: + connection.starttls() + connection.ehlo() + + if connection.does_esmtp: + if self.username is not None and self.password is not None: + connection.login(self.username, self.password) + elif self.username: + raise RuntimeError('Mailhost does not support ESMTP but a username ' + 'is configured') + + connection.sendmail(fromaddr, toaddrs, message) + try: + connection.quit() + except socket.sslerror: + #something weird happened while quiting + connection.close() diff -Nru zope3-3.4.0/src/zope/sendmail/meta.zcml zope3-3.5~bzr18/src/zope/sendmail/meta.zcml --- zope3-3.4.0/src/zope/sendmail/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,26 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/sendmail/queue.py zope3-3.5~bzr18/src/zope/sendmail/queue.py --- zope3-3.4.0/src/zope/sendmail/queue.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/queue.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,489 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Queue processor thread + +This module contains the queue processor thread. + +$Id: queue.py 107776 2010-01-07 10:37:46Z kobold $ +""" +__docformat__ = 'restructuredtext' + +import atexit +import ConfigParser +import logging +import os +import smtplib +import stat +import threading +import time + +from zope.sendmail.maildir import Maildir +from zope.sendmail.mailer import SMTPMailer + +import sys +if sys.platform == 'win32': + import win32file + _os_link = lambda src, dst: win32file.CreateHardLink(dst, src, None) +else: + _os_link = os.link + +# The longest time sending a file is expected to take. Longer than this and +# the send attempt will be assumed to have failed. This means that sending +# very large files or using very slow mail servers could result in duplicate +# messages sent. +MAX_SEND_TIME = 60*60*3 + +# The below diagram depicts the operations performed while sending a message in +# the ``run`` method of ``QueueProcessorThread``. This sequence of operations +# will be performed for each file in the maildir each time the thread "wakes +# up" to send messages. +# +# Any error conditions not depected on the diagram will provoke the catch-all +# exception logging of the ``run`` method. +# +# In the diagram the "message file" is the file in the maildir's "cur" directory +# that contains the message and "tmp file" is a hard link to the message file +# created in the maildir's "tmp" directory. +# +# ( start trying to deliver a message ) +# | +# | +# V +# +-----( get tmp file mtime ) +# | | +# | | file exists +# | V +# | ( check age )-----------------------------+ +# tmp file | | file is new | +# does not | | file is old | +# exist | | | +# | ( unlink tmp file )-----------------------+ | +# | | file does | | +# | | file unlinked not exist | | +# | V | | +# +---->( touch message file )------------------+ | | +# | file does | | | +# | not exist | | | +# V | | | +# ( link message file to tmp file )----------+ | | | +# | tmp file | | | | +# | already exists | | | | +# | | | | | +# V V V V V +# ( send message ) ( skip this message ) +# | +# V +# ( unlink message file )---------+ +# | | +# | file unlinked | file no longer exists +# | | +# | +-----------------+ +# | | +# | V +# ( unlink tmp file )------------+ +# | | +# | file unlinked | file no longer exists +# V | +# ( message delivered )<---------+ + +class QueueProcessorThread(threading.Thread): + """This thread is started at configuration time from the + `mail:queuedDelivery` directive handler if processorThread is True. + """ + + log = logging.getLogger("QueueProcessorThread") + _stopped = False + interval = 3.0 # process queue every X second + + def __init__(self, interval=3.0): + threading.Thread.__init__(self) + self.interval = interval + self._lock = threading.Lock() + self.setDaemon(True) + + def setMaildir(self, maildir): + """Set the maildir. + + This method is used just to provide a `maildir` stubs .""" + self.maildir = maildir + + def setQueuePath(self, path): + self.maildir = Maildir(path, True) + + def setMailer(self, mailer): + self.mailer = mailer + + def _parseMessage(self, message): + """Extract fromaddr and toaddrs from the first two lines of + the `message`. + + Returns a fromaddr string, a toaddrs tuple and the message + string. + """ + + fromaddr = "" + toaddrs = () + rest = "" + + try: + first, second, rest = message.split('\n', 2) + except ValueError: + return fromaddr, toaddrs, message + + if first.startswith("X-Zope-From: "): + i = len("X-Zope-From: ") + fromaddr = first[i:] + + if second.startswith("X-Zope-To: "): + i = len("X-Zope-To: ") + toaddrs = tuple(second[i:].split(", ")) + + return fromaddr, toaddrs, rest + + def run(self, forever=True): + atexit.register(self.stop) + while not self._stopped: + for filename in self.maildir: + # if we are asked to stop while sending messages, do so + if self._stopped: + break + + fromaddr = '' + toaddrs = () + head, tail = os.path.split(filename) + tmp_filename = os.path.join(head, '.sending-' + tail) + rejected_filename = os.path.join(head, '.rejected-' + tail) + try: + # perform a series of operations in an attempt to ensure + # that no two threads/processes send this message + # simultaneously as well as attempting to not generate + # spurious failure messages in the log; a diagram that + # represents these operations is included in a + # comment above this class + try: + # find the age of the tmp file (if it exists) + age = None + mtime = os.stat(tmp_filename)[stat.ST_MTIME] + age = time.time() - mtime + except OSError, e: + if e.errno == 2: # file does not exist + # the tmp file could not be stated because it + # doesn't exist, that's fine, keep going + pass + else: + # the tmp file could not be stated for some reason + # other than not existing; we'll report the error + raise + + # if the tmp file exists, check it's age + if age is not None: + try: + if age > MAX_SEND_TIME: + # the tmp file is "too old"; this suggests + # that during an attemt to send it, the + # process died; remove the tmp file so we + # can try again + os.unlink(tmp_filename) + else: + # the tmp file is "new", so someone else may + # be sending this message, try again later + continue + # if we get here, the file existed, but was too + # old, so it was unlinked + except OSError, e: + if e.errno == 2: # file does not exist + # it looks like someone else removed the tmp + # file, that's fine, we'll try to deliver the + # message again later + continue + + # now we know that the tmp file doesn't exist, we need to + # "touch" the message before we create the tmp file so the + # mtime will reflect the fact that the file is being + # processed (there is a race here, but it's OK for two or + # more processes to touch the file "simultaneously") + try: + os.utime(filename, None) + except OSError, e: + if e.errno == 2: # file does not exist + # someone removed the message before we could + # touch it, no need to complain, we'll just keep + # going + continue + + # creating this hard link will fail if another process is + # also sending this message + try: + #os.link(filename, tmp_filename) + _os_link(filename, tmp_filename) + except OSError, e: + if e.errno == 17: # file exists, *nix + # it looks like someone else is sending this + # message too; we'll try again later + continue + except Exception, e: + if e[0] == 183 and e[1] == 'CreateHardLink': + # file exists, win32 + continue + + # read message file and send contents + file = open(filename) + message = file.read() + file.close() + fromaddr, toaddrs, message = self._parseMessage(message) + # The next block is the only one that is sensitive to + # interruptions. Everywhere else, if this daemon thread + # stops, we should be able to correctly handle a restart. + # In this block, if we send the message, but we are + # stopped before we unlink the file, we will resend the + # message when we are restarted. We limit the likelihood + # of this somewhat by using a lock to link the two + # operations. When the process gets an interrupt, it + # will call the atexit that we registered (``stop`` + # below). This will try to get the same lock before it + # lets go. Because this can cause the daemon thread to + # continue (that is, to not act like a daemon thread), we + # still use the _stopped flag to communicate. + self._lock.acquire() + try: + try: + self.mailer.send(fromaddr, toaddrs, message) + except smtplib.SMTPResponseException, e: + if 500 <= e.smtp_code <= 599: + # permanent error, ditch the message + self.log.error( + "Discarding email from %s to %s due to" + " a permanent error: %s", + fromaddr, ", ".join(toaddrs), str(e)) + #os.link(filename, rejected_filename) + _os_link(filename, rejected_filename) + else: + # Log an error and retry later + raise + + try: + os.unlink(filename) + except OSError, e: + if e.errno == 2: # file does not exist + # someone else unlinked the file; oh well + pass + else: + # something bad happend, log it + raise + finally: + self._lock.release() + try: + os.unlink(tmp_filename) + except OSError, e: + if e.errno == 2: # file does not exist + # someone else unlinked the file; oh well + pass + else: + # something bad happend, log it + raise + + # TODO: maybe log the Message-Id of the message sent + self.log.info("Mail from %s to %s sent.", + fromaddr, ", ".join(toaddrs)) + # Blanket except because we don't want + # this thread to ever die + except: + if fromaddr != '' or toaddrs != (): + self.log.error( + "Error while sending mail from %s to %s.", + fromaddr, ", ".join(toaddrs), exc_info=True) + else: + self.log.error( + "Error while sending mail : %s ", + filename, exc_info=True) + else: + if forever: + time.sleep(self.interval) + + # A testing plug + if not forever: + break + + def stop(self): + self._stopped = True + self._lock.acquire() + self._lock.release() + + +def boolean(s): + s = str(s).lower() + return s.startswith("t") or s.startswith("y") or s.startswith("1") + + +def string_or_none(s): + if s == 'None': + return None + return s + + +class ConsoleApp(object): + """Allows running of Queue Processor from the console.""" + + _usage = """%(script_name)s [OPTIONS] path/to/maildir + + OPTIONS: + + --daemon Run in daemon mode, periodically checking queue + and sending messages. Default is to send all + messages in queue once and exit. + + --interval <#secs> How often to check queue when in daemon mode. + Default is 3 seconds. + + --hostname Name of smtp host to use for delivery. Default is + localhost. + + --port Which port on smtp server to deliver mail to. + Default is 25. + + --username Username to use to log in to smtp server. Default + is none. + + --password Password to use to log in to smtp server. Must be + specified if username is specified. + + --force-tls Do not connect if TLS is not available. Not + enabled by default. + + --no-tls Do not use TLS even if is available. Not enabled + by default. + + --config Get configuration from specificed ini file; it must + contain a section [app:zope-sendmail]. + + """ + + _error = False + daemon = False + interval = 3 + hostname = 'localhost' + port = 25 + username = None + password = None + force_tls = False + no_tls = False + queue_path = None + + def __init__(self, argv=sys.argv, verbose=True): + self.script_name = argv[0] + self.verbose = verbose + self._process_args(argv[1:]) + self.mailer = SMTPMailer(self.hostname, self.port, self.username, + self.password, self.no_tls, self.force_tls) + + def main(self): + if self._error: + return + queue = QueueProcessorThread(self.interval) + queue.setMailer(self.mailer) + queue.setQueuePath(self.queue_path) + queue.run(forever=self.daemon) + + def _process_args(self, args): + got_queue_path = False + while args: + arg = args.pop(0) + if arg == "--daemon": + self.daemon = True + elif arg == "--interval": + try: + self.interval = float(args.pop(0)) + except: + self._error_usage() + elif arg == "--hostname": + if not args: + self._error_usage() + self.hostname = args.pop(0) + elif arg == "--port": + try: + self.port = int(args.pop(0)) + except: + self._error_usage() + elif arg == "--username": + if not args: + self._error_usage() + self.username = args.pop(0) + elif arg == "--password": + if not args: + self._error_usage() + self.password = args.pop(0) + elif arg == "--force-tls": + self.force_tls = True + elif arg == "--no-tls": + self.no_tls = True + elif arg == "--config": + if not args: + self._error_usage() + self._load_config(args.pop(0)) + elif arg.startswith("-") or got_queue_path: + self._error_usage() + else: + self.queue_path = arg + got_queue_path = True + if not self.queue_path: + self._error_usage() + if (self.username or self.password) and \ + not (self.username and self.password): + if self.verbose: + print >>sys.stderr, "Must use username and password together." + self._error = True + if self.force_tls and self.no_tls: + if self.verbose: + print >>sys.stderr, \ + "--force-tls and --no-tls are mutually exclusive." + self._error = True + + def _load_config(self, path): + section = "app:zope-sendmail" + names = [ + "interval", + "hostname", + "port", + "username", + "password", + "force_tls", + "no_tls", + "queue_path", + ] + defaults = dict([(name, str(getattr(self, name))) for name in names]) + config = ConfigParser.ConfigParser(defaults) + config.read(path) + self.interval = float(config.get(section, "interval")) + self.hostname = config.get(section, "hostname") + self.port = int(config.get(section, "port")) + self.username = string_or_none(config.get(section, "username")) + self.password = string_or_none(config.get(section, "password")) + self.force_tls = boolean(config.get(section, "force_tls")) + self.no_tls = boolean(config.get(section, "no_tls")) + self.queue_path = string_or_none(config.get(section, "queue_path")) + + def _error_usage(self): + if self.verbose: + print >>sys.stderr, self._usage % {"script_name": self.script_name,} + self._error = True + + +def run(): + logging.basicConfig() + app = ConsoleApp() + app.main() + + +if __name__ == "__main__": + run_console() diff -Nru zope3-3.4.0/src/zope/sendmail/README.txt zope3-3.5~bzr18/src/zope/sendmail/README.txt --- zope3-3.4.0/src/zope/sendmail/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,127 @@ +Using zope.sendmail +=================== + +This package is useful when your Zope 3 application wants to send email. It +integrates with the transaction mechanism and queues your emails to be sent on +successful commits only. + + +API +--- + +An application that wants to send an email can do so by getting the appropriate +IMailDelivery utility. The standard library's email module is useful for +formatting the message according to RFC-2822:: + + import email.MIMEText + import email.Header + from zope.sendmail.interfaces import IMailDelivery + from zope.component import getUtility + + def send_email(sender, recipient, subject, body): + msg = email.MIMEText.MIMEText(body.encode('UTF-8'), 'plain', 'UTF-8') + msg["From"] = sender + msg["To"] = recipient + msg["Subject"] = email.Header.Header(subject, 'UTF-8') + mailer = getUtility(IMailDelivery, 'my-app.mailer') + mailer.send(sender, [recipient], msg.as_string()) + +In real-world code you may need to do extra work to format the 'From' and 'To' +headers correctly, if the addresses contain a real-name part with non-ASCII +characters. You can find a recipe for that in this blog post: +http://mg.pov.lt/blog/unicode-emails-in-python.html + + +Configuration +------------- + +The code above used a named IMailDelivery utility. It is your responsibility +to define one, as Zope 3 doesn't provide one by default. You can define +an IMailDelivery utility in your site.zcml with a configuration directive:: + + + + + +The ``mail:queuedDelivery`` directive stores every email in a queue (a standard +Maildir folder on the file system in a given directory) and sends them from a +background thread. There's an alternative directive, ``mail:directDelivery``, +that sends them from the same thread. This may slow down transaction commits +(especially if the SMTP server is slow to respond) and increase the loading +time of web pages. + + +Mailers +------- + +The ``mailer`` argument of the ``mail:queuedDelivery`` utility chooses the +appropriate IMailer utility that will be used to deliver email. There +are alternative ways of doing that, for example, SMTP or piping the message to +an external program. Currently ``zope.sendmail`` supports only plain SMTP. +[#]_ + +.. [#] There was once a mailer utility that invoked /usr/sbin/sendmail, but + it had security issues related to the difficulty of quoting command-line + arguments in a portable way. + +If the same system that runs your Zope 3 server also has an SMTP server on +port 25, you can use the default ``smtp`` mailer. If you want to use a +different SMTP server, define your own utility like this:: + + + + + + + + +Testing +------- + +Obviously, you don't want your automated unit/functional test runs to send +real emails. You'll have to define a fake email delivery utility in your +test layer. Something like this will do the trick:: + + class FakeMailDelivery(object): + implements(IMailDelivery) + + def send(self, source, dest, body): + print "*** Sending email from %s to %s:" % (source, dest) + print body + return 'fake-message-id@example.com' + +Register it with the standard ``utility`` directive:: + + + + +Problems with zope.sendmail +--------------------------- + +* The API is a bit inconvenient to use (e.g. you have to do the message + formatting by yourself). + +* The configuration should be done in zope.conf, not in ZCML. + +* The IMailSentEvent and IMailErrorEvent events aren't used and can't be used + (you don't want to send emails during the commit phase). diff -Nru zope3-3.4.0/src/zope/sendmail/tests/__init__.py zope3-3.5~bzr18/src/zope/sendmail/tests/__init__.py --- zope3-3.4.0/src/zope/sendmail/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# make this directory a package diff -Nru zope3-3.4.0/src/zope/sendmail/tests/mail.zcml zope3-3.5~bzr18/src/zope/sendmail/tests/mail.zcml --- zope3-3.4.0/src/zope/sendmail/tests/mail.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/mail.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/sendmail/tests/test_delivery.py zope3-3.5~bzr18/src/zope/sendmail/tests/test_delivery.py --- zope3-3.4.0/src/zope/sendmail/tests/test_delivery.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/test_delivery.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,313 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Mail Delivery Tests + +Simple implementation of the MailDelivery, Mailers and MailEvents. + +$Id: test_delivery.py 110390 2010-04-01 12:33:34Z mgedmin $ +""" + +import smtplib +from unittest import TestCase, TestSuite, makeSuite, main +import doctest + +import transaction +from zope.interface import implements +from zope.interface.verify import verifyObject +from zope.sendmail.interfaces import IMailer + + +class MailerStub(object): + + implements(IMailer) + def __init__(self, *args, **kw): + self.sent_messages = [] + + def send(self, fromaddr, toaddrs, message): + self.sent_messages.append((fromaddr, toaddrs, message)) + + +class TestMailDataManager(TestCase): + + def testInterface(self): + from transaction.interfaces import IDataManager + from zope.sendmail.delivery import MailDataManager + manager = MailDataManager(object, (1, 2)) + verifyObject(IDataManager, manager) + self.assertEqual(manager.callable, object) + self.assertEqual(manager.args, (1, 2)) + + +def print_success(*args): + print "message successfully sent, args: %s" % (args, ) + +def print_abort(): + print "message aborted" + + +def doctest_successful_commit(): + """Regression test for http://www.zope.org/Collectors/Zope3-dev/590 + + Let's do a full two-phase commit. + + >>> from zope.sendmail.delivery import MailDataManager + >>> manager = MailDataManager(print_success, ('foo', 'bar'), + ... onAbort=print_abort) + >>> transaction = object() + >>> manager.tpc_begin(transaction) + >>> manager.commit(transaction) + >>> manager.tpc_vote(transaction) + >>> manager.tpc_finish(transaction) + message successfully sent, args: ('foo', 'bar') + + """ + + +def doctest_unsuccessful_commit(): + """Regression test for http://www.zope.org/Collectors/Zope3-dev/590 + + Let's start a two-phase commit, then abort it. + + >>> from zope.sendmail.delivery import MailDataManager + >>> manager = MailDataManager(print_success, onAbort=print_abort) + >>> manager.tpc_begin(transaction) + >>> manager.commit(transaction) + >>> manager.tpc_vote(transaction) + >>> manager.tpc_abort(transaction) + message aborted + + """ + + +class TestDirectMailDelivery(TestCase): + + def testInterface(self): + from zope.sendmail.interfaces import IDirectMailDelivery + from zope.sendmail.delivery import DirectMailDelivery + mailer = MailerStub() + delivery = DirectMailDelivery(mailer) + verifyObject(IDirectMailDelivery, delivery) + self.assertEqual(delivery.mailer, mailer) + + def testSend(self): + from zope.sendmail.delivery import DirectMailDelivery + mailer = MailerStub() + delivery = DirectMailDelivery(mailer) + fromaddr = 'Jim ', + 'Steve ') + opt_headers = ('From: Jim \n' + 'To: some-zope-coders:;\n' + 'Date: Mon, 19 May 2003 10:17:36 -0400\n' + 'Message-Id: <20030519.1234@example.org>\n') + message = ('Subject: example\n' + '\n' + 'This is just an example\n') + + msgid = delivery.send(fromaddr, toaddrs, opt_headers + message) + self.assertEquals(msgid, '20030519.1234@example.org') + self.assertEquals(mailer.sent_messages, []) + transaction.commit() + self.assertEquals(mailer.sent_messages, + [(fromaddr, toaddrs, opt_headers + message)]) + + mailer.sent_messages = [] + msgid = delivery.send(fromaddr, toaddrs, message) + self.assert_('@' in msgid) + self.assertEquals(mailer.sent_messages, []) + transaction.commit() + self.assertEquals(len(mailer.sent_messages), 1) + self.assertEquals(mailer.sent_messages[0][0], fromaddr) + self.assertEquals(mailer.sent_messages[0][1], toaddrs) + self.assert_(mailer.sent_messages[0][2].endswith(message)) + new_headers = mailer.sent_messages[0][2][:-len(message)] + self.assert_(new_headers.find('Message-Id: <%s>' % msgid) != -1) + + mailer.sent_messages = [] + msgid = delivery.send(fromaddr, toaddrs, opt_headers + message) + self.assertEquals(mailer.sent_messages, []) + transaction.abort() + self.assertEquals(mailer.sent_messages, []) + + +class MaildirWriterStub(object): + + data = '' + commited_messages = [] # this list is shared among all instances + aborted_messages = [] # this one too + _closed = False + + def write(self, str): + if self._closed: + raise AssertionError('already closed') + self.data += str + + def writelines(self, seq): + if self._closed: + raise AssertionError('already closed') + self.data += ''.join(seq) + + def close(self): + self._closed = True + + def commit(self): + if not self._closed: + raise AssertionError('for this test we want the message explicitly' + ' closed before it is committed') + self._commited = True + self.commited_messages.append(self.data) + + def abort(self): + if not self._closed: + raise AssertionError('for this test we want the message explicitly' + ' closed before it is committed') + self._aborted = True + self.aborted_messages.append(self.data) + + +class MaildirStub(object): + + def __init__(self, path, create=False): + self.path = path + self.create = create + self.msgs = [] + self.files = [] + + def __iter__(self): + return iter(self.files) + + def newMessage(self): + m = MaildirWriterStub() + self.msgs.append(m) + return m + + +class LoggerStub(object): + + def __init__(self): + self.infos = [] + self.errors = [] + + def getLogger(self, name): + return self + + def error(self, msg, *args, **kwargs): + self.errors.append((msg, args, kwargs)) + + def info(self, msg, *args, **kwargs): + self.infos.append((msg, args, kwargs)) + + +class BizzarreMailError(IOError): + pass + + +class BrokenMailerStub(object): + + implements(IMailer) + def __init__(self, *args, **kw): + pass + + def send(self, fromaddr, toaddrs, message): + raise BizzarreMailError("bad things happened while sending mail") + + +class SMTPResponseExceptionMailerStub(object): + + implements(IMailer) + def __init__(self, code): + self.code = code + + def send(self, fromaddr, toaddrs, message): + raise smtplib.SMTPResponseException(self.code, 'Serious Error') + + +class TestQueuedMailDelivery(TestCase): + + def setUp(self): + import zope.sendmail.delivery as mail_delivery_module + self.mail_delivery_module = mail_delivery_module + self.old_Maildir = mail_delivery_module.Maildir + mail_delivery_module.Maildir = MaildirStub + + def tearDown(self): + self.mail_delivery_module.Maildir = self.old_Maildir + MaildirWriterStub.commited_messages = [] + MaildirWriterStub.aborted_messages = [] + + def testInterface(self): + from zope.sendmail.interfaces import IQueuedMailDelivery + from zope.sendmail.delivery import QueuedMailDelivery + delivery = QueuedMailDelivery('/path/to/mailbox') + verifyObject(IQueuedMailDelivery, delivery) + self.assertEqual(delivery.queuePath, '/path/to/mailbox') + + def testSend(self): + from zope.sendmail.delivery import QueuedMailDelivery + delivery = QueuedMailDelivery('/path/to/mailbox') + fromaddr = 'jim@example.com' + toaddrs = ('guido@example.com', + 'steve@examplecom') + zope_headers = ('X-Zope-From: jim@example.com\n' + 'X-Zope-To: guido@example.com, steve@examplecom\n') + opt_headers = ('From: Jim \n' + 'To: some-zope-coders:;\n' + 'Date: Mon, 19 May 2003 10:17:36 -0400\n' + 'Message-Id: <20030519.1234@example.org>\n') + message = ('Subject: example\n' + '\n' + 'This is just an example\n') + + msgid = delivery.send(fromaddr, toaddrs, opt_headers + message) + self.assertEquals(msgid, '20030519.1234@example.org') + self.assertEquals(MaildirWriterStub.commited_messages, []) + self.assertEquals(MaildirWriterStub.aborted_messages, []) + transaction.commit() + self.assertEquals(MaildirWriterStub.commited_messages, + [zope_headers + opt_headers + message]) + self.assertEquals(MaildirWriterStub.aborted_messages, []) + + MaildirWriterStub.commited_messages = [] + msgid = delivery.send(fromaddr, toaddrs, message) + self.assert_('@' in msgid) + self.assertEquals(MaildirWriterStub.commited_messages, []) + self.assertEquals(MaildirWriterStub.aborted_messages, []) + transaction.commit() + self.assertEquals(len(MaildirWriterStub.commited_messages), 1) + self.assert_(MaildirWriterStub.commited_messages[0].endswith(message)) + new_headers = MaildirWriterStub.commited_messages[0][:-len(message)] + self.assert_(new_headers.find('Message-Id: <%s>' % msgid) != -1) + self.assert_(new_headers.find('X-Zope-From: %s' % fromaddr) != 1) + self.assert_(new_headers.find('X-Zope-To: %s' % ", ".join(toaddrs)) != 1) + self.assertEquals(MaildirWriterStub.aborted_messages, []) + + MaildirWriterStub.commited_messages = [] + msgid = delivery.send(fromaddr, toaddrs, opt_headers + message) + self.assertEquals(MaildirWriterStub.commited_messages, []) + self.assertEquals(MaildirWriterStub.aborted_messages, []) + transaction.abort() + self.assertEquals(MaildirWriterStub.commited_messages, []) + self.assertEquals(len(MaildirWriterStub.aborted_messages), 1) + + +def test_suite(): + return TestSuite(( + makeSuite(TestMailDataManager), + makeSuite(TestDirectMailDelivery), + makeSuite(TestQueuedMailDelivery), + doctest.DocTestSuite(), + )) + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/zope/sendmail/tests/test_directives.py zope3-3.5~bzr18/src/zope/sendmail/tests/test_directives.py --- zope3-3.4.0/src/zope/sendmail/tests/test_directives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/test_directives.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,111 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 the gts ZCML namespace directives. + +$Id: test_directives.py 107770 2010-01-07 07:11:43Z kobold $ +""" +import os +import shutil +import unittest +import threading +import tempfile +import time + +import zope.component +from zope.component.testing import PlacelessSetup +from zope.configuration import xmlconfig +from zope.interface import implements + +from zope.sendmail.interfaces import \ + IMailDelivery, IMailer, ISMTPMailer +from zope.sendmail import delivery +from zope.sendmail.queue import QueueProcessorThread +import zope.sendmail.tests + + +class MaildirStub(object): + + def __init__(self, path, create=False): + self.path = path + self.create = create + + def __iter__(self): + return iter(()) + + def newMessage(self): + return None + +class Mailer(object): + implements(IMailer) + + +class DirectivesTest(PlacelessSetup, unittest.TestCase): + + def setUp(self): + self.mailbox = os.path.join(tempfile.mkdtemp(), "mailbox") + + super(DirectivesTest, self).setUp() + self.testMailer = Mailer() + + gsm = zope.component.getGlobalSiteManager() + gsm.registerUtility(Mailer(), IMailer, "test.smtp") + gsm.registerUtility(self.testMailer, IMailer, "test.mailer") + + here = os.path.dirname(__file__) + zcmlfile = open(os.path.join(here, "mail.zcml"), 'r') + zcml = zcmlfile.read() + zcmlfile.close() + + self.context = xmlconfig.string( + zcml.replace('path/to/tmp/mailbox', self.mailbox)) + self.orig_maildir = delivery.Maildir + delivery.Maildir = MaildirStub + + def tearDown(self): + delivery.Maildir = self.orig_maildir + + # Tear down the mail queue processor thread. + # Give the other thread a chance to start: + time.sleep(0.001) + threads = list(threading.enumerate()) + for thread in threads: + if isinstance(thread, QueueProcessorThread): + thread.stop() + thread.join() + + shutil.rmtree(self.mailbox, True) + super(DirectivesTest, self).tearDown() + + def testQueuedDelivery(self): + delivery = zope.component.getUtility(IMailDelivery, "Mail") + self.assertEqual('QueuedMailDelivery', delivery.__class__.__name__) + self.assertEqual(self.mailbox, delivery.queuePath) + + def testDirectDelivery(self): + delivery = zope.component.getUtility(IMailDelivery, "Mail2") + self.assertEqual('DirectMailDelivery', delivery.__class__.__name__) + self.assert_(self.testMailer is delivery.mailer) + + def testSMTPMailer(self): + mailer = zope.component.getUtility(IMailer, "smtp") + self.assert_(ISMTPMailer.providedBy(mailer)) + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(DirectivesTest), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/sendmail/tests/test_event.py zope3-3.5~bzr18/src/zope/sendmail/tests/test_event.py --- zope3-3.4.0/src/zope/sendmail/tests/test_event.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/test_event.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,50 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Mailer Events Tests + +$Id: test_event.py 66922 2006-04-12 23:28:07Z jinty $ +""" +from unittest import TestCase, TestSuite, makeSuite + +from zope.interface.verify import verifyObject + +from zope.sendmail.interfaces import IMailSentEvent, IMailErrorEvent +from zope.sendmail.event import MailSentEvent + + +class TestMailEvents(TestCase): + + def testMailSendEvent(self): + msgid = '<1234@example.com>' + m = MailSentEvent(msgid) + verifyObject(IMailSentEvent, m) + self.assertEquals(m.messageId, msgid) + + def testMailErrorEvent(self): + from zope.sendmail.event import MailErrorEvent + msgid = '<1234@example.com>' + error = '550 Relay access denied' + m = MailErrorEvent(msgid, error) + verifyObject(IMailErrorEvent, m) + self.assertEquals(m.messageId, msgid) + self.assertEquals(m.errorMessage, error) + + +def test_suite(): + return TestSuite(( + makeSuite(TestMailEvents), + )) + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/sendmail/tests/test_maildir.py zope3-3.5~bzr18/src/zope/sendmail/tests/test_maildir.py --- zope3-3.4.0/src/zope/sendmail/tests/test_maildir.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/test_maildir.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,320 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Unit tests for zope.sendmail.maildir module + +$Id: test_maildir.py 109588 2010-03-03 09:57:07Z kobold $ +""" + +import unittest +import stat +import os +import errno + +from zope.interface.verify import verifyObject + + +class FakeSocketModule(object): + + def gethostname(self): + return 'myhostname' + +class FakeTimeModule(object): + + _timer = 1234500000 + + def time(self): + return self._timer + + def sleep(self, n): + self._timer += n + +class FakeOsPathModule(object): + + def __init__(self, files, dirs): + self.files = files + self.dirs = dirs + mtimes = {} + for t,f in enumerate(files): + mtimes[f] = 9999 - t + self._mtimes = mtimes + + def join(self, *args): + return '/'.join(args) + + def isdir(self, dir): + return dir in self.dirs + + def getmtime(self, f): + return self._mtimes.get(f, 10000) + + +class FakeOsModule(object): + + F_OK = 0 + O_CREAT = os.O_CREAT + O_EXCL = os.O_EXCL + O_WRONLY = os.O_WRONLY + + _stat_mode = { + '/path/to/maildir': stat.S_IFDIR, + '/path/to/maildir/new': stat.S_IFDIR, + '/path/to/maildir/new/1': stat.S_IFREG, + '/path/to/maildir/new/2': stat.S_IFREG, + '/path/to/maildir/cur': stat.S_IFDIR, + '/path/to/maildir/cur/1': stat.S_IFREG, + '/path/to/maildir/cur/2': stat.S_IFREG, + '/path/to/maildir/tmp': stat.S_IFDIR, + '/path/to/maildir/tmp/1': stat.S_IFREG, + '/path/to/maildir/tmp/2': stat.S_IFREG, + '/path/to/maildir/tmp/1234500000.4242.myhostname.*': stat.S_IFREG, + '/path/to/maildir/tmp/1234500001.4242.myhostname.*': stat.S_IFREG, + '/path/to/regularfile': stat.S_IFREG, + '/path/to/emptydirectory': stat.S_IFDIR, + } + _listdir = { + '/path/to/maildir/new': ['1', '2', '.svn'], + '/path/to/maildir/cur': ['2', '1', '.tmp'], + '/path/to/maildir/tmp': ['1', '2', '.ignore'], + } + + path = FakeOsPathModule(_stat_mode, _listdir) + + _made_directories = () + _removed_files = () + _renamed_files = () + + _all_files_exist = False + + def __init__(self): + self._descriptors = {} + + def access(self, path, mode): + if self._all_files_exist: + return True + if path in self._stat_mode: + return True + if path.rsplit('.', 1)[0] + '.*' in self._stat_mode: + return True + return False + + def stat(self, path): + if path in self._stat_mode: + return (self._stat_mode[path], 0, 0, 1, 0, 0, 0, 0, 0, 0) + raise OSError('%s does not exist' % path) + + def listdir(self, path): + return self._listdir.get(path, []) + + def mkdir(self, path): + self._made_directories += (path, ) + + def getpid(self): + return 4242 + + def unlink(self, path): + self._removed_files += (path, ) + + def rename(self, old, new): + self._renamed_files += ((old, new), ) + + def open(self, filename, flags, mode=0777): + if (flags & os.O_EXCL and flags & os.O_CREAT + and self.access(filename, 0)): + raise OSError(errno.EEXIST, 'file already exists') + if not flags & os.O_CREAT and not self.access(filename, 0): + raise OSError('file not found') + fd = max(self._descriptors.keys() + [2]) + 1 + self._descriptors[fd] = filename, flags, mode + return fd + + def fdopen(self, fd, mode='r'): + try: + filename, flags, permissions = self._descriptors[fd] + except KeyError: + raise AssertionError('os.fdopen() called with an unknown' + ' file descriptor') + if mode == 'r': + assert not flags & os.O_WRONLY + assert not flags & os.O_RDWR + elif mode == 'w': + assert flags & os.O_WRONLY + assert not flags & os.O_RDWR + elif mode == 'r+': + assert not flags & os.O_WRONLY + assert flags & os.O_RDWR + else: + raise AssertionError("don't know how to verify if flags match" + " mode %r" % mode) + return FakeFile(filename, mode) + + +class FakeFile(object): + + def __init__(self, filename, mode): + self._filename = filename + self._mode = mode + self._written = '' + self._closed = False + + def close(self): + self._closed = True + + def write(self, data): + self._written += data + + def writelines(self, lines): + self._written += ''.join(lines) + + +class TestMaildir(unittest.TestCase): + + def setUp(self): + import zope.sendmail.maildir as maildir_module + self.maildir_module = maildir_module + self.old_os_module = maildir_module.os + self.old_time_module = maildir_module.time + self.old_socket_module = maildir_module.socket + maildir_module.os = self.fake_os_module = FakeOsModule() + maildir_module.time = FakeTimeModule() + maildir_module.socket = FakeSocketModule() + + def tearDown(self): + self.maildir_module.os = self.old_os_module + self.maildir_module.time = self.old_time_module + self.maildir_module.socket = self.old_socket_module + self.fake_os_module._stat_never_fails = False + self.fake_os_module._all_files_exist = False + + def test_factory(self): + from zope.sendmail.interfaces import IMaildirFactory, IMaildir + from zope.sendmail.maildir import Maildir + verifyObject(IMaildirFactory, Maildir) + + # Case 1: normal maildir + m = Maildir('/path/to/maildir') + verifyObject(IMaildir, m) + + # Case 2a: directory does not exist, create = False + self.assertRaises(ValueError, Maildir, '/path/to/nosuchfolder', False) + + # Case 2b: directory does not exist, create = True + m = Maildir('/path/to/nosuchfolder', True) + verifyObject(IMaildir, m) + dirs = list(self.fake_os_module._made_directories) + dirs.sort() + self.assertEquals(dirs, ['/path/to/nosuchfolder', + '/path/to/nosuchfolder/cur', + '/path/to/nosuchfolder/new', + '/path/to/nosuchfolder/tmp']) + + # Case 3: it is a file, not a directory + self.assertRaises(ValueError, Maildir, '/path/to/regularfile', False) + self.assertRaises(ValueError, Maildir, '/path/to/regularfile', True) + + # Case 4: it is a directory, but not a maildir + self.assertRaises(ValueError, Maildir, '/path/to/emptydirectory', False) + self.assertRaises(ValueError, Maildir, '/path/to/emptydirectory', True) + + def test_iteration(self): + from zope.sendmail.maildir import Maildir + m = Maildir('/path/to/maildir') + messages = list(m) + self.assertEquals(messages, ['/path/to/maildir/cur/2', + '/path/to/maildir/cur/1', + '/path/to/maildir/new/2', + '/path/to/maildir/new/1']) + + def test_newMessage(self): + from zope.sendmail.maildir import Maildir + from zope.sendmail.interfaces import IMaildirMessageWriter + m = Maildir('/path/to/maildir') + fd = m.newMessage() + verifyObject(IMaildirMessageWriter, fd) + self.assert_(fd._filename.startswith( + '/path/to/maildir/tmp/1234500002.4242.myhostname.')) + + def test_newMessage_never_loops(self): + from zope.sendmail.maildir import Maildir + from zope.sendmail.interfaces import IMaildirMessageWriter + self.fake_os_module._all_files_exist = True + m = Maildir('/path/to/maildir') + self.assertRaises(RuntimeError, m.newMessage) + + def test_message_writer_and_abort(self): + from zope.sendmail.maildir import MaildirMessageWriter + filename1 = '/path/to/maildir/tmp/1234500002.4242.myhostname' + filename2 = '/path/to/maildir/new/1234500002.4242.myhostname' + fd = FakeFile(filename1, 'w') + writer = MaildirMessageWriter(fd, filename1, filename2) + self.assertEquals(writer._fd._filename, filename1) + self.assertEquals(writer._fd._mode, 'w') # TODO or 'wb'? + print >> writer, 'fee', + writer.write(' fie') + writer.writelines([' foe', ' foo']) + self.assertEquals(writer._fd._written, 'fee fie foe foo') + + writer.abort() + self.assertEquals(writer._fd._closed, True) + self.assert_(filename1 in self.fake_os_module._removed_files) + # Once aborted, abort does nothing + self.fake_os_module._removed_files = () + writer.abort() + writer.abort() + self.assertEquals(self.fake_os_module._removed_files, ()) + # Once aborted, commit fails + self.assertRaises(RuntimeError, writer.commit) + + def test_message_writer_commit(self): + from zope.sendmail.maildir import MaildirMessageWriter + filename1 = '/path/to/maildir/tmp/1234500002.4242.myhostname' + filename2 = '/path/to/maildir/new/1234500002.4242.myhostname' + fd = FakeFile(filename1, 'w') + writer = MaildirMessageWriter(fd, filename1, filename2) + writer.commit() + self.assertEquals(writer._fd._closed, True) + self.assert_((filename1, filename2) + in self.fake_os_module._renamed_files) + # Once commited, commit does nothing + self.fake_os_module._renamed_files = () + writer.commit() + writer.commit() + self.assertEquals(self.fake_os_module._renamed_files, ()) + # Once commited, abort does nothing + writer.abort() + writer.abort() + self.assertEquals(self.fake_os_module._renamed_files, ()) + + def test_message_writer_unicode(self): + from zope.sendmail.maildir import MaildirMessageWriter + filename1 = '/path/to/maildir/tmp/1234500002.4242.myhostname' + filename2 = '/path/to/maildir/new/1234500002.4242.myhostname' + fd = FakeFile(filename1, 'w') + writer = MaildirMessageWriter(fd, filename1, filename2) + self.assertEquals(writer._fd._filename, filename1) + self.assertEquals(writer._fd._mode, 'w') # TODO or 'wb'? + print >> writer, u'fe\xe8', + writer.write(u' fi\xe8') + writer.writelines([u' fo\xe8', u' fo\xf2']) + self.assertEquals(writer._fd._written, + 'fe\xc3\xa8 fi\xc3\xa8 fo\xc3\xa8 fo\xc3\xb2') + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestMaildir)) + return suite + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/sendmail/tests/test_mailer.py zope3-3.5~bzr18/src/zope/sendmail/tests/test_mailer.py --- zope3-3.4.0/src/zope/sendmail/tests/test_mailer.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/test_mailer.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,173 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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 mailers. + +$Id: test_mailer.py 92756 2008-11-03 12:50:08Z adamg $ +""" + +from StringIO import StringIO +from zope.interface.verify import verifyObject +from zope.sendmail.interfaces import ISMTPMailer +from zope.sendmail.mailer import SMTPMailer +import socket +import unittest + + +class TestSMTPMailer(unittest.TestCase): + + def setUp(self, port=None): + global SMTP + class SMTP(object): + + fail_on_quit = False + + def __init__(myself, h, p): + myself.hostname = h + myself.port = p + myself.quitted = False + myself.closed = False + if type(p) == type(u""): + raise socket.error("Int or String expected") + self.smtp = myself + + def sendmail(self, f, t, m): + self.fromaddr = f + self.toaddrs = t + self.msgtext = m + + def login(self, username, password): + self.username = username + self.password = password + + def quit(self): + if self.fail_on_quit: + raise socket.sslerror("dang") + self.quitted = True + self.close() + + def close(self): + self.closed = True + + def has_extn(self, ext): + return True + + def ehlo(self): + self.does_esmtp = True + return (200, 'Hello, I am your stupid MTA mock') + + def starttls(self): + pass + + + if port is None: + self.mailer = SMTPMailer() + else: + self.mailer = SMTPMailer(u'localhost', port) + self.mailer.smtp = SMTP + + def test_interface(self): + verifyObject(ISMTPMailer, self.mailer) + + def test_send(self): + for run in (1,2): + if run == 2: + self.setUp(u'25') + fromaddr = 'me@example.com' + toaddrs = ('you@example.com', 'him@example.com') + msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' + self.mailer.send(fromaddr, toaddrs, msgtext) + self.assertEquals(self.smtp.fromaddr, fromaddr) + self.assertEquals(self.smtp.toaddrs, toaddrs) + self.assertEquals(self.smtp.msgtext, msgtext) + self.assert_(self.smtp.quitted) + self.assert_(self.smtp.closed) + + def test_send_auth(self): + fromaddr = 'me@example.com' + toaddrs = ('you@example.com', 'him@example.com') + msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' + self.mailer.username = 'foo' + self.mailer.password = 'evil' + self.mailer.hostname = 'spamrelay' + self.mailer.port = 31337 + self.mailer.send(fromaddr, toaddrs, msgtext) + self.assertEquals(self.smtp.username, 'foo') + self.assertEquals(self.smtp.password, 'evil') + self.assertEquals(self.smtp.hostname, 'spamrelay') + self.assertEquals(self.smtp.port, '31337') + self.assertEquals(self.smtp.fromaddr, fromaddr) + self.assertEquals(self.smtp.toaddrs, toaddrs) + self.assertEquals(self.smtp.msgtext, msgtext) + self.assert_(self.smtp.quitted) + self.assert_(self.smtp.closed) + + def test_send_failQuit(self): + self.mailer.smtp.fail_on_quit = True + try: + fromaddr = 'me@example.com' + toaddrs = ('you@example.com', 'him@example.com') + msgtext = 'Headers: headers\n\nbodybodybody\n-- \nsig\n' + self.mailer.send(fromaddr, toaddrs, msgtext) + self.assertEquals(self.smtp.fromaddr, fromaddr) + self.assertEquals(self.smtp.toaddrs, toaddrs) + self.assertEquals(self.smtp.msgtext, msgtext) + self.assert_(not self.smtp.quitted) + self.assert_(self.smtp.closed) + finally: + self.mailer.smtp.fail_on_quit = False + + +class TestSMTPMailerWithNoEHLO(TestSMTPMailer): + + def setUp(self, port=None): + + class SMTPWithNoEHLO(SMTP): + does_esmtp = False + + def __init__(myself, h, p): + myself.hostname = h + myself.port = p + myself.quitted = False + myself.closed = False + if type(p) == type(u""): + raise socket.error("Int or String expected") + self.smtp = myself + + def helo(self): + return (200, 'Hello, I am your stupid MTA mock') + + def ehlo(self): + return (502, 'I don\'t understand EHLO') + + + if port is None: + self.mailer = SMTPMailer() + else: + self.mailer = SMTPMailer(u'localhost', port) + self.mailer.smtp = SMTPWithNoEHLO + + def test_send_auth(self): + # This test requires ESMTP, which we're intentionally not enabling + # here, so pass. + pass + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestSMTPMailer)) + suite.addTest(unittest.makeSuite(TestSMTPMailerWithNoEHLO)) + return suite + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/sendmail/tests/test_queue.py zope3-3.5~bzr18/src/zope/sendmail/tests/test_queue.py --- zope3-3.4.0/src/zope/sendmail/tests/test_queue.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/test_queue.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,263 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Mail Delivery Tests + +Simple implementation of the MailDelivery, Mailers and MailEvents. + +$Id: test_queue.py 107776 2010-01-07 10:37:46Z kobold $ +""" + +import os.path +import shutil + +from tempfile import mkdtemp +from unittest import TestCase, TestSuite, makeSuite, main + +from zope.sendmail.queue import ConsoleApp +from zope.sendmail.tests.test_delivery import MaildirStub, LoggerStub, \ + BrokenMailerStub, SMTPResponseExceptionMailerStub, MailerStub + + +class TestQueueProcessorThread(TestCase): + + def setUp(self): + from zope.sendmail.queue import QueueProcessorThread + self.md = MaildirStub('/foo/bar/baz') + self.thread = QueueProcessorThread() + self.thread.setMaildir(self.md) + self.mailer = MailerStub() + self.thread.setMailer(self.mailer) + self.thread.log = LoggerStub() + self.dir = mkdtemp() + + def tearDown(self): + shutil.rmtree(self.dir) + + def test_parseMessage(self): + hdr = ('X-Zope-From: foo@example.com\n' + 'X-Zope-To: bar@example.com, baz@example.com\n') + msg = ('Header: value\n' + '\n' + 'Body\n') + f, t, m = self.thread._parseMessage(hdr + msg) + self.assertEquals(f, 'foo@example.com') + self.assertEquals(t, ('bar@example.com', 'baz@example.com')) + self.assertEquals(m, msg) + + def test_deliveration(self): + self.filename = os.path.join(self.dir, 'message') + temp = open(self.filename, "w+b") + temp.write('X-Zope-From: foo@example.com\n' + 'X-Zope-To: bar@example.com, baz@example.com\n' + 'Header: value\n\nBody\n') + temp.close() + self.md.files.append(self.filename) + self.thread.run(forever=False) + self.assertEquals(self.mailer.sent_messages, + [('foo@example.com', + ('bar@example.com', 'baz@example.com'), + 'Header: value\n\nBody\n')]) + self.failIf(os.path.exists(self.filename), 'File exists') + self.assertEquals(self.thread.log.infos, + [('Mail from %s to %s sent.', + ('foo@example.com', + 'bar@example.com, baz@example.com'), + {})]) + + def test_error_logging(self): + self.thread.setMailer(BrokenMailerStub()) + self.filename = os.path.join(self.dir, 'message') + temp = open(self.filename, "w+b") + temp.write('X-Zope-From: foo@example.com\n' + 'X-Zope-To: bar@example.com, baz@example.com\n' + 'Header: value\n\nBody\n') + temp.close() + self.md.files.append(self.filename) + self.thread.run(forever=False) + self.assertEquals(self.thread.log.errors, + [('Error while sending mail from %s to %s.', + ('foo@example.com', + 'bar@example.com, baz@example.com'), + {'exc_info': 1})]) + + def test_smtp_response_error_transient(self): + # Test a transient error + self.thread.setMailer(SMTPResponseExceptionMailerStub(451)) + self.filename = os.path.join(self.dir, 'message') + temp = open(self.filename, "w+b") + temp.write('X-Zope-From: foo@example.com\n' + 'X-Zope-To: bar@example.com, baz@example.com\n' + 'Header: value\n\nBody\n') + temp.close() + self.md.files.append(self.filename) + self.thread.run(forever=False) + + # File must remail were it was, so it will be retried + self.failUnless(os.path.exists(self.filename)) + self.assertEquals(self.thread.log.errors, + [('Error while sending mail from %s to %s.', + ('foo@example.com', + 'bar@example.com, baz@example.com'), + {'exc_info': 1})]) + + def test_smtp_response_error_permanent(self): + # Test a permanent error + self.thread.setMailer(SMTPResponseExceptionMailerStub(550)) + self.filename = os.path.join(self.dir, 'message') + temp = open(self.filename, "w+b") + temp.write('X-Zope-From: foo@example.com\n' + 'X-Zope-To: bar@example.com, baz@example.com\n' + 'Header: value\n\nBody\n') + temp.close() + self.md.files.append(self.filename) + self.thread.run(forever=False) + + # File must be moved aside + self.failIf(os.path.exists(self.filename)) + self.failUnless(os.path.exists(os.path.join(self.dir, + '.rejected-message'))) + self.assertEquals(self.thread.log.errors, + [('Discarding email from %s to %s due to a ' + 'permanent error: %s', + ('foo@example.com', + 'bar@example.com, baz@example.com', + "(550, 'Serious Error')"), {})]) + +test_ini = """[app:zope-sendmail] +interval = 33 +hostname = testhost +port = 2525 +username = Chris +password = Rossi +force_tls = False +no_tls = True +queue_path = hammer/dont/hurt/em +""" + +class TestConsoleApp(TestCase): + def setUp(self): + from zope.sendmail.delivery import QueuedMailDelivery + from zope.sendmail.maildir import Maildir + self.dir = mkdtemp() + self.queue_dir = os.path.join(self.dir, "queue") + self.delivery = QueuedMailDelivery(self.queue_dir) + self.maildir = Maildir(self.queue_dir, True) + self.mailer = MailerStub() + + def tearDown(self): + shutil.rmtree(self.dir) + + def test_args_processing(self): + # simplest case that works + cmdline = "zope-sendmail %s" % self.dir + app = ConsoleApp(cmdline.split(), verbose=False) + self.assertEquals("zope-sendmail", app.script_name) + self.assertFalse(app._error) + self.assertEquals(self.dir, app.queue_path) + self.assertFalse(app.daemon) + self.assertEquals(3, app.interval) + self.assertEquals("localhost", app.hostname) + self.assertEquals(25, app.port) + self.assertEquals(None, app.username) + self.assertEquals(None, app.password) + self.assertFalse(app.force_tls) + self.assertFalse(app.no_tls) + # simplest case that doesn't work + cmdline = "zope-sendmail" + app = ConsoleApp(cmdline.split(), verbose=False) + self.assertEquals("zope-sendmail", app.script_name) + self.assertTrue(app._error) + self.assertEquals(None, app.queue_path) + self.assertFalse(app.daemon) + self.assertEquals(3, app.interval) + self.assertEquals("localhost", app.hostname) + self.assertEquals(25, app.port) + self.assertEquals(None, app.username) + self.assertEquals(None, app.password) + self.assertFalse(app.force_tls) + self.assertFalse(app.no_tls) + # use (almost) all of the options + cmdline = "zope-sendmail --daemon --interval 7 --hostname foo --port 75 " \ + "--username chris --password rossi --force-tls " \ + "%s" % self.dir + app = ConsoleApp(cmdline.split(), verbose=False) + self.assertEquals("zope-sendmail", app.script_name) + self.assertFalse(app._error) + self.assertEquals(self.dir, app.queue_path) + self.assertTrue(app.daemon) + self.assertEquals(7, app.interval) + self.assertEquals("foo", app.hostname) + self.assertEquals(75, app.port) + self.assertEquals("chris", app.username) + self.assertEquals("rossi", app.password) + self.assertTrue(app.force_tls) + self.assertFalse(app.no_tls) + # test username without password + cmdline = "zope-sendmail --username chris %s" % self.dir + app = ConsoleApp(cmdline.split(), verbose=False) + self.assertTrue(app._error) + # test --tls and --no-tls together + cmdline = "zope-sendmail --tls --no-tls %s" % self.dir + app = ConsoleApp(cmdline.split(), verbose=False) + self.assertTrue(app._error) + # test force_tls and no_tls + comdline = "zope-sendmail --force-tls --no-tls %s" % self.dir + self.assertTrue(app._error) + + def test_ini_parse(self): + ini_path = os.path.join(self.dir, "zope-sendmail.ini") + f = open(ini_path, "w") + f.write(test_ini) + f.close() + # override most everything + cmdline = """zope-sendmail --config %s""" % ini_path + app = ConsoleApp(cmdline.split(), verbose=False) + self.assertEquals("zope-sendmail", app.script_name) + self.assertFalse(app._error) + self.assertEquals("hammer/dont/hurt/em", app.queue_path) + self.assertFalse(app.daemon) + self.assertEquals(33, app.interval) + self.assertEquals("testhost", app.hostname) + self.assertEquals(2525, app.port) + self.assertEquals("Chris", app.username) + self.assertEquals("Rossi", app.password) + self.assertFalse(app.force_tls) + self.assertTrue(app.no_tls) + # override nothing, make sure defaults come through + f = open(ini_path, "w") + f.write("[app:zope-sendmail]\n\nqueue_path=foo\n") + f.close() + cmdline = """zope-sendmail --config %s %s""" % (ini_path, self.dir) + app = ConsoleApp(cmdline.split(), verbose=False) + self.assertEquals("zope-sendmail", app.script_name) + self.assertFalse(app._error) + self.assertEquals(self.dir, app.queue_path) + self.assertFalse(app.daemon) + self.assertEquals(3, app.interval) + self.assertEquals("localhost", app.hostname) + self.assertEquals(25, app.port) + self.assertEquals(None, app.username) + self.assertEquals(None, app.password) + self.assertFalse(app.force_tls) + self.assertFalse(app.no_tls) + + +def test_suite(): + return TestSuite(( + makeSuite(TestQueueProcessorThread), + makeSuite(TestConsoleApp), + )) + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/zope/sendmail/tests/test_vocabulary.py zope3-3.5~bzr18/src/zope/sendmail/tests/test_vocabulary.py --- zope3-3.4.0/src/zope/sendmail/tests/test_vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/tests/test_vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +############################################################################## +# +# Copyright (c) 2006 Zope Corporation 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. +# +############################################################################## +"""Mail delivery names vocabulary test + +$Id: test_vocabulary.py 110390 2010-04-01 12:33:34Z mgedmin $ +""" +import unittest +from doctest import DocTestSuite +from zope.component.testing import setUp, tearDown + +def test_suite(): + return unittest.TestSuite([ + DocTestSuite('zope.sendmail.vocabulary', + setUp=setUp, tearDown=tearDown), + ]) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/sendmail/vocabulary.py zope3-3.5~bzr18/src/zope/sendmail/vocabulary.py --- zope3-3.4.0/src/zope/sendmail/vocabulary.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/vocabulary.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,57 @@ +############################################################################## +# +# Copyright (c) 2006 Zope Corporation 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. +# +############################################################################## +"""Mail vocabularies + +$Id: vocabulary.py 98165 2009-03-16 21:56:33Z nadako $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +from zope.interface import directlyProvides +from zope.schema.interfaces import IVocabularyFactory +from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm +from zope.sendmail.interfaces import IMailDelivery + +def MailDeliveryNames(context=None): + """Vocabulary with names of mail delivery utilities + + Let's provide a few stub utilities: + + >>> from zope.interface import implements + >>> class StubMailDelivery(object): + ... implements(IMailDelivery) + + >>> from zope.component import provideUtility + >>> for name in 'and now for something completely different'.split(): + ... provideUtility(StubMailDelivery(), name=name) + + Let's also provide another utility to verify that we only see mail + delivery utilities: + + >>> provideUtility(MailDeliveryNames, name='Mail Delivery Names') + + Let's see what's in the vocabulary: + + >>> vocabulary = MailDeliveryNames(None) + >>> names = [term.value for term in vocabulary] + >>> names.sort() + >>> print ' '.join(names) + and completely different for now something + """ + + utils = zope.component.getUtilitiesFor(IMailDelivery, context) + terms = [SimpleTerm(name) for name, util in utils] + return SimpleVocabulary(terms) + +directlyProvides(MailDeliveryNames, IVocabularyFactory) diff -Nru zope3-3.4.0/src/zope/sendmail/zcml.py zope3-3.5~bzr18/src/zope/sendmail/zcml.py --- zope3-3.4.0/src/zope/sendmail/zcml.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/sendmail/zcml.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,175 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""'mail' ZCML Namespaces Schemas + +$Id: zcml.py 107770 2010-01-07 07:11:43Z kobold $ +""" +__docformat__ = 'restructuredtext' + +from zope.component import queryUtility +from zope.component.zcml import handler +from zope.configuration.fields import Path +from zope.configuration.exceptions import ConfigurationError +from zope.interface import Interface +from zope.schema import TextLine, BytesLine, Int, Bool + +from zope.sendmail.delivery import QueuedMailDelivery, DirectMailDelivery +from zope.sendmail.interfaces import IMailer, IMailDelivery +from zope.sendmail.mailer import SMTPMailer +from zope.sendmail.queue import QueueProcessorThread + +try: + from zope.component.security import proxify + from zope.security.zcml import Permission +except ImportError: + SECURITY_SUPPORT = False + from zope.schema import TextLine as Permission +else: + SECURITY_SUPPORT = True + +def _assertPermission(permission, interfaces, component): + if not SECURITY_SUPPORT: + raise ConfigurationError("security proxied components are not " + "supported because zope.security is not available") + return proxify(component, provides=interfaces, permission=permission) + + +class IDeliveryDirective(Interface): + """This abstract directive describes a generic mail delivery utility + registration.""" + + name = TextLine( + title=u"Name", + description=u'Specifies the Delivery name of the mail utility. '\ + u'The default is "Mail".', + default=u"Mail", + required=False) + + mailer = TextLine( + title=u"Mailer", + description=u"Defines the mailer to be used for sending mail.", + required=True) + + permission = Permission( + title=u"Permission", + description=u"Defines the permission needed to use this service.", + required=False) + + +class IQueuedDeliveryDirective(IDeliveryDirective): + """This directive creates and registers a global queued mail utility. It + should be only called once during startup.""" + + queuePath = Path( + title=u"Queue Path", + description=u"Defines the path for the queue directory.", + required=True) + + processorThread = Bool( + title=u"Run Queue Processor Thread", + description=u"Indicates whether to run queue processor in a thread " + "in this process.", + required=False, + default=True) + + +def queuedDelivery(_context, queuePath, mailer, permission=None, name="Mail", + processorThread=True): + + def createQueuedDelivery(): + delivery = QueuedMailDelivery(queuePath) + if permission is not None: + delivery = _assertPermission(permission, IMailDelivery, delivery) + + handler('registerUtility', delivery, IMailDelivery, name) + + mailerObject = queryUtility(IMailer, mailer) + if mailerObject is None: + raise ConfigurationError("Mailer %r is not defined" %mailer) + + if processorThread: + thread = QueueProcessorThread() + thread.setMailer(mailerObject) + thread.setQueuePath(queuePath) + thread.start() + + _context.action( + discriminator = ('delivery', name), + callable = createQueuedDelivery, + args = () ) + +class IDirectDeliveryDirective(IDeliveryDirective): + """This directive creates and registers a global direct mail utility. It + should be only called once during startup.""" + +def directDelivery(_context, mailer, permission=None, name="Mail"): + + def createDirectDelivery(): + mailerObject = queryUtility(IMailer, mailer) + if mailerObject is None: + raise ConfigurationError("Mailer %r is not defined" %mailer) + + delivery = DirectMailDelivery(mailerObject) + if permission is not None: + delivery = _assertPermission(permission, IMailDelivery, delivery) + + handler('registerUtility', delivery, IMailDelivery, name) + + _context.action( + discriminator = ('utility', IMailDelivery, name), + callable = createDirectDelivery, + args = () ) + +class IMailerDirective(Interface): + """A generic directive registering a mailer for the mail utility.""" + + name = TextLine( + title=u"Name", + description=u"Name of the Mailer.", + required=True) + + +class ISMTPMailerDirective(IMailerDirective): + """Registers a new SMTP mailer.""" + + hostname = BytesLine( + title=u"Hostname", + description=u"Hostname of the SMTP host.", + default="localhost", + required=False) + + port = Int( + title=u"Port", + description=u"Port of the SMTP server.", + default=25, + required=False) + + username = TextLine( + title=u"Username", + description=u"A username for SMTP AUTH.", + required=False) + + password = TextLine( + title=u"Password", + description=u"A password for SMTP AUTH.", + required=False) + +def smtpMailer(_context, name, hostname="localhost", port="25", + username=None, password=None): + _context.action( + discriminator = ('utility', IMailer, name), + callable = handler, + args = ('registerUtility', + SMTPMailer(hostname, port, username, password), IMailer, name) + ) diff -Nru zope3-3.4.0/src/zope/server/adjustments.py zope3-3.5~bzr18/src/zope/server/adjustments.py --- zope3-3.4.0/src/zope/server/adjustments.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/adjustments.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,77 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Adjustments are tunable parameters. +""" +import socket + +from zope.server import maxsockets + + +class Adjustments(object): + """This class contains tunable communication parameters. + + You can either change default_adj to adjust parameters for + all sockets, or you can create a new instance of this class, + change its attributes, and pass it to the channel constructors. + """ + + # backlog is the argument to pass to socket.listen(). + backlog = 1024 + + # recv_bytes is the argument to pass to socket.recv(). + recv_bytes = 8192 + + # send_bytes is the number of bytes to send to socket.send(). + # Multiples of 9000 should avoid partly-filled packets, but don't + # set this larger than the TCP write buffer size. In Linux, + # /proc/sys/net/ipv4/tcp_wmem controls the minimum, default, and + # maximum sizes of TCP write buffers. + send_bytes = 9000 + + # copy_bytes is the number of bytes to copy from one file to another. + copy_bytes = 65536 + + # Create a tempfile if the pending output data gets larger + # than outbuf_overflow. With RAM so cheap, this probably + # ought to be set to the 16-32 MB range (circa 2001) for + # good performance with big transfers. The default is + # conservative. + outbuf_overflow = 1050000 + + # Create a tempfile if the data received gets larger + # than inbuf_overflow. + inbuf_overflow = 525000 + + # Stop accepting new connections if too many are already active. + connection_limit = maxsockets.max_select_sockets() - 3 # Safe + + # Minimum seconds between cleaning up inactive channels. + cleanup_interval = 300 + + # Maximum seconds to leave an inactive connection open. + channel_timeout = 900 + + # Boolean: turn off to not log premature client disconnects. + log_socket_errors = 1 + + # The socket options to set on receiving a connection. + # It is a list of (level, optname, value) tuples. + # TCP_NODELAY is probably good for Zope, since Zope buffers + # data itself. + socket_options = [ + (socket.SOL_TCP, socket.TCP_NODELAY, 1), + ] + + +default_adj = Adjustments() diff -Nru zope3-3.4.0/src/zope/server/buffers.py zope3-3.5~bzr18/src/zope/server/buffers.py --- zope3-3.4.0/src/zope/server/buffers.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/buffers.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,234 @@ +############################################################################## +# +# Copyright (c) 2001-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. +# +############################################################################## +"""Buffers +""" +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + + +# copy_bytes controls the size of temp. strings for shuffling data around. +COPY_BYTES = 1 << 18 # 256K + +# The maximum number of bytes to buffer in a simple string. +STRBUF_LIMIT = 8192 + + +class FileBasedBuffer(object): + + remain = 0 + + def __init__(self, file, from_buffer=None): + self.file = file + if from_buffer is not None: + from_file = from_buffer.getfile() + read_pos = from_file.tell() + from_file.seek(0) + while 1: + data = from_file.read(COPY_BYTES) + if not data: + break + file.write(data) + self.remain = int(file.tell() - read_pos) + from_file.seek(read_pos) + file.seek(read_pos) + + def __len__(self): + return self.remain + + def append(self, s): + file = self.file + read_pos = file.tell() + file.seek(0, 2) + file.write(s) + file.seek(read_pos) + self.remain = self.remain + len(s) + + def get(self, bytes=-1, skip=0): + file = self.file + if not skip: + read_pos = file.tell() + if bytes < 0: + # Read all + res = file.read() + else: + res = file.read(bytes) + if skip: + self.remain -= len(res) + else: + file.seek(read_pos) + return res + + def skip(self, bytes, allow_prune=0): + if self.remain < bytes: + raise ValueError("Can't skip %d bytes in buffer of %d bytes" % ( + bytes, self.remain)) + self.file.seek(bytes, 1) + self.remain = self.remain - bytes + + def newfile(self): + raise NotImplementedError() + + def prune(self): + file = self.file + if self.remain == 0: + read_pos = file.tell() + file.seek(0, 2) + sz = file.tell() + file.seek(read_pos) + if sz == 0: + # Nothing to prune. + return + nf = self.newfile() + while 1: + data = file.read(COPY_BYTES) + if not data: + break + nf.write(data) + self.file = nf + + def getfile(self): + return self.file + + + +class TempfileBasedBuffer(FileBasedBuffer): + + def __init__(self, from_buffer=None): + FileBasedBuffer.__init__(self, self.newfile(), from_buffer) + + def newfile(self): + from tempfile import TemporaryFile + return TemporaryFile('w+b') + + + +class StringIOBasedBuffer(FileBasedBuffer): + + def __init__(self, from_buffer=None): + if from_buffer is not None: + FileBasedBuffer.__init__(self, StringIO(), from_buffer) + else: + # Shortcut. :-) + self.file = StringIO() + + def newfile(self): + return StringIO() + + + +class OverflowableBuffer(object): + """ + This buffer implementation has four stages: + - No data + - String-based buffer + - StringIO-based buffer + - Temporary file storage + The first two stages are fastest for simple transfers. + """ + + overflowed = 0 + buf = None + strbuf = '' # String-based buffer. + + def __init__(self, overflow): + # overflow is the maximum to be stored in a StringIO buffer. + self.overflow = overflow + + def __len__(self): + buf = self.buf + if buf is not None: + return len(buf) + else: + return len(self.strbuf) + + def _create_buffer(self): + # print 'creating buffer' + strbuf = self.strbuf + if len(strbuf) >= self.overflow: + self._set_large_buffer() + else: + self._set_small_buffer() + buf = self.buf + if strbuf: + buf.append(self.strbuf) + self.strbuf = '' + return buf + + def _set_small_buffer(self): + self.buf = StringIOBasedBuffer(self.buf) + self.overflowed = 0 + + def _set_large_buffer(self): + self.buf = TempfileBasedBuffer(self.buf) + self.overflowed = 1 + + def append(self, s): + buf = self.buf + if buf is None: + strbuf = self.strbuf + if len(strbuf) + len(s) < STRBUF_LIMIT: + self.strbuf = strbuf + s + return + buf = self._create_buffer() + buf.append(s) + sz = len(buf) + if not self.overflowed: + if sz >= self.overflow: + self._set_large_buffer() + + def get(self, bytes=-1, skip=0): + buf = self.buf + if buf is None: + strbuf = self.strbuf + if not skip: + return strbuf + buf = self._create_buffer() + return buf.get(bytes, skip) + + def skip(self, bytes, allow_prune=0): + buf = self.buf + if buf is None: + strbuf = self.strbuf + if allow_prune and bytes == len(strbuf): + # We could slice instead of converting to + # a buffer, but that would eat up memory in + # large transfers. + self.strbuf = '' + return + buf = self._create_buffer() + buf.skip(bytes, allow_prune) + + def prune(self): + """ + A potentially expensive operation that removes all data + already retrieved from the buffer. + """ + buf = self.buf + if buf is None: + self.strbuf = '' + return + buf.prune() + if self.overflowed: + sz = len(buf) + if sz < self.overflow: + # Revert to a faster buffer. + self._set_small_buffer() + + def getfile(self): + buf = self.buf + if buf is None: + buf = self._create_buffer() + return buf.getfile() diff -Nru zope3-3.4.0/src/zope/server/dualmodechannel.py zope3-3.5~bzr18/src/zope/server/dualmodechannel.py --- zope3-3.4.0/src/zope/server/dualmodechannel.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/dualmodechannel.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,208 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Dual-mode channel +""" +import asyncore +import socket +from time import time + +from zope.server import trigger +from zope.server.adjustments import default_adj +from zope.server.buffers import OverflowableBuffer + + +# Create the main trigger if it doesn't exist yet. +the_trigger = trigger.trigger() + + +class DualModeChannel(asyncore.dispatcher): + """Channel that switches between asynchronous and synchronous mode. + + Call set_sync() before using a channel in a thread other than + the thread handling the main loop. + + Call set_async() to give the channel back to the thread handling + the main loop. + """ + + # will_close is set to True to close the socket. + will_close = False + + # boolean: async or sync mode + async_mode = True + + def __init__(self, conn, addr, adj=None): + self.addr = addr + if adj is None: + adj = default_adj + self.adj = adj + self.outbuf = OverflowableBuffer(adj.outbuf_overflow) + self.creation_time = time() + asyncore.dispatcher.__init__(self, conn) + + # + # ASYNCHRONOUS METHODS + # + + def handle_close(self): + self.close() + + def writable(self): + if not self.async_mode: + return 0 + return self.will_close or self.outbuf + + def handle_write(self): + if not self.async_mode: + return + if self.outbuf: + try: + self._flush_some() + except socket.error: + self.handle_comm_error() + elif self.will_close: + self.close() + + def readable(self): + if not self.async_mode: + return 0 + return not self.will_close + + def handle_read(self): + if not self.async_mode or self.will_close: + return + try: + data = self.recv(self.adj.recv_bytes) + except socket.error: + self.handle_comm_error() + return + self.received(data) + + def received(self, data): + """ + Override to receive data in async mode. + """ + pass + + def handle_comm_error(self): + """ + Designed for handling communication errors that occur + during asynchronous operations *only*. Probably should log + this, but in a different place. + """ + self.handle_error() + + def set_sync(self): + """Switches to synchronous mode. + + The main thread will stop calling received(). + """ + self.async_mode = False + + # + # SYNCHRONOUS METHODS + # + + def flush(self, block=True): + """Sends pending data. + + If block is set, this pauses the application. If it is turned + off, only the amount of data that can be sent without blocking + is sent. + """ + if not block: + while self._flush_some(): + pass + return + blocked = False + try: + while self.outbuf: + # We propagate errors to the application on purpose. + if not blocked: + self.socket.setblocking(1) + blocked = True + self._flush_some() + finally: + if blocked: + self.socket.setblocking(0) + + def set_async(self): + """Switches to asynchronous mode. + + The main thread will begin calling received() again. + """ + self.async_mode = True + self.pull_trigger() + + # + # METHODS USED IN BOTH MODES + # + + def write(self, data): + wrote = 0 + if isinstance(data, str): + if data: + self.outbuf.append(data) + wrote = len(data) + else: + for v in data: + if v: + self.outbuf.append(v) + wrote += len(v) + + while len(self.outbuf) >= self.adj.send_bytes: + # Send what we can without blocking. + # We propagate errors to the application on purpose + # (to stop the application if the connection closes). + if not self._flush_some(): + break + + return wrote + + def pull_trigger(self): + """Wakes up the main loop. + """ + the_trigger.pull_trigger() + + def _flush_some(self): + """Flushes data. + + Returns 1 if some data was sent.""" + outbuf = self.outbuf + if outbuf and self.connected: + chunk = outbuf.get(self.adj.send_bytes) + num_sent = self.send(chunk) + if num_sent: + outbuf.skip(num_sent, 1) + return 1 + return 0 + + def close_when_done(self): + # Flush all possible. + while self._flush_some(): + pass + self.will_close = True + if not self.async_mode: + # For safety, don't close the socket until the + # main thread calls handle_write(). + self.async_mode = True + self.pull_trigger() + + def close(self): + # Always close in asynchronous mode. If the connection is + # closed in a thread, the main loop can end up with a bad file + # descriptor. + assert self.async_mode + self.connected = False + asyncore.dispatcher.close(self) diff -Nru zope3-3.4.0/src/zope/server/fixedstreamreceiver.py zope3-3.5~bzr18/src/zope/server/fixedstreamreceiver.py --- zope3-3.4.0/src/zope/server/fixedstreamreceiver.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/fixedstreamreceiver.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,50 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Fixed Stream Receiver +""" + +from zope.server.interfaces import IStreamConsumer +from zope.interface import implements + + +class FixedStreamReceiver(object): + + implements(IStreamConsumer) + + # See IStreamConsumer + completed = 0 + + def __init__(self, cl, buf): + self.remain = cl + self.buf = buf + + def received(self, data): + 'See IStreamConsumer' + rm = self.remain + if rm < 1: + self.completed = 1 # Avoid any chance of spinning + return 0 + datalen = len(data) + if rm <= datalen: + self.buf.append(data[:rm]) + self.remain = 0 + self.completed = 1 + return rm + else: + self.buf.append(data) + self.remain -= datalen + return datalen + + def getfile(self): + return self.buf.getfile() diff -Nru zope3-3.4.0/src/zope/server/ftp/__init__.py zope3-3.5~bzr18/src/zope/server/ftp/__init__.py --- zope3-3.4.0/src/zope/server/ftp/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/server/ftp/logger.py zope3-3.5~bzr18/src/zope/server/ftp/logger.py --- zope3-3.4.0/src/zope/server/ftp/logger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/logger.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,32 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Common FTP Activity Logger +""" +import time + +from zope.server.http.commonaccesslogger import CommonAccessLogger + +class CommonFTPActivityLogger(CommonAccessLogger): + """Outputs hits in common HTTP log format.""" + + def log(self, task): + """Receives a completed task and logs it in the common log format.""" + now = time.time() + message = ' - %s [%s] "%s %s"' % (task.channel.username, + self.log_date_string(now), + task.m_name[4:].upper(), + task.channel.cwd, + ) + + self.output.logRequest(task.channel.addr[0], message) diff -Nru zope3-3.4.0/src/zope/server/ftp/publisher.py zope3-3.5~bzr18/src/zope/server/ftp/publisher.py --- zope3-3.4.0/src/zope/server/ftp/publisher.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/publisher.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,147 @@ +############################################################################## +# +# 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 Publisher-based FTP Server + +This FTP server uses the Zope 3 Publisher to execute commands. +""" +import posixpath + +from cStringIO import StringIO + +from zope.server.interfaces.ftp import IFileSystem +from zope.server.interfaces.ftp import IFileSystemAccess + +from zope.server.ftp.server import FTPServer +from zope.publisher.publish import publish + +from zope.interface import implements + +class PublisherFileSystem(object): + """Generic Publisher FileSystem implementation.""" + + implements(IFileSystem) + + def __init__ (self, credentials, request_factory): + self.credentials = credentials + self.request_factory = request_factory + + def type(self, path): + if path == '/': + return 'd' + + return self._execute(path, 'type') + + def names(self, path, filter=None): + return self._execute(path, 'names', split=False, filter=filter) + + def ls(self, path, filter=None): + return self._execute(path, 'ls', split=False, filter=filter) + + def readfile(self, path, outstream, start=0, end=None): + return self._execute(path, 'readfile', + outstream=outstream, start=start, end=end) + + def lsinfo(self, path): + return self._execute(path, 'lsinfo') + + def mtime(self, path): + return self._execute(path, 'mtime') + + def size(self, path): + return self._execute(path, 'size') + + def mkdir(self, path): + return self._execute(path, 'mkdir') + + def remove(self, path): + return self._execute(path, 'remove') + + def rmdir(self, path): + return self._execute(path, 'rmdir') + + def rename(self, old, new): + 'See IWriteFileSystem' + old = self._translate(old) + new = self._translate(new) + path0, old = posixpath.split(old) + path1, new = posixpath.split(new) + assert path0 == path1 + return self._execute(path0, 'rename', split=False, old=old, new=new) + + def writefile(self, path, instream, start=None, end=None, append=False): + 'See IWriteFileSystem' + return self._execute( + path, 'writefile', + instream=instream, start=start, end=end, append=append) + + def writable(self, path): + 'See IWriteFileSystem' + return self._execute(path, 'writable') + + def _execute(self, path, command, split=True, **kw): + env = {} + env.update(kw) + env['command'] = command + + path = self._translate(path) + + if split: + env['path'], env['name'] = posixpath.split(path) + else: + env['path'] = path + + env['credentials'] = self.credentials + request = self.request_factory(StringIO(''), env) + + # Note that publish() calls close() on request, which deletes the + # response from the request, so that we need to keep track of it. + # agroszer: 2008.feb.1.: currently the above seems not to be true + # request will KEEP the response on close() + # even more if a retry occurs in the publisher, + # the response will be LOST, so we must accept the returned request + request = publish(request) + return request.response.getResult() + + def _translate (self, path): + # Normalize + path = posixpath.normpath(path) + if path.startswith('..'): + # Someone is trying to get lower than the permitted root. + # We just ignore it. + path = '/' + return path + + +class PublisherFTPServer(FTPServer): + """Generic FTP Server""" + + def __init__(self, request_factory, name, ip, port, *args, **kw): + fs_access = PublisherFileSystemAccess(request_factory) + super(PublisherFTPServer, self).__init__(ip, port, fs_access, + *args, **kw) + +class PublisherFileSystemAccess(object): + + implements(IFileSystemAccess) + + def __init__(self, request_factory): + self.request_factory = request_factory + + def authenticate(self, credentials): + # We can't actually do any authentication initially, as the + # user may not be defined at the root. + pass + + def open(self, credentials): + return PublisherFileSystem(credentials, self.request_factory) diff -Nru zope3-3.4.0/src/zope/server/ftp/README.txt zope3-3.5~bzr18/src/zope/server/ftp/README.txt --- zope3-3.4.0/src/zope/server/ftp/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ +FTP Framework + + This file contains documentation on the FTP server + framework. + + The core server is implemented in server.py. This relies on a + file-system abstraction, defined in zope.server.interfaces.py. + + The publisher module provides the connection to the object + publsihing system by providing a file-system implementation that + delegates file-system operations to objects through the publisher. diff -Nru zope3-3.4.0/src/zope/server/ftp/server.py zope3-3.5~bzr18/src/zope/server/ftp/server.py --- zope3-3.4.0/src/zope/server/ftp/server.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/server.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,957 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""FTP Server +""" +import asyncore +import posixpath +import socket +from datetime import date, timedelta +from getopt import getopt, GetoptError + +from zope.security.interfaces import Unauthorized +from zope.interface import implements +from zope.server.buffers import OverflowableBuffer +from zope.server.interfaces import ITask +from zope.server.interfaces.ftp import IFileSystemAccess +from zope.server.interfaces.ftp import IFTPCommandHandler +from zope.server.linereceiver.lineserverchannel import LineServerChannel +from zope.server.serverbase import ServerBase +from zope.server.dualmodechannel import DualModeChannel, the_trigger + +status_messages = { + 'OPEN_DATA_CONN' : '150 Opening %s mode data connection for file list', + 'OPEN_CONN' : '150 Opening %s connection for %s', + 'SUCCESS_200' : '200 %s command successful.', + 'TYPE_SET_OK' : '200 Type set to %s.', + 'STRU_OK' : '200 STRU F Ok.', + 'MODE_OK' : '200 MODE S Ok.', + 'FILE_DATE' : '213 %4d%02d%02d%02d%02d%02d', + 'FILE_SIZE' : '213 %d Bytes', + 'HELP_START' : '214-The following commands are recognized', + 'HELP_END' : '214 Help done.', + 'SERVER_TYPE' : '215 %s Type: %s', + 'SERVER_READY' : '220 %s FTP server (Zope Async/Thread V0.1) ready.', + 'GOODBYE' : '221 Goodbye.', + 'SUCCESS_226' : '226 %s command successful.', + 'TRANS_SUCCESS' : '226 Transfer successful.', + 'PASV_MODE_MSG' : '227 Entering Passive Mode (%s,%d,%d)', + 'LOGIN_SUCCESS' : '230 Login Successful.', + 'SUCCESS_250' : '250 %s command successful.', + 'SUCCESS_257' : '257 %s command successful.', + 'ALREADY_CURRENT' : '257 "%s" is the current directory.', + 'PASS_REQUIRED' : '331 Password required', + 'RESTART_TRANSFER' : '350 Restarting at %d. Send STORE or ' + 'RETRIEVE to initiate transfer.', + 'READY_FOR_DEST' : '350 File exists, ready for destination.', + 'NO_DATA_CONN' : "425 Can't build data connection", + 'TRANSFER_ABORTED' : '426 Connection closed; transfer aborted.', + 'CMD_UNKNOWN' : "500 '%s': command not understood.", + 'INTERNAL_ERROR' : "500 Internal error: %s", + 'ERR_ARGS' : '500 Bad command arguments', + 'MODE_UNKOWN' : '502 Unimplemented MODE type', + 'WRONG_BYTE_SIZE' : '504 Byte size must be 8', + 'STRU_UNKNOWN' : '504 Unimplemented STRU type', + 'NOT_AUTH' : "530 You are not authorized to perform the " + "'%s' command", + 'LOGIN_REQUIRED' : '530 Please log in with USER and PASS', + 'LOGIN_MISMATCH' : '530 The username and password do not match.', + 'ERR_NO_LIST' : '550 Could not list directory or file: %s', + 'ERR_NO_DIR' : '550 "%s": No such directory.', + 'ERR_NO_FILE' : '550 "%s": No such file.', + 'ERR_NO_DIR_FILE' : '550 "%s": No such file or directory.', + 'ERR_IS_NOT_FILE' : '550 "%s": Is not a file', + 'ERR_CREATE_FILE' : '550 Error creating file.', + 'ERR_CREATE_DIR' : '550 Error creating directory: %s', + 'ERR_DELETE_FILE' : '550 Error deleting file: %s', + 'ERR_DELETE_DIR' : '550 Error removing directory: %s', + 'ERR_OPEN_READ' : '553 Could not open file for reading: %s', + 'ERR_OPEN_WRITE' : '553 Could not open file for writing: %s', + 'ERR_IO' : '553 I/O Error: %s', + 'ERR_RENAME' : '560 Could not rename "%s" to "%s": %s', + 'ERR_RNFR_SOURCE' : '560 No source filename specify. Call RNFR first.', + } + +class FTPServerChannel(LineServerChannel): + """The FTP Server Channel represents a connection to a particular + client. We can therefore store information here.""" + + implements(IFTPCommandHandler) + + + # List of commands that are always available + special_commands = ( + 'cmd_quit', 'cmd_type', 'cmd_noop', 'cmd_user', 'cmd_pass') + + # These are the commands that are accessing the filesystem. + # Since this could be also potentially a longer process, these commands + # are also the ones that are executed in a different thread. + thread_commands = ( + 'cmd_appe', 'cmd_cdup', 'cmd_cwd', 'cmd_dele', + 'cmd_list', 'cmd_nlst', 'cmd_mdtm', 'cmd_mkd', + 'cmd_pass', 'cmd_retr', 'cmd_rmd', 'cmd_rnfr', + 'cmd_rnto', 'cmd_size', 'cmd_stor', 'cmd_stru') + + # Define the status messages + status_messages = status_messages + + # Define the type of directory listing this server is returning + system = ('UNIX', 'L8') + + # comply with (possibly troublesome) RFC959 requirements + # This is necessary to correctly run an active data connection + # through a firewall that triggers on the source port (expected + # to be 'L-1', or 20 in the normal case). + bind_local_minus_one = 0 + + restart_position = 0 + + type_map = {'a':'ASCII', 'i':'Binary', 'e':'EBCDIC', 'l':'Binary'} + + type_mode_map = {'a':'t', 'i':'b', 'e':'b', 'l':'b'} + + + def __init__(self, server, conn, addr, adj=None): + super(FTPServerChannel, self).__init__(server, conn, addr, adj) + + self.port_addr = None # The client's PORT address + self.passive_listener = None # The PASV listener + self.client_dc = None # The data connection + + self.transfer_mode = 'a' # Have to default to ASCII :-| + self.passive_mode = 0 + self.cwd = '/' + self._rnfr = None + + self.username = '' + self.credentials = None + + self.reply('SERVER_READY', self.server.server_name) + + + def _getFileSystem(self): + """Open the filesystem using the current credentials.""" + return self.server.fs_access.open(self.credentials) + + + def cmd_abor(self, args): + 'See IFTPCommandHandler' + assert self.async_mode + self.reply('TRANSFER_ABORTED') + self.abortPassive() + self.abortData() + + + def cmd_appe (self, args): + 'See IFTPCommandHandler' + return self.cmd_stor(args, 'a') + + + def cmd_cdup(self, args): + 'See IFTPCommandHandler' + path = self._generatePath('../') + if self._getFileSystem().type(path): + self.cwd = path + self.reply('SUCCESS_250', 'CDUP') + else: + self.reply('ERR_NO_FILE', path) + + + def cmd_cwd(self, args): + 'See IFTPCommandHandler' + path = self._generatePath(args) + if self._getFileSystem().type(path) == 'd': + self.cwd = path + self.reply('SUCCESS_250', 'CWD') + else: + self.reply('ERR_NO_DIR', path) + + + def cmd_dele(self, args): + 'See IFTPCommandHandler' + if not args: + self.reply('ERR_ARGS') + return + path = self._generatePath(args) + + try: + self._getFileSystem().remove(path) + except OSError, err: + self.reply('ERR_DELETE_FILE', str(err)) + else: + self.reply('SUCCESS_250', 'DELE') + + + def cmd_help(self, args): + 'See IFTPCommandHandler' + self.reply('HELP_START') + self.write('Help goes here somewhen.\r\n') + self.reply('HELP_END') + + + def cmd_list(self, args, long=1): + 'See IFTPCommandHandler' + opts = () + if args.strip().startswith('-'): + try: + opts, args = getopt(args.split(), 'Llad') + except GetoptError: + self.reply('ERR_ARGS') + return + if len(args) > 1: + self.reply('ERR_ARGS') + return + args = args and args[0] or '' + + fs = self._getFileSystem() + path = self._generatePath(args) + if not fs.type(path): + self.reply('ERR_NO_DIR_FILE', path) + return + args = args.split() + try: + s = self.getList( + args, long, + directory=bool([opt for opt in opts if opt[0]=='-d']) + ) + except OSError, err: + self.reply('ERR_NO_LIST', str(err)) + return + ok_reply = ('OPEN_DATA_CONN', self.type_map[self.transfer_mode]) + cdc = RETRChannel(self, ok_reply) + try: + cdc.write(s) + cdc.close_when_done() + except OSError, err: + self.reply('ERR_NO_LIST', str(err)) + cdc.reported = True + cdc.close_when_done() + + def getList(self, args, long=0, directory=0): + # we need to scan the command line for arguments to '/bin/ls'... + fs = self._getFileSystem() + path_args = [] + for arg in args: + if arg[0] != '-': + path_args.append (arg) + else: + # ignore arguments + pass + if len(path_args) < 1: + path = '.' + else: + path = path_args[0] + + path = self._generatePath(path) + + if fs.type(path) == 'd' and not directory: + if long: + file_list = map(ls, fs.ls(path)) + else: + file_list = fs.names(path) + else: + if long: + file_list = [ls(fs.lsinfo(path))] + else: + file_list = [posixpath.split(path)[1]] + + return '\r\n'.join(file_list) + '\r\n' + + + def cmd_mdtm(self, args): + 'See IFTPCommandHandler' + fs = self._getFileSystem() + # We simply do not understand this non-standard extension to MDTM + if len(args.split()) > 1: + self.reply('ERR_ARGS') + return + path = self._generatePath(args) + + if fs.type(path) != 'f': + self.reply('ERR_IS_NOT_FILE', path) + else: + mtime = fs.mtime(path) + if mtime is not None: + mtime = (mtime.year, mtime.month, mtime.day, + mtime.hour, mtime. minute, mtime.second) + else: + mtime = 0, 0, 0, 0, 0, 0 + + self.reply('FILE_DATE', mtime) + + + def cmd_mkd(self, args): + 'See IFTPCommandHandler' + if not args: + self.reply('ERR_ARGS') + return + path = self._generatePath(args) + try: + self._getFileSystem().mkdir(path) + except OSError, err: + self.reply('ERR_CREATE_DIR', str(err)) + else: + self.reply('SUCCESS_257', 'MKD') + + + def cmd_mode(self, args): + 'See IFTPCommandHandler' + if len(args) == 1 and args in 'sS': + self.reply('MODE_OK') + else: + self.reply('MODE_UNKNOWN') + + + def cmd_nlst(self, args): + 'See IFTPCommandHandler' + self.cmd_list(args, 0) + + + def cmd_noop(self, args): + 'See IFTPCommandHandler' + self.reply('SUCCESS_200', 'NOOP') + + + def cmd_pass(self, args): + 'See IFTPCommandHandler' + self.authenticated = 0 + password = args + credentials = (self.username, password) + try: + self.server.fs_access.authenticate(credentials) + except Unauthorized: + self.reply('LOGIN_MISMATCH') + self.close_when_done() + else: + self.credentials = credentials + self.authenticated = 1 + self.reply('LOGIN_SUCCESS') + + + def cmd_pasv(self, args): + 'See IFTPCommandHandler' + assert self.async_mode + # Kill any existing passive listener first. + self.abortPassive() + local_addr = self.getsockname()[0] + self.passive_listener = PassiveListener(self, local_addr) + port = self.passive_listener.port + self.reply('PASV_MODE_MSG', (','.join(local_addr.split('.')), + port/256, + port%256 ) ) + + + def cmd_port(self, args): + 'See IFTPCommandHandler' + info = args.split(',') + ip = '.'.join(info[:4]) + port = int(info[4])*256 + int(info[5]) + # how many data connections at a time? + # I'm assuming one for now... + # TODO: we should (optionally) verify that the + # ip number belongs to the client. [wu-ftpd does this?] + self.port_addr = (ip, port) + self.reply('SUCCESS_200', 'PORT') + + + def cmd_pwd(self, args): + 'See IFTPCommandHandler' + self.reply('ALREADY_CURRENT', self.cwd) + + + def cmd_quit(self, args): + 'See IFTPCommandHandler' + self.reply('GOODBYE') + self.close_when_done() + + + def cmd_retr(self, args): + 'See IFTPCommandHandler' + fs = self._getFileSystem() + if not args: + self.reply('CMD_UNKNOWN', 'RETR') + path = self._generatePath(args) + + if not (fs.type(path) == 'f'): + self.reply('ERR_IS_NOT_FILE', path) + return + + start = 0 + if self.restart_position: + start = self.restart_position + self.restart_position = 0 + + ok_reply = 'OPEN_CONN', (self.type_map[self.transfer_mode], path) + cdc = RETRChannel(self, ok_reply) + outstream = ApplicationOutputStream(cdc) + + try: + fs.readfile(path, outstream, start) + cdc.close_when_done() + except OSError, err: + self.reply('ERR_OPEN_READ', str(err)) + cdc.reported = True + cdc.close_when_done() + except IOError, err: + self.reply('ERR_IO', str(err)) + cdc.reported = True + cdc.close_when_done() + + + def cmd_rest(self, args): + 'See IFTPCommandHandler' + try: + pos = int(args) + except ValueError: + self.reply('ERR_ARGS') + return + self.restart_position = pos + self.reply('RESTART_TRANSFER', pos) + + + def cmd_rmd(self, args): + 'See IFTPCommandHandler' + if not args: + self.reply('ERR_ARGS') + return + path = self._generatePath(args) + try: + self._getFileSystem().rmdir(path) + except OSError, err: + self.reply('ERR_DELETE_DIR', str(err)) + else: + self.reply('SUCCESS_250', 'RMD') + + + def cmd_rnfr(self, args): + 'See IFTPCommandHandler' + path = self._generatePath(args) + if self._getFileSystem().type(path): + self._rnfr = path + self.reply('READY_FOR_DEST') + else: + self.reply('ERR_NO_FILE', path) + + + def cmd_rnto(self, args): + 'See IFTPCommandHandler' + path = self._generatePath(args) + if self._rnfr is None: + self.reply('ERR_RENAME') + try: + self._getFileSystem().rename(self._rnfr, path) + except OSError, err: + self.reply('ERR_RENAME', (self._rnfr, path, str(err))) + else: + self.reply('SUCCESS_250', 'RNTO') + self._rnfr = None + + + def cmd_size(self, args): + 'See IFTPCommandHandler' + path = self._generatePath(args) + fs = self._getFileSystem() + if fs.type(path) != 'f': + self.reply('ERR_NO_FILE', path) + else: + self.reply('FILE_SIZE', fs.size(path)) + + + def cmd_stor(self, args, write_mode='w'): + 'See IFTPCommandHandler' + if not args: + self.reply('ERR_ARGS') + return + path = self._generatePath(args) + + start = 0 + if self.restart_position: + self.start = self.restart_position + mode = write_mode + self.type_mode_map[self.transfer_mode] + + if not self._getFileSystem().writable(path): + self.reply('ERR_OPEN_WRITE', "Can't write file") + return + + cdc = STORChannel(self, (path, mode, start)) + self.syncConnectData(cdc) + self.reply('OPEN_CONN', (self.type_map[self.transfer_mode], path)) + + + def finishSTOR(self, buffer, (path, mode, start)): + """Called by STORChannel when the client has sent all data.""" + assert not self.async_mode + try: + infile = buffer.getfile() + infile.seek(0) + self._getFileSystem().writefile(path, infile, start, + append=(mode[0]=='a')) + except OSError, err: + self.reply('ERR_OPEN_WRITE', str(err)) + except IOError, err: + self.reply('ERR_IO', str(err)) + except: + self.exception() + else: + self.reply('TRANS_SUCCESS') + + + def cmd_stru(self, args): + 'See IFTPCommandHandler' + if len(args) == 1 and args in 'fF': + self.reply('STRU_OK') + else: + self.reply('STRU_UNKNOWN') + + + def cmd_syst(self, args): + 'See IFTPCommandHandler' + self.reply('SERVER_TYPE', self.system) + + + def cmd_type(self, args): + 'See IFTPCommandHandler' + # ascii, ebcdic, image, local + args = args.split() + t = args[0].lower() + # no support for EBCDIC + # if t not in ['a','e','i','l']: + if t not in ['a','i','l']: + self.reply('ERR_ARGS') + elif t == 'l' and (len(args) > 2 and args[2] != '8'): + self.reply('WRONG_BYTE_SIZE') + else: + self.transfer_mode = t + self.reply('TYPE_SET_OK', self.type_map[t]) + + + def cmd_user(self, args): + 'See IFTPCommandHandler' + self.authenticated = 0 + if len(args) > 1: + self.username = args + self.reply('PASS_REQUIRED') + else: + self.reply('ERR_ARGS') + + ############################################################ + + def _generatePath(self, args): + """Convert relative paths to absolute paths.""" + # We use posixpath even on non-Posix platforms because we don't want + # slashes converted to backslashes. + path = posixpath.join(self.cwd, args) + return posixpath.normpath(path) + + def syncConnectData(self, cdc): + """Calls asyncConnectData in the asynchronous thread.""" + the_trigger.pull_trigger(lambda: self.asyncConnectData(cdc)) + + def asyncConnectData(self, cdc): + """Starts connecting the data channel. + + This is a little complicated because the data connection might + be established already (in passive mode) or might be + established in the near future (in port or passive mode.) If + the connection has already been established, + self.passive_listener already has a socket and is waiting for + a call to connectData(). If the connection has not been + established in passive mode, the passive listener will + remember the data channel and send it when it's ready. In port + mode, this method tells the data connection to connect. + """ + self.abortData() + self.client_dc = cdc + if self.passive_listener is not None: + # Connect via PASV + self.passive_listener.connectData(cdc) + if self.port_addr: + # Connect via PORT + a = self.port_addr + self.port_addr = None + cdc.connectPort(a) + + def connectedPassive(self): + """Accepted a passive connection.""" + self.passive_listener = None + + def abortPassive(self): + """Close the passive listener.""" + if self.passive_listener is not None: + self.passive_listener.abort() + self.passive_listener = None + + def abortData(self): + """Close the data connection.""" + if self.client_dc is not None: + self.client_dc.abort() + self.client_dc = None + + def closedData(self): + self.client_dc = None + + def close(self): + # Make sure the passive listener and active client DC get closed. + self.abortPassive() + self.abortData() + LineServerChannel.close(self) + + + +def ls(ls_info): + """Formats a directory entry similarly to the 'ls' command. + """ + + info = { + 'owner_name': 'na', + 'owner_readable': True, + 'owner_writable': True, + 'group_name': "na", + 'group_readable': True, + 'group_writable': True, + 'other_readable': False, + 'other_writable': False, + 'nlinks': 1, + 'size': 0, + } + + if ls_info['type'] == 'd': + info['owner_executable'] = True + info['group_executable'] = True + info['other_executable'] = True + else: + info['owner_executable'] = False + info['group_executable'] = False + info['other_executable'] = False + + info.update(ls_info) + + mtime = info.get('mtime') + if mtime is not None: + if date.today() - mtime.date() > timedelta(days=180): + mtime = mtime.strftime('%b %d %Y') + else: + mtime = mtime.strftime('%b %d %H:%M') + else: + mtime = "Jan 02 0000" + + return "%s%s%s%s%s%s%s%s%s%s %3d %-8s %-8s %8d %s %s" % ( + info['type'] == 'd' and 'd' or '-', + info['owner_readable'] and 'r' or '-', + info['owner_writable'] and 'w' or '-', + info['owner_executable'] and 'x' or '-', + info['group_readable'] and 'r' or '-', + info['group_writable'] and 'w' or '-', + info['group_executable'] and 'x' or '-', + info['other_readable'] and 'r' or '-', + info['other_writable'] and 'w' or '-', + info['other_executable'] and 'x' or '-', + info['nlinks'], + info['owner_name'], + info['group_name'], + info['size'], + mtime, + info['name'], + ) + + +class PassiveListener(asyncore.dispatcher): + """This socket accepts a data connection, used when the server has + been placed in passive mode. Although the RFC implies that we + ought to be able to use the same listener over and over again, + this presents a problem: how do we shut it off, so that we are + accepting connections only when we expect them? [we can't] + + wuftpd, and probably all the other servers, solve this by + allowing only one connection to hit this listener. They then + close it. Any subsequent data-connection command will then try + for the default port on the client side [which is of course + never there]. So the 'always-send-PORT/PASV' behavior seems + required. + + Another note: wuftpd will also be listening on the channel as + soon as the PASV command is sent. It does not wait for a data + command first. + """ + + def __init__ (self, control_channel, local_addr): + asyncore.dispatcher.__init__ (self) + self.control_channel = control_channel + self.accepted = None # The accepted socket address + self.client_dc = None # The data connection to accept the socket + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.closed = False + # bind to an address on the interface where the + # control connection is connected. + self.bind((local_addr, 0)) + self.port = self.getsockname()[1] + self.listen(1) + + def log (self, *ignore): + pass + + def abort(self): + """Abort the passive listener.""" + if not self.closed: + self.closed = True + self.close() + if self.accepted is not None: + self.accepted.close() + + def handle_accept (self): + """Accept a connection from the client. + + For some reason, sometimes accept() returns None instead of a + socket. This code ignores that case. + """ + v = self.accept() + if v is None: + return + self.accepted, addr = v + if self.accepted is None: + return + self.accepted.setblocking(0) + self.closed = True + self.close() + if self.client_dc is not None: + self.connectData(self.client_dc) + + def connectData(self, cdc): + """Sends the connection to the data channel. + + If the connection has not yet been made, sends the connection + when it becomes available. + """ + if self.accepted is not None: + cdc.set_socket(self.accepted) + # Note that this method will be called twice, once by the + # control channel, and once by handle_accept, and the two + # calls may come in either order. If handle_accept calls + # first, we don't want to call set_socket() on the data + # connection twice, so set self.accepted = None to keep a + # record that the data connection already has the socket. + self.accepted = None + self.control_channel.connectedPassive() + else: + self.client_dc = cdc + + +class FTPDataChannel(DualModeChannel): + """Base class for FTP data connections. + + Note that data channels are always in async mode. + """ + + def __init__ (self, control_channel): + self.control_channel = control_channel + self.reported = False + self.closed = False + DualModeChannel.__init__(self, None, None, control_channel.adj) + + def connectPort(self, client_addr): + """Connect to a port on the client""" + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + #if bind_local_minus_one: + # self.bind(('', self.control_channel.server.port - 1)) + try: + self.connect(client_addr) + except socket.error: + self.report('NO_DATA_CONN') + + def abort(self): + """Abort the data connection without reporting.""" + self.reported = True + if not self.closed: + self.closed = True + self.close() + + def report(self, *reply_args): + """Reports the result of the data transfer.""" + self.reported = True + if self.control_channel is not None: + self.control_channel.reply(*reply_args) + + def reportDefault(self): + """Provide a default report on close.""" + pass + + def close(self): + """Notifies the control channel when the data connection closes.""" + c = self.control_channel + try: + if c is not None and c.connected and not self.reported: + self.reportDefault() + finally: + self.control_channel = None + DualModeChannel.close(self) + if c is not None: + c.closedData() + + +class STORChannel(FTPDataChannel): + """Channel for uploading one file from client to server""" + + complete_transfer = 0 + _fileno = None # provide a default for asyncore.dispatcher._fileno + + def __init__ (self, control_channel, finish_args): + self.finish_args = finish_args + self.inbuf = OverflowableBuffer(control_channel.adj.inbuf_overflow) + FTPDataChannel.__init__(self, control_channel) + # Note that this channel starts in async mode. + + def writable (self): + return 0 + + def handle_connect (self): + pass + + def received (self, data): + if data: + self.inbuf.append(data) + + def handle_close (self): + """Client closed, indicating EOF.""" + c = self.control_channel + task = FinishSTORTask(c, self.inbuf, self.finish_args) + self.complete_transfer = 1 + self.close() + c.queue_task(task) + + def reportDefault(self): + if not self.complete_transfer: + self.report('TRANSFER_ABORTED') + # else the transfer completed and FinishSTORTask will + # provide a complete reply through finishSTOR(). + + +class FinishSTORTask(object): + """Calls control_channel.finishSTOR() in an application thread. + + This task executes after the client has finished uploading. + """ + + implements(ITask) + + def __init__(self, control_channel, inbuf, finish_args): + self.control_channel = control_channel + self.inbuf = inbuf + self.finish_args = finish_args + + def service(self): + """Called to execute the task. + """ + close_on_finish = 0 + c = self.control_channel + try: + try: + c.finishSTOR(self.inbuf, self.finish_args) + except socket.error: + close_on_finish = 1 + if c.adj.log_socket_errors: + raise + finally: + if close_on_finish: + c.close_when_done() + + def cancel(self): + 'See ITask' + self.control_channel.close_when_done() + + def defer(self): + 'See ITask' + pass + + +class RETRChannel(FTPDataChannel): + """Channel for downloading one file from server to client + + Also used for directory listings. + """ + + opened = 0 + _fileno = None # provide a default for asyncore.dispatcher._fileno + + def __init__ (self, control_channel, ok_reply_args): + self.ok_reply_args = ok_reply_args + FTPDataChannel.__init__(self, control_channel) + + def _open(self): + """Signal the client to open the connection.""" + self.opened = 1 + self.control_channel.reply(*self.ok_reply_args) + self.control_channel.asyncConnectData(self) + + def write(self, data): + if self.control_channel is None: + raise IOError('Client FTP connection closed') + if not self.opened: + self._open() + return FTPDataChannel.write(self, data) + + def readable(self): + return not self.connected + + def handle_read(self): + # This may be called upon making the connection. + try: + self.recv(1) + except socket.error: + # The connection failed. + self.report('NO_DATA_CONN') + self.close() + + def handle_connect(self): + pass + + def handle_comm_error(self): + self.report('TRANSFER_ABORTED') + self.close() + + def reportDefault(self): + if not len(self.outbuf): + # All data transferred + if not self.opened: + # Zero-length file + self._open() + self.report('TRANS_SUCCESS') + else: + # Not all data transferred + self.report('TRANSFER_ABORTED') + + +class ApplicationOutputStream(object): + """Provide stream output to RETRChannel. + + Maps close() to close_when_done(). + """ + + def __init__(self, retr_channel): + self.write = retr_channel.write + self.flush = retr_channel.flush + self.close = retr_channel.close_when_done + + +class FTPServer(ServerBase): + """Generic FTP Server""" + + channel_class = FTPServerChannel + SERVER_IDENT = 'zope.server.ftp' + + + def __init__(self, ip, port, fs_access, *args, **kw): + + assert IFileSystemAccess.providedBy(fs_access) + self.fs_access = fs_access + + super(FTPServer, self).__init__(ip, port, *args, **kw) diff -Nru zope3-3.4.0/src/zope/server/ftp/tests/demofs.py zope3-3.5~bzr18/src/zope/server/ftp/tests/demofs.py --- zope3-3.4.0/src/zope/server/ftp/tests/demofs.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/tests/demofs.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,308 @@ +############################################################################## +# 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. +############################################################################## +"""Demo file-system implementation, for testing +""" +import posixpath +from zope.security.interfaces import Unauthorized +from zope.server.interfaces.ftp import IFileSystem +from zope.server.interfaces.ftp import IFileSystemAccess +from zope.interface import implements + +execute = 1 +read = 2 +write = 4 + +class File(object): + type = 'f' + modified=None + + def __init__(self): + self.access = {'anonymous': read} + + def accessable(self, user, access=read): + return (user == 'root' + or (self.access.get(user, 0) & access) + or (self.access.get('anonymous', 0) & access) + ) + + def grant(self, user, access): + self.access[user] = self.access.get(user, 0) | access + + def revoke(self, user, access): + self.access[user] = self.access.get(user, 0) ^ access + +class Directory(File): + + type = 'd' + + def __init__(self): + super(Directory, self).__init__() + self.files = {} + + def get(self, name, default=None): + return self.files.get(name, default) + + def __getitem__(self, name): + return self.files[name] + + def __setitem__(self, name, v): + self.files[name] = v + + def __delitem__(self, name): + del self.files[name] + + def __contains__(self, name): + return name in self.files + + def __iter__(self): + return iter(self.files) + +class DemoFileSystem(object): + __doc__ = IFileSystem.__doc__ + + implements(IFileSystem) + + File = File + Directory = Directory + + def __init__(self, files, user=''): + self.files = files + self.user = user + + def get(self, path, default=None): + + while path.startswith('/'): + path = path[1:] + + d = self.files + if path: + for name in path.split('/'): + if d.type is not 'd': + return default + if not d.accessable(self.user): + raise Unauthorized + d = d.get(name) + if d is None: + break + + return d + + def getany(self, path): + d = self.get(path) + if d is None: + raise OSError("No such file or directory:", path) + return d + + def getdir(self, path): + d = self.getany(path) + if d.type != 'd': + raise OSError("Not a directory:", path) + return d + + def getfile(self, path): + d = self.getany(path) + if d.type != 'f': + raise OSError("Not a file:", path) + return d + + def getwdir(self, path): + d = self.getdir(path) + if not d.accessable(self.user, write): + raise OSError("Permission denied") + return d + + def type(self, path): + "See zope.server.interfaces.ftp.IFileSystem" + f = self.get(path) + return getattr(f, 'type', None) + + def names(self, path, filter=None): + "See zope.server.interfaces.ftp.IFileSystem" + f = list(self.getdir(path)) + if filter is not None: + f = [name for name in f if filter(name)] + + return f + + def _lsinfo(self, name, file): + info = { + 'type': file.type, + 'name': name, + 'group_read': file.accessable(self.user, read), + 'group_write': file.accessable(self.user, write), + } + if file.type == 'f': + info['size'] = len(file.data) + if file.modified is not None: + info['mtime'] = file.modified + + return info + + def ls(self, path, filter=None): + "See zope.server.interfaces.ftp.IFileSystem" + f = self.getdir(path) + if filter is None: + return [self._lsinfo(name, f.files[name]) + for name in f + ] + + return [self._lsinfo(name, f.files[name]) + for name in f + if filter(name)] + + def readfile(self, path, outstream, start=0, end=None): + "See zope.server.interfaces.ftp.IFileSystem" + f = self.getfile(path) + + data = f.data + if end is not None: + data = data[:end] + if start: + data = data[start:] + + outstream.write(data) + + def lsinfo(self, path): + "See zope.server.interfaces.ftp.IFileSystem" + f = self.getany(path) + return self._lsinfo(posixpath.split(path)[1], f) + + def mtime(self, path): + "See zope.server.interfaces.ftp.IFileSystem" + f = self.getany(path) + return f.modified + + def size(self, path): + "See zope.server.interfaces.ftp.IFileSystem" + f = self.getany(path) + return len(getattr(f, 'data', '')) + + def mkdir(self, path): + "See zope.server.interfaces.ftp.IFileSystem" + path, name = posixpath.split(path) + d = self.getwdir(path) + if name in d.files: + raise OSError("Already exists:", name) + newdir = self.Directory() + newdir.grant(self.user, read | write) + d.files[name] = newdir + + def remove(self, path): + "See zope.server.interfaces.ftp.IFileSystem" + path, name = posixpath.split(path) + d = self.getwdir(path) + if name not in d.files: + raise OSError("Not exists:", name) + f = d.files[name] + if f.type == 'd': + raise OSError('Is a directory:', name) + del d.files[name] + + def rmdir(self, path): + "See zope.server.interfaces.ftp.IFileSystem" + path, name = posixpath.split(path) + d = self.getwdir(path) + if name not in d.files: + raise OSError("Not exists:", name) + f = d.files[name] + if f.type != 'd': + raise OSError('Is not a directory:', name) + del d.files[name] + + def rename(self, old, new): + "See zope.server.interfaces.ftp.IFileSystem" + oldpath, oldname = posixpath.split(old) + newpath, newname = posixpath.split(new) + + olddir = self.getwdir(oldpath) + newdir = self.getwdir(newpath) + + if oldname not in olddir.files: + raise OSError("Not exists:", oldname) + if newname in newdir.files: + raise OSError("Already exists:", newname) + + newdir.files[newname] = olddir.files[oldname] + del olddir.files[oldname] + + def writefile(self, path, instream, start=None, end=None, append=False): + "See zope.server.interfaces.ftp.IFileSystem" + path, name = posixpath.split(path) + d = self.getdir(path) + f = d.files.get(name) + if f is None: + d = self.getwdir(path) + f = d.files[name] = self.File() + f.grant(self.user, read | write) + elif f.type != 'f': + raise OSError("Can't overwrite a directory") + + if not f.accessable(self.user, write): + raise OSError("Permission denied") + + if append: + f.data += instream.read() + else: + + if start: + if start < 0: + raise ValueError("Negative starting file position") + prefix = f.data[:start] + if len(prefix) < start: + prefix += '\0' * (start - len(prefix)) + else: + prefix = '' + start=0 + + if end: + if end < 0: + raise ValueError("Negative ending file position") + l = end - start + newdata = instream.read(l) + + f.data = prefix+newdata+f.data[start+len(newdata):] + else: + f.data = prefix + instream.read() + + def writable(self, path): + "See zope.server.interfaces.ftp.IFileSystem" + path, name = posixpath.split(path) + try: + d = self.getdir(path) + except OSError: + return False + if name not in d: + return d.accessable(self.user, write) + f = d[name] + return f.type == 'f' and f.accessable(self.user, write) + +class DemoFileSystemAccess(object): + __doc__ = IFileSystemAccess.__doc__ + + implements(IFileSystemAccess) + + def __init__(self, files, users): + self.files = files + self.users = users + + def authenticate(self, credentials): + "See zope.server.interfaces.ftp.IFileSystemAccess" + user, password = credentials + if user != 'anonymous': + if self.users.get(user) != password: + raise Unauthorized + return user + + def open(self, credentials): + "See zope.server.interfaces.ftp.IFileSystemAccess" + user = self.authenticate(credentials) + return DemoFileSystem(self.files, user) diff -Nru zope3-3.4.0/src/zope/server/ftp/tests/fstests.py zope3-3.5~bzr18/src/zope/server/ftp/tests/fstests.py --- zope3-3.4.0/src/zope/server/ftp/tests/fstests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/tests/fstests.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,154 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Abstract file-system tests +""" +from StringIO import StringIO +from zope.interface.verify import verifyObject +from zope.server.interfaces.ftp import IFileSystem + +class FileSystemTests(object): + """Tests of a readable filesystem + """ + + filesystem = None + dir_name = '/dir' + file_name = '/dir/file.txt' + unwritable_filename = '/dir/protected.txt' + dir_contents = ['file.txt', 'protected.txt'] + file_contents = 'Lengthen your stride' + + def test_type(self): + self.assertEqual(self.filesystem.type(self.dir_name), 'd') + self.assertEqual(self.filesystem.type(self.file_name), 'f') + + + def test_names(self): + lst = self.filesystem.names(self.dir_name) + lst.sort() + self.assertEqual(lst, self.dir_contents) + + def test_readfile(self): + s = StringIO() + self.filesystem.readfile(self.file_name, s) + self.assertEqual(s.getvalue(), self.file_contents) + + + def testReadPartOfFile(self): + s = StringIO() + self.filesystem.readfile(self.file_name, s, 2) + self.assertEqual(s.getvalue(), self.file_contents[2:]) + + + def testReadPartOfFile2(self): + s = StringIO() + self.filesystem.readfile(self.file_name, s, 1, 5) + self.assertEqual(s.getvalue(), self.file_contents[1:5]) + + def test_IFileSystemInterface(self): + verifyObject(IFileSystem, self.filesystem) + + def testRemove(self): + self.filesystem.remove(self.file_name) + self.failIf(self.filesystem.type(self.file_name)) + + + def testMkdir(self): + path = self.dir_name + '/x' + self.filesystem.mkdir(path) + self.assertEqual(self.filesystem.type(path), 'd') + + def testRmdir(self): + self.filesystem.remove(self.file_name) + self.filesystem.rmdir(self.dir_name) + self.failIf(self.filesystem.type(self.dir_name)) + + + def testRename(self): + self.filesystem.rename(self.file_name, self.file_name + '.bak') + self.assertEqual(self.filesystem.type(self.file_name), None) + self.assertEqual(self.filesystem.type(self.file_name + '.bak'), 'f') + + + def testWriteFile(self): + s = StringIO() + self.filesystem.readfile(self.file_name, s) + self.assertEqual(s.getvalue(), self.file_contents) + + data = 'Always ' + self.file_contents + s = StringIO(data) + self.filesystem.writefile(self.file_name, s) + + s = StringIO() + self.filesystem.readfile(self.file_name, s) + self.assertEqual(s.getvalue(), data) + + + def testAppendToFile(self): + data = ' again' + s = StringIO(data) + self.filesystem.writefile(self.file_name, s, append=True) + + s = StringIO() + self.filesystem.readfile(self.file_name, s) + self.assertEqual(s.getvalue(), self.file_contents + data) + + def testWritePartOfFile(self): + data = '123' + s = StringIO(data) + self.filesystem.writefile(self.file_name, s, 3, 6) + + expect = self.file_contents[:3] + data + self.file_contents[6:] + + s = StringIO() + self.filesystem.readfile(self.file_name, s) + self.assertEqual(s.getvalue(), expect) + + def testWritePartOfFile_and_truncate(self): + data = '123' + s = StringIO(data) + self.filesystem.writefile(self.file_name, s, 3) + + expect = self.file_contents[:3] + data + + s = StringIO() + self.filesystem.readfile(self.file_name, s) + self.assertEqual(s.getvalue(), expect) + + def testWriteBeyondEndOfFile(self): + partlen = len(self.file_contents) - 6 + data = 'daylight savings' + s = StringIO(data) + self.filesystem.writefile(self.file_name, s, partlen) + + expect = self.file_contents[:partlen] + data + + s = StringIO() + self.filesystem.readfile(self.file_name, s) + self.assertEqual(s.getvalue(), expect) + + + def testWriteNewFile(self): + s = StringIO(self.file_contents) + self.filesystem.writefile(self.file_name + '.new', s) + + s = StringIO() + self.filesystem.readfile(self.file_name, s) + self.assertEqual(s.getvalue(), self.file_contents) + + + def test_writable(self): + self.failIf(self.filesystem.writable(self.dir_name)) + self.failIf(self.filesystem.writable(self.unwritable_filename)) + self.failUnless(self.filesystem.writable(self.file_name)) + self.failUnless(self.filesystem.writable(self.file_name+'1')) diff -Nru zope3-3.4.0/src/zope/server/ftp/tests/__init__.py zope3-3.5~bzr18/src/zope/server/ftp/tests/__init__.py --- zope3-3.4.0/src/zope/server/ftp/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +# Make this directory a package. diff -Nru zope3-3.4.0/src/zope/server/ftp/tests/test_demofs.py zope3-3.5~bzr18/src/zope/server/ftp/tests/test_demofs.py --- zope3-3.4.0/src/zope/server/ftp/tests/test_demofs.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/tests/test_demofs.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,38 @@ +############################################################################## +# +# 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 the Demo Filesystem implementation. +""" +import demofs +from unittest import TestCase, TestSuite, main, makeSuite +from fstests import FileSystemTests +from StringIO import StringIO + +class Test(FileSystemTests, TestCase): + + def setUp(self): + root = demofs.Directory() + root.grant('bob', demofs.write) + fs = self.filesystem = demofs.DemoFileSystem(root, 'bob') + fs.mkdir(self.dir_name) + fs.writefile(self.file_name, StringIO(self.file_contents)) + fs.writefile(self.unwritable_filename, StringIO("save this")) + fs.get(self.unwritable_filename).revoke('bob', demofs.write) + +def test_suite(): + return TestSuite(( + makeSuite(Test), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/server/ftp/tests/test_ftpserver.py zope3-3.5~bzr18/src/zope/server/ftp/tests/test_ftpserver.py --- zope3-3.4.0/src/zope/server/ftp/tests/test_ftpserver.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/tests/test_ftpserver.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,397 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""FTP Server tests +""" +import asyncore +import ftplib +import unittest +import socket +import sys +import time +import traceback +import unittest + +from types import StringType +from StringIO import StringIO +from threading import Thread, Event + +from zope.server.adjustments import Adjustments +from zope.server.ftp.tests import demofs +from zope.server.taskthreads import ThreadedTaskDispatcher +from zope.server.tests.asyncerror import AsyncoreErrorHook + +td = ThreadedTaskDispatcher() + +LOCALHOST = '127.0.0.1' +SERVER_PORT = 0 # Set these port numbers to 0 to auto-bind, or +CONNECT_TO_PORT = 0 # use specific numbers to inspect using TCPWatch. + + +my_adj = Adjustments() + + +def retrlines(ftpconn, cmd): + res = [] + ftpconn.retrlines(cmd, res.append) + return ''.join(res) + + +class Tests(unittest.TestCase, AsyncoreErrorHook): + + def setUp(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import FTPServer + td.setThreadCount(1) + if len(asyncore.socket_map) != 1: + # Let sockets die off. + # TODO tests should be more careful to clear the socket map. + asyncore.poll(0.1) + self.orig_map_size = len(asyncore.socket_map) + self.hook_asyncore_error() + + root_dir = demofs.Directory() + root_dir['test'] = demofs.Directory() + root_dir['test'].access['foo'] = 7 + root_dir['private'] = demofs.Directory() + root_dir['private'].access['foo'] = 7 + root_dir['private'].access['anonymous'] = 0 + + fs = demofs.DemoFileSystem(root_dir, 'foo') + fs.writefile('/test/existing', StringIO('test initial data')) + fs.writefile('/private/existing', StringIO('private initial data')) + + self.__fs = fs = demofs.DemoFileSystem(root_dir, 'root') + fs.writefile('/existing', StringIO('root initial data')) + + fs_access = demofs.DemoFileSystemAccess(root_dir, {'foo': 'bar'}) + + self.server = FTPServer(LOCALHOST, SERVER_PORT, fs_access, + task_dispatcher=td, adj=my_adj) + if CONNECT_TO_PORT == 0: + self.port = self.server.socket.getsockname()[1] + else: + self.port = CONNECT_TO_PORT + self.run_loop = 1 + self.counter = 0 + self.thread_started = Event() + self.thread = Thread(target=self.loop) + self.thread.setDaemon(True) + self.thread.start() + self.thread_started.wait(10.0) + self.assert_(self.thread_started.isSet()) + + def tearDown(self): + self.run_loop = 0 + self.thread.join() + td.shutdown() + self.server.close() + # Make sure all sockets get closed by asyncore normally. + timeout = time.time() + 2 + while 1: + if len(asyncore.socket_map) == self.orig_map_size: + # Clean! + break + if time.time() >= timeout: + self.fail('Leaked a socket: %s' % `asyncore.socket_map`) + break + asyncore.poll(0.1) + + self.unhook_asyncore_error() + + def loop(self): + self.thread_started.set() + import select + from errno import EBADF + while self.run_loop: + self.counter = self.counter + 1 + # Note that it isn't acceptable to fail out of + # this loop. That will likely make the tests hang. + try: + asyncore.poll(0.1) + continue + except select.error, data: + print "EXCEPTION POLLING IN LOOP(): ", data + if data[0] == EBADF: + for key in asyncore.socket_map.keys(): + print + try: + select.select([], [], [key], 0.0) + except select.error, v: + print "Bad entry in socket map", key, v + print asyncore.socket_map[key] + print asyncore.socket_map[key].__class__ + del asyncore.socket_map[key] + else: + print "OK entry in socket map", key + print asyncore.socket_map[key] + print asyncore.socket_map[key].__class__ + print + except: + print "WEIRD EXCEPTION IN LOOP" + traceback.print_exception(*(sys.exc_info()+(100,))) + print + + def getFTPConnection(self, login=1): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + ftp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ftp.connect((LOCALHOST, self.port)) + result = ftp.recv(10000).split()[0] + self.assertEqual(result, '220') + if login: + ftp.send('USER foo\r\n') + self.assertEqual(ftp.recv(1024), + status_messages['PASS_REQUIRED'] +'\r\n') + ftp.send('PASS bar\r\n') + self.assertEqual(ftp.recv(1024), + status_messages['LOGIN_SUCCESS'] +'\r\n') + + return ftp + + + def execute(self, commands, login=1): + ftp = self.getFTPConnection(login) + + try: + if type(commands) is StringType: + commands = (commands,) + + for command in commands: + ftp.send('%s\r\n' %command) + time.sleep(.01) + result = ftp.recv(10000) + self.failUnless(result.endswith('\r\n')) + finally: + ftp.close() + return result + + + def testABOR(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + self.assertEqual(self.execute('ABOR', 1).rstrip(), + status_messages['TRANSFER_ABORTED']) + + + def testAPPE(self): + conn = ftplib.FTP() + try: + conn.connect(LOCALHOST, self.port) + conn.login('foo', 'bar') + fp = StringIO('Charity never faileth') + # Successful write + conn.storbinary('APPE /test/existing', fp) + self.assertEqual(self.__fs.files['test']['existing'].data, + 'test initial dataCharity never faileth') + finally: + conn.close() + # Make sure no garbage was left behind. + self.testNOOP() + + def testAPPE_errors(self): + conn = ftplib.FTP() + try: + conn.connect(LOCALHOST, self.port) + conn.login('foo', 'bar') + + fp = StringIO('Speak softly') + + # Can't overwrite directory + self.assertRaises( + ftplib.error_perm, conn.storbinary, 'APPE /test', fp) + + # No such file + self.assertRaises( + ftplib.error_perm, conn.storbinary, 'APPE /nosush', fp) + + # No such dir + self.assertRaises( + ftplib.error_perm, conn.storbinary, 'APPE /nosush/f', fp) + + # Not allowed + self.assertRaises( + ftplib.error_perm, conn.storbinary, 'APPE /existing', fp) + + finally: + conn.close() + # Make sure no garbage was left behind. + self.testNOOP() + + def testCDUP(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + self.execute('CWD test', 1) + self.assertEqual(self.execute('CDUP', 1).rstrip(), + status_messages['SUCCESS_250'] %'CDUP') + self.assertEqual(self.execute('CDUP', 1).rstrip(), + status_messages['SUCCESS_250'] %'CDUP') + + + def testCWD(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + self.assertEqual(self.execute('CWD test', 1).rstrip(), + status_messages['SUCCESS_250'] %'CWD') + self.assertEqual(self.execute('CWD foo', 1).rstrip(), + status_messages['ERR_NO_DIR'] %'/foo') + + + def testDELE(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + self.assertEqual(self.execute('DELE test/existing', 1).rstrip(), + status_messages['SUCCESS_250'] %'DELE') + res = self.execute('DELE bar', 1).split()[0] + self.assertEqual(res, '550') + self.assertEqual(self.execute('DELE', 1).rstrip(), + status_messages['ERR_ARGS']) + + + def testHELP(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + # TODO This test doesn't work. I think it is because execute() + # doesn't read the whole reply. The execeute() helper + # function should be fixed, but that's for another day. + result = status_messages['HELP_START'] + '\r\n' + result += 'Help goes here somewhen.\r\n' + result += status_messages['HELP_END'] + '\r\n' + + self.assertEqual(self.execute('HELP', 1), result) + + + def testLIST(self): + conn = ftplib.FTP() + try: + conn.connect(LOCALHOST, self.port) + conn.login('anonymous', 'bar') + self.assertRaises(ftplib.Error, retrlines, conn, 'LIST /foo') + listing = retrlines(conn, 'LIST') + self.assert_(len(listing) > 0) + listing = retrlines(conn, 'LIST -la') + self.assert_(len(listing) > 0) + finally: + conn.close() + # Make sure no garbage was left behind. + self.testNOOP() + + def testMKDLIST(self): + self.execute(['MKD test/f1', 'MKD test/f2'], 1) + conn = ftplib.FTP() + try: + conn.connect(LOCALHOST, self.port) + conn.login('foo', 'bar') + listing = [] + conn.retrlines('LIST /test', listing.append) + self.assert_(len(listing) > 2) + listing = [] + conn.retrlines('LIST -lad test/f1', listing.append) + self.assertEqual(len(listing), 1) + self.assertEqual(listing[0][0], 'd') + finally: + conn.close() + # Make sure no garbage was left behind. + self.testNOOP() + + + def testNOOP(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + self.assertEqual(self.execute('NOOP', 0).rstrip(), + status_messages['SUCCESS_200'] %'NOOP') + self.assertEqual(self.execute('NOOP', 1).rstrip(), + status_messages['SUCCESS_200'] %'NOOP') + + + def testPASS(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + self.assertEqual(self.execute('PASS', 0).rstrip(), + status_messages['LOGIN_MISMATCH']) + self.execute('USER blah', 0) + self.assertEqual(self.execute('PASS bar', 0).rstrip(), + status_messages['LOGIN_MISMATCH']) + + + def testQUIT(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + self.assertEqual(self.execute('QUIT', 0).rstrip(), + status_messages['GOODBYE']) + self.assertEqual(self.execute('QUIT', 1).rstrip(), + status_messages['GOODBYE']) + + + def testSTOR(self): + conn = ftplib.FTP() + try: + conn.connect(LOCALHOST, self.port) + conn.login('foo', 'bar') + fp = StringIO('Speak softly') + # Can't overwrite directory + self.assertRaises( + ftplib.error_perm, conn.storbinary, 'STOR /test', fp) + fp = StringIO('Charity never faileth') + # Successful write + conn.storbinary('STOR /test/stuff', fp) + self.assertEqual(self.__fs.files['test']['stuff'].data, + 'Charity never faileth') + finally: + conn.close() + # Make sure no garbage was left behind. + self.testNOOP() + + + def testSTOR_over(self): + conn = ftplib.FTP() + try: + conn.connect(LOCALHOST, self.port) + conn.login('foo', 'bar') + fp = StringIO('Charity never faileth') + conn.storbinary('STOR /test/existing', fp) + self.assertEqual(self.__fs.files['test']['existing'].data, + 'Charity never faileth') + finally: + conn.close() + # Make sure no garbage was left behind. + self.testNOOP() + + + def testUSER(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.server import status_messages + self.assertEqual(self.execute('USER foo', 0).rstrip(), + status_messages['PASS_REQUIRED']) + self.assertEqual(self.execute('USER', 0).rstrip(), + status_messages['ERR_ARGS']) + + + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Tests) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/server/ftp/tests/test_publisher.py zope3-3.5~bzr18/src/zope/server/ftp/tests/test_publisher.py --- zope3-3.4.0/src/zope/server/ftp/tests/test_publisher.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/ftp/tests/test_publisher.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,120 @@ +############################################################################## +# +# 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 the FTP publisher. +""" +import demofs +from unittest import TestCase, TestSuite, main, makeSuite +from fstests import FileSystemTests +from StringIO import StringIO +from zope.publisher.publish import mapply + +class DemoFileSystem(demofs.DemoFileSystem): + + def rename(self, path, old, new): + return demofs.DemoFileSystem.rename( + self, "%s/%s" % (path, old), "%s/%s" % (path, new)) + +class Publication(object): + + def __init__(self, root): + self.root = root + + def beforeTraversal(self, request): + pass + + def getApplication(self, request): + return self.root + + def afterTraversal(self, request, ob): + pass + + def callObject(self, request, ob): + command = getattr(ob, request.env['command']) + if 'name' in request.env: + request.env['path'] += "/" + request.env['name'] + return mapply(command, request = request.env) + + def afterCall(self, request, ob): + pass + + def endRequest(self, request, ob): + pass + + def handleException(self, object, request, info, retry_allowed=True): + request.response._exc = info[:2] + + +class Request(object): + + def __init__(self, input, env): + self.env = env + self.response = Response() + self.user = env['credentials'] + del env['credentials'] + + def processInputs(self): + pass + + def traverse(self, root): + root.user = self.user + return root + + def close(self): + pass + +class Response(object): + + _exc = _body = None + + def setResult(self, result): + self._result = result + + def getResult(self): + if self._exc: + raise self._exc[0], self._exc[1] + return self._result + +class RequestFactory(object): + + def __init__(self, root): + self.pub = Publication(root) + + def __call__(self, input, env): + r = Request(input, env) + r.publication = self.pub + return r + +class TestPublisherFileSystem(FileSystemTests, TestCase): + + def setUp(self): + root = demofs.Directory() + root.grant('bob', demofs.write) + fs = DemoFileSystem(root, 'bob') + fs.mkdir(self.dir_name) + fs.writefile(self.file_name, StringIO(self.file_contents)) + fs.writefile(self.unwritable_filename, StringIO("save this")) + fs.get(self.unwritable_filename).revoke('bob', demofs.write) + + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.ftp.publisher import PublisherFileSystem + self.filesystem = PublisherFileSystem('bob', RequestFactory(fs)) + +def test_suite(): + return TestSuite(( + makeSuite(TestPublisherFileSystem), + )) + +if __name__=='__main__': + main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/server/http/chunking.py zope3-3.5~bzr18/src/zope/server/http/chunking.py --- zope3-3.4.0/src/zope/server/http/chunking.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/chunking.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,106 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Data Chunk Receiver +""" + +from zope.server.utilities import find_double_newline +from zope.server.interfaces import IStreamConsumer +from zope.interface import implements + + +class ChunkedReceiver(object): + + implements(IStreamConsumer) + + chunk_remainder = 0 + control_line = '' + all_chunks_received = 0 + trailer = '' + completed = 0 + + # max_control_line = 1024 + # max_trailer = 65536 + + + def __init__(self, buf): + self.buf = buf + + def received(self, s): + # Returns the number of bytes consumed. + if self.completed: + return 0 + orig_size = len(s) + while s: + rm = self.chunk_remainder + if rm > 0: + # Receive the remainder of a chunk. + to_write = s[:rm] + self.buf.append(to_write) + written = len(to_write) + s = s[written:] + self.chunk_remainder -= written + elif not self.all_chunks_received: + # Receive a control line. + s = self.control_line + s + pos = s.find('\n') + if pos < 0: + # Control line not finished. + self.control_line = s + s = '' + else: + # Control line finished. + line = s[:pos] + s = s[pos + 1:] + self.control_line = '' + line = line.strip() + if line: + # Begin a new chunk. + semi = line.find(';') + if semi >= 0: + # discard extension info. + line = line[:semi] + sz = int(line.strip(), 16) # hexadecimal + if sz > 0: + # Start a new chunk. + self.chunk_remainder = sz + else: + # Finished chunks. + self.all_chunks_received = 1 + # else expect a control line. + else: + # Receive the trailer. + trailer = self.trailer + s + if trailer.startswith('\r\n'): + # No trailer. + self.completed = 1 + return orig_size - (len(trailer) - 2) + elif trailer.startswith('\n'): + # No trailer. + self.completed = 1 + return orig_size - (len(trailer) - 1) + pos = find_double_newline(trailer) + if pos < 0: + # Trailer not finished. + self.trailer = trailer + s = '' + else: + # Finished the trailer. + self.completed = 1 + self.trailer = trailer[:pos] + return orig_size - (len(trailer) - pos) + return orig_size + + + def getfile(self): + return self.buf.getfile() diff -Nru zope3-3.4.0/src/zope/server/http/commonaccesslogger.py zope3-3.5~bzr18/src/zope/server/http/commonaccesslogger.py --- zope3-3.4.0/src/zope/server/http/commonaccesslogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/commonaccesslogger.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,94 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Common Access Logger +""" + +import time + +from zope.server.http.http_date import monthname +from zope.server.logger.pythonlogger import PythonLogger +from zope.server.logger.resolvinglogger import ResolvingLogger +from zope.server.logger.unresolvinglogger import UnresolvingLogger + +class CommonAccessLogger(object): + """Outputs accesses in common HTTP log format. + """ + + def __init__(self, logger_object=None, resolver=None): + if logger_object is None: + # logger_object is an IMessageLogger + logger_object = PythonLogger('accesslog') + + # self.output is an IRequestLogger + if resolver is not None: + self.output = ResolvingLogger(resolver, logger_object) + else: + self.output = UnresolvingLogger(logger_object) + + def compute_timezone_for_log(self, tz): + if tz > 0: + neg = 1 + else: + neg = 0 + tz = -tz + h, rem = divmod (tz, 3600) + m, rem = divmod (rem, 60) + if neg: + return '-%02d%02d' % (h, m) + else: + return '+%02d%02d' % (h, m) + + tz_for_log = None + tz_for_log_alt = None + + def log_date_string(self, when): + logtime = time.localtime(when) + Y, M, D, h, m, s = logtime[:6] + + if not time.daylight: + tz = self.tz_for_log + if tz is None: + tz = self.compute_timezone_for_log(time.timezone) + self.tz_for_log = tz + else: + tz = self.tz_for_log_alt + if tz is None: + tz = self.compute_timezone_for_log(time.altzone) + self.tz_for_log_alt = tz + + return '%d/%s/%02d:%02d:%02d:%02d %s' % ( + D, monthname[M], Y, h, m, s, tz) + + def log(self, task): + """Receives a completed task and logs it in the common log format.""" + now = time.time() + request_data = task.request_data + req_headers = request_data.headers + + user_name = task.auth_user_name or 'anonymous' + user_agent = req_headers.get('USER_AGENT', '') + referer = req_headers.get('REFERER', '') + + self.output.logRequest( + task.channel.addr[0], + ' - %s [%s] "%s" %s %d "%s" "%s"\n' % ( + user_name, + self.log_date_string(now), + request_data.first_line, + task.status, + task.bytes_written, + referer, + user_agent + ) + ) diff -Nru zope3-3.4.0/src/zope/server/http/http_date.py zope3-3.5~bzr18/src/zope/server/http/http_date.py --- zope3-3.4.0/src/zope/server/http/http_date.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/http_date.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,145 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""HTTP Server Date/Time utilities +""" +import re +import string +import time +import calendar + +def concat(*args): + return ''.join(args) + +def join(seq, field=' '): + return field.join(seq) + +def group(s): + return '(' + s + ')' + +short_days = ['sun','mon','tue','wed','thu','fri','sat'] +long_days = ['sunday','monday','tuesday','wednesday', + 'thursday','friday','saturday'] + +short_day_reg = group(join(short_days, '|')) +long_day_reg = group(join(long_days, '|')) + +daymap = {} +for i in range(7): + daymap[short_days[i]] = i + daymap[long_days[i]] = i + +hms_reg = join(3 * [group('[0-9][0-9]')], ':') + +months = ['jan','feb','mar','apr','may','jun','jul', + 'aug','sep','oct','nov','dec'] + +monmap = {} +for i in range(12): + monmap[months[i]] = i+1 + +months_reg = group(join(months, '|')) + +# From draft-ietf-http-v11-spec-07.txt/3.3.1 +# Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 +# Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 +# Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + +# rfc822 format +rfc822_date = join( + [concat (short_day_reg,','), # day + group('[0-9][0-9]?'), # date + months_reg, # month + group('[0-9]+'), # year + hms_reg, # hour minute second + 'gmt' + ], + ' ' + ) + +rfc822_reg = re.compile(rfc822_date) + +def unpack_rfc822(m): + g = m.group + a = string.atoi + return ( + a(g(4)), # year + monmap[g(3)], # month + a(g(2)), # day + a(g(5)), # hour + a(g(6)), # minute + a(g(7)), # second + 0, + 0, + 0 + ) + + # rfc850 format +rfc850_date = join( + [concat(long_day_reg,','), + join( + [group ('[0-9][0-9]?'), + months_reg, + group ('[0-9]+') + ], + '-' + ), + hms_reg, + 'gmt' + ], + ' ' + ) + +rfc850_reg = re.compile(rfc850_date) +# they actually unpack the same way +def unpack_rfc850(m): + g = m.group + a = string.atoi + return ( + a(g(4)), # year + monmap[g(3)], # month + a(g(2)), # day + a(g(5)), # hour + a(g(6)), # minute + a(g(7)), # second + 0, + 0, + 0 + ) + + # parsdate.parsedate - ~700/sec. + # parse_http_date - ~1333/sec. + +weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] +monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + +def build_http_date(when): + year, month, day, hh, mm, ss, wd, y, z = time.gmtime(when) + return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( + weekdayname[wd], + day, monthname[month], year, + hh, mm, ss) + +def parse_http_date(d): + d = d.lower() + m = rfc850_reg.match(d) + if m and m.end() == len(d): + retval = int(calendar.timegm(unpack_rfc850(m))) + else: + m = rfc822_reg.match(d) + if m and m.end() == len(d): + retval = int(calendar.timegm(unpack_rfc822(m))) + else: + return 0 + return retval diff -Nru zope3-3.4.0/src/zope/server/http/httprequestparser.py zope3-3.5~bzr18/src/zope/server/http/httprequestparser.py --- zope3-3.4.0/src/zope/server/http/httprequestparser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/httprequestparser.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,212 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""HTTP Request Parser + +This server uses asyncore to accept connections and do initial +processing but threads to do work. +""" +import re +from urllib import unquote + +from zope.server.fixedstreamreceiver import FixedStreamReceiver +from zope.server.buffers import OverflowableBuffer +from zope.server.utilities import find_double_newline +from zope.server.interfaces import IStreamConsumer +from zope.interface import implements + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + + +class HTTPRequestParser(object): + """A structure that collects the HTTP request. + + Once the stream is completed, the instance is passed to + a server task constructor. + """ + + implements(IStreamConsumer) + + completed = 0 # Set once request is completed. + empty = 0 # Set if no request was made. + header_plus = '' + chunked = 0 + content_length = 0 + body_rcv = None + # Other attributes: first_line, header, headers, command, uri, version, + # path, query, fragment + + # headers is a mapping containing keys translated to uppercase + # with dashes turned into underscores. + + def __init__(self, adj): + """ + adj is an Adjustments object. + """ + self.headers = {} + self.adj = adj + + def received(self, data): + """ + Receives the HTTP stream for one request. + Returns the number of bytes consumed. + Sets the completed flag once both the header and the + body have been received. + """ + if self.completed: + return 0 # Can't consume any more. + datalen = len(data) + br = self.body_rcv + if br is None: + # In header. + s = self.header_plus + data + index = find_double_newline(s) + if index >= 0: + # Header finished. + header_plus = s[:index] + consumed = len(data) - (len(s) - index) + self.in_header = 0 + # Remove preceeding blank lines. + header_plus = header_plus.lstrip() + if not header_plus: + self.empty = 1 + self.completed = 1 + else: + self.parse_header(header_plus) + if self.body_rcv is None: + self.completed = 1 + return consumed + else: + # Header not finished yet. + self.header_plus = s + return datalen + else: + # In body. + consumed = br.received(data) + if br.completed: + self.completed = 1 + return consumed + + + def parse_header(self, header_plus): + """ + Parses the header_plus block of text (the headers plus the + first line of the request). + """ + index = header_plus.find('\n') + if index >= 0: + first_line = header_plus[:index].rstrip() + header = header_plus[index + 1:] + else: + first_line = header_plus.rstrip() + header = '' + self.first_line = first_line + self.header = header + + lines = self.get_header_lines() + headers = self.headers + for line in lines: + index = line.find(':') + if index > 0: + key = line[:index] + value = line[index + 1:].strip() + key1 = key.upper().replace('-', '_') + # If a header already exists, we append subsequent values + # seperated by a comma. Applications already need to handle + # the comma seperated values, as HTTP front ends might do + # the concatenation for you (behavior specified in RFC2616). + try: + headers[key1] += ', %s' % value + except KeyError: + headers[key1] = value + # else there's garbage in the headers? + + command, uri, version = self.crack_first_line() + self.command = str(command) + self.uri = str(uri) + self.version = version + self.split_uri() + + if version == '1.1': + te = headers.get('TRANSFER_ENCODING', '') + if te == 'chunked': + from zope.server.http.chunking import ChunkedReceiver + self.chunked = 1 + buf = OverflowableBuffer(self.adj.inbuf_overflow) + self.body_rcv = ChunkedReceiver(buf) + if not self.chunked: + try: + cl = int(headers.get('CONTENT_LENGTH', 0)) + except ValueError: + cl = 0 + self.content_length = cl + if cl > 0: + buf = OverflowableBuffer(self.adj.inbuf_overflow) + self.body_rcv = FixedStreamReceiver(cl, buf) + + + def get_header_lines(self): + """ + Splits the header into lines, putting multi-line headers together. + """ + r = [] + lines = self.header.split('\n') + for line in lines: + if line and line[0] in ' \t': + r[-1] = r[-1] + line[1:] + else: + r.append(line) + return r + + first_line_re = re.compile ( + '([^ ]+) (?:[^ :?#]+://[^ ?#/]*)?([^ ]+)(( HTTP/([0-9.]+))$|$)') + + def crack_first_line(self): + r = self.first_line + m = self.first_line_re.match (r) + if m is not None and m.end() == len(r): + if m.group(3): + version = m.group(5) + else: + version = None + return m.group(1).upper(), m.group(2), version + else: + return None, None, None + + path_regex = re.compile ( + # path query fragment + r'([^?#]*)(\?[^#]*)?(#.*)?' + ) + + def split_uri(self): + m = self.path_regex.match (self.uri) + if m.end() != len(self.uri): + raise ValueError("Broken URI") + else: + path, query, self.fragment = m.groups() + if path and '%' in path: + path = unquote(path) + self.path = path + if query: + query = query[1:] + self.query = query + + def getBodyStream(self): + body_rcv = self.body_rcv + if body_rcv is not None: + return body_rcv.getfile() + else: + return StringIO('') diff -Nru zope3-3.4.0/src/zope/server/http/httpserverchannel.py zope3-3.5~bzr18/src/zope/server/http/httpserverchannel.py --- zope3-3.4.0/src/zope/server/http/httpserverchannel.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/httpserverchannel.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""HTTP Server Channel +""" +from zope.server.serverchannelbase import ServerChannelBase +from zope.server.http.httptask import HTTPTask +from zope.server.http.httprequestparser import HTTPRequestParser + + +class HTTPServerChannel(ServerChannelBase): + """HTTP-specific Server Channel""" + + task_class = HTTPTask + parser_class = HTTPRequestParser diff -Nru zope3-3.4.0/src/zope/server/http/httpserver.py zope3-3.5~bzr18/src/zope/server/http/httpserver.py --- zope3-3.4.0/src/zope/server/http/httpserver.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/httpserver.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,53 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""HTTP Server + +This server uses asyncore to accept connections and do initial +processing but threads to do work. +""" + +from zope.server.serverbase import ServerBase +from zope.server.http.httpserverchannel import HTTPServerChannel + + +class HTTPServer(ServerBase): + """This is a generic HTTP Server.""" + + channel_class = HTTPServerChannel + SERVER_IDENT = 'zope.server.http' + + def executeRequest(self, task): + """Execute an HTTP request.""" + # This is a default implementation, meant to be overridden. + body = "The HTTP server is running!\r\n" * 10 + task.response_headers['Content-Type'] = 'text/plain' + task.response_headers['Content-Length'] = str(len(body)) + task.write(body) + + +if __name__ == '__main__': + + from zope.server.taskthreads import ThreadedTaskDispatcher + td = ThreadedTaskDispatcher() + td.setThreadCount(4) + HTTPServer('', 8080, task_dispatcher=td) + + try: + import asyncore + while 1: + asyncore.poll(5) + + except KeyboardInterrupt: + print 'shutting down...' + td.shutdown() diff -Nru zope3-3.4.0/src/zope/server/http/httptask.py zope3-3.5~bzr18/src/zope/server/http/httptask.py --- zope3-3.4.0/src/zope/server/http/httptask.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/httptask.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,244 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""HTTP Task + +An HTTP task that can execute an HTTP request with the help of the channel and +the server it belongs to. +""" +import socket +import time + +from zope.server.http.http_date import build_http_date +from zope.publisher.interfaces.http import IHeaderOutput +from zope.server.interfaces import ITask + +from zope.interface import implements + +rename_headers = { + 'CONTENT_LENGTH' : 'CONTENT_LENGTH', + 'CONTENT_TYPE' : 'CONTENT_TYPE', + 'CONNECTION' : 'CONNECTION_TYPE', + } + +class HTTPTask(object): + """An HTTP task accepts a request and writes to a channel. + + Subclass this and override the execute() method. + """ + + implements(ITask, IHeaderOutput) #, IOutputStream + + instream = None + close_on_finish = 1 + status = '200' + reason = 'OK' + wrote_header = 0 + accumulated_headers = None + bytes_written = 0 + auth_user_name = '' + cgi_env = None + + def __init__(self, channel, request_data): + self.channel = channel + self.request_data = request_data + self.response_headers = { + 'Server': channel.server.SERVER_IDENT, + } + version = request_data.version + if version not in ('1.0', '1.1'): + # fall back to a version we support. + version = '1.0' + self.version = version + + def service(self): + """See zope.server.interfaces.ITask""" + try: + try: + self.start() + self.channel.server.executeRequest(self) + self.finish() + except socket.error: + self.close_on_finish = 1 + if self.channel.adj.log_socket_errors: + raise + finally: + if self.close_on_finish: + self.channel.close_when_done() + + def cancel(self): + """See zope.server.interfaces.ITask""" + self.channel.close_when_done() + + def defer(self): + """See zope.server.interfaces.ITask""" + pass + + def setResponseStatus(self, status, reason): + """See zope.publisher.interfaces.http.IHeaderOutput""" + self.status = status + self.reason = reason + + def setResponseHeaders(self, mapping): + """See zope.publisher.interfaces.http.IHeaderOutput""" + self.response_headers.update(mapping) + + def appendResponseHeaders(self, lst): + """See zope.publisher.interfaces.http.IHeaderOutput""" + accum = self.accumulated_headers + if accum is None: + self.accumulated_headers = accum = [] + accum.extend(lst) + + def wroteResponseHeader(self): + """See zope.publisher.interfaces.http.IHeaderOutput""" + return self.wrote_header + + def setAuthUserName(self, name): + """See zope.publisher.interfaces.http.IHeaderOutput""" + self.auth_user_name = name + + def prepareResponseHeaders(self): + version = self.version + # Figure out whether the connection should be closed. + connection = self.request_data.headers.get('CONNECTION', '').lower() + close_it = 0 + response_headers = self.response_headers + accumulated_headers = self.accumulated_headers + if accumulated_headers is None: + accumulated_headers = [] + + if version == '1.0': + if connection == 'keep-alive': + if not ('Content-Length' in response_headers): + close_it = 1 + else: + response_headers['Connection'] = 'Keep-Alive' + else: + close_it = 1 + elif version == '1.1': + if 'connection: close' in (header.lower() for header in + accumulated_headers): + close_it = 1 + if connection == 'close': + close_it = 1 + elif 'Transfer-Encoding' in response_headers: + if not response_headers['Transfer-Encoding'] == 'chunked': + close_it = 1 + elif self.status == '304': + # Replying with headers only. + pass + elif not ('Content-Length' in response_headers): + # accumulated_headers is a simple list, we need to cut off + # the value of content-length manually + if 'content-length' not in (header[:14].lower() for header in + accumulated_headers): + close_it = 1 + # under HTTP 1.1 keep-alive is default, no need to set the header + else: + # Close if unrecognized HTTP version. + close_it = 1 + + self.close_on_finish = close_it + if close_it: + self.response_headers['Connection'] = 'close' + + def buildResponseHeader(self): + self.prepareResponseHeaders() + first_line = 'HTTP/%s %s %s' % (self.version, self.status, self.reason) + lines = [first_line] + ['%s: %s' % hv + for hv in self.response_headers.items()] + accum = self.accumulated_headers + if accum is not None: + lines.extend(accum) + res = '%s\r\n\r\n' % '\r\n'.join(lines) + return res + + def getCGIEnvironment(self): + """Returns a CGI-like environment.""" + env = self.cgi_env + if env is not None: + # Return the cached copy. + return env + + request_data = self.request_data + path = request_data.path + channel = self.channel + server = channel.server + + while path and path.startswith('/'): + path = path[1:] + + env = {} + env['REQUEST_METHOD'] = request_data.command.upper() + env['SERVER_PORT'] = str(server.port) + env['SERVER_NAME'] = server.server_name + env['SERVER_SOFTWARE'] = server.SERVER_IDENT + env['SERVER_PROTOCOL'] = "HTTP/%s" % self.version + env['CHANNEL_CREATION_TIME'] = channel.creation_time + env['SCRIPT_NAME']='' + env['PATH_INFO']='/' + path + query = request_data.query + if query: + env['QUERY_STRING'] = query + env['GATEWAY_INTERFACE'] = 'CGI/1.1' + addr = channel.addr[0] + env['REMOTE_ADDR'] = addr + + # If the server has a resolver, try to get the + # remote host from the resolver's cache. + resolver = getattr(server, 'resolver', None) + if resolver is not None: + dns_cache = resolver.cache + if addr in dns_cache: + remote_host = dns_cache[addr][2] + if remote_host is not None: + env['REMOTE_HOST'] = remote_host + + env_has = env.has_key + + for key, value in request_data.headers.items(): + value = value.strip() + mykey = rename_headers.get(key, None) + if mykey is None: + mykey = 'HTTP_%s' % key + if not env_has(mykey): + env[mykey] = value + + self.cgi_env = env + return env + + def start(self): + now = time.time() + self.start_time = now + self.response_headers['Date'] = build_http_date (now) + + def finish(self): + if not self.wrote_header: + self.write('') + hit_log = self.channel.server.hit_log + if hit_log is not None: + hit_log.log(self) + + def write(self, data): + channel = self.channel + if not self.wrote_header: + rh = self.buildResponseHeader() + channel.write(rh) + self.bytes_written += len(rh) + self.wrote_header = 1 + if data: + self.bytes_written += channel.write(data) + + def flush(self): + self.channel.flush() diff -Nru zope3-3.4.0/src/zope/server/http/__init__.py zope3-3.5~bzr18/src/zope/server/http/__init__.py --- zope3-3.4.0/src/zope/server/http/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/server/http/publisherhttpserver.py zope3-3.5~bzr18/src/zope/server/http/publisherhttpserver.py --- zope3-3.4.0/src/zope/server/http/publisherhttpserver.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/publisherhttpserver.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,60 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""HTTP Server that uses the Zope Publisher for executing a task. +""" +from zope.server.http import wsgihttpserver +from zope.publisher.publish import publish +import zope.security.management + + +class PublisherHTTPServer(wsgihttpserver.WSGIHTTPServer): + + def __init__(self, request_factory, sub_protocol=None, *args, **kw): + + def application(environ, start_response): + request = request_factory(environ['wsgi.input'], environ) + request = publish(request) + response = request.response + start_response(response.getStatusString(), response.getHeaders()) + return response.consumeBody() + + return super(PublisherHTTPServer, self).__init__( + application, sub_protocol, *args, **kw) + + +class PMDBHTTPServer(wsgihttpserver.WSGIHTTPServer): + + def __init__(self, request_factory, sub_protocol=None, *args, **kw): + + def application(environ, start_response): + request = request_factory(environ['wsgi.input'], environ) + try: + request = publish(request, handle_errors=False) + except: + import sys, pdb + print "%s:" % sys.exc_info()[0] + print sys.exc_info()[1] + zope.security.management.restoreInteraction() + try: + pdb.post_mortem(sys.exc_info()[2]) + raise + finally: + zope.security.management.endInteraction() + + response = request.response + start_response(response.getStatusString(), response.getHeaders()) + return response.consumeBody() + + return super(PublisherHTTPServer, self).__init__( + application, sub_protocol, *args, **kw) diff -Nru zope3-3.4.0/src/zope/server/http/tests/__init__.py zope3-3.5~bzr18/src/zope/server/http/tests/__init__.py --- zope3-3.4.0/src/zope/server/http/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/server/http/tests/test_commonaccesslogger.py zope3-3.5~bzr18/src/zope/server/http/tests/test_commonaccesslogger.py --- zope3-3.4.0/src/zope/server/http/tests/test_commonaccesslogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/tests/test_commonaccesslogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,47 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Common Access Logger tests +""" +import unittest +import logging + + +class TestCommonAccessLogger(unittest.TestCase): + + def test_default_constructor(self): + from zope.server.http.commonaccesslogger import CommonAccessLogger + from zope.server.logger.unresolvinglogger import UnresolvingLogger + from zope.server.logger.pythonlogger import PythonLogger + logger = CommonAccessLogger() + # CommonHitLogger is registered as an argumentless factory via + # ZCML, so the defaults should be sensible + self.assert_(isinstance(logger.output, UnresolvingLogger)) + self.assert_(isinstance(logger.output.logger, PythonLogger)) + self.assert_(logger.output.logger.name, 'accesslog') + self.assert_(logger.output.logger.level, logging.INFO) + + # TODO: please add unit tests for other methods as well: + # compute_timezone_for_log + # log_date_string + # log + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestCommonAccessLogger)) + return suite + + +if __name__ == '__main__': + unittest.main(defaultTest="test_suite") diff -Nru zope3-3.4.0/src/zope/server/http/tests/test_httpdate.py zope3-3.5~bzr18/src/zope/server/http/tests/test_httpdate.py --- zope3-3.4.0/src/zope/server/http/tests/test_httpdate.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/tests/test_httpdate.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,33 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Test HTTP date converters +""" +import unittest +from zope.server.http.http_date import build_http_date, parse_http_date + +class Tests(unittest.TestCase): + + # test roundtrip conversion. + def testDateRoundTrip(self): + from time import time + t = int(time()) + self.assertEquals(t, parse_http_date(build_http_date(t))) + + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Tests) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/server/http/tests/test_httprequestparser.py zope3-3.5~bzr18/src/zope/server/http/tests/test_httprequestparser.py --- zope3-3.4.0/src/zope/server/http/tests/test_httprequestparser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/tests/test_httprequestparser.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,111 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""HTTP Request Parser tests +""" +import unittest +from zope.server.http.httprequestparser import HTTPRequestParser +from zope.server.adjustments import Adjustments + + +my_adj = Adjustments() + +class Tests(unittest.TestCase): + + def setUp(self): + self.parser = HTTPRequestParser(my_adj) + + def feed(self, data): + parser = self.parser + for n in xrange(100): # make sure we never loop forever + consumed = parser.received(data) + data = data[consumed:] + if parser.completed: + return + raise ValueError('Looping') + + def testSimpleGET(self): + data = """\ +GET /foobar HTTP/8.4 +FirstName: mickey +lastname: Mouse +content-length: 7 + +Hello. +""" + parser = self.parser + self.feed(data) + self.failUnless(parser.completed) + self.assertEqual(parser.version, '8.4') + self.failIf(parser.empty) + self.assertEqual(parser.headers, + {'FIRSTNAME': 'mickey', + 'LASTNAME': 'Mouse', + 'CONTENT_LENGTH': '7', + }) + self.assertEqual(parser.path, '/foobar') + self.assertEqual(parser.command, 'GET') + self.assertEqual(parser.query, None) + self.assertEqual(parser.getBodyStream().getvalue(), 'Hello.\n') + + def testComplexGET(self): + data = """\ +GET /foo/a+%2B%2F%C3%A4%3D%26a%3Aint?d=b+%2B%2F%3D%26b%3Aint&c+%2B%2F%3D%26c%3Aint=6 HTTP/8.4 +FirstName: mickey +lastname: Mouse +content-length: 10 + +Hello mickey. +""" + parser = self.parser + self.feed(data) + self.assertEqual(parser.command, 'GET') + self.assertEqual(parser.version, '8.4') + self.failIf(parser.empty) + self.assertEqual(parser.headers, + {'FIRSTNAME': 'mickey', + 'LASTNAME': 'Mouse', + 'CONTENT_LENGTH': '10', + }) + # path should be utf-8 encoded + self.assertEqual(parser.path, '/foo/a++/\xc3\xa4=&a:int') + self.assertEqual(parser.query, + 'd=b+%2B%2F%3D%26b%3Aint&c+%2B%2F%3D%26c%3Aint=6') + self.assertEqual(parser.getBodyStream().getvalue(), 'Hello mick') + + def testDuplicateHeaders(self): + # Ensure that headers with the same key get concatenated as per + # RFC2616. + data = """\ +GET /foobar HTTP/8.4 +x-forwarded-for: 10.11.12.13 +x-forwarded-for: unknown,127.0.0.1 +X-Forwarded_for: 255.255.255.255 +content-length: 7 + +Hello. +""" + self.feed(data) + self.failUnless(self.parser.completed) + self.assertEqual(self.parser.headers, { + 'CONTENT_LENGTH': '7', + 'X_FORWARDED_FOR': + '10.11.12.13, unknown,127.0.0.1, 255.255.255.255', + }) + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Tests) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/server/http/tests/test_httpserver.py zope3-3.5~bzr18/src/zope/server/http/tests/test_httpserver.py --- zope3-3.4.0/src/zope/server/http/tests/test_httpserver.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/tests/test_httpserver.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,392 @@ +############################################################################## +# +# 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 HTTP Server +""" +import unittest +from asyncore import socket_map, poll +import socket + +from threading import Thread, Event +from zope.server.taskthreads import ThreadedTaskDispatcher +from zope.server.adjustments import Adjustments +from zope.server.interfaces import ITask +from zope.server.tests.asyncerror import AsyncoreErrorHook +from zope.interface import implements + +from httplib import HTTPConnection +from httplib import HTTPResponse as ClientHTTPResponse + +from time import sleep, time + +td = ThreadedTaskDispatcher() + +LOCALHOST = '127.0.0.1' +SERVER_PORT = 0 # Set these port numbers to 0 to auto-bind, or +CONNECT_TO_PORT = 0 # use specific numbers to inspect using TCPWatch. + + +my_adj = Adjustments() +# Reduce overflows to make testing easier. +my_adj.outbuf_overflow = 10000 +my_adj.inbuf_overflow = 10000 + + + +class SleepingTask(object): + + implements(ITask) + + def service(self): + sleep(0.2) + + def cancel(self): + pass + + def defer(self): + pass + + +class Tests(unittest.TestCase, AsyncoreErrorHook): + + def setUp(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.http.httpserver import HTTPServer + class EchoHTTPServer(HTTPServer): + + def executeRequest(self, task): + headers = task.request_data.headers + if 'CONTENT_LENGTH' in headers: + cl = headers['CONTENT_LENGTH'] + task.response_headers['Content-Length'] = cl + instream = task.request_data.getBodyStream() + while 1: + data = instream.read(8192) + if not data: + break + task.write(data) + + td.setThreadCount(4) + if len(socket_map) != 1: + # Let sockets die off. + # TODO tests should be more careful to clear the socket map. + poll(0.1) + self.orig_map_size = len(socket_map) + self.hook_asyncore_error() + self.server = EchoHTTPServer(LOCALHOST, SERVER_PORT, + task_dispatcher=td, adj=my_adj) + if CONNECT_TO_PORT == 0: + self.port = self.server.socket.getsockname()[1] + else: + self.port = CONNECT_TO_PORT + self.run_loop = 1 + self.counter = 0 + self.thread_started = Event() + self.thread = Thread(target=self.loop) + self.thread.setDaemon(True) + self.thread.start() + self.thread_started.wait(10.0) + self.assert_(self.thread_started.isSet()) + + def tearDown(self): + self.run_loop = 0 + self.thread.join() + td.shutdown() + self.server.close() + # Make sure all sockets get closed by asyncore normally. + timeout = time() + 5 + while 1: + if len(socket_map) == self.orig_map_size: + # Clean! + break + if time() >= timeout: + self.fail('Leaked a socket: %s' % `socket_map`) + poll(0.1) + self.unhook_asyncore_error() + + def loop(self): + self.thread_started.set() + while self.run_loop: + self.counter = self.counter + 1 + #print 'loop', self.counter + poll(0.1) + + def testEchoResponse(self, h=None, add_headers=None, body=''): + if h is None: + h = HTTPConnection(LOCALHOST, self.port) + headers = {} + if add_headers: + headers.update(add_headers) + headers["Accept"] = "text/plain" + # Content-Length header automatically added by HTTPConnection.request + #if body: + # headers["Content-Length"] = str(int(len(body))) + h.request("GET", "/", body, headers) + response = h.getresponse() + self.failUnlessEqual(int(response.status), 200) + length = int(response.getheader('Content-Length', '0')) + response_body = response.read() + self.failUnlessEqual(length, len(response_body)) + self.failUnlessEqual(response_body, body) + + def testMultipleRequestsWithoutBody(self): + # Tests the use of multiple requests in a single connection. + h = HTTPConnection(LOCALHOST, self.port) + for n in range(3): + self.testEchoResponse(h) + self.testEchoResponse(h, {'Connection': 'close'}) + + def testMultipleRequestsWithBody(self): + # Tests the use of multiple requests in a single connection. + h = HTTPConnection(LOCALHOST, self.port) + for n in range(3): + self.testEchoResponse(h, body='Hello, world!') + self.testEchoResponse(h, {'Connection': 'close'}) + + def testPipelining(self): + # Tests the use of several requests issued at once. + s = ("GET / HTTP/1.0\r\n" + "Connection: %s\r\n" + "Content-Length: %d\r\n" + "\r\n" + "%s") + to_send = '' + count = 25 + for n in range(count): + body = "Response #%d\r\n" % (n + 1) + if n + 1 < count: + conn = 'keep-alive' + else: + conn = 'close' + to_send += s % (conn, len(body), body) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((LOCALHOST, self.port)) + sock.send(to_send) + for n in range(count): + expect_body = "Response #%d\r\n" % (n + 1) + response = ClientHTTPResponse(sock) + response.begin() + self.failUnlessEqual(int(response.status), 200) + length = int(response.getheader('Content-Length', '0')) + response_body = response.read(length) + self.failUnlessEqual(length, len(response_body)) + self.failUnlessEqual(response_body, expect_body) + + def testWithoutCRLF(self): + # Tests the use of just newlines rather than CR/LFs. + data = "Echo\nthis\r\nplease" + s = ("GET / HTTP/1.0\n" + "Connection: close\n" + "Content-Length: %d\n" + "\n" + "%s") % (len(data), data) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((LOCALHOST, self.port)) + sock.send(s) + response = ClientHTTPResponse(sock) + response.begin() + self.failUnlessEqual(int(response.status), 200) + length = int(response.getheader('Content-Length', '0')) + response_body = response.read(length) + self.failUnlessEqual(length, len(data)) + self.failUnlessEqual(response_body, data) + + def testLargeBody(self): + # Tests the use of multiple requests in a single connection. + h = HTTPConnection(LOCALHOST, self.port) + s = 'This string has 32 characters.\r\n' * 32 # 1024 characters. + self.testEchoResponse(h, body=(s * 1024)) # 1 MB + self.testEchoResponse(h, {'Connection': 'close'}, + body=(s * 100)) # 100 KB + + def testManyClients(self): + import sys + + # Set the number of connections to make. A previous comment said + # Linux kernel (2.4.8) doesn't like > 128. + # The test used to use 50. Win98SE can't handle that many, dying + # with + # File "C:\PYTHON23\Lib\httplib.py", line 548, in connect + # raise socket.error, msg + # error: (10055, 'No buffer space available') + nconn = 50 + if sys.platform == 'win32': + platform = sys.getwindowsversion()[3] + if platform < 2: + # 0 is Win32s on Windows 3.1 + # 1 is 95/98/ME + # 2 is NT/2000/XP + + # Pre-NT. 20 should work. The exact number you can get away + # with depends on what you're running at the same time (e.g., + # browsers and AIM and email delivery consume sockets too). + nconn = 20 + + conns = [] + for n in range(nconn): + #print 'open', n, clock() + h = HTTPConnection(LOCALHOST, self.port) + #h.debuglevel = 1 + h.request("GET", "/", headers={"Accept": "text/plain"}) + conns.append(h) + # If you uncomment the next line, you can raise the + # number of connections much higher without running + # into delays. + #sleep(0.01) + responses = [] + for h in conns: + response = h.getresponse() + self.failUnlessEqual(response.status, 200) + responses.append(response) + for response in responses: + response.read() + + def testThreading(self): + # Ensures the correct number of threads keep running. + for n in range(4): + td.addTask(SleepingTask()) + # Try to confuse the task manager. + td.setThreadCount(2) + td.setThreadCount(1) + sleep(0.5) + # There should be 1 still running. + self.failUnlessEqual(len(td.threads), 1) + + def testChunkingRequestWithoutContent(self): + h = HTTPConnection(LOCALHOST, self.port) + h.request("GET", "/", headers={"Accept": "text/plain", + "Transfer-Encoding": "chunked"}) + h.send("0\r\n\r\n") + response = h.getresponse() + self.failUnlessEqual(int(response.status), 200) + response_body = response.read() + self.failUnlessEqual(response_body, '') + + def testChunkingRequestWithContent(self): + control_line="20;\r\n" # 20 hex = 32 dec + s = 'This string has 32 characters.\r\n' + expect = s * 12 + + h = HTTPConnection(LOCALHOST, self.port) + h.request("GET", "/", headers={"Accept": "text/plain", + "Transfer-Encoding": "chunked"}) + for n in range(12): + h.send(control_line) + h.send(s) + h.send("0\r\n\r\n") + response = h.getresponse() + self.failUnlessEqual(int(response.status), 200) + response_body = response.read() + self.failUnlessEqual(response_body, expect) + + def testKeepaliveHttp10(self): + # Handling of Keep-Alive within HTTP 1.0 + data = "Default: Don't keep me alive" + s = ("GET / HTTP/1.0\n" + "Content-Length: %d\n" + "\n" + "%s") % (len(data), data) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((LOCALHOST, self.port)) + sock.send(s) + response = ClientHTTPResponse(sock) + response.begin() + self.failUnlessEqual(int(response.status), 200) + connection = response.getheader('Connection', '') + # We sent no Connection: Keep-Alive header + # Connection: close (or no header) is default. + self.failUnless(connection != 'Keep-Alive') + + # If header Connection: Keep-Alive is explicitly sent, + # we want to keept the connection open, we also need to return + # the corresponding header + data = "Keep me alive" + s = ("GET / HTTP/1.0\n" + "Connection: Keep-Alive\n" + "Content-Length: %d\n" + "\n" + "%s") % (len(data), data) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((LOCALHOST, self.port)) + sock.send(s) + response = ClientHTTPResponse(sock) + response.begin() + self.failUnlessEqual(int(response.status), 200) + connection = response.getheader('Connection', '') + self.failUnlessEqual(connection, 'Keep-Alive') + + def testKeepaliveHttp11(self): + # Handling of Keep-Alive within HTTP 1.1 + + # All connections are kept alive, unless stated otherwise + data = "Default: Keep me alive" + s = ("GET / HTTP/1.1\n" + "Content-Length: %d\n" + "\n" + "%s") % (len(data), data) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((LOCALHOST, self.port)) + sock.send(s) + response = ClientHTTPResponse(sock) + response.begin() + self.failUnlessEqual(int(response.status), 200) + self.failUnless(response.getheader('connection') != 'close') + + # Explicitly set keep-alive + data = "Default: Keep me alive" + s = ("GET / HTTP/1.1\n" + "Connection: keep-alive\n" + "Content-Length: %d\n" + "\n" + "%s") % (len(data), data) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((LOCALHOST, self.port)) + sock.send(s) + response = ClientHTTPResponse(sock) + response.begin() + self.failUnlessEqual(int(response.status), 200) + self.failUnless(response.getheader('connection') != 'close') + + # no idea why the test publisher handles this request incorrectly + # it would be less typing in the test :) + # h = HTTPConnection(LOCALHOST, self.port) + # h.request("GET", "/") + # response = h.getresponse() + # self.failUnlessEqual(int(response.status), 200) + # self.failUnless(response.getheader('connection') != 'close') + + # specifying Connection: close explicitly + data = "Don't keep me alive" + s = ("GET / HTTP/1.1\n" + "Connection: close\n" + "Content-Length: %d\n" + "\n" + "%s") % (len(data), data) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((LOCALHOST, self.port)) + sock.send(s) + response = ClientHTTPResponse(sock) + response.begin() + self.failUnlessEqual(int(response.status), 200) + self.failUnlessEqual(response.getheader('connection'), 'close') + + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Tests) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/server/http/tests/test_wsgiserver.py zope3-3.5~bzr18/src/zope/server/http/tests/test_wsgiserver.py --- zope3-3.4.0/src/zope/server/http/tests/test_wsgiserver.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/tests/test_wsgiserver.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,276 @@ +############################################################################## +# +# Copyright (c) 2001 Zope Foundation and Contributors. +# +# This software is subject to the provisions of the Zope Public License, +# Version 1.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 Puvlisher-based HTTP Server +""" +import StringIO +import unittest +from asyncore import socket_map, poll +from threading import Thread +from time import sleep +from httplib import HTTPConnection + +from zope.server.taskthreads import ThreadedTaskDispatcher + +from zope.component.testing import PlacelessSetup +import zope.component + +from zope.i18n.interfaces import IUserPreferredCharsets + +from zope.publisher.publish import publish +from zope.publisher.http import IHTTPRequest +from zope.publisher.http import HTTPCharsets +from zope.publisher.browser import BrowserRequest +from zope.publisher.base import DefaultPublication +from zope.publisher.interfaces import Redirect, Retry +from zope.publisher.http import HTTPRequest + +td = ThreadedTaskDispatcher() + +LOCALHOST = '127.0.0.1' + +HTTPRequest.STAGGER_RETRIES = 0 # Don't pause. + + +class Conflict(Exception): + """ + Pseudo ZODB conflict error. + """ + + +class PublicationWithConflict(DefaultPublication): + + def handleException(self, object, request, exc_info, retry_allowed=1): + if exc_info[0] is Conflict and retry_allowed: + # This simulates a ZODB retry. + raise Retry(exc_info) + else: + DefaultPublication.handleException(self, object, request, exc_info, + retry_allowed) + +class Accepted(Exception): + pass + +class tested_object(object): + """Docstring required by publisher.""" + tries = 0 + + def __call__(self, REQUEST): + return 'URL invoked: %s' % REQUEST.URL + + def redirect_method(self, REQUEST): + "Generates a redirect using the redirect() method." + REQUEST.response.redirect("/redirect") + + def redirect_exception(self): + "Generates a redirect using an exception." + raise Redirect("/exception") + + def conflict(self, REQUEST, wait_tries): + """ + Returns 202 status only after (wait_tries) tries. + """ + if self.tries >= int(wait_tries): + raise Accepted + else: + self.tries += 1 + raise Conflict + + +class WSGIInfo(object): + """Docstring required by publisher""" + + def __call__(self, REQUEST): + """Return a list of variables beginning with 'wsgi.'""" + r = [] + for name in REQUEST.keys(): + if name.startswith('wsgi.'): + r.append(name) + return ' '.join(r) + + def version(self, REQUEST): + """Return WSGI version""" + return str(REQUEST['wsgi.version']) + + def url_scheme(self, REQUEST): + """Return WSGI URL scheme""" + return REQUEST['wsgi.url_scheme'] + + def multithread(self, REQUEST): + """Return WSGI multithreadedness""" + return str(bool(REQUEST['wsgi.multithread'])) + + def multiprocess(self, REQUEST): + """Return WSGI multiprocessedness""" + return str(bool(REQUEST['wsgi.multiprocess'])) + + def run_once(self, REQUEST): + """Return whether WSGI app is invoked only once or not""" + return str(bool(REQUEST['wsgi.run_once'])) + +class Tests(PlacelessSetup, unittest.TestCase): + + def setUp(self): + # import only now to prevent the testrunner from importing it too early + # Otherwise dualmodechannel.the_trigger is closed by the ZEO tests + from zope.server.http.wsgihttpserver import WSGIHTTPServer + super(Tests, self).setUp() + zope.component.provideAdapter(HTTPCharsets, [IHTTPRequest], + IUserPreferredCharsets, '') + obj = tested_object() + obj.folder = tested_object() + obj.folder.item = tested_object() + obj._protected = tested_object() + obj.wsgi = WSGIInfo() + + pub = PublicationWithConflict(obj) + + def application(environ, start_response): + request = BrowserRequest(environ['wsgi.input'], environ) + request.setPublication(pub) + request = publish(request) + response = request.response + start_response(response.getStatusString(), response.getHeaders()) + return response.consumeBodyIter() + + td.setThreadCount(4) + # Bind to any port on localhost. + self.server = WSGIHTTPServer(application, 'Browser', + LOCALHOST, 0, task_dispatcher=td) + + self.port = self.server.socket.getsockname()[1] + self.run_loop = 1 + self.thread = Thread(target=self.loop) + self.thread.start() + sleep(0.1) # Give the thread some time to start. + + def tearDown(self): + self.run_loop = 0 + self.thread.join() + td.shutdown() + self.server.close() + super(Tests, self).tearDown() + + def loop(self): + while self.run_loop: + poll(0.1, socket_map) + + def invokeRequest(self, path='/', add_headers=None, request_body=''): + h = HTTPConnection(LOCALHOST, self.port) + h.putrequest('GET', path) + h.putheader('Accept', 'text/plain') + if add_headers: + for k, v in add_headers.items(): + h.putheader(k, v) + if request_body: + h.putheader('Content-Length', str(int(len(request_body)))) + h.endheaders() + if request_body: + h.send(request_body) + response = h.getresponse() + length = int(response.getheader('Content-Length', '0')) + if length: + response_body = response.read(length) + else: + response_body = '' + + self.assertEqual(length, len(response_body)) + + return response.status, response_body + + + def testDeeperPath(self): + status, response_body = self.invokeRequest('/folder/item') + self.assertEqual(status, 200) + expect_response = 'URL invoked: http://%s:%d/folder/item' % ( + LOCALHOST, self.port) + self.assertEqual(response_body, expect_response) + + def testNotFound(self): + status, response_body = self.invokeRequest('/foo/bar') + self.assertEqual(status, 404) + + def testUnauthorized(self): + status, response_body = self.invokeRequest('/_protected') + self.assertEqual(status, 401) + + def testRedirectMethod(self): + status, response_body = self.invokeRequest('/redirect_method') + self.assertEqual(status, 303) + + def testRedirectException(self): + status, response_body = self.invokeRequest('/redirect_exception') + self.assertEqual(status, 303) + status, response_body = self.invokeRequest('/folder/redirect_exception') + self.assertEqual(status, 303) + + def testConflictRetry(self): + status, response_body = self.invokeRequest('/conflict?wait_tries=2') + # Expect the "Accepted" response since the retries will succeed. + self.assertEqual(status, 202) + + def testFailedConflictRetry(self): + status, response_body = self.invokeRequest('/conflict?wait_tries=10') + # Expect a "Conflict" response since there will be too many + # conflicts. + self.assertEqual(status, 409) + + def testWSGIVariables(self): + # Assert that the environment contains all required WSGI variables + status, response_body = self.invokeRequest('/wsgi') + wsgi_variables = set(response_body.split()) + self.assertEqual(wsgi_variables, + set(['wsgi.version', 'wsgi.url_scheme', 'wsgi.input', + 'wsgi.errors', 'wsgi.multithread', + 'wsgi.multiprocess', 'wsgi.run_once'])) + + def testWSGIVersion(self): + status, response_body = self.invokeRequest('/wsgi/version') + self.assertEqual("(1, 0)", response_body) + + def testWSGIURLScheme(self): + status, response_body = self.invokeRequest('/wsgi/url_scheme') + self.assertEqual('http', response_body) + + def testWSGIMultithread(self): + status, response_body = self.invokeRequest('/wsgi/multithread') + self.assertEqual('True', response_body) + + def testWSGIMultiprocess(self): + status, response_body = self.invokeRequest('/wsgi/multiprocess') + self.assertEqual('True', response_body) + + def testWSGIRunOnce(self): + status, response_body = self.invokeRequest('/wsgi/run_once') + self.assertEqual('False', response_body) + + def test_server_uses_iterable(self): + # Make sure that the task write method isn't called with a + # str or non iterable + class FakeTask: + getCGIEnvironment = lambda _: {} + class request_data: + getBodyStream = lambda _: StringIO.StringIO() + request_data = request_data() + setResponseStatus = appendResponseHeaders = lambda *_: None + + def write(self, v): + if isinstance(v, str): + raise TypeError("Should only write iterables") + list(v) + self.server.executeRequest(FakeTask()) + + +def test_suite(): + return unittest.TestSuite(unittest.makeSuite(Tests)) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/server/http/tests/wsgi_app.py zope3-3.5~bzr18/src/zope/server/http/tests/wsgi_app.py --- zope3-3.4.0/src/zope/server/http/tests/wsgi_app.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/tests/wsgi_app.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ +# a WSGI app for testing + +def test_app(environ, start_response): + status = '200 OK' + response_headers = [('Content-type', 'text/plain')] + start_response(status, response_headers) + return ["Hello world! zope.server is delivering WSGI v%s.%s using %s." + % (environ['wsgi.version'] + (environ['wsgi.url_scheme'],))] + +def test_app_factory(global_config, **local_config): + return test_app diff -Nru zope3-3.4.0/src/zope/server/http/wsgihttpserver.py zope3-3.5~bzr18/src/zope/server/http/wsgihttpserver.py --- zope3-3.4.0/src/zope/server/http/wsgihttpserver.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/http/wsgihttpserver.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,124 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""WSGI-compliant HTTP Server that uses the Zope Publisher for executing a task. +""" +import asyncore +import re +import sys +from zope.server.http.httpserver import HTTPServer +from zope.server.taskthreads import ThreadedTaskDispatcher +import zope.security.management + + +def fakeWrite(body): + raise NotImplementedError( + "Zope 3's HTTP Server does not support the WSGI write() function.") + + +class WSGIHTTPServer(HTTPServer): + """Zope Publisher-specific WSGI-compliant HTTP Server""" + + application = None + + def __init__(self, application, sub_protocol=None, *args, **kw): + + if sys.platform[:3] == "win" and args[0] == 'localhost': + args = ('',) + args[1:] + + self.application = application + + if sub_protocol: + self.SERVER_IDENT += ' (%s)' %str(sub_protocol) + + HTTPServer.__init__(self, *args, **kw) + + def _constructWSGIEnvironment(self, task): + env = task.getCGIEnvironment() + + # deduce the URL scheme (http or https) + if (env.get('HTTPS', '').lower() == "on" or + env.get('SERVER_PORT_SECURE') == "1"): + protocol = 'https' + else: + protocol = 'http' + + # the following environment variables are required by the WSGI spec + env['wsgi.version'] = (1,0) + env['wsgi.url_scheme'] = protocol + env['wsgi.errors'] = sys.stderr # apps should use the logging module + env['wsgi.multithread'] = True + env['wsgi.multiprocess'] = True + env['wsgi.run_once'] = False + env['wsgi.input'] = task.request_data.getBodyStream() + return env + + def executeRequest(self, task): + """Overrides HTTPServer.executeRequest().""" + env = self._constructWSGIEnvironment(task) + + def start_response(status, headers): + # Prepare the headers for output + status, reason = re.match('([0-9]*) (.*)', status).groups() + task.setResponseStatus(status, reason) + task.appendResponseHeaders(['%s: %s' % i for i in headers]) + + # Return the write method used to write the response data. + return fakeWrite + + # Call the application to handle the request and write a response + task.write(self.application(env, start_response)) + + +class PMDBWSGIHTTPServer(WSGIHTTPServer): + """Enter the post-mortem debugger when there's an error""" + + def executeRequest(self, task): + """Overrides HTTPServer.executeRequest().""" + env = self._constructWSGIEnvironment(task) + env['wsgi.handleErrors'] = False + + def start_response(status, headers): + # Prepare the headers for output + status, reason = re.match('([0-9]*) (.*)', status).groups() + task.setResponseStatus(status, reason) + task.appendResponseHeaders(['%s: %s' % i for i in headers]) + + # Return the write method used to write the response data. + return fakeWrite + + # Call the application to handle the request and write a response + try: + task.write(self.application(env, start_response)) + except: + import sys, pdb + print "%s:" % sys.exc_info()[0] + print sys.exc_info()[1] + zope.security.management.restoreInteraction() + try: + pdb.post_mortem(sys.exc_info()[2]) + raise + finally: + zope.security.management.endInteraction() + + +def run_paste(wsgi_app, global_conf, name='zope.server.http', + host='127.0.0.1', port=8080, threads=4): + port = int(port) + threads = int(threads) + + task_dispatcher = ThreadedTaskDispatcher() + task_dispatcher.setThreadCount(threads) + server = WSGIHTTPServer(wsgi_app, name, host, port, + task_dispatcher=task_dispatcher) + asyncore.loop() diff -Nru zope3-3.4.0/src/zope/server/__init__.py zope3-3.5~bzr18/src/zope/server/__init__.py --- zope3-3.4.0/src/zope/server/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,25 @@ +############################################################################## +# +# 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 3's Servers + +This package contains generic base classes for channel-based servers, the +servers themselves and helper objects, such as tasks and requests. +""" +import asyncore + +from zope.server.interfaces import IDispatcher +from zope.interface import classImplements + +# Tell the the async.dispatcher that it implements IDispatcher. +classImplements(asyncore.dispatcher, IDispatcher) diff -Nru zope3-3.4.0/src/zope/server/interfaces/ftp.py zope3-3.5~bzr18/src/zope/server/interfaces/ftp.py --- zope3-3.4.0/src/zope/server/interfaces/ftp.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/interfaces/ftp.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,413 @@ +############################################################################## +# 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. +############################################################################## +"""FTP server specific interfaces. +""" +from zope.interface import Interface + +class IFTPCommandHandler(Interface): + """This interface defines all the FTP commands that are supported by the + server. + + Every command takes the command line as first arguments, since it is + responsible + """ + + def cmd_abor(args): + """Abort operation. No read access required. + """ + + def cmd_appe(args): + """Append to a file. Write access required. + """ + + def cmd_cdup(args): + """Change to parent of current working directory. + """ + + def cmd_cwd(args): + """Change working directory. + """ + + def cmd_dele(args): + """Delete a file. Write access required. + """ + + def cmd_help(args): + """Give help information. No read access required. + """ + + def cmd_list(args): + """Give list files in a directory or displays the info of one file. + """ + + def cmd_mdtm(args): + """Show last modification time of file. + + Example output: 213 19960301204320 + + Geez, there seems to be a second syntax for this fiel, where one + can also set the modification time using: + MDTM datestring pathname + + """ + + def cmd_mkd(args): + """Make a directory. Write access required. + """ + + def cmd_mode(args): + """Set file transfer mode. No read access required. Obselete. + """ + + def cmd_nlst(args): + """Give name list of files in directory. + """ + + def cmd_noop(args): + """Do nothing. No read access required. + """ + + def cmd_pass(args): + """Specify password. + """ + + def cmd_pasv(args): + """Prepare for server-to-server transfer. No read access required. + """ + + def cmd_port(args): + """Specify data connection port. No read access required. + """ + + def cmd_pwd(args): + """Print the current working directory. + """ + + def cmd_quit(args): + """Terminate session. No read access required. + """ + + def cmd_rest(args): + """Restart incomplete transfer. + """ + + def cmd_retr(args): + """Retrieve a file. + """ + + def cmd_rmd(args): + """Remove a directory. Write access required. + """ + + def cmd_rnfr(args): + """Specify rename-from file name. Write access required. + """ + + def cmd_rnto(args): + """Specify rename-to file name. Write access required. + """ + + def cmd_size(args): + """Return size of file. + """ + + def cmd_stat(args): + """Return status of server. No read access required. + """ + + def cmd_stor(args): + """Store a file. Write access required. + """ + + def cmd_stru(args): + """Set file transfer structure. Obselete.""" + + def cmd_syst(args): + """Show operating system type of server system. + + No read access required. + + Replying to this command is of questionable utility, + because this server does not behave in a predictable way + w.r.t. the output of the LIST command. We emulate Unix ls + output, but on win32 the pathname can contain drive + information at the front Currently, the combination of + ensuring that os.sep == '/' and removing the leading slash + when necessary seems to work. [cd'ing to another drive + also works] + + This is how wuftpd responds, and is probably the most + expected. The main purpose of this reply is so that the + client knows to expect Unix ls-style LIST output. + + one disadvantage to this is that some client programs + assume they can pass args to /bin/ls. a few typical + responses: + + 215 UNIX Type: L8 (wuftpd) + 215 Windows_NT version 3.51 + 215 VMS MultiNet V3.3 + 500 'SYST': command not understood. (SVR4) + """ + + def cmd_type(args): + """Specify data transfer type. No read access required. + """ + + def cmd_user(args): + """Specify user name. No read access required. + """ + + + +# this is the command list from the wuftpd man page +# '!' requires write access +# +not_implemented_commands = { + 'acct': 'specify account (ignored)', + 'allo': 'allocate storage (vacuously)', + 'site': 'non-standard commands (see next section)', + 'stou': 'store a file with a unique name', #! + 'xcup': 'change to parent of current working directory (deprecated)', + 'xcwd': 'change working directory (deprecated)', + 'xmkd': 'make a directory (deprecated)', #! + 'xpwd': 'print the current working directory (deprecated)', + 'xrmd': 'remove a directory (deprecated)', #! +} + + +class IFileSystemAccess(Interface): + """Provides authenticated access to a filesystem.""" + + def authenticate(credentials): + """Verifies filesystem access based on the presented credentials. + + Should raise zope.security.interfaces.Unauthorized if the user can + not be authenticated. + + This method checks only general access and is not used for each + call to open(). Rather, open() should do its own verification. + + Credentials are passed as (username, password) tuples. + """ + + def open(credentials): + """Returns an IFileSystem. + + Should raise zope.security.interfaces.Unauthorized if the user + can not be authenticated. + + Credentials are passed as (username, password) tuples. + """ + + +class IFileSystem(Interface): + """An abstract filesystem. + + Opening files for reading, and listing directories, should + return a producer. + + All paths are POSIX paths, even when run on Windows, + which mainly means that FS implementations always expect forward + slashes, and filenames are case-sensitive. + + `IFileSystem`, in generel, could be created many times per + request. Thus it is not advisable to store state in them. However, if + you have a special kind of `IFileSystemAccess` object that somhow + manages an `IFileSystem` for each set of credentials, then it would be + possible to store some state on this obejct. + """ + + def type(path): + """Return the file type at `path`. + + The return valie is 'd', for a directory, 'f', for a file, and + None if there is no file at `path`. + + This method doesn't raise exceptions. + """ + + def names(path, filter=None): + """Return a sequence of the names in a directory. + + If `filter` is not None, include only those names for which + `filter` returns a true value. + """ + + def ls(path, filter=None): + """Return a sequence of information objects. + + Returm item info objects (see the ls_info operation) for the files + in a directory. + + If `filter` is not None, include only those names for which + `filter` returns a true value. + """ + + def readfile(path, outstream, start=0, end=None): + """Outputs the file at `path` to a stream. + + Data are copied starting from `start`. If `end` is not None, + data are copied up to `end`. + + """ + + def lsinfo(path): + """Return information for a unix-style ls listing for `path`. + + Information is returned as a dictionary containing the following keys: + + type + + The path type, either 'd' or 'f'. + + owner_name + + Defaults to "na". Must not include spaces. + + owner_readable + + Defaults to True. + + owner_writable + + Defaults to True. + + owner_executable + + Defaults to True for directories and False otherwise. + + group_name + + Defaults to "na". Must not include spaces. + + group_readable + + Defaults to True. + + group_writable + + Defaults to True. + + group_executable + + Defaults to True for directories and False otherwise. + + other_readable + + Defaults to False. + + other_writable + + Defaults to False. + + other_executable + + Defaults to True for directories and false otherwise. + + mtime + + Optional time, as a datetime.datetime object. + + nlinks + + The number of links. Defaults to 1. + + size + + The file size. Defaults to 0. + + name + + The file name. + """ + + def mtime(path): + """Return the modification time for the file at `path`. + + This method returns the modification time. It is assumed that the path + exists. You can use the `type(path)` method to determine whether + `path` points to a valid file. + + If the modification time is unknown, then return `None`. + """ + + def size(path): + """Return the size of the file at path. + + This method returns the modification time. It is assumed that the path + exists. You can use the `type(path)` method to determine whether + `path` points to a valid file. + """ + + def mkdir(path): + """Create a directory. + + If it is not possible or allowed to create the directory, an `OSError` + should be raised describing the reason of failure. + """ + + def remove(path): + """Remove a file. Same as unlink. + + If it is not possible or allowed to remove the file, an `OSError` + should be raised describing the reason of failure. + """ + + def rmdir(path): + """Remove a directory. + + If it is not possible or allowed to remove the directory, an `OSError` + should be raised describing the reason of failure. + """ + + def rename(old, new): + """Rename a file or directory.""" + + def writefile(path, instream, start=None, end=None, append=False): + """Write data to a file. + + Both `start` and `end` must be either None or a non-negative + integer. + + If `append` is true, `start` and `end` are ignored. + + If `start` or `end` is not None, they specify the part of the + file that is to be written. + + If `end` is None, the file is truncated after the data are + written. If `end` is not None, any parts of the file after + `end` are left unchanged. + + Note that if `end` is not `None`, and there is not enough data + in the `instream` it will fill the file up to `end`, then the missing + data are undefined. + + If both `start` is `None` and `end` is `None`, then the file contents + are overwritten. + + If `start` is specified and the file doesn't exist or is shorter + than `start`, the data in the file before `start` file will be + undefined. + + If you do not want to handle incorrect starting and ending indices, + you can also raise an `IOError`, which will be properly handled by the + server. + """ + + def writable(path): + """Return boolean indicating whether a file at path is writable. + + Note that a true value should be returned if the file doesn't + exist but its directory is writable. + + """ diff -Nru zope3-3.4.0/src/zope/server/interfaces/__init__.py zope3-3.5~bzr18/src/zope/server/interfaces/__init__.py --- zope3-3.4.0/src/zope/server/interfaces/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/interfaces/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,298 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Server interfaces. +""" +from zope.interface import Interface +from zope.interface import Attribute + +class ISocket(Interface): + """Represents a socket. + + Note: Most of this documentation is taken from the Python Library + Reference. + """ + + def listen(backlog): + """Listen for connections made to the socket. + + The 'backlog' argument specifies the maximum number of queued + connections and should be at least 1; the maximum value is + system-dependent (usually 5). + """ + + def bind(addr): + """Bind the socket to address. + + The socket must not already be bound. + """ + + def connect(address): + """Connect to a remote socket at address.""" + + def accept(): + """Accept a connection. + + The socket must be bound to an address and listening for + connections. The return value is a pair (conn, address) where conn is + a new socket object usable to send and receive data on the connection, + and address is the address bound to the socket on the other end of the + connection. + """ + + def recv(buffer_size): + """Receive data from the socket. + + The return value is a string representing the data received. The + maximum amount of data to be received at once is specified by + bufsize. See the Unix manual page recv(2) for the meaning of the + optional argument flags; it defaults to zero. + """ + + def send(data): + """Send data to the socket. + + The socket must be connected to a remote socket. The optional flags + argument has the same meaning as for recv() above. Returns the number + of bytes sent. Applications are responsible for checking that all data + has been sent; if only some of the data was transmitted, the + application needs to attempt delivery of the remaining data. + """ + + def close(): + """Close the socket. + + All future operations on the socket object will fail. The remote end + will receive no more data (after queued data is flushed). Sockets are + automatically closed when they are garbage-collected. + """ + + +class ITaskDispatcher(Interface): + """An object that accepts tasks and dispatches them to threads. + """ + + def setThreadCount(count): + """Sets the number of handler threads. + """ + + def addTask(task): + """Receives a task and dispatches it to a thread. + + Note that, depending on load, a task may have to wait a + while for its turn. + """ + + def shutdown(cancel_pending=True, timeout=5): + """Shuts down all handler threads and may cancel pending tasks. + """ + + def getPendingTasksEstimate(): + """Returns an estimate of the number of tasks waiting to be serviced. + + This method may be useful for monitoring purposes. If the + number of pending tasks is continually climbing, your server + is becoming overloaded and the operator should be notified. + """ + + +class ITask(Interface): + """ + The interface expected of an object placed in the queue of + a ThreadedTaskDispatcher. Provides facilities for executing + or canceling the task. + """ + + def service(): + """ + Services the task. Either service() or cancel() is called + for every task queued. + """ + + def cancel(): + """ + Called instead of service() during shutdown or if an + exception occurs that prevents the task from being + serviced. Must return quickly and should not throw exceptions. + """ + + def defer(): + """ + Called just before the task is queued to be executed in + a different thread. + """ + +class IDispatcherEventHandler(Interface): + """The Dispatcher can receive several different types of events. This + interface describes the necessary methods that handle these common + event types. + """ + + def handle_read_event(): + """Given a read event, a server has to handle the event and + read the input from the client. + """ + + def handle_write_event(): + """Given a write event, a server has to handle the event and + write the output to the client. + """ + + def handle_expt_event(): + """An exception event was handed to the server. + """ + + def handle_error(): + """An error occurred, but we are still trying to fix it. + """ + + def handle_expt(): + """Handle unhandled exceptions. This is usually a time to log. + """ + + def handle_read(): + """Read output from client. + """ + + def handle_write(): + """Write output via the socket to the client. + """ + + def handle_connect(): + """A client requests a connection, now we need to do soemthing. + """ + + def handle_accept(): + """A connection is accepted. + """ + + def handle_close(): + """A connection is being closed. + """ + + +class IStreamConsumer(Interface): + """Consumes a data stream until reaching a completion point. + + The actual amount to be consumed might not be known ahead of time. + """ + + def received(data): + """Accepts data, returning the number of bytes consumed.""" + + completed = Attribute( + 'completed', 'Set to a true value when finished consuming data.') + + +class IServer(Interface): + """This interface describes the basic base server. + + The most unusual part about the Zope servers (since they all + implement this interface or inherit its base class) is that it + uses a mix of asynchronous and thread-based mechanism to + serve. While the low-level socket listener uses async, the + actual request is executed in a thread. This is important + because even if a request takes a long time to process, the + server can service other requests simultaneously. + """ + + channel_class = Attribute(""" + The channel class defines the type of channel + to be used by the server. See IServerChannel + for more information. + """) + + SERVER_IDENT = Attribute(""" + This string identifies the server. By default + this is 'zope.server.' and should be + overridden. + """) + + +class IDispatcherLogging(Interface): + """This interface provides methods through which the Dispatcher will + write its logs. A distinction is made between hit and message logging, + since they often go to different output types and can have very + different structure. + """ + + def log (message): + """Logs general requests made to the server. + """ + + def log_info(message, type='info'): + """Logs informational messages, warnings and errors. + """ + + +class IServerChannel(Interface): + + parser_class = Attribute("""Subclasses must provide a parser class""") + task_class = Attribute("""Specifies the ITask class to be used for + generating tasks.""") + + def queue_task(task): + """Queues a channel-related task to be processed in sequence. + """ + + +class IDispatcher(ISocket, IDispatcherEventHandler, IDispatcherLogging): + """The dispatcher is the most low-level component of a server. + + 1. It manages the socket connections and distributes the + request to the appropriate channel. + + 2. It handles the events passed to it, such as reading input, + writing output and handling errors. More about this + functionality can be found in IDispatcherEventHandler. + + 3. It handles logging of the requests passed to the server as + well as other informational messages and erros. Please see + IDispatcherLogging for more details. + + Note: Most of this documentation is taken from the Python + Library Reference. + """ + + def add_channel(map=None): + """After the low-level socket connection negotiation is + completed, a channel is created that handles all requests + and responses until the end of the connection. + """ + + def del_channel(map=None): + """Delete a channel. This should include also closing the + socket to the client. + """ + + def create_socket(family, type): + """This is identical to the creation of a normal socket, and + will use the same options for creation. Refer to the socket + documentation for information on creating sockets. + """ + + def readable(): + """Each time through the select() loop, the set of sockets is + scanned, and this method is called to see if there is any + interest in reading. The default method simply returns 1, + indicating that by default, all channels will be + interested. + """ + + def writable(): + """Each time through the select() loop, the set of sockets is + scanned, and this method is called to see if there is any + interest in writing. The default method simply returns 1, + indicating that by default, all channels will be + interested. + """ diff -Nru zope3-3.4.0/src/zope/server/interfaces/logger.py zope3-3.5~bzr18/src/zope/server/interfaces/logger.py --- zope3-3.4.0/src/zope/server/interfaces/logger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/interfaces/logger.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,34 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Logger interfaces +""" +from zope.interface import Interface + + +class IRequestLogger(Interface): + """This interface describes a requets logger, which logs + ip addresses and messages. + """ + + def logRequest(ip, message): + """Logs the ip address and message at the appropriate place.""" + + +class IMessageLogger(Interface): + """This interface describes a message logger, which logs + with the resolution of one message. + """ + + def logMessage(message): + """Logs the message at the appropriate place.""" diff -Nru zope3-3.4.0/src/zope/server/linereceiver/__init__.py zope3-3.5~bzr18/src/zope/server/linereceiver/__init__.py --- zope3-3.4.0/src/zope/server/linereceiver/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/linereceiver/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/server/linereceiver/linecommandparser.py zope3-3.5~bzr18/src/zope/server/linereceiver/linecommandparser.py --- zope3-3.4.0/src/zope/server/linereceiver/linecommandparser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/linereceiver/linecommandparser.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,68 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Line Command Parser +""" +from zope.server.interfaces import IStreamConsumer +from zope.interface import implements + + +class LineCommandParser(object): + """Line Command parser. Arguments are left alone for now.""" + + implements(IStreamConsumer) + + # See IStreamConsumer + completed = 0 + inbuf = '' + cmd = '' + args = '' + empty = 0 + + max_line_length = 1024 # Not a hard limit + + + def __init__(self, adj): + """ + adj is an Adjustments object. + """ + self.adj = adj + + + def received(self, data): + 'See IStreamConsumer' + if self.completed: + return 0 # Can't consume any more. + pos = data.find('\n') + datalen = len(data) + if pos < 0: + self.inbuf = self.inbuf + data + if len(self.inbuf) > self.max_line_length: + # Don't accept any more. + self.completed = 1 + return datalen + else: + # Line finished. + s = data[:pos + 1] + self.inbuf = self.inbuf + s + self.completed = 1 + line = self.inbuf.strip() + self.parseLine(line) + return len(s) + + def parseLine(self, line): + parts = line.split(' ', 1) + if len(parts) == 2: + self.cmd, self.args = parts + else: + self.cmd = parts[0] diff -Nru zope3-3.4.0/src/zope/server/linereceiver/lineserverchannel.py zope3-3.5~bzr18/src/zope/server/linereceiver/lineserverchannel.py --- zope3-3.4.0/src/zope/server/linereceiver/lineserverchannel.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/linereceiver/lineserverchannel.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,140 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Line receiver channel + +This channels evaluates requests line by line. This is particular useful for +protocols that use a line-based command structure. +""" + +from asyncore import compact_traceback +import os +import sys + +from zope.server.serverchannelbase import ServerChannelBase +from zope.server.linereceiver.linecommandparser import LineCommandParser +from zope.server.linereceiver.linetask import LineTask + +DEBUG = os.environ.get('ZOPE_SERVER_DEBUG') + +class LineServerChannel(ServerChannelBase): + """The Line Server Channel represents a connection to a particular + client. We can therefore store information here.""" + + # Wrapper class that is used to execute a command in a different thread + task_class = LineTask + + # Class that is being initialized to parse the input + parser_class = LineCommandParser + + # List of commands that are always available + special_commands = ('cmd_quit') + + # Commands that are run in a separate thread + thread_commands = () + + # Define the authentication status of the channel. Note that only the + # "special commands" can be executed without having authenticated. + authenticated = 0 + + # Define the reply code for non-authenticated responses + not_auth_reply = 'LOGIN_REQUIRED' + + # Define the reply code for an unrecognized command + unknown_reply = 'CMD_UNKNOWN' + + # Define the error message that occurs, when the reply code was not found. + reply_error = '500 Unknown Reply Code: %s.' + + # Define the status messages + status_messages = { + 'CMD_UNKNOWN' : "500 '%s': command not understood.", + 'INTERNAL_ERROR' : "500 Internal error: %s", + 'LOGIN_REQUIRED' : '530 Please log in with USER and PASS', + } + + + def handle_request(self, command): + """Processes a command. + + Some commands use an alternate thread. + """ + assert isinstance(command, LineCommandParser) + cmd = command.cmd + method = 'cmd_' + cmd.lower() + if (not self.authenticated and method not in self.special_commands): + # The user is not logged in, therefore don't allow anything + self.reply(self.not_auth_reply) + + elif method in self.thread_commands: + # Process in another thread. + task = self.task_class(self, command, method) + self.queue_task(task) + + elif hasattr(self, method): + try: + getattr(self, method)(command.args) + except: + self.exception() + else: + self.reply(self.unknown_reply, cmd.upper()) + + + def reply(self, code, args=(), flush=1): + """ """ + try: + msg = self.status_messages[code] %args + except: + msg = self.reply_error %code + + self.write('%s\r\n' %msg) + + if flush: + self.flush(0) + + # TODO: Some logging should go on here. + + + def handle_error_no_close(self): + """See asyncore.dispatcher.handle_error()""" + nil, t, v, tbinfo = compact_traceback() + + # sometimes a user repr method will crash. + try: + self_repr = repr(self) + except: + self_repr = '<__repr__(self) failed for object at %0x>' % id(self) + + self.log_info( + 'uncaptured python exception, closing channel %s (%s:%s %s)' % ( + self_repr, + t, + v, + tbinfo + ), + 'error' + ) + + + def exception(self): + if DEBUG: + import traceback + traceback.print_exc() + t, v = sys.exc_info()[:2] + try: + info = '%s: %s' % (getattr(t, '__name__', t), v) + except: + info = str(t) + self.reply('INTERNAL_ERROR', info) + self.handle_error_no_close() + self.close_when_done() diff -Nru zope3-3.4.0/src/zope/server/linereceiver/linetask.py zope3-3.5~bzr18/src/zope/server/linereceiver/linetask.py --- zope3-3.4.0/src/zope/server/linereceiver/linetask.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/linereceiver/linetask.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,68 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Line Task +""" +import socket +import time +from zope.server.interfaces import ITask +from zope.interface import implements + + +class LineTask(object): + """This is a generic task that can be used with command line + protocols to handle commands in a separate thread. + """ + implements(ITask) + + def __init__(self, channel, command, m_name): + self.channel = channel + self.m_name = m_name + self.args = command.args + + self.close_on_finish = 0 + + def service(self): + """Called to execute the task. + """ + try: + try: + self.start() + getattr(self.channel, self.m_name)(self.args) + self.finish() + except socket.error: + self.close_on_finish = 1 + if self.channel.adj.log_socket_errors: + raise + except: + self.channel.exception() + finally: + if self.close_on_finish: + self.channel.close_when_done() + + def cancel(self): + 'See ITask' + self.channel.close_when_done() + + def defer(self): + 'See ITask' + pass + + def start(self): + now = time.time() + self.start_time = now + + def finish(self): + hit_log = self.channel.server.hit_log + if hit_log is not None: + hit_log.log(self) diff -Nru zope3-3.4.0/src/zope/server/logger/filelogger.py zope3-3.5~bzr18/src/zope/server/logger/filelogger.py --- zope3-3.4.0/src/zope/server/logger/filelogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/filelogger.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,69 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""File Logger +""" +from types import StringType + +from zope.server.interfaces.logger import IMessageLogger +from zope.interface import implements + +class FileLogger(object): + """Simple File Logger + """ + + implements(IMessageLogger) + + def __init__(self, file, flush=1, mode='a'): + """pass this either a path or a file object.""" + if type(file) is StringType: + if (file == '-'): + import sys + self.file = sys.stdout + else: + self.file = open(file, mode) + else: + self.file = file + self.do_flush = flush + + def __repr__(self): + return '' % self.file + + def write(self, data): + self.file.write(data) + self.maybe_flush() + + def writeline(self, line): + self.file.writeline(line) + self.maybe_flush() + + def writelines(self, lines): + self.file.writelines(lines) + self.maybe_flush() + + def maybe_flush(self): + if self.do_flush: + self.file.flush() + + def flush(self): + self.file.flush() + + def softspace(self, *args): + pass + + def logMessage(self, message): + 'See IMessageLogger' + if message[-1] not in ('\r', '\n'): + self.write(message + '\n') + else: + self.write(message) diff -Nru zope3-3.4.0/src/zope/server/logger/__init__.py zope3-3.5~bzr18/src/zope/server/logger/__init__.py --- zope3-3.4.0/src/zope/server/logger/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/server/logger/m_syslog.py zope3-3.5~bzr18/src/zope/server/logger/m_syslog.py --- zope3-3.4.0/src/zope/server/logger/m_syslog.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/m_syslog.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,177 @@ +# -*- Mode: Python; tab-width: 4 -*- + +# ====================================================================== +# Copyright 1997 by Sam Rushing +# +# All Rights Reserved +# +# Permission to use, copy, modify, and distribute this software and +# its documentation for any purpose and without fee is hereby +# granted, provided that the above copyright notice appear in all +# copies and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of Sam +# Rushing not be used in advertising or publicity pertaining to +# distribution of the software without specific, written prior +# permission. +# +# SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +# NO EVENT SHALL SAM RUSHING BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# ====================================================================== + +"""socket interface to unix syslog. +On Unix, there are usually two ways of getting to syslog: via a +local unix-domain socket, or via the TCP service. + +Usually "/dev/log" is the unix domain socket. This may be different +for other systems. + +>>> my_client = syslog_client ('/dev/log') + +Otherwise, just use the UDP version, port 514. + +>>> my_client = syslog_client (('my_log_host', 514)) + +On win32, you will have to use the UDP version. Note that +you can use this to log to other hosts (and indeed, multiple +hosts). + +This module is not a drop-in replacement for the python + extension module - the interface is different. + +Usage: + +>>> c = syslog_client() +>>> c = syslog_client ('/strange/non_standard_log_location') +>>> c = syslog_client (('other_host.com', 514)) +>>> c.log ('testing', facility='local0', priority='debug') + +""" + +# TODO: support named-pipe syslog. +# [see ftp://sunsite.unc.edu/pub/Linux/system/Daemons/syslog-fifo.tar.z] + +# from : +# =========================================================================== +# priorities/facilities are encoded into a single 32-bit quantity, where the +# bottom 3 bits are the priority (0-7) and the top 28 bits are the facility +# (0-big number). Both the priorities and the facilities map roughly +# one-to-one to strings in the syslogd(8) source code. This mapping is +# included in this file. +# +# priorities (these are ordered) + +LOG_EMERG = 0 # system is unusable +LOG_ALERT = 1 # action must be taken immediately +LOG_CRIT = 2 # critical conditions +LOG_ERR = 3 # error conditions +LOG_WARNING = 4 # warning conditions +LOG_NOTICE = 5 # normal but significant condition +LOG_INFO = 6 # informational +LOG_DEBUG = 7 # debug-level messages + +# facility codes +LOG_KERN = 0 # kernel messages +LOG_USER = 1 # random user-level messages +LOG_MAIL = 2 # mail system +LOG_DAEMON = 3 # system daemons +LOG_AUTH = 4 # security/authorization messages +LOG_SYSLOG = 5 # messages generated internally by syslogd +LOG_LPR = 6 # line printer subsystem +LOG_NEWS = 7 # network news subsystem +LOG_UUCP = 8 # UUCP subsystem +LOG_CRON = 9 # clock daemon +LOG_AUTHPRIV = 10 # security/authorization messages (private) + +# other codes through 15 reserved for system use +LOG_LOCAL0 = 16 # reserved for local use +LOG_LOCAL1 = 17 # reserved for local use +LOG_LOCAL2 = 18 # reserved for local use +LOG_LOCAL3 = 19 # reserved for local use +LOG_LOCAL4 = 20 # reserved for local use +LOG_LOCAL5 = 21 # reserved for local use +LOG_LOCAL6 = 22 # reserved for local use +LOG_LOCAL7 = 23 # reserved for local use + +priority_names = { + "alert": LOG_ALERT, + "crit": LOG_CRIT, + "debug": LOG_DEBUG, + "emerg": LOG_EMERG, + "err": LOG_ERR, + "error": LOG_ERR, # DEPRECATED + "info": LOG_INFO, + "notice": LOG_NOTICE, + "panic": LOG_EMERG, # DEPRECATED + "warn": LOG_WARNING, # DEPRECATED + "warning": LOG_WARNING, + } + +facility_names = { + "auth": LOG_AUTH, + "authpriv": LOG_AUTHPRIV, + "cron": LOG_CRON, + "daemon": LOG_DAEMON, + "kern": LOG_KERN, + "lpr": LOG_LPR, + "mail": LOG_MAIL, + "news": LOG_NEWS, + "security": LOG_AUTH, # DEPRECATED + "syslog": LOG_SYSLOG, + "user": LOG_USER, + "uucp": LOG_UUCP, + "local0": LOG_LOCAL0, + "local1": LOG_LOCAL1, + "local2": LOG_LOCAL2, + "local3": LOG_LOCAL3, + "local4": LOG_LOCAL4, + "local5": LOG_LOCAL5, + "local6": LOG_LOCAL6, + "local7": LOG_LOCAL7, + } + +import socket + +class syslog_client(object): + + def __init__(self, address='/dev/log'): + self.address = address + if type(address) == type(''): + try: # APUE 13.4.2 specifes /dev/log as datagram socket + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + self.socket.connect(address) + except: # older linux may create as stream socket + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket.connect(address) + self.unix = 1 + else: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.unix = 0 + + + log_format_string = '<%d>%s\000' + + def log(self, message, facility=LOG_USER, priority=LOG_INFO): + message = self.log_format_string % ( + self.encode_priority(facility, priority), + message + ) + if self.unix: + self.socket.send(message) + else: + self.socket.sendto(message, self.address) + + def encode_priority(self, facility, priority): + if type(facility) == type(''): + facility = facility_names[facility] + if type(priority) == type(''): + priority = priority_names[priority] + return (facility<<3) | priority + + def close(self): + if self.unix: + self.socket.close() diff -Nru zope3-3.4.0/src/zope/server/logger/pythonlogger.py zope3-3.5~bzr18/src/zope/server/logger/pythonlogger.py --- zope3-3.4.0/src/zope/server/logger/pythonlogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/pythonlogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,38 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Proxy between the server's and Python's logger interfaces. +""" +import logging + +from zope.server.interfaces.logger import IMessageLogger +from zope.interface import implements + +class PythonLogger(object): + """Proxy for Python's logging module""" + + implements(IMessageLogger) + + def __init__(self, name=None, level=logging.INFO): + self.name = name + self.level = level + self.logger = logging.getLogger(name) + + def __repr__(self): + return '' % (self.name, + logging.getLevelName(self.level)) + + def logMessage(self, message): + """See IMessageLogger""" + self.logger.log(self.level, message.rstrip()) + diff -Nru zope3-3.4.0/src/zope/server/logger/resolvinglogger.py zope3-3.5~bzr18/src/zope/server/logger/resolvinglogger.py --- zope3-3.4.0/src/zope/server/logger/resolvinglogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/resolvinglogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,50 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Resolving Logger +""" +from zope.server.interfaces.logger import IRequestLogger +from zope.interface import implements + + +class ResolvingLogger(object): + """Feed (ip, message) combinations into this logger to get a + resolved hostname in front of the message. The message will not + be logged until the PTR request finishes (or fails).""" + + implements(IRequestLogger) + + def __init__(self, resolver, logger): + self.resolver = resolver + # logger is an IMessageLogger + self.logger = logger + + class logger_thunk(object): + def __init__(self, message, logger): + self.message = message + self.logger = logger + + def __call__(self, host, ttl, answer): + if not answer: + answer = host + self.logger.logMessage('%s%s' % (answer, self.message)) + + def logRequest(self, ip, message): + 'See IRequestLogger' + self.resolver.resolve_ptr( + ip, + self.logger_thunk( + message, + self.logger + ) + ) diff -Nru zope3-3.4.0/src/zope/server/logger/rotatingfilelogger.py zope3-3.5~bzr18/src/zope/server/logger/rotatingfilelogger.py --- zope3-3.4.0/src/zope/server/logger/rotatingfilelogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/rotatingfilelogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,91 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Rotating File Logger + +Rotates a log file in time intervals. +""" +import time +import os +import stat + +from zope.server.logger.filelogger import FileLogger + +class RotatingFileLogger(FileLogger): + """ If freq is non-None we back up 'daily', 'weekly', or + 'monthly'. Else if maxsize is non-None we back up whenever + the log gets to big. If both are None we never back up. + + Like a FileLogger, but it must be attached to a filename. + When the log gets too full, or a certain time has passed, it + backs up the log and starts a new one. Note that backing up + the log is done via 'mv' because anything else (cp, gzip) + would take time, during which medusa would do nothing else. + """ + + def __init__(self, file, freq=None, maxsize=None, flush=1, mode='a'): + self.filename = file + self.mode = mode + self.file = open(file, mode) + self.freq = freq + self.maxsize = maxsize + self.rotate_when = self.next_backup(self.freq) + self.do_flush = flush + + def __repr__(self): + return '' % self.file + + # We back up at midnight every 1) day, 2) monday, or 3) 1st of month + def next_backup(self, freq): + (yr, mo, day, hr, min, sec, wd, jday, dst) = \ + time.localtime(time.time()) + if freq == 'daily': + return time.mktime((yr,mo,day+1, 0,0,0, 0,0,-1)) + elif freq == 'weekly': + # wd(monday)==0 + return time.mktime((yr,mo,day-wd+7, 0,0,0, 0,0,-1)) + elif freq == 'monthly': + return time.mktime((yr,mo+1,1, 0,0,0, 0,0,-1)) + else: + return None # not a date-based backup + + def maybe_flush(self): # rotate first if necessary + self.maybe_rotate() + if self.do_flush: # from file_logger() + self.file.flush() + + def maybe_rotate(self): + if self.freq and time.time() > self.rotate_when: + self.rotate() + self.rotate_when = self.next_backup(self.freq) + elif self.maxsize: # rotate when we get too big + try: + if os.stat(self.filename)[stat.ST_SIZE] > self.maxsize: + self.rotate() + except os.error: # file not found, probably + self.rotate() # will create a new file + + def rotate(self): + yr, mo, day, hr, min, sec, wd, jday, dst = time.localtime(time.time()) + try: + self.file.close() + newname = '%s.ends%04d%02d%02d' % (self.filename, yr, mo, day) + try: + open(newname, "r").close() # check if file exists + newname = newname + "-%02d%02d%02d" % (hr, min, sec) + except IOError: # concatenation of YEAR MO DY is unique + pass + os.rename(self.filename, newname) + self.file = open(self.filename, self.mode) + except IOError: + pass diff -Nru zope3-3.4.0/src/zope/server/logger/socketlogger.py zope3-3.5~bzr18/src/zope/server/logger/socketlogger.py --- zope3-3.4.0/src/zope/server/logger/socketlogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/socketlogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,46 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Socket Logger + +Sends logging messages to a socket. +""" +import asynchat +import socket + +from zope.server.interfaces.logger import IMessageLogger +from zope.interface import implements + +class SocketLogger(asynchat.async_chat): + """Log to a stream socket, asynchronously.""" + + implements(IMessageLogger) + + def __init__(self, address): + if type(address) == type(''): + self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM) + else: + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + + self.connect(address) + self.address = address + + def __repr__(self): + return '' % (self.address) + + def logMessage(self, message): + 'See IMessageLogger' + if message[-2:] != '\r\n': + self.socket.push(message + '\r\n') + else: + self.socket.push(message) diff -Nru zope3-3.4.0/src/zope/server/logger/sysloglogger.py zope3-3.5~bzr18/src/zope/server/logger/sysloglogger.py --- zope3-3.4.0/src/zope/server/logger/sysloglogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/sysloglogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,58 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Syslog Logger + +Writes log messages to syslog. +""" + +import os +from zope.server.logger import m_syslog + +from zope.server.interfaces.logger import IMessageLogger +from zope.interface import implements + + +class SyslogLogger(m_syslog.syslog_client): + """syslog is a line-oriented log protocol - this class would be + appropriate for FTP or HTTP logs, but not for dumping stderr + to. + + TODO: a simple safety wrapper that will ensure that the line + sent to syslog is reasonable. + + TODO: async version of syslog_client: now, log entries use + blocking send() + """ + + implements(IMessageLogger) + + svc_name = 'zope' + pid_str = str(os.getpid()) + + def __init__ (self, address, facility='user'): + m_syslog.syslog_client.__init__ (self, address) + self.facility = m_syslog.facility_names[facility] + self.address=address + + def __repr__ (self): + return '' % (repr(self.address)) + + def logMessage(self, message): + 'See IMessageLogger' + m_syslog.syslog_client.log ( + self, + '%s[%s]: %s' % (self.svc_name, self.pid_str, message), + facility=self.facility, + priority=m_syslog.LOG_INFO + ) diff -Nru zope3-3.4.0/src/zope/server/logger/taillogger.py zope3-3.5~bzr18/src/zope/server/logger/taillogger.py --- zope3-3.4.0/src/zope/server/logger/taillogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/taillogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,40 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Tail Logger +""" +from zope.server.interfaces.logger import IMessageLogger +from zope.interface import implements + +class TailLogger(object): + """Keep track of the last log messages""" + + implements(IMessageLogger) + + def __init__(self, logger, size=500): + self.size = size + self.logger = logger + self.messages = [] + + def logMessage(self, message): + 'See IMessageLogger' + self.messages.append(strip_eol(message)) + if len(self.messages) > self.size: + del self.messages[0] + self.logger.logMessage(message) + + +def strip_eol(line): + while line and line[-1] in '\r\n': + line = line[:-1] + return line diff -Nru zope3-3.4.0/src/zope/server/logger/tests/__init__.py zope3-3.5~bzr18/src/zope/server/logger/tests/__init__.py --- zope3-3.4.0/src/zope/server/logger/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/server/logger/tests/test_pythonlogger.py zope3-3.5~bzr18/src/zope/server/logger/tests/test_pythonlogger.py --- zope3-3.4.0/src/zope/server/logger/tests/test_pythonlogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/tests/test_pythonlogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,66 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Python Logger tests +""" +import unittest +import logging +from zope.interface.verify import verifyObject + + +class HandlerStub(logging.Handler): + + last_record = None + + def emit(self, record): + self.last_record = record + + +class TestPythonLogger(unittest.TestCase): + + name = 'test.pythonlogger' + + def setUp(self): + self.logger = logging.getLogger(self.name) + self.logger.propagate = False + self.logger.setLevel(logging.INFO) + self.handler = HandlerStub() + self.logger.addHandler(self.handler) + + def tearDown(self): + self.logger.removeHandler(self.handler) + + def test(self): + from zope.server.logger.pythonlogger import PythonLogger + from zope.server.interfaces.logger import IMessageLogger + plogger = PythonLogger(self.name) + verifyObject(IMessageLogger, plogger) + msg1 = 'test message 1' + plogger.logMessage(msg1) + self.assertEquals(self.handler.last_record.msg, msg1) + self.assertEquals(self.handler.last_record.levelno, logging.INFO) + msg2 = 'test message 2\r\n' + plogger.level = logging.ERROR + plogger.logMessage(msg2) + self.assertEquals(self.handler.last_record.msg, msg2.rstrip()) + self.assertEquals(self.handler.last_record.levelno, logging.ERROR) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestPythonLogger)) + return suite + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/server/logger/unresolvinglogger.py zope3-3.5~bzr18/src/zope/server/logger/unresolvinglogger.py --- zope3-3.4.0/src/zope/server/logger/unresolvinglogger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/logger/unresolvinglogger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Unresolving Logger +""" +from zope.server.interfaces.logger import IRequestLogger +from zope.interface import implements + +class UnresolvingLogger(object): + """Just in case you don't want to resolve""" + + implements(IRequestLogger) + + def __init__(self, logger): + self.logger = logger + + def logRequest(self, ip, message): + 'See IRequestLogger' + self.logger.logMessage('%s%s' % (ip, message)) diff -Nru zope3-3.4.0/src/zope/server/maxsockets.py zope3-3.5~bzr18/src/zope/server/maxsockets.py --- zope3-3.4.0/src/zope/server/maxsockets.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/maxsockets.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,90 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Find max number of sockets allowed. +""" +# Medusa max_sockets module. + +import socket +import select + +# several factors here we might want to test: +# 1) max we can create +# 2) max we can bind +# 3) max we can listen on +# 4) max we can connect + +def max_server_sockets(): + # TODO: This should be a configuration value as it takes a long time to + # compute on Mac OSX + return 100 + sl = [] + while 1: + try: + s = socket.socket (socket.AF_INET, socket.SOCK_STREAM) + s.bind (('',0)) + s.listen(5) + sl.append (s) + except: + break + num = len(sl) + for s in sl: + s.close() + del sl + return num + +def max_client_sockets(): + # TODO: This should be a configuration value as it takes a long time to + # compute on Mac OSX + return 100 + # make a server socket + server = socket.socket (socket.AF_INET, socket.SOCK_STREAM) + server.bind (('', 9999)) + server.listen (5) + sl = [] + while 1: + try: + s = socket.socket (socket.AF_INET, socket.SOCK_STREAM) + s.connect (('', 9999)) + conn, addr = server.accept() + sl.append ((s,conn)) + except: + break + num = len(sl) + for s,c in sl: + s.close() + c.close() + del sl + return num + +def max_select_sockets(): + # TODO: This should be a configuration value as it takes a long time to + # compute on Mac OSX + return 100 + sl = [] + while 1: + try: + num = len(sl) + for i in range(1 + len(sl) // 20): + # Increase exponentially. + s = socket.socket (socket.AF_INET, socket.SOCK_STREAM) + s.bind (('',0)) + s.listen(5) + sl.append (s) + select.select(sl,[],[],0) + except: + break + for s in sl: + s.close() + del sl + return num diff -Nru zope3-3.4.0/src/zope/server/serverbase.py zope3-3.5~bzr18/src/zope/server/serverbase.py --- zope3-3.4.0/src/zope/server/serverbase.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/serverbase.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,148 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Server Base Class + +This module provides a base implementation for a channel-based server. It can +only be used as a mix-in to actual server implementations. +""" +import asyncore +import logging +import socket + +from zope.server.adjustments import default_adj +from zope.server.interfaces import IServer +from zope.interface import implements + + +class ServerBase(asyncore.dispatcher, object): + """Async. server base for launching derivatives of ServerChannelBase.""" + + implements(IServer) + + # See zope.server.interfaces.IServer + channel_class = None # Override with a channel class. + SERVER_IDENT = 'zope.server.serverbase' # Override. + + def __init__(self, ip, port, task_dispatcher=None, adj=None, start=1, + hit_log=None, verbose=0): + if adj is None: + adj = default_adj + self.adj = adj + asyncore.dispatcher.__init__(self) + self.port = port + self.task_dispatcher = task_dispatcher + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind((ip, port)) + self.verbose = verbose + self.hit_log = hit_log + self.logger = logging.getLogger(self.__class__.__name__) + self.server_name = self.computeServerName(ip) + + if start: + self.accept_connections() + + def log(self, message): + """See zope.server.interfaces.IDispatcherLogging""" + # Override asyncore's default log() + self.logger.info(message) + + level_mapping = { + 'info': logging.INFO, + 'error': logging.ERROR, + 'warning': logging.WARN, + } + + def log_info(self, message, type='info'): + """See zope.server.interfaces.IDispatcherLogging""" + self.logger.log(self.level_mapping.get(type, logging.INFO), message) + + def computeServerName(self, ip=''): + """Given an IP, try to determine the server name.""" + if ip: + server_name = str(ip) + else: + server_name = str(socket.gethostname()) + # Convert to a host name if necessary. + is_hostname = 0 + for c in server_name: + if c != '.' and not c.isdigit(): + is_hostname = 1 + break + if not is_hostname: + if self.verbose: + self.log_info('Computing hostname', 'info') + try: + server_name = socket.gethostbyaddr(server_name)[0] + except socket.error: + if self.verbose: + self.log_info('Cannot do reverse lookup', 'info') + return server_name + + def accept_connections(self): + self.accepting = 1 + self.socket.listen(self.adj.backlog) # Circumvent asyncore's NT limit + if self.verbose: + self.log_info('%s started.\n' + '\tHostname: %s\n\tPort: %d' % ( + self.SERVER_IDENT, + self.server_name, + self.port + )) + + + def addTask(self, task): + """See zope.server.interfaces.ITaskDispatcher""" + td = self.task_dispatcher + if td is not None: + td.addTask(task) + else: + task.service() + + def readable(self): + """See zope.server.interfaces.IDispatcher""" + return (self.accepting and + len(asyncore.socket_map) < self.adj.connection_limit) + + def writable(self): + """See zope.server.interfaces.IDispatcher""" + return 0 + + def handle_read(self): + """See zope.server.interfaces.IDispatcherEventHandler""" + pass + + def handle_connect(self): + """See zope.server.interfaces.IDispatcherEventHandler""" + pass + + def handle_accept(self): + """See zope.server.interfaces.IDispatcherEventHandler""" + try: + v = self.accept() + if v is None: + return + conn, addr = v + except socket.error: + # Linux: On rare occasions we get a bogus socket back from + # accept. socketmodule.c:makesockaddr complains that the + # address family is unknown. We don't want the whole server + # to shut down because of this. + if self.adj.log_socket_errors: + self.log_info ('warning: server accept() threw an exception', + 'warning') + return + for (level, optname, value) in self.adj.socket_options: + conn.setsockopt(level, optname, value) + self.channel_class(self, conn, addr, self.adj) diff -Nru zope3-3.4.0/src/zope/server/serverchannelbase.py zope3-3.5~bzr18/src/zope/server/serverchannelbase.py --- zope3-3.4.0/src/zope/server/serverchannelbase.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/serverchannelbase.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,231 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Server-Channel Base Class + +This module provides a base implementation for the server channel. It can only +be used as a mix-in to actual server channel implementations. +""" +import os +import time +import sys +import asyncore +from thread import allocate_lock +from zope.interface import implements + +from zope.server.dualmodechannel import DualModeChannel +from zope.server.interfaces import IServerChannel, ITask + +# task_lock is useful for synchronizing access to task-related attributes. +task_lock = allocate_lock() + + +class ServerChannelBase(DualModeChannel, object): + """Base class for a high-performance, mixed-mode server-side channel.""" + + implements(IServerChannel, ITask) + + # See zope.server.interfaces.IServerChannel + parser_class = None # Subclasses must provide a parser class + task_class = None # ... and a task class. + + active_channels = {} # Class-specific channel tracker + next_channel_cleanup = [0] # Class-specific cleanup time + proto_request = None # A request parser instance + last_activity = 0 # Time of last activity + tasks = None # List of channel-related tasks to execute + running_tasks = False # True when another thread is running tasks + + # + # ASYNCHRONOUS METHODS (including __init__) + # + + def __init__(self, server, conn, addr, adj=None): + """See async.dispatcher""" + DualModeChannel.__init__(self, conn, addr, adj) + self.server = server + self.last_activity = t = self.creation_time + self.check_maintenance(t) + + def add_channel(self, map=None): + """See async.dispatcher + + This hook keeps track of opened channels. + """ + DualModeChannel.add_channel(self, map) + self.__class__.active_channels[self._fileno] = self + + def del_channel(self, map=None): + """See async.dispatcher + + This hook keeps track of closed channels. + """ + DualModeChannel.del_channel(self, map) + ac = self.__class__.active_channels + fd = self._fileno + if fd in ac: + del ac[fd] + + def check_maintenance(self, now): + """See async.dispatcher + + Performs maintenance if necessary. + """ + ncc = self.__class__.next_channel_cleanup + if now < ncc[0]: + return + ncc[0] = now + self.adj.cleanup_interval + self.maintenance() + + def maintenance(self): + """See async.dispatcher + + Kills off dead connections. + """ + self.kill_zombies() + + def kill_zombies(self): + """See async.dispatcher + + Closes connections that have not had any activity in a while. + + The timeout is configured through adj.channel_timeout (seconds). + """ + now = time.time() + cutoff = now - self.adj.channel_timeout + for channel in self.active_channels.values(): + if (channel is not self and not channel.running_tasks and + channel.last_activity < cutoff): + channel.close() + + def received(self, data): + """See async.dispatcher + + Receives input asynchronously and send requests to + handle_request(). + """ + preq = self.proto_request + while data: + if preq is None: + preq = self.parser_class(self.adj) + n = preq.received(data) + if preq.completed: + # The request is ready to use. + self.proto_request = None + if not preq.empty: + self.handle_request(preq) + preq = None + else: + self.proto_request = preq + if n >= len(data): + break + data = data[n:] + + def handle_request(self, req): + """Creates and queues a task for processing a request. + + Subclasses may override this method to handle some requests + immediately in the main async thread. + """ + task = self.task_class(self, req) + self.queue_task(task) + + def handle_error(self): + """See async.dispatcher + + Handles program errors (not communication errors) + """ + t, v = sys.exc_info()[:2] + if t is SystemExit or t is KeyboardInterrupt: + raise t(v) + asyncore.dispatcher.handle_error(self) + + def handle_comm_error(self): + """See async.dispatcher + + Handles communication errors (not program errors) + """ + if self.adj.log_socket_errors: + self.handle_error() + else: + # Ignore socket errors. + self.close() + + # + # BOTH MODES + # + + def queue_task(self, task): + """Queue a channel-related task to be executed in another thread.""" + start = False + task_lock.acquire() + try: + if self.tasks is None: + self.tasks = [] + self.tasks.append(task) + if not self.running_tasks: + self.running_tasks = True + start = True + finally: + task_lock.release() + if start: + self.set_sync() + self.server.addTask(self) + + # + # ITask implementation. Delegates to the queued tasks. + # + + def service(self): + """Execute all pending tasks""" + while True: + task = None + task_lock.acquire() + try: + if self.tasks: + task = self.tasks.pop(0) + else: + # No more tasks + self.running_tasks = False + self.set_async() + break + finally: + task_lock.release() + try: + task.service() + except: + # propagate the exception, but keep executing tasks + self.server.addTask(self) + raise + + def cancel(self): + """Cancels all pending tasks""" + task_lock.acquire() + try: + if self.tasks: + old = self.tasks[:] + else: + old = [] + self.tasks = [] + self.running_tasks = False + finally: + task_lock.release() + try: + for task in old: + task.cancel() + finally: + self.set_async() + + def defer(self): + pass + diff -Nru zope3-3.4.0/src/zope/server/taskthreads.py zope3-3.5~bzr18/src/zope/server/taskthreads.py --- zope3-3.4.0/src/zope/server/taskthreads.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/taskthreads.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,124 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Threaded Task Dispatcher +""" +from Queue import Queue, Empty +from thread import allocate_lock, start_new_thread +from time import time, sleep +import logging + +from zope.server.interfaces import ITaskDispatcher +from zope.interface import implements + + +log = logging.getLogger(__name__) + + +class ThreadedTaskDispatcher(object): + """A Task Dispatcher that creates a thread for each task.""" + + implements(ITaskDispatcher) + + stop_count = 0 # Number of threads that will stop soon. + + def __init__(self): + self.threads = {} # { thread number -> 1 } + self.queue = Queue() + self.thread_mgmt_lock = allocate_lock() + + def handlerThread(self, thread_no): + threads = self.threads + try: + while threads.get(thread_no): + task = self.queue.get() + if task is None: + # Special value: kill this thread. + break + try: + task.service() + except: + log.exception('Exception during task') + finally: + mlock = self.thread_mgmt_lock + mlock.acquire() + try: + self.stop_count -= 1 + try: del threads[thread_no] + except KeyError: pass + finally: + mlock.release() + + def setThreadCount(self, count): + """See zope.server.interfaces.ITaskDispatcher""" + mlock = self.thread_mgmt_lock + mlock.acquire() + try: + threads = self.threads + thread_no = 0 + running = len(threads) - self.stop_count + while running < count: + # Start threads. + while thread_no in threads: + thread_no = thread_no + 1 + threads[thread_no] = 1 + running += 1 + start_new_thread(self.handlerThread, (thread_no,)) + thread_no = thread_no + 1 + if running > count: + # Stop threads. + to_stop = running - count + self.stop_count += to_stop + for n in range(to_stop): + self.queue.put(None) + running -= 1 + finally: + mlock.release() + + def addTask(self, task): + """See zope.server.interfaces.ITaskDispatcher""" + if task is None: + raise ValueError("No task passed to addTask().") + # assert ITask.providedBy(task) + try: + task.defer() + self.queue.put(task) + except: + task.cancel() + raise + + def shutdown(self, cancel_pending=True, timeout=5): + """See zope.server.interfaces.ITaskDispatcher""" + self.setThreadCount(0) + # Ensure the threads shut down. + threads = self.threads + expiration = time() + timeout + while threads: + if time() >= expiration: + log.error("%d thread(s) still running" % len(threads)) + break + sleep(0.1) + if cancel_pending: + # Cancel remaining tasks. + try: + queue = self.queue + while not queue.empty(): + task = queue.get() + if task is not None: + task.cancel() + except Empty: + pass + + def getPendingTasksEstimate(self): + """See zope.server.interfaces.ITaskDispatcher""" + return self.queue.qsize() diff -Nru zope3-3.4.0/src/zope/server/tests/asyncerror.py zope3-3.5~bzr18/src/zope/server/tests/asyncerror.py --- zope3-3.4.0/src/zope/server/tests/asyncerror.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/tests/asyncerror.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,49 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Mixin class to turn uncaught asyncore errors into test failues. + +By default, asyncore handles uncaught exceptions in dispatchers by +printing a message to the console. If a test causes such uncaught +exceptions, the test is marked as a failure, because asyncore handles +the exception. This framework causes the unit test to fail. If code +being tested expects the errors to occur, it can add code to prevent +the error from propagating all the way back to asyncore. +""" +import asyncore +import sys +import traceback + +class AsyncoreErrorHook(object): + """Convert asyncore errors into unittest failures. + + Call hook_asyncore_error in setUp() and unhook_asyncore_error() in + tearDown(), or use super() to call setUp() and tearDown() here. + """ + + def setUp(self): + self.hook_asyncore_error() + + def tearDown(self): + self.unhook_asycnore_error() + + def hook_asyncore_error(self): + self._asyncore_traceback = asyncore.compact_traceback + asyncore.compact_traceback = self.handle_asyncore_error + + def unhook_asyncore_error(self): + asyncore.compact_traceback = self._asyncore_traceback + + def handle_asyncore_error(self): + L = traceback.format_exception(*sys.exc_info()) + self.fail("".join(L)) diff -Nru zope3-3.4.0/src/zope/server/tests/__init__.py zope3-3.5~bzr18/src/zope/server/tests/__init__.py --- zope3-3.4.0/src/zope/server/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/server/tests/test_serverbase.py zope3-3.5~bzr18/src/zope/server/tests/test_serverbase.py --- zope3-3.4.0/src/zope/server/tests/test_serverbase.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/tests/test_serverbase.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,99 @@ +############################################################################## +# +# Copyright (c) 2005 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 zope.server.serverbase +""" +import unittest + +from zope.testing import doctest + + +def doctest_ServerBase(): + r"""Regression test for ServerBase + + Bug: if the `ip` argument of ServerBase is a string containing a numberic + IP address, and the verbose argument is enabled, ServerBase.__init__ + would try to use self.logger before it was initialized. + + We will use a subclass of ServerBase so that unit tests do not actually try + to bind to ports. + + >>> from zope.server.serverbase import ServerBase + >>> class ServerBaseForTest(ServerBase): + ... def bind(self, (ip, port)): + ... print "Listening on %s:%d" % (ip or '*', port) + >>> sb = ServerBaseForTest('127.0.0.1', 80, start=False, verbose=True) + Listening on 127.0.0.1:80 + + """ + +class FakeSocket: + data = '' + setblocking = lambda *_: None + fileno = lambda *_: 42 + getpeername = lambda *_: ('localhost', 42) + + def send(self, data): + self.data += data + return len(data) + + +def channels_accept_iterables(): + r""" +Channels accept iterables (they special-case strings). + + >>> from zope.server.dualmodechannel import DualModeChannel + >>> socket = FakeSocket() + >>> channel = DualModeChannel(socket, ('localhost', 42)) + + >>> channel.write("First") + 5 + + >>> channel.flush() + >>> print socket.data + First + + >>> channel.write(["\n", "Second", "\n", "Third"]) + 13 + + >>> channel.flush() + >>> print socket.data + First + Second + Third + + >>> def count(): + ... yield '\n1\n2\n3\n' + ... yield 'I love to count. Ha ha ha.' + + >>> channel.write(count()) + 33 + + >>> channel.flush() + >>> print socket.data + First + Second + Third + 1 + 2 + 3 + I love to count. Ha ha ha. + +""" + +def test_suite(): + return doctest.DocTestSuite() + + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/server/trigger.py zope3-3.5~bzr18/src/zope/server/trigger.py --- zope3-3.4.0/src/zope/server/trigger.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/trigger.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,228 @@ +############################################################################## +# +# Copyright (c) 2001-2005 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 asyncore +import os +import socket +import struct +import thread +import errno + +_ADDRESS_MASK = 256 ** struct.calcsize('P') +def positive_id(obj): + """Return id(obj) as a non-negative integer.""" + + result = id(obj) + if result < 0: + result += _ADDRESS_MASK + assert result > 0 + return result + +# Original comments follow; they're hard to follow in the context of +# ZEO's use of triggers. TODO: rewrite from a ZEO perspective. + +# Wake up a call to select() running in the main thread. +# +# This is useful in a context where you are using Medusa's I/O +# subsystem to deliver data, but the data is generated by another +# thread. Normally, if Medusa is in the middle of a call to +# select(), new output data generated by another thread will have +# to sit until the call to select() either times out or returns. +# If the trigger is 'pulled' by another thread, it should immediately +# generate a READ event on the trigger object, which will force the +# select() invocation to return. +# +# A common use for this facility: letting Medusa manage I/O for a +# large number of connections; but routing each request through a +# thread chosen from a fixed-size thread pool. When a thread is +# acquired, a transaction is performed, but output data is +# accumulated into buffers that will be emptied more efficiently +# by Medusa. [picture a server that can process database queries +# rapidly, but doesn't want to tie up threads waiting to send data +# to low-bandwidth connections] +# +# The other major feature provided by this class is the ability to +# move work back into the main thread: if you call pull_trigger() +# with a thunk argument, when select() wakes up and receives the +# event it will call your thunk from within that thread. The main +# purpose of this is to remove the need to wrap thread locks around +# Medusa's data structures, which normally do not need them. [To see +# why this is true, imagine this scenario: A thread tries to push some +# new data onto a channel's outgoing data queue at the same time that +# the main thread is trying to remove some] + +class _triggerbase(object): + """OS-independent base class for OS-dependent trigger class.""" + + kind = None # subclass must set to "pipe" or "loopback"; used by repr + + def __init__(self): + self._closed = False + + # `lock` protects the `thunks` list from being traversed and + # appended to simultaneously. + self.lock = thread.allocate_lock() + + # List of no-argument callbacks to invoke when the trigger is + # pulled. These run in the thread running the asyncore mainloop, + # regardless of which thread pulls the trigger. + self.thunks = [] + + def readable(self): + return 1 + + def writable(self): + return 0 + + def handle_connect(self): + pass + + def handle_close(self): + self.close() + + # Override the asyncore close() method, because it doesn't know about + # (so can't close) all the gimmicks we have open. Subclass must + # supply a _close() method to do platform-specific closing work. _close() + # will be called iff we're not already closed. + def close(self): + if not self._closed: + self._closed = True + self.del_channel() + self._close() # subclass does OS-specific stuff + + def _close(self): # see close() above; subclass must supply + raise NotImplementedError + + def pull_trigger(self, thunk=None): + if thunk: + self.lock.acquire() + try: + self.thunks.append(thunk) + finally: + self.lock.release() + self._physical_pull() + + # Subclass must supply _physical_pull, which does whatever the OS + # needs to do to provoke the "write" end of the trigger. + def _physical_pull(self): + raise NotImplementedError + + def handle_read(self): + try: + self.recv(8192) + except socket.error: + return + self.lock.acquire() + try: + for thunk in self.thunks: + try: + thunk() + except: + nil, t, v, tbinfo = asyncore.compact_traceback() + print ('exception in trigger thunk:' + ' (%s:%s %s)' % (t, v, tbinfo)) + self.thunks = [] + finally: + self.lock.release() + + def __repr__(self): + return '' % (self.kind, positive_id(self)) + +if os.name == 'posix': + + class trigger(_triggerbase, asyncore.file_dispatcher): + kind = "pipe" + + def __init__(self): + _triggerbase.__init__(self) + r, self.trigger = self._fds = os.pipe() + asyncore.file_dispatcher.__init__(self, r) + + def _close(self): + for fd in self._fds: + os.close(fd) + self._fds = [] + + def _physical_pull(self): + os.write(self.trigger, 'x') + +else: + # Windows version; uses just sockets, because a pipe isn't select'able + # on Windows. + + class trigger(_triggerbase, asyncore.dispatcher): + kind = "loopback" + + def __init__(self): + _triggerbase.__init__(self) + + # Get a pair of connected sockets. The trigger is the 'w' + # end of the pair, which is connected to 'r'. 'r' is put + # in the asyncore socket map. "pulling the trigger" then + # means writing something on w, which will wake up r. + + w = socket.socket() + # Disable buffering -- pulling the trigger sends 1 byte, + # and we want that sent immediately, to wake up asyncore's + # select() ASAP. + w.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + + count = 0 + while 1: + count += 1 + # Bind to a local port; for efficiency, let the OS pick + # a free port for us. + # Unfortunately, stress tests showed that we may not + # be able to connect to that port ("Address already in + # use") despite that the OS picked it. This appears + # to be a race bug in the Windows socket implementation. + # So we loop until a connect() succeeds (almost always + # on the first try). See the long thread at + # http://mail.zope.org/pipermail/zope/2005-July/160433.html + # for hideous details. + a = socket.socket() + a.bind(("127.0.0.1", 0)) + connect_address = a.getsockname() # assigned (host, port) pair + a.listen(1) + try: + w.connect(connect_address) + break # success + except socket.error, detail: + if detail[0] != errno.WSAEADDRINUSE: + # "Address already in use" is the only error + # I've seen on two WinXP Pro SP2 boxes, under + # Pythons 2.3.5 and 2.4.1. + raise + # (10048, 'Address already in use') + # assert count <= 2 # never triggered in Tim's tests + if count >= 10: # I've never seen it go above 2 + a.close() + w.close() + raise BindError("Cannot bind trigger!") + # Close `a` and try again. Note: I originally put a short + # sleep() here, but it didn't appear to help or hurt. + a.close() + + r, addr = a.accept() # r becomes asyncore's (self.)socket + a.close() + self.trigger = w + asyncore.dispatcher.__init__(self, r) + + def _close(self): + # self.socket is r, and self.trigger is w, from __init__ + self.socket.close() + self.trigger.close() + + def _physical_pull(self): + self.trigger.send('x') diff -Nru zope3-3.4.0/src/zope/server/utilities.py zope3-3.5~bzr18/src/zope/server/utilities.py --- zope3-3.4.0/src/zope/server/utilities.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/utilities.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,32 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Server utility functions +""" + +def find_double_newline(s): + """Returns the position just after a double newline in the given string.""" + pos1 = s.find('\n\r\n') # One kind of double newline + if pos1 >= 0: + pos1 += 3 + pos2 = s.find('\n\n') # Another kind of double newline + if pos2 >= 0: + pos2 += 2 + + if pos1 >= 0: + if pos2 >= 0: + return min(pos1, pos2) + else: + return pos1 + else: + return pos2 diff -Nru zope3-3.4.0/src/zope/server/zlogintegration.py zope3-3.5~bzr18/src/zope/server/zlogintegration.py --- zope3-3.4.0/src/zope/server/zlogintegration.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/server/zlogintegration.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,34 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Make asyncore log to the logging module. + +As a side effect of importing this module, asyncore's logging will be +redirected to the logging module. +""" + +import logging + +logger = logging.getLogger("zope.server") + +severity = { + 'info': logging.INFO, + 'warning': logging.WARN, + 'error': logging.ERROR, + } + +def log_info(self, message, type='info'): + logger.log(severity.get(type, logging.INFO), message) + +import asyncore +asyncore.dispatcher.log_info = log_info diff -Nru zope3-3.4.0/src/zope/session/adapters.zcml zope3-3.5~bzr18/src/zope/session/adapters.zcml --- zope3-3.4.0/src/zope/session/adapters.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/adapters.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/session/apidoc.zcml zope3-3.5~bzr18/src/zope/session/apidoc.zcml --- zope3-3.4.0/src/zope/session/apidoc.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/apidoc.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/session/api.txt zope3-3.5~bzr18/src/zope/session/api.txt --- zope3-3.4.0/src/zope/session/api.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/api.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,153 @@ +Zope3 Session Implementation +============================ + +Overview +-------- + +.. CAUTION:: + Session data is maintained on the server. This gives a security + advantage in that we can assume that a client has not tampered with + the data. However, this can have major implications for scalability + as modifying session data too frequently can put a significant load + on servers and in extreme situations render your site unusable. + Developers should keep this in mind when writing code or risk + problems when their application is run in a production environment. + + Applications requiring write-intensive session implementations (such + as page counters) should consider using cookies or specialized + session implementations. + +Sessions allow us to fake state over a stateless protocol - HTTP. +We do this by having a unique identifier stored across multiple +HTTP requests, be it a cookie or some id mangled into the URL. + + +The `IClientIdManager` Utility provides this unique id. It is +responsible for propagating this id so that future requests from +the client get the same id (eg. by setting an HTTP cookie). This +utility is used when we adapt the request to the unique client id: + + >>> client_id = IClientId(request) + +The `ISession` adapter gives us a mapping that can be used to store +and retrieve session data. A unique key (the package id) is used +to avoid namespace clashes: + + >>> pkg_id = 'products.foo' + >>> session = ISession(request)[pkg_id] + >>> session['color'] = 'red' + + >>> session2 = ISession(request)['products.bar'] + >>> session2['color'] = 'blue' + + >>> session['color'] + 'red' + >>> session2['color'] + 'blue' + + +Data Storage +------------ + +The actual data is stored in an `ISessionDataContainer` utility. +`ISession` chooses which `ISessionDataContainer` should be used by +looking up as a named utility using the package id. This allows +the site administrator to configure where the session data is actually +stored by adding a registration for desired `ISessionDataContainer` +with the correct name. + + >>> import zope.component + >>> sdc = zope.component.getUtility(ISessionDataContainer, pkg_id) + >>> sdc[client_id][pkg_id] is session + True + >>> sdc[client_id][pkg_id]['color'] + 'red' + +If no `ISessionDataContainer` utility can be located by name using the +package id, then the unnamed `ISessionDataContainer` utility is used as +a fallback. An unnamed `ISessionDataContainer` is automatically created +for you, which may replaced with a different implementation if desired. + + >>> ISession(request)['unknown'] \ + ... is zope.component.getUtility(ISessionDataContainer)[client_id]\ + ... ['unknown'] + True + +The `ISessionDataContainer` contains `ISessionData` objects, and +`ISessionData` objects in turn contain `ISessionPkgData` objects. You +should never need to know this unless you are writing administrative +views for the session machinery. + + >>> ISessionData.providedBy(sdc[client_id]) + True + >>> ISessionPkgData.providedBy(sdc[client_id][pkg_id]) + True + +The `ISessionDataContainer` is responsible for expiring session data. +The expiry time can be configured by settings its `timeout` attribute. + + >>> sdc.timeout = 1200 # 1200 seconds or 20 minutes + + +Restrictions +------------ + +Data stored in the session must be persistent or picklable. + + >>> session['oops'] = open(__file__) + >>> import transaction + >>> transaction.commit() + Traceback (most recent call last): + [...] + TypeError: can't pickle file objects + +Clean up: + + >>> transaction.abort() + + +Page Templates +-------------- + +Session data may be accessed in page template documents using TALES:: + + + green + + +or:: + +

+ + + +
+ + +Session Timeout +--------------- + +Sessions have a timeout (defaulting to an hour, in seconds). + + >>> import zope.session.session + >>> data_container = zope.session.session.PersistentSessionDataContainer() + >>> data_container.timeout + 3600 + +We need to keep up with when the session was last used (to know when it needs +to be expired), but it would be too resource-intensive to write the last access +time every, single time the session data is touched. The session machinery +compromises by only recording the last access time periodically. That period +is called the "resolution". That also means that if the last-access-time + +the-resolution < now, then the session is considered to have timed out. + +The default resolution is 10 minutes (600 seconds), meaning that a users +session will actually time out sometime between 50 and 60 minutes. + + >>> data_container.resolution + 600 diff -Nru zope3-3.4.0/src/zope/session/classes.zcml zope3-3.5~bzr18/src/zope/session/classes.zcml --- zope3-3.4.0/src/zope/session/classes.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/classes.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/session/configure.zcml zope3-3.5~bzr18/src/zope/session/configure.zcml --- zope3-3.4.0/src/zope/session/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + + + + diff -Nru zope3-3.4.0/src/zope/session/design.txt zope3-3.5~bzr18/src/zope/session/design.txt --- zope3-3.4.0/src/zope/session/design.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/design.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,112 @@ +Sessions +======== + +Sessions provide a way to temporarily associate information with a +client without requiring the authentication of a principal. We +associate an identifier with a particular client. Whenever we get a +request from that client, we compute the identifier and use the +identifier to look up associated information, which is stored on the +server. + +A major disadvantage of sessions is that they require management of +information on the server. This can have major implications for +scalability. It is possible for a framework to make use of session +data very easy for the developer. This is great if scalability is not +an issue, otherwise, it is a booby trap. + +Design Issues +------------- + +Sessions introduce a number of issues to be considered: + +- Clients have to be identified. A number of approaches are possible, + including: + + o Using HTTP cookies. The application assigns a client identifier, + which is stored in a cookie. This technique is the most + straightforward, but can be defeated if the client does not + support HTTP cookies (usually because the feature has been + disabled). + + o Using URLs. The application assigns a client identifier, which is + stored in the URL. This makes URLs a bit uglier and requires some + care. If people copy URLs and send them to others, then you could + end up with multiple clients with the same session + identifier. There are a number of ways to reduce the risk of + accidental reuse of session identifiers: + + - Embed the client IP address in the identifier + + - Expire the identifier + + o Use hidden form variables. This complicates applications. It + requires all requests to be POST requests and requires the + maintenance of the hidden variables. + + o Use the client IP address + + This doesn't work very well, because an IP address may be shared by + many clients. + +- Data storage + + Data can be simply stored in the object database. This provides lots + of flexibility. You can store pretty much anything you want as long + as it is persistent. You get the full benefit of the object database, + such as transactions, transparency, clustering, and so on. Using + the object database is especially useful when: + + - Writes are infrequent + + - Data are complex + + If writes are frequent, then the object database introduces + scalability problems. Really, any transactional database is likely + to introduce problems with frequent writes. If you are tempted to + update session data on every request, think very hard about it. You + are creating a scalability problem. + + If you know that scalability is not (and never will be) an issue, + you can just use the object database. + + If you have client data that needs to be updated often (as in every + request), consider storing the data on the client. (Like all data + received from a client, it may be tainted and, in most instances, + should not be trusted. Sensitive information that the user should + not see should likewise not be stored on the client, unless + encrypted with a key the client has no access to.) If you can't + store it on the client, then consider some other storage mechanism, + like a fast database, possibly without transaction support. + + You may be tempted to store session data in memory for speed. This + doesn't turn out to work very well. If you need scalability, then + you need to be able to use an application-server cluster and storage + of session data in memory defeats that. You can use + "server-affinity" to assure that requests from a client always go + back to the same server, but not all load balancers support server + affinity, and, for those that do, enabling server affinity tends to + defeat load balancing. + +- Session expiration + + You may wish to ensure that sessions terminate after some period of + time. This may be for security reasons, or to avoid accidental + sharing of a session among multiple clients. The policy might be + expressed in terms of total session time, or maximum inactive time, + or some combination. + + There are a number of ways to approach this. You can expire client + ids. You can expire session data. + +- Data expiration + + Because HTTP is a stateless protocol, you can't tell whether a user + is thinking about a task or has simply stopped working on it. Some + means is needed to free server session storage that is no-longer needed. + + The simplest strategy is to never remove data. This strategy has + some obvious disadvantages. Other strategies can be viewed as + optimizations of the basic strategy. It is important to realize that + a data expiration strategy can be informed by, but need not be + constrained by a session-expiration strategy. + diff -Nru zope3-3.4.0/src/zope/session/http.py zope3-3.5~bzr18/src/zope/session/http.py --- zope3-3.4.0/src/zope/session/http.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/http.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,598 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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. +# +############################################################################## +"""Session implementation using cookies + +$Id: http.py 105964 2009-11-23 15:19:21Z patricks $ +""" +import hmac +import logging +import random +import re +import string +import sys +import time +from cStringIO import StringIO + +if sys.version_info[:2] >= (2, 5): + from hashlib import sha1 + from email.utils import formatdate +else: + import sha as sha1 + from email.Utils import formatdate + +import zope.location +from persistent import Persistent +from zope import schema, component +from zope.interface import implements +from zope.publisher.interfaces.http import IHTTPRequest +from zope.publisher.interfaces.http import IHTTPApplicationRequest +from zope.i18nmessageid import ZopeMessageFactory as _ +from zope.session.interfaces import IClientIdManager +from zope.schema.fieldproperty import FieldProperty + +__docformat__ = 'restructuredtext' + +cookieSafeTrans = string.maketrans("+/", "-.") + +logger = logging.getLogger() + +def digestEncode(s): + """Encode SHA digest for cookie.""" + return s.encode("base64")[:-2].translate(cookieSafeTrans) + +class MissingClientIdException(Exception): + """No ClientId found in Request""" + +class ICookieClientIdManager(IClientIdManager): + """Manages sessions using a cookie""" + + namespace = schema.ASCIILine( + title=_('Cookie Name'), + description=_( + "Name of cookie used to maintain state. " + "Must be unique to the site domain name, and only contain " + "ASCII letters, digits and '_'" + ), + required=True, + min_length=1, + max_length=30, + constraint=re.compile("^[\d\w_]+$").search, + ) + + cookieLifetime = schema.Int( + title=_('Cookie Lifetime'), + description=_( + "Number of seconds until the browser expires the cookie. " + "Leave blank expire the cookie when the browser is quit. " + "Set to 0 to never expire. " + ), + min=0, + required=False, + default=None, + missing_value=None, + ) + + thirdparty = schema.Bool( + title=_('Third party cookie'), + description=_( + "Is a third party issuing the identification cookie? " + "Servers like Apache or Nginx have capabilities to issue " + "identification cookies too. If Third party cookies are " + "beeing used, Zope will never send a cookie back, just check " + "for them." + ), + required=False, + default=False, + ) + + domain = schema.TextLine( + title=_('Effective domain'), + description=_( + "An identification cookie can be restricted to a specific domain " + "using this option. This option sets the ``domain`` attribute " + "for the cookie header. It is useful for setting one " + "identification cookie for multiple subdomains. So if this " + "option is set to ``.example.org``, the cookie will be available " + "for subdomains like ``yourname.example.org``. " + "Note that if you set this option to some domain, the identification " + "cookie won't be available for other domains, so, for example " + "you won't be able to login using the SessionCredentials plugin " + "via another domain." + ), + required=False, + ) + + secure = schema.Bool( + title=_('Request Secure communication'), + required=False, + default=False, + ) + + postOnly = schema.Bool( + title=_('Only set cookie on POST requests'), + required=False, + default=False, + ) + +class CookieClientIdManager(zope.location.Location, Persistent): + """Session utility implemented using cookies.""" + + implements(IClientIdManager, ICookieClientIdManager) + + thirdparty = FieldProperty(ICookieClientIdManager['thirdparty']) + cookieLifetime = FieldProperty(ICookieClientIdManager['cookieLifetime']) + secure = FieldProperty(ICookieClientIdManager['secure']) + postOnly = FieldProperty(ICookieClientIdManager['postOnly']) + domain = FieldProperty(ICookieClientIdManager['domain']) + namespace = FieldProperty(ICookieClientIdManager['namespace']) + + def __init__(self, namespace=None, secret=None): + """Create the cookie-based cleint id manager + + We can pass namespace (cookie name) and/or secret string + for generating client unique ids. + + If we don't pass either of them, they will be generated + automatically, this is very handy when storing id manager + in the persistent database, so they are saved between + application restarts. + + >>> manager1 = CookieClientIdManager() + >>> len(manager1.namespace) > 0 + True + >>> len(manager1.secret) > 0 + True + + We can specify cookie name by hand. + + >>> manager2 = CookieClientIdManager('service_cookie') + >>> manager2.namespace + 'service_cookie' + + If we want to use CookieClientIdManager object as a non-persistent + utility, we need to specify some constant secret, so it won't be + recreated on each application restart. + + >>> manager3 = CookieClientIdManager(secret='some_secret') + >>> manager3.secret + 'some_secret' + + Of course, we can specify both cookie name and secret. + + >>> manager4 = CookieClientIdManager('service_cookie', 'some_secret') + >>> manager4.namespace + 'service_cookie' + >>> manager4.secret + 'some_secret' + + """ + if namespace is None: + namespace = "zope3_cs_%x" % (int(time.time()) - 1000000000) + if secret is None: + secret = '%.20f' % random.random() + else: + secret = str(secret) + self.namespace = namespace + self.secret = secret + + def getClientId(self, request): + """Get the client id + + This creates one if necessary: + + >>> from zope.publisher.http import HTTPRequest + >>> request = HTTPRequest(StringIO(''), {}) + >>> bim = CookieClientIdManager() + >>> id = bim.getClientId(request) + >>> id == bim.getClientId(request) + True + + The id is retained accross requests: + + >>> request2 = HTTPRequest(StringIO(''), {}) + >>> request2._cookies = dict( + ... [(name, cookie['value']) + ... for (name, cookie) in request.response._cookies.items() + ... ]) + >>> id == bim.getClientId(request2) + True + >>> bool(id) + True + + Note that the return value of this function is a string, not + an IClientId. This is because this method is used to implement + the IClientId Adapter. + + >>> type(id) == type('') + True + + We don't set the client id unless we need to, so, for example, + the second response doesn't have cookies set: + + >>> request2.response._cookies + {} + + An exception to this is if the cookieLifetime is set to a + non-zero integer value, in which case we do set it on every + request, regardless of when it was last set: + + >>> bim.cookieLifetime = 3600 # one hour + >>> id == bim.getClientId(request2) + True + + >>> bool(request2.response._cookies) + True + + If the postOnly attribute is set to a true value, then cookies + will only be set on POST requests. + + >>> bim.postOnly = True + >>> request = HTTPRequest(StringIO(''), {}) + >>> bim.getClientId(request) + Traceback (most recent call last): + ... + MissingClientIdException + + >>> print request.response.getCookie(bim.namespace) + None + + >>> request = HTTPRequest(StringIO(''), {'REQUEST_METHOD': 'POST'}) + >>> id = bim.getClientId(request) + >>> id == bim.getClientId(request) + True + + >>> request.response.getCookie(bim.namespace) is not None + True + + >>> bim.postOnly = False + + It's also possible to use third-party cookies. E.g. Apache `mod_uid` + or Nginx `ngx_http_userid_module` are able to issue user tracking + cookies in front of Zope. In case thirdparty is activated Zope may + not set a cookie. + + >>> bim.thirdparty = True + >>> request = HTTPRequest(StringIO(''), {}) + >>> bim.getClientId(request) + Traceback (most recent call last): + ... + MissingClientIdException + + >>> print request.response.getCookie(bim.namespace) + None + + """ + sid = self.getRequestId(request) + if sid is None: + if (self.thirdparty + or + (self.postOnly and not (request.method == 'POST')) + ): + raise MissingClientIdException + else: + sid = self.generateUniqueId() + self.setRequestId(request, sid) + elif (not self.thirdparty) and self.cookieLifetime: + # If we have a finite cookie lifetime, then set the cookie + # on each request to avoid losing it. + self.setRequestId(request, sid) + + return sid + + def generateUniqueId(self): + """Generate a new, random, unique id. + + >>> bim = CookieClientIdManager() + >>> id1 = bim.generateUniqueId() + >>> id2 = bim.generateUniqueId() + >>> id1 != id2 + True + + """ + data = "%.20f%.20f%.20f" % (random.random(), time.time(), time.clock()) + # BBB code for Python 2.4, inspired by the fallback in hmac + if hasattr(sha1, '__call__'): + digest = sha1(data).digest() + else: + digest = sha1.new(data).digest() + s = digestEncode(digest) + # we store a HMAC of the random value together with it, which makes + # our session ids unforgeable. + mac = hmac.new(self.secret, s, digestmod=sha1).digest() + return s + digestEncode(mac) + + def getRequestId(self, request): + """Return the browser id encoded in request as a string + + Return None if an id is not set. + + For example: + + >>> from zope.publisher.http import HTTPRequest + >>> request = HTTPRequest(StringIO(''), {}, None) + >>> bim = CookieClientIdManager() + + Because no cookie has been set, we get no id: + + >>> bim.getRequestId(request) is None + True + + We can set an id: + + >>> id1 = bim.generateUniqueId() + >>> bim.setRequestId(request, id1) + + And get it back: + + >>> bim.getRequestId(request) == id1 + True + + When we set the request id, we also set a response cookie. We + can simulate getting this cookie back in a subsequent request: + + >>> request2 = HTTPRequest(StringIO(''), {}, None) + >>> request2._cookies = dict( + ... [(name, cookie['value']) + ... for (name, cookie) in request.response._cookies.items() + ... ]) + + And we get the same id back from the new request: + + >>> bim.getRequestId(request) == bim.getRequestId(request2) + True + + Test a corner case where Python 2.6 hmac module does not allow + unicode as input: + + >>> id_uni = unicode(bim.generateUniqueId()) + >>> bim.setRequestId(request, id_uni) + >>> bim.getRequestId(request) == id_uni + True + + If another server is managing the ClientId cookies (Apache, Nginx) + we're returning their value without checking: + + >>> bim.namespace = 'uid' + >>> bim.thirdparty = True + >>> request3 = HTTPRequest(StringIO(''), {}, None) + >>> request3._cookies = {'uid': 'AQAAf0Y4gjgAAAQ3AwMEAg=='} + >>> bim.getRequestId(request3) + 'AQAAf0Y4gjgAAAQ3AwMEAg==' + + """ + response_cookie = request.response.getCookie(self.namespace) + if response_cookie: + sid = response_cookie['value'] + else: + request = IHTTPApplicationRequest(request) + sid = request.getCookies().get(self.namespace, None) + if self.thirdparty: + return sid + else: + + # If there is an id set on the response, use that but + # don't trust it. We need to check the response in case + # there has already been a new session created during the + # course of this request. + + if sid is None or len(sid) != 54: + return None + s, mac = sid[:27], sid[27:] + + # call encode() on value s a workaround a bug where the hmac + # module only accepts str() types in Python 2.6 + if (digestEncode(hmac.new( + self.secret, s.encode(), digestmod=sha1 + ).digest()) != mac): + return None + else: + return sid + + def setRequestId(self, request, id): + """Set cookie with id on request. + + This sets the response cookie: + + See the examples in getRequestId. + + Note that the id is checked for validity. Setting an + invalid value is silently ignored: + + >>> from zope.publisher.http import HTTPRequest + >>> request = HTTPRequest(StringIO(''), {}, None) + >>> bim = CookieClientIdManager() + >>> bim.getRequestId(request) + >>> bim.setRequestId(request, 'invalid id') + >>> bim.getRequestId(request) + + For now, the cookie path is the application URL: + + >>> cookie = request.response.getCookie(bim.namespace) + >>> cookie['path'] == request.getApplicationURL(path_only=True) + True + + By default, session cookies don't expire: + + >>> cookie.has_key('expires') + False + + Expiry time of 0 means never (well - close enough) + + >>> bim.cookieLifetime = 0 + >>> request = HTTPRequest(StringIO(''), {}, None) + >>> bid = bim.getClientId(request) + >>> cookie = request.response.getCookie(bim.namespace) + >>> cookie['expires'] + 'Tue, 19 Jan 2038 00:00:00 GMT' + + A non-zero value means to expire after than number of seconds: + + >>> bim.cookieLifetime = 3600 + >>> request = HTTPRequest(StringIO(''), {}, None) + >>> bid = bim.getClientId(request) + >>> cookie = request.response.getCookie(bim.namespace) + >>> import rfc822 + >>> expires = time.mktime(rfc822.parsedate(cookie['expires'])) + >>> expires > time.mktime(time.gmtime()) + 55*60 + True + + If another server in front of Zope (Apache, Nginx) is managing the + cookies we won't set any ClientId cookies: + + >>> request = HTTPRequest(StringIO(''), {}, None) + >>> bim.thirdparty = True + >>> bim.setRequestId(request, '1234') + >>> cookie = request.response.getCookie(bim.namespace) + >>> cookie + + If the secure attribute is set to a true value, then the + secure cookie option is included. + + >>> bim.thirdparty = False + >>> bim.cookieLifetime = None + >>> request = HTTPRequest(StringIO(''), {}, None) + >>> bim.secure = True + >>> bim.setRequestId(request, '1234') + >>> print request.response.getCookie(bim.namespace) + {'path': '/', 'secure': True, 'value': '1234'} + + If the domain is specified, it will be set as a cookie attribute. + + >>> bim.domain = u'.example.org' + >>> bim.setRequestId(request, '1234') + >>> print request.response.getCookie(bim.namespace) + {'path': '/', 'domain': u'.example.org', 'secure': True, 'value': '1234'} + + When the cookie is set, cache headers are added to the + response to try to prevent the cookie header from being cached: + + >>> request.response.getHeader('Cache-Control') + 'no-cache="Set-Cookie,Set-Cookie2"' + >>> request.response.getHeader('Pragma') + 'no-cache' + >>> request.response.getHeader('Expires') + 'Mon, 26 Jul 1997 05:00:00 GMT' + + """ + # TODO: Currently, the path is the ApplicationURL. This is reasonable, + # and will be adequate for most purposes. + # A better path to use would be that of the folder that contains + # the site manager this service is registered within. However, + # that would be expensive to look up on each request, and would + # have to be altered to take virtual hosting into account. + # Seeing as this utility instance has a unique namespace for its + # cookie, using ApplicationURL shouldn't be a problem. + + if self.thirdparty: + logger.warning('ClientIdManager is using thirdparty cookies, ' + 'ignoring setIdRequest call') + return + + response = request.response + options = {} + if self.cookieLifetime is not None: + if self.cookieLifetime: + expires = formatdate(time.time() + self.cookieLifetime, + localtime=False, usegmt=True) + else: + expires = 'Tue, 19 Jan 2038 00:00:00 GMT' + + options['expires'] = expires + + if self.secure: + options['secure'] = True + + if self.domain: + options['domain'] = self.domain + + response.setCookie( + self.namespace, id, + path=request.getApplicationURL(path_only=True), + **options) + + response.setHeader('Cache-Control', 'no-cache="Set-Cookie,Set-Cookie2"') + response.setHeader('Pragma', 'no-cache') + response.setHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT') + +def notifyVirtualHostChanged(event): + """Adjust cookie paths when IVirtualHostRequest information changes. + + Given an event, this method should call a CookieClientIdManager's + setRequestId if a cookie is present in the response for that manager. To + demonstrate we create a dummy manager object and event: + + >>> class DummyManager(object): + ... implements(ICookieClientIdManager) + ... namespace = 'foo' + ... thirdparty = False + ... request_id = None + ... def setRequestId(self, request, id): + ... self.request_id = id + ... + >>> manager = DummyManager() + >>> component.provideUtility(manager, IClientIdManager) + >>> from zope.publisher.http import HTTPRequest + >>> class DummyEvent (object): + ... request = HTTPRequest(StringIO(''), {}, None) + >>> event = DummyEvent() + + With no cookies present, the manager should not be called: + + >>> notifyVirtualHostChanged(event) + >>> manager.request_id is None + True + + However, when a cookie *has* been set, the manager is called so it can + update the cookie if need be: + + >>> event.request.response.setCookie('foo', 'bar') + >>> notifyVirtualHostChanged(event) + >>> manager.request_id + 'bar' + + If a server in front of Zope manages the ClientIds (Apache, Nginx), we + don't need to take care about the cookies + + >>> manager2 = DummyManager() + >>> manager2.thirdparty = True + >>> event2 = DummyEvent() + + However, when a cookie *has* been set, the manager is called so it can + update the cookie if need be: + + >>> event2.request.response.setCookie('foo2', 'bar2') + >>> notifyVirtualHostChanged(event2) + >>> id = manager2.request_id + >>> id is None + True + + Cleanup of the utility registration: + + >>> import zope.component.testing + >>> zope.component.testing.tearDown() + + """ + # the event sends us a IHTTPApplicationRequest, but we need a + # IHTTPRequest for the response attribute, and so does the cookie- + # manager. + request = IHTTPRequest(event.request, None) + if event.request is None: + return + for name, manager in component.getUtilitiesFor(IClientIdManager): + if manager and ICookieClientIdManager.providedBy(manager): + # Third party ClientId Managers need no modification at all + if not manager.thirdparty: + cookie = request.response.getCookie(manager.namespace) + if cookie: + manager.setRequestId(request, cookie['value']) diff -Nru zope3-3.4.0/src/zope/session/__init__.py zope3-3.5~bzr18/src/zope/session/__init__.py --- zope3-3.4.0/src/zope/session/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,18 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Core session interfaces and implementation + +$Id: __init__.py 26427 2004-07-12 16:05:02Z Zen $ +""" + diff -Nru zope3-3.4.0/src/zope/session/interfaces.py zope3-3.5~bzr18/src/zope/session/interfaces.py --- zope3-3.4.0/src/zope/session/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,163 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Interfaces for session utility. + +$Id: interfaces.py 87351 2008-06-12 21:02:43Z gary $ +""" +from zope.interface import Interface +from zope.interface.common.mapping import IMapping, IReadMapping, IWriteMapping +from zope import schema +from zope.i18nmessageid import ZopeMessageFactory as _ + +__docformat__ = 'restructuredtext' + +class IClientIdManager(Interface): + """Manages sessions - fake state over multiple client requests.""" + + def getClientId(request): + """Return the client id for the given request as a string. + + If the request doesn't have an attached sessionId a new one will be + generated. + + This will do whatever is possible to do the HTTP request to ensure the + session id will be preserved. Depending on the specific method, + further action might be necessary on the part of the user. See the + documentation for the specific implementation and its interfaces. + + """ + + +class IClientId(Interface): + """A unique id representing a session""" + + def __str__(): + """As a unique ASCII string""" + + +class ISessionDataContainer(IReadMapping, IWriteMapping): + """Stores data objects for sessions. + + The object implementing this interface is responsible for expiring data as + it feels appropriate. + + Usage:: + + session_data_container[client_id][product_id][key] = value + + Note that this interface does not support the full mapping interface - + the keys need to remain secret so we can't give access to keys(), + values() etc. + + """ + timeout = schema.Int( + title=_(u"Timeout"), + description=_( + "Number of seconds before data becomes stale and may " + "be removed. A value of '0' means no expiration."), + default=3600, + required=True, + min=0, + ) + resolution = schema.Int( + title=_("Timeout resolution (in seconds)"), + description=_( + "Defines what the 'resolution' of item timeout is. " + "Setting this higher allows the transience machinery to " + "do fewer 'writes' at the expense of causing items to time " + "out later than the 'Data object timeout value' by a factor " + "of (at most) this many seconds." + ), + default=10*60, + required=True, + min=0, + ) + + def __getitem__(self, product_id): + """Return an ISessionPkgData""" + + def __setitem__(self, product_id, value): + """Store an ISessionPkgData""" + + +class ISession(Interface): + """This object allows retrieval of the correct ISessionData + for a particular product id + + >>> session = ISession(request)[product_id] + >>> session['color'] = 'red' + True + + >>> ISessionData.providedBy(session) + True + + """ + + def __getitem__(product_id): + """Return the relevant ISessionPkgData + + This involves locating the correct ISessionDataContainer for the + given product id, determining the client id, and returning the + relevant ISessionPkgData. + + """ + + def get(product_id, default=None): + """Return the relevant ISessionPkgData or default if not + available""" + + +class ISessionData(IReadMapping, IMapping): + """Storage for a particular product id's session data + + Contains 0 or more ISessionPkgData instances + + """ + + def getLastAccessTime(): + "return approximate epoch time this ISessionData was last retrieved" + + def setLastAccessTime(): + "An API for ISessionDataContainer to set the last retrieved epoch time" + + # consider deprecating this property, or at least making it readonly. The + # setter should be used instead of setting this property because of + # conflict resolution: see https://bugs.launchpad.net/zope3/+bug/239531 + lastAccessTime = schema.Int( + title=_("Last Access Time"), + description=_( + "Approximate epoch time this ISessionData was last retrieved " + "from its ISessionDataContainer" + ), + default=0, + required=True, + ) + + # Note that only IReadMapping and IWriteMaping are implemented. + # We cannot give access to the keys, as they need to remain secret. + + def __getitem__(self, client_id): + """Return an ISessionPkgData""" + + def __setitem__(self, client_id, session_pkg_data): + """Store an ISessionPkgData""" + + +class ISessionPkgData(IMapping): + """Storage for a particular product id and browser id's session data + + Data is stored persistently and transactionally. Data stored must + be persistent or picklable. + + """ diff -Nru zope3-3.4.0/src/zope/session/session.py zope3-3.5~bzr18/src/zope/session/session.py --- zope3-3.4.0/src/zope/session/session.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/session.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,531 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Session implementation + +$Id: session.py 98119 2009-03-15 11:39:12Z nadako $ +""" +from cStringIO import StringIO +import time, string, random, thread +from UserDict import IterableUserDict +from heapq import heapify, heappop + +import ZODB +import ZODB.MappingStorage +import zope.location +import zope.minmax +import persistent +from BTrees.OOBTree import OOBTree + +import zope.component +import zope.interface +from zope.component.interfaces import ComponentLookupError +from zope.publisher.interfaces import IRequest + +from zope.session.interfaces import \ + IClientIdManager, IClientId, ISession, ISessionDataContainer, \ + ISessionPkgData, ISessionData + +__docformat__ = 'restructuredtext' + +cookieSafeTrans = string.maketrans("+/", "-.") + +def digestEncode(s): + """Encode SHA digest for cookie.""" + return s.encode("base64")[:-2].translate(cookieSafeTrans) + + +class ClientId(str): + """See zope.session.interfaces.IClientId + + >>> import tests + >>> request = tests.setUp() + + >>> id1 = ClientId(request) + >>> id2 = ClientId(request) + >>> id1 == id2 + True + + >>> tests.tearDown() + + """ + zope.interface.implements(IClientId) + zope.component.adapts(IRequest) + + def __new__(cls, request): + return str.__new__(cls, + zope.component.getUtility(IClientIdManager).getClientId(request) + ) + + +class PersistentSessionDataContainer(zope.location.Location, + persistent.Persistent, + IterableUserDict): + """A SessionDataContainer that stores data in the ZODB""" + + zope.interface.implements(ISessionDataContainer) + + _v_last_sweep = 0 # Epoch time sweep last run + + def __init__(self): + self.data = OOBTree() + self.timeout = 1 * 60 * 60 + # The "resolution" should be a small fraction of the timeout. + self.resolution = 10 * 60 + + def __getitem__(self, pkg_id): + """Retrieve an ISessionData + + >>> sdc = PersistentSessionDataContainer() + + >>> sdc.timeout = 60 + >>> sdc.resolution = 3 + >>> sdc['clientid'] = sd = SessionData() + + To ensure stale data is removed, we can wind + back the clock using undocumented means... + + >>> sd.setLastAccessTime(sd.getLastAccessTime() - 64) + >>> sdc._v_last_sweep = sdc._v_last_sweep - 4 + + Now the data should be garbage collected + + >>> sdc['clientid'] + Traceback (most recent call last): + [...] + KeyError: 'clientid' + + Ensure the lastAccessTime on the ISessionData is being updated + occasionally. The ISessionDataContainer maintains this whenever + the ISessionData is set or retrieved. + + lastAccessTime on the ISessionData is set when it is added + to the ISessionDataContainer + + >>> sdc['client_id'] = sd = SessionData() + >>> sd.getLastAccessTime() > 0 + True + + The lastAccessTime is also updated whenever the ISessionData + is retrieved through the ISessionDataContainer, at most + once every 'resolution' seconds. + + >>> then = sd.getLastAccessTime() - 4 + >>> sd.setLastAccessTime(then) + >>> now = sdc['client_id'].getLastAccessTime() + >>> now > then + True + >>> time.sleep(1) + >>> now == sdc['client_id'].getLastAccessTime() + True + + Ensure the lastAccessTime is not modified and no garbage collection + occurs when timeout == 0. We test this by faking a stale + ISessionData object. + + >>> sdc.timeout = 0 + >>> sd.setLastAccessTime(sd.getLastAccessTime() - 5000) + >>> lastAccessTime = sd.getLastAccessTime() + >>> sdc['client_id'].getLastAccessTime() == lastAccessTime + True + + Next, we test session expiration functionality beyond transactions. + + >>> import transaction + >>> from ZODB.DB import DB + >>> from ZODB.DemoStorage import DemoStorage + >>> sdc = PersistentSessionDataContainer() + >>> sdc.timeout = 60 + >>> sdc.resolution = 3 + >>> db = DB(DemoStorage('test_storage')) + >>> c = db.open() + >>> c.root()['sdc'] = sdc + >>> sdc['pkg_id'] = sd = SessionData() + >>> sd['name'] = 'bob' + >>> transaction.commit() + + Access immediately. the data should be accessible. + + >>> c.root()['sdc']['pkg_id']['name'] + 'bob' + + Change the clock time and stale the session data. + + >>> sdc = c.root()['sdc'] + >>> sd = sdc['pkg_id'] + >>> sd.setLastAccessTime(sd.getLastAccessTime() - 64) + >>> sdc._v_last_sweep = sdc._v_last_sweep - 4 + >>> transaction.commit() + + The data should be garbage collected. + + >>> c.root()['sdc']['pkg_id']['name'] + Traceback (most recent call last): + [...] + KeyError: 'pkg_id' + + Then abort transaction and access the same data again. + The previous GC was cancelled, but deadline is over. + The data should be garbage collected again. + + >>> transaction.abort() + >>> c.root()['sdc']['pkg_id']['name'] + Traceback (most recent call last): + [...] + KeyError: 'pkg_id' + + """ + if self.timeout == 0: + return IterableUserDict.__getitem__(self, pkg_id) + + now = time.time() + + # TODO: When scheduler exists, sweeping should be done by + # a scheduled job since we are currently busy handling a + # request and may end up doing simultaneous sweeps + + # If transaction is aborted after sweep. _v_last_sweep keep + # incorrect sweep time. So when self.data is ghost, revert the time + # to the previous _v_last_sweep time(_v_old_sweep). + if self.data._p_state < 0: + try: + self._v_last_sweep = self._v_old_sweep + del self._v_old_sweep + except AttributeError: + pass + + if self._v_last_sweep + self.resolution < now: + self.sweep() + if getattr(self, '_v_old_sweep', None) is None: + self._v_old_sweep = self._v_last_sweep + self._v_last_sweep = now + + rv = IterableUserDict.__getitem__(self, pkg_id) + # Only update the lastAccessTime once every few minutes, rather than + # every hit, to avoid ZODB bloat and conflicts + if rv.getLastAccessTime() + self.resolution < now: + rv.setLastAccessTime(int(now)) + return rv + + def __setitem__(self, pkg_id, session_data): + """Set an ISessionPkgData + + >>> sdc = PersistentSessionDataContainer() + >>> sad = SessionData() + + __setitem__ sets the ISessionData's lastAccessTime + + >>> sad.getLastAccessTime() + 0 + >>> sdc['1'] = sad + >>> 0 < sad.getLastAccessTime() <= time.time() + True + + We can retrieve the same object we put in + + >>> sdc['1'] is sad + True + + """ + session_data.setLastAccessTime(int(time.time())) + return IterableUserDict.__setitem__(self, pkg_id, session_data) + + def sweep(self): + """Clean out stale data + + >>> sdc = PersistentSessionDataContainer() + >>> sdc['1'] = SessionData() + >>> sdc['2'] = SessionData() + + Wind back the clock on one of the ISessionData's + so it gets garbage collected + + >>> sdc['2'].setLastAccessTime( + ... sdc['2'].getLastAccessTime() - sdc.timeout * 2) + + Sweep should leave '1' and remove '2' + + >>> sdc.sweep() + >>> sd1 = sdc['1'] + >>> sd2 = sdc['2'] + Traceback (most recent call last): + [...] + KeyError: '2' + + """ + # We only update the lastAccessTime every 'resolution' seconds. + # To compensate for this, we factor in the resolution when + # calculating the expiry time to ensure that we never remove + # data that has been accessed within timeout seconds. + expire_time = time.time() - self.timeout - self.resolution + heap = [(v.getLastAccessTime(), k) for k,v in self.data.items()] + heapify(heap) + while heap: + lastAccessTime, key = heappop(heap) + if lastAccessTime < expire_time: + del self.data[key] + else: + return + + +class RAMSessionDataContainer(PersistentSessionDataContainer): + """A SessionDataContainer that stores data in RAM. + + Currently session data is not shared between Zope clients, so + server affinity will need to be maintained to use this in a ZEO cluster. + + >>> sdc = RAMSessionDataContainer() + >>> sdc['1'] = SessionData() + >>> sdc['1'] is sdc['1'] + True + >>> ISessionData.providedBy(sdc['1']) + True + + """ + def __init__(self): + self.resolution = 5*60 + self.timeout = 1 * 60 * 60 + # Something unique + self.key = '%s.%s.%s' % (time.time(), random.random(), id(self)) + + _ram_storage = ZODB.MappingStorage.MappingStorage() + _ram_db = ZODB.DB(_ram_storage) + _conns = {} + + def _getData(self): + + # Open a connection to _ram_storage per thread + tid = thread.get_ident() + if not self._conns.has_key(tid): + self._conns[tid] = self._ram_db.open() + + root = self._conns[tid].root() + if not root.has_key(self.key): + root[self.key] = OOBTree() + return root[self.key] + + data = property(_getData, None) + + def sweep(self): + super(RAMSessionDataContainer, self).sweep() + self._ram_db.pack(time.time()) + + +class Session(object): + """See zope.session.interfaces.ISession""" + zope.interface.implements(ISession) + zope.component.adapts(IRequest) + + def __init__(self, request): + self.client_id = str(IClientId(request)) + + def _sdc(self, pkg_id): + # Locate the ISessionDataContainer by looking up the named + # Utility, and falling back to the unnamed one. + try: + return zope.component.getUtility(ISessionDataContainer, pkg_id) + except ComponentLookupError: + return zope.component.getUtility(ISessionDataContainer) + + def get(self, pkg_id, default=None): + + """See zope.session.interfaces.ISession + + >>> import tests + >>> request = tests.setUp(PersistentSessionDataContainer) + + If we use get we get None or default returned if the pkg_id + is not there. + + >>> session = Session(request).get('not.there', 'default') + >>> session + 'default' + + This method is lazy and does not create the session data. + >>> session = Session(request).get('not.there') + >>> session is None + True + + The __getitem__ method instead creates the data. + >>> session = Session(request)['not.there'] + >>> session is None + False + >>> session = Session(request).get('not.there') + >>> session is None + False + >>> tests.tearDown() + + """ + + + # The ISessionDataContainer contains two levels: + # ISessionDataContainer[client_id] == ISessionData + # ISessionDataContainer[client_id][pkg_id] == ISessionPkgData + sdc = self._sdc(pkg_id) + try: + sd = sdc[self.client_id] + except KeyError: + return default + try: + return sd[pkg_id] + except KeyError: + return default + + + def __getitem__(self, pkg_id): + """See zope.session.interfaces.ISession + + >>> import tests + >>> request = tests.setUp(PersistentSessionDataContainer) + >>> request2 = tests.HTTPRequest(StringIO(''), {}, None) + + >>> ISession.providedBy(Session(request)) + True + + Setup some sessions, each with a distinct namespace + + >>> session1 = Session(request)['products.foo'] + >>> session2 = Session(request)['products.bar'] + >>> session3 = Session(request2)['products.bar'] + + If we use the same parameters, we should retrieve the + same object + + >>> session1 is Session(request)['products.foo'] + True + + Make sure it returned sane values + + >>> ISessionPkgData.providedBy(session1) + True + + Make sure that pkg_ids don't share a namespace. + + >>> session1['color'] = 'red' + >>> session2['color'] = 'blue' + >>> session3['color'] = 'vomit' + >>> session1['color'] + 'red' + >>> session2['color'] + 'blue' + >>> session3['color'] + 'vomit' + + >>> tests.tearDown() + + """ + sdc = self._sdc(pkg_id) + + # The ISessionDataContainer contains two levels: + # ISessionDataContainer[client_id] == ISessionData + # ISessionDataContainer[client_id][pkg_id] == ISessionPkgData + try: + sd = sdc[self.client_id] + except KeyError: + sd = sdc[self.client_id] = SessionData() + + try: + return sd[pkg_id] + except KeyError: + spd = sd[pkg_id] = SessionPkgData() + return spd + + +class SessionData(persistent.Persistent, IterableUserDict): + """See zope.session.interfaces.ISessionData + + >>> session = SessionData() + >>> ISessionData.providedBy(session) + True + >>> session.getLastAccessTime() + 0 + + Before the zope.minmax package this class used to have an attribute + lastAccessTime initialized in the class itself to zero. + To avoid changing the interface, that attribute has been turned into a + property. This part tests the behavior of a legacy session which would + have the lastAccessTime attribute loaded from the database. + The implementation should work for that case as well as with the new + session where lastAccessTime is a property. These tests will + be removed in a later release (see the comments in the code below). + + First, create an instance of SessionData and remove a protected attribute + _lastAccessTime from it to make it more like the legacy SessionData. The + subsequent attempt to get lastAccessTime will return a 0, because the + lastAccessTime is not there and the dictionary returns the default value + zero supplied to its get() method. + + >>> legacy_session = SessionData() + >>> del legacy_session._lastAccessTime + >>> legacy_session.getLastAccessTime() + 0 + + Now, artificially add lastAccessTime to the instance's dictionary. + This should make it exactly like the legacy SessionData(). + + >>> legacy_session.__dict__['lastAccessTime'] = 42 + >>> legacy_session.getLastAccessTime() + 42 + + Finally, assign to lastAccessTime. Since the instance now looks like a + legacy instance, this will trigger, through the property mechanism, a + creation of a zope.minmax.Maximum() object which will take over the + handling of this value and its conflict resolution from now on. + + >>> legacy_session.setLastAccessTime(13) + >>> legacy_session._lastAccessTime.value + 13 + + """ + zope.interface.implements(ISessionData) + + # this is for support of legacy sessions; this comment and + # the next line will be removed in a later release + _lastAccessTime = None + + def __init__(self): + self.data = OOBTree() + self._lastAccessTime = zope.minmax.Maximum(0) + + # we include this for parallelism with setLastAccessTime + def getLastAccessTime(self): + # this conditional is for legacy sessions; this comment and + # the next two lines will be removed in a later release + if self._lastAccessTime is None: + return self.__dict__.get('lastAccessTime', 0) + return self._lastAccessTime.value + + # we need to set this value with setters in order to get optimal conflict + # resolution behavior + def setLastAccessTime(self, value): + # this conditional is for legacy sessions; this comment and + # the next two lines will be removed in a later release + if self._lastAccessTime is None: + self._lastAccessTime = zope.minmax.Maximum(0) + self._lastAccessTime.value = value + + lastAccessTime = property(fget=getLastAccessTime, + fset=setLastAccessTime, # consider deprecating + doc='integer value of the last access time') + + +class SessionPkgData(persistent.Persistent, IterableUserDict): + """See zope.session.interfaces.ISessionPkgData + + >>> session = SessionPkgData() + >>> ISessionPkgData.providedBy(session) + True + """ + zope.interface.implements(ISessionPkgData) + def __init__(self): + self.data = OOBTree() diff -Nru zope3-3.4.0/src/zope/session/subscribers.zcml zope3-3.5~bzr18/src/zope/session/subscribers.zcml --- zope3-3.4.0/src/zope/session/subscribers.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/subscribers.zcml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + diff -Nru zope3-3.4.0/src/zope/session/tests.py zope3-3.5~bzr18/src/zope/session/tests.py --- zope3-3.4.0/src/zope/session/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/session/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,143 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Session tests + +$Id: tests.py 98144 2009-03-16 15:46:45Z nadako $ +""" +from cStringIO import StringIO +import unittest, os, os.path + +import zope.component +from zope.testing import doctest, cleanup +import transaction + +from zope.component import provideHandler, getGlobalSiteManager +from zope.session.interfaces import IClientId, IClientIdManager, ISession +from zope.session.interfaces import ISessionDataContainer +from zope.session.interfaces import ISessionPkgData, ISessionData +from zope.session.session import ClientId, Session +from zope.session.session import PersistentSessionDataContainer +from zope.session.session import RAMSessionDataContainer +from zope.session.http import CookieClientIdManager + +from zope.publisher.interfaces import IRequest +from zope.publisher.http import HTTPRequest + +def setUp(session_data_container_class=PersistentSessionDataContainer): + cleanup.setUp() + zope.component.provideAdapter(ClientId, (IRequest,), IClientId) + zope.component.provideAdapter(Session, (IRequest,), ISession) + zope.component.provideUtility(CookieClientIdManager(), IClientIdManager) + sdc = session_data_container_class() + for product_id in ('', 'products.foo', 'products.bar', 'products.baz'): + zope.component.provideUtility(sdc, ISessionDataContainer, product_id) + request = HTTPRequest(StringIO(), {}, None) + return request + +def tearDown(): + cleanup.tearDown() + +# Test the code in our API documentation is correct +def test_documentation(): + pass +test_documentation.__doc__ = ''' + >>> request = setUp(RAMSessionDataContainer) + + %s + + >>> tearDown() + + ''' % (open(os.path.join(os.path.dirname(__file__), 'api.txt')).read(),) + + +def tearDownTransaction(test): + transaction.abort() + + +def testConflicts(): + """The SessionData objects have been plagued with unnecessary + ConflictErrors. The current implementation makes the most common source + of ConflictErrors in the past, setting the lastAccessTime, no longer a + problem in this regard. + + To illustrate this, we will do a bit of an integration test. We'll begin + by getting a connection and putting a session data container in the root, + within transaction manager "A". + + >>> try: + ... # ZODB 3.8 + ... from ZODB.DB import DB + ... from ZODB.tests.util import ConflictResolvingMappingStorage + ... db = DB(ConflictResolvingMappingStorage()) + ... except ImportError: + ... # ZODB 3.9 (ConflictResolvingMappingStorage no longer exists) + ... import ZODB.DB + ... db = ZODB.DB('Data.fs') + >>> from zope.session.session import ( + ... PersistentSessionDataContainer, SessionData) + >>> import transaction + >>> tm_A = transaction.TransactionManager() + >>> conn_A = db.open(transaction_manager=tm_A) + >>> root_A = conn_A.root() + >>> sdc_A = root_A['sdc'] = PersistentSessionDataContainer() + >>> sdc_A.resolution = 3 + >>> sd_A = sdc_A['clientid'] = SessionData() + >>> then = sd_A.getLastAccessTime() - 4 + >>> sd_A.setLastAccessTime(then) + >>> tm_A.commit() + + Now we have a session data container with a session data lastAccessTime + that is set to four seconds ago. Since we set the resolution to three + seconds, the next time the session is accessed, the lastAccessTime should + be updated. + + We will access the session simultaneously in two transactions, which will + set the updated lastAccessTime on both objects, and then commit. Because + of the conflict resolution code in zope.minmax, both commits will succeed, + which is what we wanted to demonstrate. + + >>> tm_B = transaction.TransactionManager() + >>> conn_B = db.open(transaction_manager=tm_B) + >>> root_B = conn_B.root() + >>> sdc_B = root_B['sdc'] + + >>> sd_B = sdc_B['clientid'] # has side effect of updating lastAccessTime + >>> sd_B.getLastAccessTime() > then + True + + >>> sd_A is sdc_A['clientid'] # has side effect of updating lastAccessTime + True + >>> sd_A.getLastAccessTime() > then + True + + >>> tm_A.commit() + >>> tm_B.commit() + + Q.E.D. + """ + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(doctest.DocTestSuite()) + suite.addTest(doctest.DocTestSuite('zope.session.session', + tearDown=tearDownTransaction)) + suite.addTest(doctest.DocTestSuite('zope.session.http', + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,) + ) + return suite + + +if __name__ == '__main__': + unittest.main() diff -Nru zope3-3.4.0/src/zope/site/configure.zcml zope3-3.5~bzr18/src/zope/site/configure.zcml --- zope3-3.4.0/src/zope/site/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/site/folder.py zope3-3.5~bzr18/src/zope/site/folder.py --- zope3-3.4.0/src/zope/site/folder.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/folder.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,81 @@ +############################################################################# +# +# Copyright (c) 2009 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. +# +############################################################################## +""" + +$Id$ +""" + +from zope.interface import implements, directlyProvides + +from zope.site.interfaces import IFolder, IRootFolder +from zope.site.site import SiteManagerContainer + +import zope.component.interfaces +import zope.container.folder + + +class Folder(zope.container.folder.Folder, SiteManagerContainer): + + implements(IFolder) + + +def rootFolder(): + f = Folder() + directlyProvides(f, IRootFolder) + return f + + +class FolderSublocations(object): + """Get the sublocations of a folder + + The subobjects of a folder include it's contents and it's site manager if + it is a site. + + >>> from zope.container.contained import Contained + >>> folder = Folder() + >>> folder['ob1'] = Contained() + >>> folder['ob2'] = Contained() + >>> folder['ob3'] = Contained() + >>> subs = list(FolderSublocations(folder).sublocations()) + >>> subs.remove(folder['ob1']) + >>> subs.remove(folder['ob2']) + >>> subs.remove(folder['ob3']) + >>> subs + [] + + >>> sm = Contained() + >>> from zope.interface import directlyProvides + >>> from zope.component.interfaces import IComponentLookup + >>> directlyProvides(sm, IComponentLookup) + >>> folder.setSiteManager(sm) + >>> directlyProvides(folder, zope.component.interfaces.ISite) + >>> subs = list(FolderSublocations(folder).sublocations()) + >>> subs.remove(folder['ob1']) + >>> subs.remove(folder['ob2']) + >>> subs.remove(folder['ob3']) + >>> subs.remove(sm) + >>> subs + [] + """ + + def __init__(self, folder): + self.folder = folder + + def sublocations(self): + folder = self.folder + for key in folder: + yield folder[key] + + if zope.component.interfaces.ISite.providedBy(folder): + yield folder.getSiteManager() diff -Nru zope3-3.4.0/src/zope/site/hooks.py zope3-3.5~bzr18/src/zope/site/hooks.py --- zope3-3.4.0/src/zope/site/hooks.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/hooks.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,30 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Hooks for getting and setting a site in the thread global namespace. + +$Id: hooks.py 105534 2009-11-09 07:37:17Z tlotze $ +""" +__docformat__ = 'restructuredtext' + +from zope.component.hooks import (read_property, + SiteInfo, + siteinfo, + setSite, + getSite, + getSiteManager, + adapter_hook, + setHooks, + resetHooks, + setSite, + clearSite) # BBB diff -Nru zope3-3.4.0/src/zope/site/__init__.py zope3-3.5~bzr18/src/zope/site/__init__.py --- zope3-3.4.0/src/zope/site/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,26 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Local Component Architecture + +$Id: __init__.py 97940 2009-03-12 00:07:28Z nadako $ +""" + +from zope.site.site import (SiteManagerContainer, SiteManagementFolder, + SiteManagerAdapter) +from zope.site.site import LocalSiteManager, changeSiteConfigurationAfterMove +from zope.site.site import threadSiteSubscriber +from zope.site.site import clearThreadSiteSubscriber + +# BBB +from zope.component import getNextUtility, queryNextUtility diff -Nru zope3-3.4.0/src/zope/site/interfaces.py zope3-3.5~bzr18/src/zope/site/interfaces.py --- zope3-3.4.0/src/zope/site/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,88 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Interfaces for the Local Component Architecture + +$Id: interfaces.py 105805 2009-11-18 09:34:13Z tlotze $ +""" + +import zope.interface +import zope.component.interfaces +import zope.container.interfaces +import zope.container.constraints +import zope.location.interfaces + +from zope.annotation.interfaces import IAttributeAnnotatable + + +class INewLocalSite(zope.interface.Interface): + """Event: a local site was created + """ + + manager = zope.interface.Attribute("The new site manager") + +class NewLocalSite(object): + """Event: a local site was created + """ + zope.interface.implements(INewLocalSite) + + def __init__(self, manager): + self.manager = manager + + +class ILocalSiteManager(zope.component.interfaces.IComponents): + """Site Managers act as containers for registerable components. + + If a Site Manager is asked for an adapter or utility, it checks for those + it contains before using a context-based lookup to find another site + manager to delegate to. If no other site manager is found they defer to + the global site manager which contains file based utilities and adapters. + """ + + subs = zope.interface.Attribute( + "A collection of registries that describe the next level " + "of the registry tree. They are the children of this " + "registry node. This attribute should never be " + "manipulated manually. Use `addSub()` and `removeSub()` " + "instead.") + + def addSub(sub): + """Add a new sub-registry to the node. + + Important: This method should *not* be used manually. It is + automatically called by `setNext()`. To add a new registry to the + tree, use `sub.setNext(self, self.base)` instead! + """ + + def removeSub(sub): + """Remove a sub-registry to the node. + + Important: This method should *not* be used manually. It is + automatically called by `setNext()`. To remove a registry from the + tree, use `sub.setNext(None)` instead! + """ + + +class ISiteManagementFolder(zope.container.interfaces.IContainer): + """Component and component registration containers.""" + + zope.container.constraints.containers( + ILocalSiteManager, '.ISiteManagementFolder') + +class IFolder(zope.container.interfaces.IContainer, + zope.component.interfaces.IPossibleSite, + IAttributeAnnotatable): + """The standard Zope Folder object interface.""" + +class IRootFolder(IFolder, zope.location.interfaces.IRoot): + """The standard Zope root Folder object interface.""" diff -Nru zope3-3.4.0/src/zope/site/next.py zope3-3.5~bzr18/src/zope/site/next.py --- zope3-3.4.0/src/zope/site/next.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/next.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ +# BBB +from zope.component import getNextUtility, queryNextUtility diff -Nru zope3-3.4.0/src/zope/site/site.py zope3-3.5~bzr18/src/zope/site/site.py --- zope3-3.4.0/src/zope/site/site.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/site.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,248 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Site and Local Site Manager implementation + +A local site manager has a number of roles: + + - A local site manager, that provides a local adapter and utility registry. + + - A place to do TTW development and/or to manage database-based code. + + - A registry for persistent modules. The Zope 3 import hook uses the + SiteManager to search for modules. + +$Id: site.py 105805 2009-11-18 09:34:13Z tlotze $ +""" + +import zope.event +import zope.interface +import zope.component +import zope.component.persistentregistry +import zope.component.hooks +import zope.component.interfaces +import zope.location +import zope.location.interfaces + +from zope.component.interfaces import ComponentLookupError +from zope.lifecycleevent import ObjectCreatedEvent +from zope.filerepresentation.interfaces import IDirectoryFactory + +from zope.container.btree import BTreeContainer +from zope.container.contained import Contained + +from zope.site import interfaces + +# BBB +from zope.component.hooks import setSite + + +class SiteManagementFolder(BTreeContainer): + zope.interface.implements(interfaces.ISiteManagementFolder) + + +class SMFolderFactory(object): + zope.interface.implements(IDirectoryFactory) + + def __init__(self, context): + self.context = context + + def __call__(self, name): + return SiteManagementFolder() + + +class SiteManagerContainer(Contained): + """Implement access to the site manager (++etc++site). + + This is a mix-in that implements the IPossibleSite + interface; for example, it is used by the Folder implementation. + """ + zope.interface.implements(zope.component.interfaces.IPossibleSite) + + _sm = None + + def getSiteManager(self): + if self._sm is not None: + return self._sm + else: + raise ComponentLookupError('no site manager defined') + + def setSiteManager(self, sm): + if zope.component.interfaces.ISite.providedBy(self): + raise TypeError("Already a site") + + if zope.component.interfaces.IComponentLookup.providedBy(sm): + self._sm = sm + else: + raise ValueError('setSiteManager requires an IComponentLookup') + + zope.interface.directlyProvides( + self, zope.component.interfaces.ISite, + zope.interface.directlyProvidedBy(self)) + zope.event.notify(interfaces.NewLocalSite(sm)) + +def _findNextSiteManager(site): + while True: + if zope.location.interfaces.IRoot.providedBy(site): + # we're the root site, return None + return None + + try: + site = zope.location.interfaces.ILocationInfo(site).getParent() + except TypeError: + # there was not enough context; probably run from a test + return None + + if zope.component.interfaces.ISite.providedBy(site): + return site.getSiteManager() + + +class _LocalAdapterRegistry( + zope.component.persistentregistry.PersistentAdapterRegistry, + zope.location.Location, + ): + pass + +class LocalSiteManager( + BTreeContainer, + zope.component.persistentregistry.PersistentComponents, + ): + """Local Site Manager implementation""" + zope.interface.implements(interfaces.ILocalSiteManager) + + subs = () + + def _setBases(self, bases): + + # Update base subs + for base in self.__bases__: + if ((base not in bases) + and interfaces.ILocalSiteManager.providedBy(base) + ): + base.removeSub(self) + + for base in bases: + if ((base not in self.__bases__) + and interfaces.ILocalSiteManager.providedBy(base) + ): + base.addSub(self) + + super(LocalSiteManager, self)._setBases(bases) + + def __init__(self, site, default_folder=True): + BTreeContainer.__init__(self) + zope.component.persistentregistry.PersistentComponents.__init__(self) + + # Locate the site manager + self.__parent__ = site + self.__name__ = '++etc++site' + + # Set base site manager + next = _findNextSiteManager(site) + if next is None: + next = zope.component.getGlobalSiteManager() + self.__bases__ = (next, ) + + # Setup default site management folder if requested + if default_folder: + folder = SiteManagementFolder() + zope.event.notify(ObjectCreatedEvent(folder)) + self['default'] = folder + + def _init_registries(self): + self.adapters = _LocalAdapterRegistry() + self.utilities = _LocalAdapterRegistry() + self.adapters.__parent__ = self.utilities.__parent__ = self + self.adapters.__name__ = u'adapters' + self.utilities.__name__ = u'utilities' + + def addSub(self, sub): + """See interfaces.registration.ILocatedRegistry""" + self.subs += (sub, ) + + def removeSub(self, sub): + """See interfaces.registration.ILocatedRegistry""" + self.subs = tuple( + [s for s in self.subs if s is not sub] ) + + +def threadSiteSubscriber(ob, event): + """A subscriber to BeforeTraverseEvent + + Sets the 'site' thread global if the object traversed is a site. + """ + zope.component.hooks.setSite(ob) + + +def clearThreadSiteSubscriber(event): + """A subscriber to EndRequestEvent + + Cleans up the site thread global after the request is processed. + """ + clearSite() + +# Clear the site thread global +clearSite = zope.component.hooks.setSite +try: + from zope.testing.cleanup import addCleanUp +except ImportError: + pass +else: + addCleanUp(clearSite) + + +@zope.component.adapter(zope.interface.Interface) +@zope.interface.implementer(zope.component.interfaces.IComponentLookup) +def SiteManagerAdapter(ob): + """An adapter from ILocation to IComponentLookup. + + The ILocation is interpreted flexibly, we just check for + ``__parent__``. + """ + current = ob + while True: + if zope.component.interfaces.ISite.providedBy(current): + return current.getSiteManager() + current = getattr(current, '__parent__', None) + if current is None: + # It is not a location or has no parent, so we return the global + # site manager + return zope.component.getGlobalSiteManager() + +def changeSiteConfigurationAfterMove(site, event): + """After a site is moved, its site manager links have to be updated.""" + if event.newParent is not None: + next = _findNextSiteManager(site) + if next is None: + next = zope.component.getGlobalSiteManager() + site.getSiteManager().__bases__ = (next, ) + + +@zope.component.adapter( + SiteManagerContainer, + zope.container.interfaces.IObjectMovedEvent) +def siteManagerContainerRemoved(container, event): + # The relation between SiteManagerContainer and LocalSiteManager is a + # kind of containment hierarchy, but it is not expressed via containment, + # but rather via an attribute (_sm). + # + # When the parent is deleted, this needs to be propagated to the children, + # and since we don't have "real" containment, we need to do that manually. + + try: + sm = container.getSiteManager() + except ComponentLookupError: + pass + else: + for ignored in zope.component.subscribers((sm, event), None): + pass # work happens during adapter fetch diff -Nru zope3-3.4.0/src/zope/site/site.txt zope3-3.5~bzr18/src/zope/site/site.txt --- zope3-3.4.0/src/zope/site/site.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/site.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,354 @@ +============================= +Sites and Local Site Managers +============================= + +This is an introduction of location-based component architecture. + +Creating and Accessing Sites +---------------------------- + +*Sites* are used to provide custom component setups for parts of your +application or web site. Every folder: + + >>> from zope.site import folder + >>> myfolder = folder.rootFolder() + +has the potential to become a site: + + >>> from zope.component.interfaces import ISite, IPossibleSite + >>> IPossibleSite.providedBy(myfolder) + True + +but is not yet one: + + >>> ISite.providedBy(myfolder) + False + +If you would like your custom content component to be able to become a site, +you can use the `SiteManagerContainer` mix-in class: + + >>> from zope import site + >>> class MyContentComponent(site.SiteManagerContainer): + ... pass + + >>> myContent = MyContentComponent() + >>> IPossibleSite.providedBy(myContent) + True + >>> ISite.providedBy(myContent) + False + +To convert a possible site to a real site, we have to provide a site manager: + + >>> sm = site.LocalSiteManager(myfolder) + >>> myfolder.setSiteManager(sm) + >>> ISite.providedBy(myfolder) + True + >>> myfolder.getSiteManager() is sm + True + +Note that an event is generated when a local site manager is created: + + >>> from zope.component.eventtesting import getEvents + >>> from zope.site.interfaces import INewLocalSite + >>> [event] = getEvents(INewLocalSite) + >>> event.manager is sm + True + +If one tries to set a bogus site manager, a `ValueError` will be raised: + + >>> myfolder2 = folder.Folder() + >>> myfolder2.setSiteManager(object) + Traceback (most recent call last): + ... + ValueError: setSiteManager requires an IComponentLookup + +If the possible site has been changed to a site already, a `TypeError` +is raised when one attempts to add a new site manager: + + >>> myfolder.setSiteManager(site.LocalSiteManager(myfolder)) + Traceback (most recent call last): + ... + TypeError: Already a site + +There is also an adapter you can use to get the next site manager from any +location: + + >>> myfolder['mysubfolder'] = folder.Folder() + >>> import zope.component + >>> zope.component.interfaces.IComponentLookup(myfolder['mysubfolder']) is sm + True + +If the location passed is a site, the site manager of that site is returned: + + >>> zope.component.interfaces.IComponentLookup(myfolder) is sm + True + + +Using the Site Manager +---------------------- + +A site manager contains several *site management folders*, which are used to +logically organize the software. When a site manager is initialized, a default +site management folder is created: + + >>> sm = myfolder.getSiteManager() + >>> default = sm['default'] + >>> default.__class__ + + +However, you can tell not to create the default site manager folder on +LocalSiteManager creation: + + >>> nodefault = site.LocalSiteManager(myfolder, default_folder=False) + >>> 'default' in nodefault + False + +Also, note that when creating LocalSiteManager, its __parent__ is set to +site that was passed to constructor and the __name__ is set to ++etc++site. + + >>> nodefault.__parent__ is myfolder + True + >>> nodefault.__name__ == '++etc++site' + True + +You can easily create a new site management folder: + + >>> sm['mySMF'] = site.SiteManagementFolder() + >>> sm['mySMF'].__class__ + + +Once you have your site management folder -- let's use the default one -- we +can register some components. Let's start with a utility: + + >>> import zope.interface + >>> class IMyUtility(zope.interface.Interface): + ... pass + + >>> import persistent + >>> from zope.container.contained import Contained + >>> class MyUtility(persistent.Persistent, Contained): + ... zope.interface.implements(IMyUtility) + ... def __init__(self, title): + ... self.title = title + ... def __repr__(self): + ... return "%s('%s')" %(self.__class__.__name__, self.title) + +Now we can create an instance of our utility and put it in the site +management folder and register it: + + >>> myutil = MyUtility('My custom utility') + >>> default['myutil'] = myutil + >>> sm.registerUtility(myutil, IMyUtility, 'u1') + +Now we can ask the site manager for the utility: + + >>> sm.queryUtility(IMyUtility, 'u1') + MyUtility('My custom utility') + +Of course, the local site manager has also access to the global component +registrations: + + >>> gutil = MyUtility('Global Utility') + >>> from zope.component import getGlobalSiteManager + >>> gsm = getGlobalSiteManager() + >>> gsm.registerUtility(gutil, IMyUtility, 'gutil') + + >>> sm.queryUtility(IMyUtility, 'gutil') + MyUtility('Global Utility') + +Next let's see whether we can also successfully register an adapter as +well. Here the adapter will provide the size of a file: + + >>> class IFile(zope.interface.Interface): + ... pass + + >>> class ISized(zope.interface.Interface): + ... pass + + >>> class File(object): + ... zope.interface.implements(IFile) + + >>> class FileSize(object): + ... zope.interface.implements(ISized) + ... def __init__(self, context): + ... self.context = context + +Now that we have the adapter we need to register it: + + >>> sm.registerAdapter(FileSize, [IFile]) + +Finally, we can get the adapter for a file: + + >>> file = File() + >>> size = sm.queryAdapter(file, ISized, name='') + >>> size.__class__ + + >>> size.context is file + True + +By the way, once you set a site + + >>> from zope.component import hooks + >>> hooks.setSite(myfolder) + +you can simply use the zope.component's `getSiteManager()` method to get +the nearest site manager: + + >>> from zope.component import getSiteManager + >>> getSiteManager() is sm + True + +This also means that you can simply use zope.component to look up your utility + + >>> from zope.component import getUtility + >>> getUtility(IMyUtility, 'gutil') + MyUtility('Global Utility') + +or the adapter via the interface's `__call__` method: + + >>> size = ISized(file) + >>> size.__class__ + + >>> size.context is file + True + + +Multiple Sites +-------------- + +Until now we have only dealt with one local and the global site. But things +really become interesting, once we have multiple sites. We can override other +local configuration. + +This behaviour uses the notion of location, therefore we need to configure the +zope.location package first: + + >>> import zope.configuration.xmlconfig + >>> _ = zope.configuration.xmlconfig.string(""" + ... + ... + ... + ... + ... """) + +Let's now create a new folder called `folder11`, add it to `myfolder` and make +it a site: + + >>> myfolder11 = folder.Folder() + >>> myfolder['myfolder11'] = myfolder11 + >>> myfolder11.setSiteManager(site.LocalSiteManager(myfolder11)) + >>> sm11 = myfolder11.getSiteManager() + +If we ask the second site manager for its next, we get + + >>> sm11.__bases__ == (sm, ) + True + +and the first site manager should have the folling sub manager: + + >>> sm.subs == (sm11,) + True + +If we now register a second utility with the same name and interface with the +new site manager folder, + + >>> default11 = sm11['default'] + >>> myutil11 = MyUtility('Utility, uno & uno') + >>> default11['myutil'] = myutil11 + + >>> sm11.registerUtility(myutil11, IMyUtility, 'u1') + +then it will will be available in the second site manager + + >>> sm11.queryUtility(IMyUtility, 'u1') + MyUtility('Utility, uno & uno') + +but not in the first one: + + >>> sm.queryUtility(IMyUtility, 'u1') + MyUtility('My custom utility') + +It is also interesting to look at the use cases of moving and copying a +site. To do that we create a second root folder and make it a site, so that +site hierarchy is as follows: + +:: + + _____ global site _____ + / \ + myfolder1 myfolder2 + | + myfolder11 + + + >>> myfolder2 = folder.rootFolder() + >>> myfolder2.setSiteManager(site.LocalSiteManager(myfolder2)) + +Before we can move or copy sites, we need to register two event subscribers +that manage the wiring of site managers after moving or copying: + + >>> from zope import container + >>> gsm.registerHandler( + ... site.changeSiteConfigurationAfterMove, + ... (ISite, container.interfaces.IObjectMovedEvent), + ... ) + +We only have to register one event listener, since the copy action causes an +`IObjectAddedEvent` to be created, which is just a special type of +`IObjectMovedEvent`. + +First, make sure that everything is setup correctly in the first place: + + >>> myfolder11.getSiteManager().__bases__ == (myfolder.getSiteManager(), ) + True + >>> myfolder.getSiteManager().subs[0] is myfolder11.getSiteManager() + True + >>> myfolder2.getSiteManager().subs + () + +Let's now move `myfolder11` from `myfolder` to `myfolder2`: + + >>> myfolder2['myfolder21'] = myfolder11 + >>> del myfolder['myfolder11'] + +Now the next site manager for `myfolder11`'s site manager should have changed: + + >>> myfolder21 = myfolder11 + >>> myfolder21.getSiteManager().__bases__ == (myfolder2.getSiteManager(), ) + True + >>> myfolder2.getSiteManager().subs[0] is myfolder21.getSiteManager() + True + >>> myfolder.getSiteManager().subs + () + +Make sure that our interfaces and classes are picklable: + + >>> import sys + >>> sys.modules['zope.site.tests'].IMyUtility = IMyUtility + >>> IMyUtility.__module__ = 'zope.site.tests' + >>> sys.modules['zope.site.tests'].MyUtility = MyUtility + >>> MyUtility.__module__ = 'zope.site.tests' + + >>> from pickle import dumps, loads + >>> data = dumps(myfolder2['myfolder21']) + >>> myfolder['myfolder11'] = loads(data) + + >>> myfolder11 = myfolder['myfolder11'] + >>> myfolder11.getSiteManager().__bases__ == (myfolder.getSiteManager(), ) + True + >>> myfolder.getSiteManager().subs[0] is myfolder11.getSiteManager() + True + >>> myfolder2.getSiteManager().subs[0] is myfolder21.getSiteManager() + True + +Finally, let's check that everything works fine when our folder is moved +to the folder that doesn't contain any site manager. Our folder's +sitemanager's bases should be set to global site manager. + + >>> myfolder11.getSiteManager().__bases__ == (myfolder.getSiteManager(), ) + True + + >>> nosm = folder.Folder() + >>> nosm['root'] = myfolder11 + >>> myfolder11.getSiteManager().__bases__ == (gsm, ) + True diff -Nru zope3-3.4.0/src/zope/site/testing.py zope3-3.5~bzr18/src/zope/site/testing.py --- zope3-3.4.0/src/zope/site/testing.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/testing.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,62 @@ +############################################################################## +# +# Copyright (c) 2001-2007 Zope Corporation 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. +# +############################################################################## +"""Reusable functionality for testing site-related code +""" +import zope.component +import zope.component.hooks +import zope.component.interfaces +import zope.container.interfaces +import zope.container.testing +import zope.site.site +from zope.component.interfaces import IComponentLookup +from zope.interface import Interface +from zope.site import LocalSiteManager, SiteManagerAdapter +from zope.site.folder import rootFolder + + +def createSiteManager(folder, setsite=False): + if not zope.component.interfaces.ISite.providedBy(folder): + folder.setSiteManager(LocalSiteManager(folder)) + if setsite: + zope.component.hooks.setSite(folder) + return folder.getSiteManager() + +def addUtility(sitemanager, name, iface, utility, suffix=''): + """Add a utility to a site manager + + This helper function is useful for tests that need to set up utilities. + """ + folder_name = (name or (iface.__name__ + 'Utility')) + suffix + default = sitemanager['default'] + default[folder_name] = utility + utility = default[folder_name] + sitemanager.registerUtility(utility, iface, name) + return utility + +def siteSetUp(site=False): + zope.container.testing.setUp() + zope.component.hooks.setHooks() + + zope.component.provideAdapter( + SiteManagerAdapter, (Interface,), IComponentLookup) + + if site: + site = rootFolder() + createSiteManager(site, setsite=True) + return site + +def siteTearDown(): + zope.container.testing.tearDown() + zope.component.hooks.resetHooks() + zope.component.hooks.setSite() diff -Nru zope3-3.4.0/src/zope/site/tests/folder.txt zope3-3.5~bzr18/src/zope/site/tests/folder.txt --- zope3-3.4.0/src/zope/site/tests/folder.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/tests/folder.txt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,58 @@ +=============================== +File representation for Folders +=============================== + +Folders can be represented in file-system-like protocols (e.g. FTP). An +adapter abstracts some internals away and adds support for accessing the +'++etc++site' folder from those protocols. + + >>> from zope.site.folder import rootFolder + >>> from zope.container.directory import ReadDirectory + >>> folder = rootFolder() + >>> from zope.site.site import LocalSiteManager + >>> folder.setSiteManager(LocalSiteManager(folder)) + >>> fs_folder = ReadDirectory(folder) + +As the root folder is a site, the ++etc++site object appears: + + >>> fs_folder.keys() + ['++etc++site'] + >>> fs_folder.get('++etc++site') + + >>> fs_folder['++etc++site'] + + >>> list(fs_folder.__iter__()) + ['++etc++site'] + >>> fs_folder.values() + [] + >>> len(fs_folder) + 1 + >>> fs_folder.items() + [('++etc++site', )] + >>> '++etc++site' in fs_folder + True + +Let's add another folder to see how a non-site folder behaves: + + >>> from zope.site.folder import Folder + >>> folder['test'] = Folder() + +The site folder now contains the new folder: + + >>> fs_folder.keys() + [u'test', '++etc++site'] + >>> fs_folder.get('test') + + >>> fs_folder['test'] + + >>> list(fs_folder.__iter__()) + [u'test', '++etc++site'] + >>> fs_folder.values() + [, ] + >>> len(fs_folder) + 2 + >>> fs_folder.items() + [(u'test', ), + ('++etc++site', )] + >>> 'test' in fs_folder + True diff -Nru zope3-3.4.0/src/zope/site/tests/__init__.py zope3-3.5~bzr18/src/zope/site/tests/__init__.py --- zope3-3.4.0/src/zope/site/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/site/tests/test_folder.py zope3-3.5~bzr18/src/zope/site/tests/test_folder.py --- zope3-3.4.0/src/zope/site/tests/test_folder.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/tests/test_folder.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,31 @@ +from unittest import TestSuite, makeSuite + +import doctest + +from zope.site.folder import Folder +from zope.site.testing import siteSetUp, siteTearDown +from zope.site.tests.test_site import BaseTestSiteManagerContainer + +def setUp(test=None): + siteSetUp() + +def tearDown(test=None): + siteTearDown() + + +class FolderTest(BaseTestSiteManagerContainer): + + def makeTestObject(self): + return Folder() + + +def test_suite(): + flags = doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE + return TestSuite(( + makeSuite(FolderTest), + doctest.DocTestSuite('zope.site.folder', + setUp=setUp, tearDown=tearDown), + doctest.DocFileSuite("folder.txt", + setUp=setUp, tearDown=tearDown, + optionflags=flags), + )) diff -Nru zope3-3.4.0/src/zope/site/tests/test_localsitemanager.py zope3-3.5~bzr18/src/zope/site/tests/test_localsitemanager.py --- zope3-3.4.0/src/zope/site/tests/test_localsitemanager.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/tests/test_localsitemanager.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,58 @@ +############################################################################## +# +# Copyright (c) 2007 Zope Corporation 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. +# +############################################################################## +"""Local sitemanager tests. + +$Id: test_localsitemanager.py 107325 2009-12-29 23:17:12Z hannosch $ +""" +import unittest + +from zope.interface import Interface +from zope import site +from zope.site.folder import Folder +import zope.site.testing + + +class I1(Interface): + pass + + +class TestLocalSiteManager(unittest.TestCase): + + def setUp(self): + zope.site.testing.siteSetUp() + self.util = object() + self.root = Folder() + self.root['site'] = Folder() + subfolder = self.root['site'] + subfolder.setSiteManager(site.LocalSiteManager(subfolder)) + subfolder.getSiteManager().registerUtility(self.util, I1) + + def tearDown(self): + zope.site.testing.siteTearDown() + + def testPersistence(self): + from pickle import dumps, loads + self.assert_( + self.root['site'].getSiteManager().getUtility(I1) is self.util) + + data = dumps(self.root['site']) + self.root['copied_site'] = loads(data) + + self.assert_( + self.root['copied_site'].getSiteManager().getUtility(I1) is not self.util) + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(TestLocalSiteManager), + )) diff -Nru zope3-3.4.0/src/zope/site/tests/test_registration.py zope3-3.5~bzr18/src/zope/site/tests/test_registration.py --- zope3-3.4.0/src/zope/site/tests/test_registration.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/tests/test_registration.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,155 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Registration Tests + +$Id: test_registration.py 111774 2010-04-30 22:08:52Z hannosch $ +""" +__docformat__ = "reStructuredText" + +import doctest +import os +import unittest +import warnings + +import ZODB.FileStorage +import persistent +import transaction +import zope.component.globalregistry +import zope.component.testing as placelesssetup +import zope.container.contained +import zope.site +from ZODB.DB import DB +from ZODB.DemoStorage import DemoStorage +from zope import interface + + +# test class for testing data conversion +class IFoo(interface.Interface): + pass +class Foo(persistent.Persistent, zope.container.contained.Contained): + interface.implements(IFoo) + name = '' + def __init__(self, name=''): + self.name = name + + def __repr__(self): + return 'Foo(%r)' % self.name + +def setUp(test): + placelesssetup.setUp(test) + test.globs['showwarning'] = warnings.showwarning + warnings.showwarning = lambda *a, **k: None + +def tearDown(test): + warnings.showwarning = test.globs['showwarning'] + placelesssetup.tearDown(test) + +def oldfs(): + return FileStorage( + os.path.join(os.path.dirname(__file__), 'gen3.fs'), + read_only=True, + ) + +# Work around a bug in ZODB +# XXX fix ZODB +class FileStorage(ZODB.FileStorage.FileStorage): + + def new_oid(self): + self._lock_acquire() + try: + last = self._oid + d = ord(last[-1]) + if d < 255: # fast path for the usual case + last = last[:-1] + chr(d+1) + else: # there's a carry out of the last byte + last_as_long, = _structunpack(">Q", last) + last = _structpack(">Q", last_as_long + 1) + self._oid = last + return last + finally: + self._lock_release() + +class GlobalRegistry: + pass + +base = zope.component.globalregistry.GlobalAdapterRegistry( + GlobalRegistry, 'adapters') +GlobalRegistry.adapters = base +def clear_base(): + base.__init__(GlobalRegistry, 'adapters') + + +def test_deghostification_of_persistent_adapter_registries(): + """ + +Note that this test duplicates one from zope.component.tests. +We should be able to get rid of this one when we get rid of +__setstate__ implementation we have in back35. + +We want to make sure that we see updates corrextly. + + >>> import ZODB.tests.util + >>> db = ZODB.tests.util.DB() + >>> tm1 = transaction.TransactionManager() + >>> c1 = db.open(transaction_manager=tm1) + >>> r1 = zope.site.site._LocalAdapterRegistry((base,)) + >>> r2 = zope.site.site._LocalAdapterRegistry((r1,)) + >>> c1.root()[1] = r1 + >>> c1.root()[2] = r2 + >>> tm1.commit() + >>> r1._p_deactivate() + >>> r2._p_deactivate() + + >>> tm2 = transaction.TransactionManager() + >>> c2 = db.open(transaction_manager=tm2) + >>> r1 = c2.root()[1] + >>> r2 = c2.root()[2] + + >>> r1.lookup((), IFoo, '') + + >>> base.register((), IFoo, '', Foo('')) + >>> r1.lookup((), IFoo, '') + Foo('') + + >>> r2.lookup((), IFoo, '1') + + >>> r1.register((), IFoo, '1', Foo('1')) + + >>> r2.lookup((), IFoo, '1') + Foo('1') + + >>> r1.lookup((), IFoo, '2') + >>> r2.lookup((), IFoo, '2') + + >>> base.register((), IFoo, '2', Foo('2')) + + >>> r1.lookup((), IFoo, '2') + Foo('2') + + >>> r2.lookup((), IFoo, '2') + Foo('2') + +Cleanup: + + >>> db.close() + >>> clear_base() + + """ + +def test_suite(): + suite = unittest.TestSuite(( + doctest.DocTestSuite(setUp=setUp, tearDown=tearDown) + )) + return suite + diff -Nru zope3-3.4.0/src/zope/site/tests/test_sitemanagercontainer.py zope3-3.5~bzr18/src/zope/site/tests/test_sitemanagercontainer.py --- zope3-3.4.0/src/zope/site/tests/test_sitemanagercontainer.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/tests/test_sitemanagercontainer.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,89 @@ +############################################################################# +# +# Copyright (c) 2009 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 unittest + +import zope.component +from zope.component import getSiteManager +import zope.container.testing +from zope.event import notify +from zope.lifecycleevent import ObjectRemovedEvent +from zope.lifecycleevent.interfaces import IObjectRemovedEvent + +from zope.site.folder import rootFolder +from zope.site.site import SiteManagerContainer +import zope.site.testing + + +class Dummy(object): + pass + + +removed_called = False +def removed_event(obj, event): + global removed_called + removed_called = True + + +def dispatch_event(obj, event): + sm = obj._sm + if sm is not None: + for k,v in sm.items(): + notify(ObjectRemovedEvent(v, sm, k)) + + +class SiteManagerContainerTest(unittest.TestCase): + + def setUp(self): + self.root = rootFolder() + zope.site.testing.siteSetUp(self.root) + + global removed_called + removed_called = False + + sm = getSiteManager() + sm.registerHandler(removed_event, (Dummy, IObjectRemovedEvent)) + sm.registerHandler( + dispatch_event, (SiteManagerContainer, IObjectRemovedEvent)) + + def tearDown(self): + zope.site.testing.siteTearDown() + + def removed_event(self, event): + self.removed_called = True + + def test_delete_smc_should_propagate_removed_event(self): + container = SiteManagerContainer() + self.root['container'] = container + + zope.site.testing.createSiteManager(container) + container.getSiteManager()['child'] = Dummy() + + del self.root['container'] + self.assert_(removed_called) + + def test_delete_when_smc_has_no_sitemanager(self): + container = SiteManagerContainer() + self.root['container'] = container + + try: + del self.root['container'] + except Exception, e: + self.fail(e) + + +def test_suite(): + return unittest.makeSuite(SiteManagerContainerTest) diff -Nru zope3-3.4.0/src/zope/site/tests/test_site.py zope3-3.5~bzr18/src/zope/site/tests/test_site.py --- zope3-3.4.0/src/zope/site/tests/test_site.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/site/tests/test_site.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,142 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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. +# +############################################################################## +"""Registration Tests + +$Id: test_site.py 111774 2010-04-30 22:08:52Z hannosch $ +""" +__docformat__ = "reStructuredText" + +import doctest +import unittest + +import zope.interface +import zope.interface.verify +from zope.component.interfaces import ISite, IPossibleSite + +from zope.site import folder + +from zope.site import interfaces +from zope import site +from zope.site import testing + +class SiteManagerStub(object): + zope.interface.implements(interfaces.ILocalSiteManager) + +class CustomFolder(folder.Folder): + + def __init__(self, name): + self.__name__ = name + super(CustomFolder, self).__init__() + + def __repr__(self): + return '<%s %s>' %(self.__class__.__name__, self.__name__) + + +def test_SiteManagerAdapter(): + """ + The site manager adapter is used to find the nearest site for any given + location. If the provided context is a site, + + >>> site = folder.Folder() + >>> sm = SiteManagerStub() + >>> site.setSiteManager(sm) + + then the adapter simply return's the site's site manager: + + >>> from zope.site import SiteManagerAdapter + >>> SiteManagerAdapter(site) is sm + True + + If the context is a location (i.e. has a `__parent__` attribute), + + >>> ob = folder.Folder() + >>> ob.__parent__ = site + >>> ob2 = folder.Folder() + >>> ob2.__parent__ = ob + + we 'acquire' the closest site and return its site manager: + + >>> SiteManagerAdapter(ob) is sm + True + >>> SiteManagerAdapter(ob2) is sm + True + + If we are unable to find a local site manager, then the global site + manager is returned. + + >>> import zope.component + >>> orphan = CustomFolder('orphan') + >>> SiteManagerAdapter(orphan) is zope.component.getGlobalSiteManager() + True + """ + +class BaseTestSiteManagerContainer(unittest.TestCase): + """This test is for objects that don't have site managers by + default and that always give back the site manager they were + given. + + Subclasses need to define a method, 'makeTestObject', that takes no + arguments and that returns a new site manager + container that has no site manager.""" + + def test_IPossibleSite_verify(self): + zope.interface.verify.verifyObject(IPossibleSite, + self.makeTestObject()) + + def test_get_and_set(self): + smc = self.makeTestObject() + self.failIf(ISite.providedBy(smc)) + sm = site.LocalSiteManager(smc) + smc.setSiteManager(sm) + self.failUnless(ISite.providedBy(smc)) + self.failUnless(smc.getSiteManager() is sm) + zope.interface.verify.verifyObject(ISite, smc) + + def test_set_w_bogus_value(self): + smc=self.makeTestObject() + self.assertRaises(Exception, smc.setSiteManager, self) + + +class SiteManagerContainerTest(BaseTestSiteManagerContainer): + def makeTestObject(self): + from zope.site import SiteManagerContainer + return SiteManagerContainer() + +def setUp(test): + testing.siteSetUp() + +def tearDown(test): + testing.siteTearDown() + + +class Layer(object): + + @staticmethod + def setUp(): + pass + + +def test_suite(): + site_suite = doctest.DocFileSuite('../site.txt', + setUp=setUp, tearDown=tearDown) + # XXX Isolate the site.txt tests within their own layer as they do some + # component registration. + site_suite.layer = Layer + + return unittest.TestSuite(( + doctest.DocTestSuite(), + unittest.makeSuite(SiteManagerContainerTest), + site_suite, + )) + diff -Nru zope3-3.4.0/src/zope/size/configure.zcml zope3-3.5~bzr18/src/zope/size/configure.zcml --- zope3-3.4.0/src/zope/size/configure.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/size/configure.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/size/DEPENDENCIES.cfg zope3-3.5~bzr18/src/zope/size/DEPENDENCIES.cfg --- zope3-3.4.0/src/zope/size/DEPENDENCIES.cfg 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/size/DEPENDENCIES.cfg 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +zope.interface +zope.i18nmessageid \ No newline at end of file diff -Nru zope3-3.4.0/src/zope/size/__init__.py zope3-3.5~bzr18/src/zope/size/__init__.py --- zope3-3.4.0/src/zope/size/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/size/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,59 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Adapters that give the size of an object. + +$Id: __init__.py 89174 2008-08-01 22:34:06Z ccomb $ +""" +from zope.interface import implements +from zope.size.interfaces import ISized +from zope.i18nmessageid import MessageFactory +_ = MessageFactory('zope') + +class DefaultSized(object): + """ + A default ISized adapter + """ + implements(ISized) + + def __init__(self, obj): + try: + size = int(obj.getSize()) + except (AttributeError, ValueError, TypeError): + self._sortingSize = None, None + else: + self._sortingSize = 'byte', size + + def sizeForSorting(self): + """See ISized""" + return self._sortingSize + + def sizeForDisplay(self): + """See ISized""" + units, size = self._sortingSize + if units == 'byte': + return byteDisplay(size) + return _('not-available', 'n/a') + +def byteDisplay(size): + """ + Returns a size with the correct unit (KB, MB), given the size in bytes. + The output should be given to zope.i18n.translate() + """ + if size == 0: + return _('0 KB') + if size <= 1024: + return _('1 KB') + if size > 1048576: + return _('${size} MB', mapping={'size': '%0.02f' % (size / 1048576.0)}) + return _('${size} KB', mapping={'size': '%d' % (size / 1024.0)}) diff -Nru zope3-3.4.0/src/zope/size/interfaces.py zope3-3.5~bzr18/src/zope/size/interfaces.py --- zope3-3.4.0/src/zope/size/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/size/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,40 @@ +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Interfaces that give the size of an object. + +$Id: interfaces.py 89174 2008-08-01 22:34:06Z ccomb $ +""" + +from zope.interface import Interface + +# basic units: +# 'byte' +# 'item' for example, number of subobjects for a folder +# None for unsized things +# 'line' for source-code like things + +class ISized(Interface): + + def sizeForSorting(): + """Returns a tuple (basic_unit, amount) + + Used for sorting among different kinds of sized objects. + 'amount' need only be sortable among things that share the + same basic unit.""" + + def sizeForDisplay(): + """Returns a string giving the size. The output string may be a + zope.i18nmessageid.message.Message with an embedded mapping, so + it should be translated with zope.i18n.translate() + """ diff -Nru zope3-3.4.0/src/zope/size/tests.py zope3-3.5~bzr18/src/zope/size/tests.py --- zope3-3.4.0/src/zope/size/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/size/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,91 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 ISized Adapter + +$Id: tests.py 70826 2006-10-20 03:41:16Z baijum $ +""" +import unittest +from zope.size.interfaces import ISized + +class DummyObject(object): + + def __init__(self, size): + self._size = size + + def getSize(self): + return self._size + +class Test(unittest.TestCase): + + def testImplementsISized(self): + from zope.size import DefaultSized + sized = DefaultSized(object()) + self.assert_(ISized.providedBy(sized)) + + def testSizeWithBytes(self): + from zope.size import DefaultSized + obj = DummyObject(1023) + sized = DefaultSized(obj) + self.assertEqual(sized.sizeForSorting(), ('byte', 1023)) + self.assertEqual(sized.sizeForDisplay(), u'1 KB') + + def testSizeWithNone(self): + from zope.size import DefaultSized + obj = DummyObject(None) + sized = DefaultSized(obj) + self.assertEqual(sized.sizeForSorting(), (None, None)) + self.assertEqual(sized.sizeForDisplay(), u'not-available') + + def testSizeNotAvailable(self): + from zope.size import DefaultSized + sized = DefaultSized(object()) + self.assertEqual(sized.sizeForSorting(), (None, None)) + self.assertEqual(sized.sizeForDisplay(), u'not-available') + + def testVariousSizes(self): + from zope.size import DefaultSized + + sized = DefaultSized(DummyObject(0)) + self.assertEqual(sized.sizeForSorting(), ('byte', 0)) + self.assertEqual(sized.sizeForDisplay(), u'0 KB') + + sized = DefaultSized(DummyObject(1)) + self.assertEqual(sized.sizeForSorting(), ('byte', 1)) + self.assertEqual(sized.sizeForDisplay(), u'1 KB') + + sized = DefaultSized(DummyObject(2048)) + self.assertEqual(sized.sizeForSorting(), ('byte', 2048)) + self.assertEqual(sized.sizeForDisplay(), u'${size} KB') + self.assertEqual(sized.sizeForDisplay().mapping, {'size': '2'}) + + sized = DefaultSized(DummyObject(2000000)) + self.assertEqual(sized.sizeForSorting(), ('byte', 2000000)) + self.assertEqual(sized.sizeForDisplay(), u'${size} MB') + self.assertEqual(sized.sizeForDisplay().mapping, {'size': '1.91'}) + + def test_byteDisplay(self): + from zope.size import byteDisplay + self.assertEqual(byteDisplay(0), u'0 KB') + self.assertEqual(byteDisplay(1), u'1 KB') + self.assertEqual(byteDisplay(2048), u'${size} KB') + self.assertEqual(byteDisplay(2048).mapping, {'size': '2'}) + self.assertEqual(byteDisplay(2000000), u'${size} MB') + self.assertEqual(byteDisplay(2000000).mapping, {'size': '1.91'}) + +def test_suite(): + loader = unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml01.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml01.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml01.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml01.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1 @@ +baseline diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml02.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml02.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml02.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml02.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,100 @@ + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml03.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml03.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml03.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml03.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,8 @@ + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml04.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml04.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml04.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml04.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,6 @@ + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml05.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml05.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml05.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml05.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml06.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml06.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml06.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml06.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,14 @@ + + + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml07.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml07.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml07.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml07.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,73 @@ + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml08.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml08.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml08.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml08.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,73 @@ + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml09.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml09.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml09.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml09.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,10 @@ + + &dtml-x0; + &dtml-x1; + &dtml-x2; + &dtml-x3; + &dtml-x4; + &dtml-x5; + &dtml-x6; + &dtml-x7; + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml10.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml10.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml10.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml10.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,102 @@ + + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml11.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml11.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml11.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml11.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,103 @@ + + &dtml-x0; + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/dtml12.html zope3-3.5~bzr18/src/zope/tal/benchmark/dtml12.html --- zope3-3.4.0/src/zope/tal/benchmark/dtml12.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/dtml12.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,12 @@ + + + &dtml-y0; + &dtml-y1; + &dtml-y2; + &dtml-y3; + &dtml-y4; + &dtml-y5; + &dtml-y6; + &dtml-y7; + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/__init__.py zope3-3.5~bzr18/src/zope/tal/benchmark/__init__.py --- zope3-3.4.0/src/zope/tal/benchmark/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal01.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal01.html --- zope3-3.4.0/src/zope/tal/benchmark/tal01.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal01.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +baseline diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal02.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal02.html --- zope3-3.4.0/src/zope/tal/benchmark/tal02.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal02.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,100 @@ + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal03.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal03.html --- zope3-3.4.0/src/zope/tal/benchmark/tal03.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal03.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal04.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal04.html --- zope3-3.4.0/src/zope/tal/benchmark/tal04.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal04.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal05.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal05.html --- zope3-3.4.0/src/zope/tal/benchmark/tal05.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal05.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal06.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal06.html --- zope3-3.4.0/src/zope/tal/benchmark/tal06.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal06.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal07.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal07.html --- zope3-3.4.0/src/zope/tal/benchmark/tal07.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal07.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal08.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal08.html --- zope3-3.4.0/src/zope/tal/benchmark/tal08.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal08.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal09.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal09.html --- zope3-3.4.0/src/zope/tal/benchmark/tal09.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal09.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal10.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal10.html --- zope3-3.4.0/src/zope/tal/benchmark/tal10.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal10.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,102 @@ + + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal11.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal11.html --- zope3-3.4.0/src/zope/tal/benchmark/tal11.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal11.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,103 @@ + + + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + A large chunk of text to be repeated. + diff -Nru zope3-3.4.0/src/zope/tal/benchmark/tal12.html zope3-3.5~bzr18/src/zope/tal/benchmark/tal12.html --- zope3-3.4.0/src/zope/tal/benchmark/tal12.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/benchmark/tal12.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/DEPENDENCIES.cfg zope3-3.5~bzr18/src/zope/tal/DEPENDENCIES.cfg --- zope3-3.4.0/src/zope/tal/DEPENDENCIES.cfg 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/DEPENDENCIES.cfg 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,4 @@ +zope.i18n +zope.i18nmessageid +zope.interface +zope.deprecation diff -Nru zope3-3.4.0/src/zope/tal/driver.py zope3-3.5~bzr18/src/zope/tal/driver.py --- zope3-3.4.0/src/zope/tal/driver.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/driver.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,209 @@ +#!/usr/bin/env python +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Driver program to test METAL and TAL implementation. + +Usage: driver.py [options] [file] +Options: + -h / --help + Print this message and exit. + -H / --html + -x / --xml + Explicitly choose HTML or XML input. The default is to automatically + select based on the file extension. These options are mutually + exclusive. + -l + Lenient structure insertion. + -m + Macro expansion only + -s + Print intermediate opcodes only + -t + Leave TAL/METAL attributes in output + -i + Leave I18N substitution strings un-interpolated. + -a + Enable source annotations + +$Id: driver.py 29651 2005-03-23 12:56:35Z hdima $ +""" + +import os +import sys + +import getopt + +if __name__ == "__main__": + import setpath # Local hack to tweak sys.path etc. + +# Import local classes +import zope.tal.taldefs +from zope.tal.dummyengine import DummyEngine +from zope.tal.dummyengine import DummyTranslationDomain + +FILE = "tests/input/test01.xml" + +class TestTranslations(DummyTranslationDomain): + def translate(self, msgid, mapping=None, context=None, + target_language=None, default=None): + if msgid == 'timefmt': + return '%(minutes)s minutes after %(hours)s %(ampm)s' % mapping + elif msgid == 'jobnum': + return '%(jobnum)s is the JOB NUMBER' % mapping + elif msgid == 'verify': + s = 'Your contact email address is recorded as %(email)s' + return s % mapping + elif msgid == 'mailto:${request/submitter}': + return 'mailto:bperson@dom.ain' + elif msgid == 'origin': + return '%(name)s was born in %(country)s' % mapping + return DummyTranslationDomain.translate( + self, msgid, mapping, context, + target_language, default=default) + + +class TestEngine(DummyEngine): + def __init__(self, macros=None): + DummyEngine.__init__(self, macros) + self.translationDomain = TestTranslations() + + def evaluatePathOrVar(self, expr): + if expr == 'here/currentTime': + return {'hours' : 6, + 'minutes': 59, + 'ampm' : 'PM', + } + elif expr == 'context/@@object_name': + return '7' + elif expr == 'request/submitter': + return 'aperson@dom.ain' + return DummyEngine.evaluatePathOrVar(self, expr) + + +# This is a disgusting hack so that we can use engines that actually know +# something about certain object paths. TimeEngine knows about +# here/currentTime. +ENGINES = {'test23.html': TestEngine, + 'test24.html': TestEngine, + 'test26.html': TestEngine, + 'test27.html': TestEngine, + 'test28.html': TestEngine, + 'test29.html': TestEngine, + 'test30.html': TestEngine, + 'test31.html': TestEngine, + 'test32.html': TestEngine, + } + +def usage(code, msg=''): + print >> sys.stderr, __doc__ + if msg: + print >> sys.stderr, msg + sys.exit(code) + +def main(): + macros = 0 + mode = None + showcode = 0 + showtal = -1 + sourceAnnotations = 0 + strictinsert = 1 + i18nInterpolate = 1 + try: + opts, args = getopt.getopt(sys.argv[1:], "hHxlmstia", + ['help', 'html', 'xml']) + except getopt.error, msg: + usage(2, msg) + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + if opt in ('-H', '--html'): + if mode == 'xml': + usage(1, '--html and --xml are mutually exclusive') + mode = "html" + if opt == '-l': + strictinsert = 0 + if opt == '-m': + macros = 1 + if opt in ('-x', '--xml'): + if mode == 'html': + usage(1, '--html and --xml are mutually exclusive') + mode = "xml" + if opt == '-s': + showcode = 1 + if opt == '-t': + showtal = 1 + if opt == '-i': + i18nInterpolate = 0 + if opt == '-a': + sourceAnnotations = 1 + if args: + file = args[0] + else: + file = FILE + it = compilefile(file, mode) + if showcode: + showit(it) + else: + # See if we need a special engine for this test + engine = None + engineClass = ENGINES.get(os.path.basename(file)) + if engineClass is not None: + engine = engineClass(macros) + interpretit(it, engine=engine, + tal=(not macros), showtal=showtal, + strictinsert=strictinsert, + i18nInterpolate=i18nInterpolate, + sourceAnnotations=sourceAnnotations) + +def interpretit(it, engine=None, stream=None, tal=1, showtal=-1, + strictinsert=1, i18nInterpolate=1, sourceAnnotations=0): + from zope.tal.talinterpreter import TALInterpreter + program, macros = it + assert zope.tal.taldefs.isCurrentVersion(program) + if engine is None: + engine = DummyEngine(macros) + TALInterpreter(program, macros, engine, stream, wrap=0, + tal=tal, showtal=showtal, strictinsert=strictinsert, + i18nInterpolate=i18nInterpolate, + sourceAnnotations=sourceAnnotations)() + +def compilefile(file, mode=None): + assert mode in ("html", "xml", None) + if mode is None: + ext = os.path.splitext(file)[1] + if ext.lower() in (".html", ".htm"): + mode = "html" + else: + mode = "xml" + from zope.tal.talgenerator import TALGenerator + filename = os.path.abspath(file) + prefix = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + if filename.startswith(prefix): + filename = filename[len(prefix):] + filename = filename.replace(os.sep, '/') # test files expect slashes + if mode == "html": + from zope.tal.htmltalparser import HTMLTALParser + p = HTMLTALParser(gen=TALGenerator(source_file=filename, xml=0)) + else: + from zope.tal.talparser import TALParser + p = TALParser(gen=TALGenerator(source_file=filename)) + p.parseFile(file) + return p.getCode() + +def showit(it): + from pprint import pprint + pprint(it) + +if __name__ == "__main__": + main() diff -Nru zope3-3.4.0/src/zope/tal/dummyengine.py zope3-3.5~bzr18/src/zope/tal/dummyengine.py --- zope3-3.4.0/src/zope/tal/dummyengine.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/dummyengine.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,330 @@ +############################################################################## +# +# Copyright (c) 2001, 2002, 2003 Zope Corporation 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. +# +############################################################################## +"""Dummy TAL expression engine so that I can test out the TAL implementation. + +$Id: dummyengine.py 93717 2008-12-06 13:25:25Z MatthewWilkes $ +""" +import re + +from zope.interface import implements +from zope.tal.taldefs import NAME_RE, TALExpressionError, ErrorInfo +from zope.tal.interfaces import ITALExpressionCompiler, ITALExpressionEngine +from zope.i18nmessageid import Message + +Default = object() + +name_match = re.compile(r"(?s)(%s):(.*)\Z" % NAME_RE).match + +class CompilerError(Exception): + pass + +class DummyEngine(object): + + position = None + source_file = None + + implements(ITALExpressionCompiler, ITALExpressionEngine) + + def __init__(self, macros=None): + if macros is None: + macros = {} + self.macros = macros + dict = {'nothing': None, 'default': Default} + self.locals = self.globals = dict + self.stack = [dict] + self.translationDomain = DummyTranslationDomain() + self.useEngineAttrDicts = False + + # zope.tal.interfaces.ITALExpressionCompiler + + def getCompilerError(self): + return CompilerError + + def compile(self, expr): + return "$%s$" % expr + + # zope.tal.interfaces.ITALExpressionEngine + + def setSourceFile(self, source_file): + self.source_file = source_file + + def setPosition(self, position): + self.position = position + + def beginScope(self): + self.stack.append(self.locals) + + def endScope(self): + assert len(self.stack) > 1, "more endScope() than beginScope() calls" + self.locals = self.stack.pop() + + def setLocal(self, name, value): + if self.locals is self.stack[-1]: + # Unmerge this scope's locals from previous scope of first set + self.locals = self.locals.copy() + self.locals[name] = value + + def setGlobal(self, name, value): + self.globals[name] = value + + def getValue(self, name, default=None): + value = self.globals.get(name, default) + if value is default: + value = self.locals.get(name, default) + return value + + def evaluate(self, expression): + assert expression.startswith("$") and expression.endswith("$"), \ + expression + expression = expression[1:-1] + m = name_match(expression) + if m: + type, expr = m.group(1, 2) + else: + type = "path" + expr = expression + + if type in ("string", "str"): + return expr + if type in ("path", "var", "global", "local"): + return self.evaluatePathOrVar(expr) + if type == "not": + return not self.evaluate(expr) + if type == "exists": + return self.locals.has_key(expr) or self.globals.has_key(expr) + if type == "python": + try: + return eval(expr, self.globals, self.locals) + except: + raise TALExpressionError("evaluation error in %s" % `expr`) + if type == "position": + # Insert the current source file name, line number, + # and column offset. + if self.position: + lineno, offset = self.position + else: + lineno, offset = None, None + return '%s (%s,%s)' % (self.source_file, lineno, offset) + raise TALExpressionError("unrecognized expression: " + `expression`) + + # implementation; can be overridden + def evaluatePathOrVar(self, expr): + expr = expr.strip() + if self.locals.has_key(expr): + return self.locals[expr] + elif self.globals.has_key(expr): + return self.globals[expr] + else: + raise TALExpressionError("unknown variable: %s" % `expr`) + + def evaluateValue(self, expr): + return self.evaluate(expr) + + def evaluateBoolean(self, expr): + return self.evaluate(expr) + + def evaluateText(self, expr): + text = self.evaluate(expr) + if isinstance(text, (str, unicode, Message)): + return text + if text is not None and text is not Default: + text = str(text) + return text + + def evaluateStructure(self, expr): + # TODO Should return None or a DOM tree + return self.evaluate(expr) + + # implementation; can be overridden + def evaluateSequence(self, expr): + # TODO: Should return a sequence + return self.evaluate(expr) + + def evaluateMacro(self, macroName): + assert macroName.startswith("$") and macroName.endswith("$"), \ + macroName + macroName = macroName[1:-1] + file, localName = self.findMacroFile(macroName) + if not file: + # Local macro + macro = self.macros[localName] + else: + # External macro + import driver + program, macros = driver.compilefile(file) + macro = macros.get(localName) + if not macro: + raise TALExpressionError("macro %s not found in file %s" % + (localName, file)) + return macro + + # internal + def findMacroFile(self, macroName): + if not macroName: + raise TALExpressionError("empty macro name") + i = macroName.rfind('/') + if i < 0: + # No slash -- must be a locally defined macro + return None, macroName + else: + # Up to last slash is the filename + fileName = macroName[:i] + localName = macroName[i+1:] + return fileName, localName + + def setRepeat(self, name, expr): + seq = self.evaluateSequence(expr) + return Iterator(name, seq, self) + + def createErrorInfo(self, err, position): + return ErrorInfo(err, position) + + def getDefault(self): + return Default + + def translate(self, msgid, domain=None, mapping=None, default=None): + self.translationDomain.domain = domain + return self.translationDomain.translate( + msgid, mapping, default=default) + + def evaluateCode(self, lang, code): + # We probably implement too much, but I use the dummy engine to test + # some of the issues that we will have. + + # For testing purposes only + locals = {} + globals = {} + if self.useEngineAttrDicts: + globals = self.globals.copy() + locals = self.locals.copy() + + assert lang == 'text/server-python' + import sys, StringIO + + # Removing probable comments + if code.strip().startswith(''): + code = code.strip()[4:-3] + + # Prepare code. + lines = code.split('\n') + lines = filter(lambda l: l.strip() != '', lines) + code = '\n'.join(lines) + # This saves us from all indentation issues :) + if code.startswith(' ') or code.startswith('\t'): + code = 'if 1 == 1:\n' + code + '\n' + tmp = sys.stdout + sys.stdout = StringIO.StringIO() + try: + exec code in globals, locals + finally: + result = sys.stdout + sys.stdout = tmp + + # For testing purposes only + self.codeLocals = locals + self.codeGlobals = globals + + self.locals.update(locals) + self.globals.update(globals) + + return result.getvalue() + +class Iterator(object): + + def __init__(self, name, seq, engine): + self.name = name + self.seq = seq + self.engine = engine + self.nextIndex = 0 + + def next(self): + i = self.nextIndex + try: + item = self.seq[i] + except IndexError: + return 0 + self.nextIndex = i+1 + self.engine.setLocal(self.name, item) + return 1 + + +class DummyTranslationDomain(object): + + domain = '' + + msgids = {} + + def appendMsgid(self, domain, data): + if not self.msgids.has_key(domain): + self.msgids[domain] = [] + self.msgids[domain].append(data) + + def getMsgids(self, domain): + return self.msgids[domain] + + def clearMsgids(self): + self.msgids = {} + + def translate(self, msgid, mapping=None, context=None, + target_language=None, default=None): + + domain = self.domain + # This is a fake translation service which simply uppercases non + # ${name} placeholder text in the message id. + # + # First, transform a string with ${name} placeholders into a list of + # substrings. Then upcase everything but the placeholders, then glue + # things back together. + + # If the domain is a string method, then transform the string + # by calling that method. + + # MessageID attributes override arguments + if isinstance(msgid, Message): + domain = msgid.domain + mapping = msgid.mapping + default = msgid.default + if default is None: # Message doesn't substitute itself for + default = msgid # missing default + + # simulate an unknown msgid by returning None + if msgid == "don't translate me": + text = default + elif domain and hasattr('', domain): + text = getattr(msgid, domain)() + else: + domain = 'default' + text = msgid.upper() + + self.appendMsgid(domain, (msgid, mapping)) + + def repl(m): + return unicode(mapping[m.group(m.lastindex).lower()]) + cre = re.compile(r'\$(?:([_A-Za-z][-\w]*)|\{([_A-Za-z][-\w]*)\})') + return cre.sub(repl, text) + +class MultipleDomainsDummyEngine(DummyEngine): + + def translate(self, msgid, domain=None, mapping=None, default=None): + + if isinstance(msgid, Message): + domain = msgid.domain + + if domain == 'a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine': + domain = 'lower' + + self.translationDomain.domain = domain + return self.translationDomain.translate( + msgid, mapping, default=default) + diff -Nru zope3-3.4.0/src/zope/tal/htmltalparser.py zope3-3.5~bzr18/src/zope/tal/htmltalparser.py --- zope3-3.4.0/src/zope/tal/htmltalparser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/htmltalparser.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,318 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Parse HTML and compile to TALInterpreter intermediate code. + +$Id: htmltalparser.py 76888 2007-06-21 10:11:17Z hdima $ +""" + +from HTMLParser import HTMLParser, HTMLParseError + +from zope.tal.taldefs import (ZOPE_METAL_NS, ZOPE_TAL_NS, ZOPE_I18N_NS, + METALError, TALError, I18NError) +from zope.tal.talgenerator import TALGenerator + + +BOOLEAN_HTML_ATTRS = frozenset([ + # List of Boolean attributes in HTML that may be given in + # minimized form (e.g. rather than ) + # From http://www.w3.org/TR/xhtml1/#guidelines (C.10) + "compact", "nowrap", "ismap", "declare", "noshade", "checked", + "disabled", "readonly", "multiple", "selected", "noresize", + "defer" + ]) + +EMPTY_HTML_TAGS = frozenset([ + # List of HTML tags with an empty content model; these are + # rendered in minimized form, e.g. . + # From http://www.w3.org/TR/xhtml1/#dtds + "base", "meta", "link", "hr", "br", "param", "img", "area", + "input", "col", "basefont", "isindex", "frame", + ]) + +PARA_LEVEL_HTML_TAGS = frozenset([ + # List of HTML elements that close open paragraph-level elements + # and are themselves paragraph-level. + "h1", "h2", "h3", "h4", "h5", "h6", "p", + ]) + +BLOCK_CLOSING_TAG_MAP = { + "tr": frozenset(["tr", "td", "th"]), + "td": frozenset(["td", "th"]), + "th": frozenset(["td", "th"]), + "li": frozenset(["li"]), + "dd": frozenset(["dd", "dt"]), + "dt": frozenset(["dd", "dt"]), + } + +BLOCK_LEVEL_HTML_TAGS = frozenset([ + # List of HTML tags that denote larger sections than paragraphs. + "blockquote", "table", "tr", "th", "td", "thead", "tfoot", "tbody", + "noframe", "ul", "ol", "li", "dl", "dt", "dd", "div", + ]) + +SECTION_LEVEL_HTML_TAGS = PARA_LEVEL_HTML_TAGS.union(BLOCK_LEVEL_HTML_TAGS) + +TIGHTEN_IMPLICIT_CLOSE_TAGS = PARA_LEVEL_HTML_TAGS.union(BLOCK_CLOSING_TAG_MAP) + + +class NestingError(HTMLParseError): + """Exception raised when elements aren't properly nested.""" + + def __init__(self, tagstack, endtag, position=(None, None)): + self.endtag = endtag + if tagstack: + if len(tagstack) == 1: + msg = ('Open tag <%s> does not match close tag ' + % (tagstack[0], endtag)) + else: + msg = ('Open tags <%s> do not match close tag ' + % ('>, <'.join(tagstack), endtag)) + else: + msg = 'No tags are open to match ' % endtag + HTMLParseError.__init__(self, msg, position) + +class EmptyTagError(NestingError): + """Exception raised when empty elements have an end tag.""" + + def __init__(self, tag, position=(None, None)): + self.tag = tag + msg = 'Close tag should be removed' % tag + HTMLParseError.__init__(self, msg, position) + +class OpenTagError(NestingError): + """Exception raised when a tag is not allowed in another tag.""" + + def __init__(self, tagstack, tag, position=(None, None)): + self.tag = tag + msg = 'Tag <%s> is not allowed in <%s>' % (tag, tagstack[-1]) + HTMLParseError.__init__(self, msg, position) + +class HTMLTALParser(HTMLParser): + + # External API + + def __init__(self, gen=None): + HTMLParser.__init__(self) + if gen is None: + gen = TALGenerator(xml=0) + self.gen = gen + self.tagstack = [] + self.nsstack = [] + self.nsdict = {'tal': ZOPE_TAL_NS, + 'metal': ZOPE_METAL_NS, + 'i18n': ZOPE_I18N_NS, + } + + def parseFile(self, file): + f = open(file) + data = f.read() + f.close() + try: + self.parseString(data) + except TALError, e: + e.setFile(file) + raise + + def parseString(self, data): + self.feed(data) + self.close() + while self.tagstack: + self.implied_endtag(self.tagstack[-1], 2) + assert self.nsstack == [], self.nsstack + + def getCode(self): + return self.gen.getCode() + + # Overriding HTMLParser methods + + def handle_starttag(self, tag, attrs): + self.close_para_tags(tag) + self.scan_xmlns(attrs) + tag, attrlist, taldict, metaldict, i18ndict \ + = self.process_ns(tag, attrs) + if tag in EMPTY_HTML_TAGS and "content" in taldict: + raise TALError( + "empty HTML tags cannot use tal:content: %s" % `tag`, + self.getpos()) + # Support for inline Python code. + if tag == 'script': + type_attr = [a for a in attrlist if a[0] == "type"] + if type_attr and type_attr[0][1].startswith('text/server-'): + attrlist.remove(type_attr[0]) + taldict = {'script': type_attr[0][1], 'omit-tag': ''} + self.tagstack.append(tag) + self.gen.emitStartElement(tag, attrlist, taldict, metaldict, i18ndict, + self.getpos()) + if tag in EMPTY_HTML_TAGS: + self.implied_endtag(tag, -1) + + def handle_startendtag(self, tag, attrs): + self.close_para_tags(tag) + self.scan_xmlns(attrs) + tag, attrlist, taldict, metaldict, i18ndict \ + = self.process_ns(tag, attrs) + if "content" in taldict: + if tag in EMPTY_HTML_TAGS: + raise TALError( + "empty HTML tags cannot use tal:content: %s" % `tag`, + self.getpos()) + self.gen.emitStartElement(tag, attrlist, taldict, metaldict, + i18ndict, self.getpos()) + self.gen.emitEndElement(tag, implied=-1, position=self.getpos()) + else: + self.gen.emitStartElement(tag, attrlist, taldict, metaldict, + i18ndict, self.getpos(), isend=1) + self.pop_xmlns() + + def handle_endtag(self, tag): + if tag in EMPTY_HTML_TAGS: + # etc. in the source is an error + raise EmptyTagError(tag, self.getpos()) + self.close_enclosed_tags(tag) + self.gen.emitEndElement(tag, position=self.getpos()) + self.pop_xmlns() + self.tagstack.pop() + + def close_para_tags(self, tag): + if tag in EMPTY_HTML_TAGS: + return + close_to = -1 + if tag in BLOCK_CLOSING_TAG_MAP: + blocks_to_close = BLOCK_CLOSING_TAG_MAP[tag] + for i, t in enumerate(self.tagstack): + if t in blocks_to_close: + if close_to == -1: + close_to = i + elif t in BLOCK_LEVEL_HTML_TAGS: + close_to = -1 + elif tag in SECTION_LEVEL_HTML_TAGS: + for i in range(len(self.tagstack) - 1, -1, -1): + closetag = self.tagstack[i] + if closetag in BLOCK_LEVEL_HTML_TAGS: + break + elif closetag in PARA_LEVEL_HTML_TAGS: + if closetag != "p": + raise OpenTagError(self.tagstack, tag, self.getpos()) + close_to = i + if close_to >= 0: + while len(self.tagstack) > close_to: + self.implied_endtag(self.tagstack[-1], 1) + + def close_enclosed_tags(self, tag): + if tag not in self.tagstack: + raise NestingError(self.tagstack, tag, self.getpos()) + while tag != self.tagstack[-1]: + self.implied_endtag(self.tagstack[-1], 1) + assert self.tagstack[-1] == tag + + def implied_endtag(self, tag, implied): + assert tag == self.tagstack[-1] + assert implied in (-1, 1, 2) + isend = (implied < 0) + if tag in TIGHTEN_IMPLICIT_CLOSE_TAGS: + # Pick out trailing whitespace from the program, and + # insert the close tag before the whitespace. + white = self.gen.unEmitWhitespace() + else: + white = None + self.gen.emitEndElement(tag, isend=isend, implied=implied, + position=self.getpos()) + if white: + self.gen.emitRawText(white) + self.tagstack.pop() + self.pop_xmlns() + + def handle_charref(self, name): + self.gen.emitRawText("&#%s;" % name) + + def handle_entityref(self, name): + self.gen.emitRawText("&%s;" % name) + + def handle_data(self, data): + self.gen.emitRawText(data) + + def handle_comment(self, data): + self.gen.emitRawText("" % data) + + def handle_decl(self, data): + self.gen.emitRawText("" % data) + + def handle_pi(self, data): + self.gen.emitRawText("" % data) + + # Internal thingies + + def scan_xmlns(self, attrs): + nsnew = {} + for key, value in attrs: + if key.startswith("xmlns:"): + nsnew[key[6:]] = value + self.nsstack.append(self.nsdict) + if nsnew: + self.nsdict = self.nsdict.copy() + self.nsdict.update(nsnew) + + def pop_xmlns(self): + self.nsdict = self.nsstack.pop() + + _namespaces = { + ZOPE_TAL_NS: "tal", + ZOPE_METAL_NS: "metal", + ZOPE_I18N_NS: "i18n", + } + + def fixname(self, name): + if ':' in name: + prefix, suffix = name.split(':', 1) + if prefix == 'xmlns': + nsuri = self.nsdict.get(suffix) + if nsuri in self._namespaces: + return name, name, prefix + else: + nsuri = self.nsdict.get(prefix) + if nsuri in self._namespaces: + return name, suffix, self._namespaces[nsuri] + return name, name, 0 + + def process_ns(self, name, attrs): + attrlist = [] + taldict = {} + metaldict = {} + i18ndict = {} + name, namebase, namens = self.fixname(name) + for item in attrs: + key, value = item + key, keybase, keyns = self.fixname(key) + ns = keyns or namens # default to tag namespace + if ns and ns != 'unknown': + item = (key, value, ns) + if ns == 'tal': + if keybase in taldict: + raise TALError("duplicate TAL attribute " + + repr(keybase), self.getpos()) + taldict[keybase] = value + elif ns == 'metal': + if keybase in metaldict: + raise METALError("duplicate METAL attribute " + + repr(keybase), self.getpos()) + metaldict[keybase] = value + elif ns == 'i18n': + if keybase in i18ndict: + raise I18NError("duplicate i18n attribute " + + repr(keybase), self.getpos()) + i18ndict[keybase] = value + attrlist.append(item) + if namens in ('metal', 'tal', 'i18n'): + taldict['tal tag'] = namens + return name, attrlist, taldict, metaldict, i18ndict diff -Nru zope3-3.4.0/src/zope/tal/__init__.py zope3-3.5~bzr18/src/zope/tal/__init__.py --- zope3-3.4.0/src/zope/tal/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/tal/interfaces.py zope3-3.5~bzr18/src/zope/tal/interfaces.py --- zope3-3.4.0/src/zope/tal/interfaces.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/interfaces.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,207 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation 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. +# +############################################################################## +"""Interface that a TAL expression implementation provides to the METAL/TAL +implementation. + +$Id: interfaces.py 37831 2005-08-10 15:36:52Z fdrake $ +""" +from zope.interface import Attribute, Interface + + +class ITALExpressionCompiler(Interface): + """Compile-time interface provided by a TAL expression implementation. + + The TAL compiler needs an instance of this interface to support + compilation of TAL expressions embedded in documents containing + TAL and METAL constructs. + """ + + def getCompilerError(): + """Return the exception class raised for compilation errors. + """ + + def compile(expression): + """Return a compiled form of 'expression' for later evaluation. + + 'expression' is the source text of the expression. + + The return value may be passed to the various evaluate*() + methods of the ITALExpressionEngine interface. No compatibility is + required for the values of the compiled expression between + different ITALExpressionEngine implementations. + """ + + def getContext(namespace): + """Create an expression execution context + + The given namespace provides the initial top-level names. + """ + +class ITALExpressionEngine(Interface): + """Render-time interface provided by a TAL expression implementation. + + The TAL interpreter uses this interface to TAL expression to support + evaluation of the compiled expressions returned by + ITALExpressionCompiler.compile(). + """ + + def getDefault(): + """Return the value of the 'default' TAL expression. + + Checking a value for a match with 'default' should be done + using the 'is' operator in Python. + """ + + def setPosition((lineno, offset)): + """Inform the engine of the current position in the source file. + + This is used to allow the evaluation engine to report + execution errors so that site developers can more easily + locate the offending expression. + """ + + def setSourceFile(filename): + """Inform the engine of the name of the current source file. + + This is used to allow the evaluation engine to report + execution errors so that site developers can more easily + locate the offending expression. + """ + + def beginScope(): + """Push a new scope onto the stack of open scopes. + """ + + def endScope(): + """Pop one scope from the stack of open scopes. + """ + + def evaluate(compiled_expression): + """Evaluate an arbitrary expression. + + No constraints are imposed on the return value. + """ + + def evaluateBoolean(compiled_expression): + """Evaluate an expression that must return a Boolean value. + """ + + def evaluateMacro(compiled_expression): + """Evaluate an expression that must return a macro program. + """ + + def evaluateStructure(compiled_expression): + """Evaluate an expression that must return a structured + document fragment. + + The result of evaluating 'compiled_expression' must be a + string containing a parsable HTML or XML fragment. Any TAL + markup contained in the result string will be interpreted. + """ + + def evaluateText(compiled_expression): + """Evaluate an expression that must return text. + + The returned text should be suitable for direct inclusion in + the output: any HTML or XML escaping or quoting is the + responsibility of the expression itself. + + If the expression evaluates to None, then that is returned. It + represents 'nothing' in TALES. + If the expression evaluates to what getDefault() of this interface + returns, by comparison using 'is', then that is returned. It + represents 'default' in TALES. + """ + + def evaluateValue(compiled_expression): + """Evaluate an arbitrary expression. + + No constraints are imposed on the return value. + """ + + def createErrorInfo(exception, (lineno, offset)): + """Returns an ITALExpressionErrorInfo object. + + The returned object is used to provide information about the + error condition for the on-error handler. + """ + + def setGlobal(name, value): + """Set a global variable. + + The variable will be named 'name' and have the value 'value'. + """ + + def setLocal(name, value): + """Set a local variable in the current scope. + + The variable will be named 'name' and have the value 'value'. + """ + + def getValue(name, default=None): + """Get a variable by name. + + If the variable does not exist, return default. + """ + + def setRepeat(name, compiled_expression): + """Start a repetition, returning an ITALIterator. + + The engine is expected to add the a value (typically the + returned iterator) for the name to the variable namespace. + """ + + def translate(msgid, domain=None, mapping=None, default=None): + """See zope.i18n.interfaces.ITranslationDomain.translate""" + + # NB: This differs from the Zope 2 equivalent in the order of + # the arguments. This will be a (hopefully minor) issue when + # creating a unified TAL implementation. + + def evaluateCode(lang, code): + """Evaluates code of the given language. + + Returns whatever the code outputs. This can be defined on a + per-language basis. In Python this usually everything the print + statement will return. + """ + + +class ITALIterator(Interface): + """A TAL iterator + + Not to be confused with a Python iterator. + """ + + def next(): + """Advance to the next value in the iteration, if possible + + Return a true value if it was possible to advance and return + a false value otherwise. + """ + + +class ITALExpressionErrorInfo(Interface): + + type = Attribute("type", + "The exception class.") + + value = Attribute("value", + "The exception instance.") + + lineno = Attribute("lineno", + "The line number the error occurred on in the source.") + + offset = Attribute("offset", + "The character offset at which the error occurred.") diff -Nru zope3-3.4.0/src/zope/tal/ndiff.py zope3-3.5~bzr18/src/zope/tal/ndiff.py --- zope3-3.4.0/src/zope/tal/ndiff.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/ndiff.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,649 @@ +#! /usr/bin/env python +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## + +# Module ndiff version 1.6.0 +# Released to the public domain 08-Dec-2000, +# by Tim Peters (tim.one@home.com). + +# Provided as-is; use at your own risk; no warranty; no promises; enjoy! + +"""ndiff [-q] file1 file2 + or +ndiff (-r1 | -r2) < ndiff_output > file1_or_file2 + +Print a human-friendly file difference report to stdout. Both inter- +and intra-line differences are noted. In the second form, recreate file1 +(-r1) or file2 (-r2) on stdout, from an ndiff report on stdin. + +In the first form, if -q ("quiet") is not specified, the first two lines +of output are + +-: file1 ++: file2 + +Each remaining line begins with a two-letter code: + + "- " line unique to file1 + "+ " line unique to file2 + " " line common to both files + "? " line not present in either input file + +Lines beginning with "? " attempt to guide the eye to intraline +differences, and were not present in either input file. These lines can be +confusing if the source files contain tab characters. + +The first file can be recovered by retaining only lines that begin with +" " or "- ", and deleting those 2-character prefixes; use ndiff with -r1. + +The second file can be recovered similarly, but by retaining only " " and +"+ " lines; use ndiff with -r2; or, on Unix, the second file can be +recovered by piping the output through + + sed -n '/^[+ ] /s/^..//p' + +See module comments for details and programmatic interface. + +$Id: ndiff.py 38178 2005-08-30 21:50:19Z mj $ +""" + +__version__ = 1, 5, 0 + +# SequenceMatcher tries to compute a "human-friendly diff" between +# two sequences (chiefly picturing a file as a sequence of lines, +# and a line as a sequence of characters, here). Unlike e.g. UNIX(tm) +# diff, the fundamental notion is the longest *contiguous* & junk-free +# matching subsequence. That's what catches peoples' eyes. The +# Windows(tm) windiff has another interesting notion, pairing up elements +# that appear uniquely in each sequence. That, and the method here, +# appear to yield more intuitive difference reports than does diff. This +# method appears to be the least vulnerable to synching up on blocks +# of "junk lines", though (like blank lines in ordinary text files, +# or maybe "

" lines in HTML files). That may be because this is +# the only method of the 3 that has a *concept* of "junk" . +# +# Note that ndiff makes no claim to produce a *minimal* diff. To the +# contrary, minimal diffs are often counter-intuitive, because they +# synch up anywhere possible, sometimes accidental matches 100 pages +# apart. Restricting synch points to contiguous matches preserves some +# notion of locality, at the occasional cost of producing a longer diff. +# +# With respect to junk, an earlier version of ndiff simply refused to +# *start* a match with a junk element. The result was cases like this: +# before: private Thread currentThread; +# after: private volatile Thread currentThread; +# If you consider whitespace to be junk, the longest contiguous match +# not starting with junk is "e Thread currentThread". So ndiff reported +# that "e volatil" was inserted between the 't' and the 'e' in "private". +# While an accurate view, to people that's absurd. The current version +# looks for matching blocks that are entirely junk-free, then extends the +# longest one of those as far as possible but only with matching junk. +# So now "currentThread" is matched, then extended to suck up the +# preceding blank; then "private" is matched, and extended to suck up the +# following blank; then "Thread" is matched; and finally ndiff reports +# that "volatile " was inserted before "Thread". The only quibble +# remaining is that perhaps it was really the case that " volatile" +# was inserted after "private". I can live with that . +# +# NOTE on junk: the module-level names +# IS_LINE_JUNK +# IS_CHARACTER_JUNK +# can be set to any functions you like. The first one should accept +# a single string argument, and return true iff the string is junk. +# The default is whether the regexp r"\s*#?\s*$" matches (i.e., a +# line without visible characters, except for at most one splat). +# The second should accept a string of length 1 etc. The default is +# whether the character is a blank or tab (note: bad idea to include +# newline in this!). +# +# After setting those, you can call fcompare(f1name, f2name) with the +# names of the files you want to compare. The difference report +# is sent to stdout. Or you can call main(args), passing what would +# have been in sys.argv[1:] had the cmd-line form been used. + +TRACE = 0 + +# define what "junk" means +import re + +def IS_LINE_JUNK(line, pat=re.compile(r"\s*#?\s*$").match): + return pat(line) is not None + +def IS_CHARACTER_JUNK(ch, ws=" \t"): + return ch in ws + +del re + +class SequenceMatcher(object): + def __init__(self, isjunk=None, a='', b=''): + # Members: + # a + # first sequence + # b + # second sequence; differences are computed as "what do + # we need to do to 'a' to change it into 'b'?" + # b2j + # for x in b, b2j[x] is a list of the indices (into b) + # at which x appears; junk elements do not appear + # b2jhas + # b2j.has_key + # fullbcount + # for x in b, fullbcount[x] == the number of times x + # appears in b; only materialized if really needed (used + # only for computing quick_ratio()) + # matching_blocks + # a list of (i, j, k) triples, where a[i:i+k] == b[j:j+k]; + # ascending & non-overlapping in i and in j; terminated by + # a dummy (len(a), len(b), 0) sentinel + # opcodes + # a list of (tag, i1, i2, j1, j2) tuples, where tag is + # one of + # 'replace' a[i1:i2] should be replaced by b[j1:j2] + # 'delete' a[i1:i2] should be deleted + # 'insert' b[j1:j2] should be inserted + # 'equal' a[i1:i2] == b[j1:j2] + # isjunk + # a user-supplied function taking a sequence element and + # returning true iff the element is "junk" -- this has + # subtle but helpful effects on the algorithm, which I'll + # get around to writing up someday <0.9 wink>. + # DON'T USE! Only __chain_b uses this. Use isbjunk. + # isbjunk + # for x in b, isbjunk(x) == isjunk(x) but much faster; + # it's really the has_key method of a hidden dict. + # DOES NOT WORK for x in a! + + self.isjunk = isjunk + self.a = self.b = None + self.set_seqs(a, b) + + def set_seqs(self, a, b): + self.set_seq1(a) + self.set_seq2(b) + + def set_seq1(self, a): + if a is self.a: + return + self.a = a + self.matching_blocks = self.opcodes = None + + def set_seq2(self, b): + if b is self.b: + return + self.b = b + self.matching_blocks = self.opcodes = None + self.fullbcount = None + self.__chain_b() + + # For each element x in b, set b2j[x] to a list of the indices in + # b where x appears; the indices are in increasing order; note that + # the number of times x appears in b is len(b2j[x]) ... + # when self.isjunk is defined, junk elements don't show up in this + # map at all, which stops the central find_longest_match method + # from starting any matching block at a junk element ... + # also creates the fast isbjunk function ... + # note that this is only called when b changes; so for cross-product + # kinds of matches, it's best to call set_seq2 once, then set_seq1 + # repeatedly + + def __chain_b(self): + # Because isjunk is a user-defined (not C) function, and we test + # for junk a LOT, it's important to minimize the number of calls. + # Before the tricks described here, __chain_b was by far the most + # time-consuming routine in the whole module! If anyone sees + # Jim Roskind, thank him again for profile.py -- I never would + # have guessed that. + # The first trick is to build b2j ignoring the possibility + # of junk. I.e., we don't call isjunk at all yet. Throwing + # out the junk later is much cheaper than building b2j "right" + # from the start. + b = self.b + self.b2j = b2j = {} + self.b2jhas = b2jhas = b2j.has_key + for i in xrange(len(b)): + elt = b[i] + if b2jhas(elt): + b2j[elt].append(i) + else: + b2j[elt] = [i] + + # Now b2j.keys() contains elements uniquely, and especially when + # the sequence is a string, that's usually a good deal smaller + # than len(string). The difference is the number of isjunk calls + # saved. + isjunk, junkdict = self.isjunk, {} + if isjunk: + for elt in b2j.keys(): + if isjunk(elt): + junkdict[elt] = 1 # value irrelevant; it's a set + del b2j[elt] + + # Now for x in b, isjunk(x) == junkdict.has_key(x), but the + # latter is much faster. Note too that while there may be a + # lot of junk in the sequence, the number of *unique* junk + # elements is probably small. So the memory burden of keeping + # this dict alive is likely trivial compared to the size of b2j. + self.isbjunk = junkdict.has_key + + def find_longest_match(self, alo, ahi, blo, bhi): + """Find longest matching block in a[alo:ahi] and b[blo:bhi]. + + If isjunk is not defined: + + Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where + alo <= i <= i+k <= ahi + blo <= j <= j+k <= bhi + and for all (i',j',k') meeting those conditions, + k >= k' + i <= i' + and if i == i', j <= j' + In other words, of all maximal matching blocks, return one + that starts earliest in a, and of all those maximal matching + blocks that start earliest in a, return the one that starts + earliest in b. + + If isjunk is defined, first the longest matching block is + determined as above, but with the additional restriction that + no junk element appears in the block. Then that block is + extended as far as possible by matching (only) junk elements on + both sides. So the resulting block never matches on junk except + as identical junk happens to be adjacent to an "interesting" + match. + + If no blocks match, return (alo, blo, 0). + """ + + # CAUTION: stripping common prefix or suffix would be incorrect. + # E.g., + # ab + # acab + # Longest matching block is "ab", but if common prefix is + # stripped, it's "a" (tied with "b"). UNIX(tm) diff does so + # strip, so ends up claiming that ab is changed to acab by + # inserting "ca" in the middle. That's minimal but unintuitive: + # "it's obvious" that someone inserted "ac" at the front. + # Windiff ends up at the same place as diff, but by pairing up + # the unique 'b's and then matching the first two 'a's. + + a, b, b2j, isbjunk = self.a, self.b, self.b2j, self.isbjunk + besti, bestj, bestsize = alo, blo, 0 + # find longest junk-free match + # during an iteration of the loop, j2len[j] = length of longest + # junk-free match ending with a[i-1] and b[j] + j2len = {} + nothing = [] + for i in xrange(alo, ahi): + # look at all instances of a[i] in b; note that because + # b2j has no junk keys, the loop is skipped if a[i] is junk + j2lenget = j2len.get + newj2len = {} + for j in b2j.get(a[i], nothing): + # a[i] matches b[j] + if j < blo: + continue + if j >= bhi: + break + k = newj2len[j] = j2lenget(j-1, 0) + 1 + if k > bestsize: + besti, bestj, bestsize = i-k+1, j-k+1, k + j2len = newj2len + + # Now that we have a wholly interesting match (albeit possibly + # empty!), we may as well suck up the matching junk on each + # side of it too. Can't think of a good reason not to, and it + # saves post-processing the (possibly considerable) expense of + # figuring out what to do with it. In the case of an empty + # interesting match, this is clearly the right thing to do, + # because no other kind of match is possible in the regions. + while besti > alo and bestj > blo and \ + isbjunk(b[bestj-1]) and \ + a[besti-1] == b[bestj-1]: + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + while besti+bestsize < ahi and bestj+bestsize < bhi and \ + isbjunk(b[bestj+bestsize]) and \ + a[besti+bestsize] == b[bestj+bestsize]: + bestsize = bestsize + 1 + + if TRACE: + print "get_matching_blocks", alo, ahi, blo, bhi + print " returns", besti, bestj, bestsize + return besti, bestj, bestsize + + def get_matching_blocks(self): + if self.matching_blocks is not None: + return self.matching_blocks + self.matching_blocks = [] + la, lb = len(self.a), len(self.b) + self.__helper(0, la, 0, lb, self.matching_blocks) + self.matching_blocks.append((la, lb, 0)) + if TRACE: + print '*** matching blocks', self.matching_blocks + return self.matching_blocks + + # builds list of matching blocks covering a[alo:ahi] and + # b[blo:bhi], appending them in increasing order to answer + + def __helper(self, alo, ahi, blo, bhi, answer): + i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi) + # a[alo:i] vs b[blo:j] unknown + # a[i:i+k] same as b[j:j+k] + # a[i+k:ahi] vs b[j+k:bhi] unknown + if k: + if alo < i and blo < j: + self.__helper(alo, i, blo, j, answer) + answer.append(x) + if i+k < ahi and j+k < bhi: + self.__helper(i+k, ahi, j+k, bhi, answer) + + def ratio(self): + """Return a measure of the sequences' similarity (float in [0,1]). + + Where T is the total number of elements in both sequences, and + M is the number of matches, this is 2*M / T. + Note that this is 1 if the sequences are identical, and 0 if + they have nothing in common. + """ + + matches = reduce(lambda sum, triple: sum + triple[-1], + self.get_matching_blocks(), 0) + return 2.0 * matches / (len(self.a) + len(self.b)) + + def quick_ratio(self): + """Return an upper bound on ratio() relatively quickly.""" + # viewing a and b as multisets, set matches to the cardinality + # of their intersection; this counts the number of matches + # without regard to order, so is clearly an upper bound + if self.fullbcount is None: + self.fullbcount = fullbcount = {} + for elt in self.b: + fullbcount[elt] = fullbcount.get(elt, 0) + 1 + fullbcount = self.fullbcount + # avail[x] is the number of times x appears in 'b' less the + # number of times we've seen it in 'a' so far ... kinda + avail = {} + availhas, matches = avail.has_key, 0 + for elt in self.a: + if availhas(elt): + numb = avail[elt] + else: + numb = fullbcount.get(elt, 0) + avail[elt] = numb - 1 + if numb > 0: + matches = matches + 1 + return 2.0 * matches / (len(self.a) + len(self.b)) + + def real_quick_ratio(self): + """Return an upper bound on ratio() very quickly""" + la, lb = len(self.a), len(self.b) + # can't have more matches than the number of elements in the + # shorter sequence + return 2.0 * min(la, lb) / (la + lb) + + def get_opcodes(self): + if self.opcodes is not None: + return self.opcodes + i = j = 0 + self.opcodes = answer = [] + for ai, bj, size in self.get_matching_blocks(): + # invariant: we've pumped out correct diffs to change + # a[:i] into b[:j], and the next matching block is + # a[ai:ai+size] == b[bj:bj+size]. So we need to pump + # out a diff to change a[i:ai] into b[j:bj], pump out + # the matching block, and move (i,j) beyond the match + tag = '' + if i < ai and j < bj: + tag = 'replace' + elif i < ai: + tag = 'delete' + elif j < bj: + tag = 'insert' + if tag: + answer.append((tag, i, ai, j, bj)) + i, j = ai+size, bj+size + # the list of matching blocks is terminated by a + # sentinel with size 0 + if size: + answer.append(('equal', ai, i, bj, j)) + return answer + +# meant for dumping lines +def dump(tag, x, lo, hi): + for i in xrange(lo, hi): + print tag, x[i], + +def plain_replace(a, alo, ahi, b, blo, bhi): + assert alo < ahi and blo < bhi + # dump the shorter block first -- reduces the burden on short-term + # memory if the blocks are of very different sizes + if bhi - blo < ahi - alo: + dump('+', b, blo, bhi) + dump('-', a, alo, ahi) + else: + dump('-', a, alo, ahi) + dump('+', b, blo, bhi) + +# When replacing one block of lines with another, this guy searches +# the blocks for *similar* lines; the best-matching pair (if any) is +# used as a synch point, and intraline difference marking is done on +# the similar pair. Lots of work, but often worth it. + +def fancy_replace(a, alo, ahi, b, blo, bhi): + if TRACE: + print '*** fancy_replace', alo, ahi, blo, bhi + dump('>', a, alo, ahi) + dump('<', b, blo, bhi) + + # don't synch up unless the lines have a similarity score of at + # least cutoff; best_ratio tracks the best score seen so far + best_ratio, cutoff = 0.74, 0.75 + cruncher = SequenceMatcher(IS_CHARACTER_JUNK) + eqi, eqj = None, None # 1st indices of equal lines (if any) + + # search for the pair that matches best without being identical + # (identical lines must be junk lines, & we don't want to synch up + # on junk -- unless we have to) + for j in xrange(blo, bhi): + bj = b[j] + cruncher.set_seq2(bj) + for i in xrange(alo, ahi): + ai = a[i] + if ai == bj: + if eqi is None: + eqi, eqj = i, j + continue + cruncher.set_seq1(ai) + # computing similarity is expensive, so use the quick + # upper bounds first -- have seen this speed up messy + # compares by a factor of 3. + # note that ratio() is only expensive to compute the first + # time it's called on a sequence pair; the expensive part + # of the computation is cached by cruncher + if cruncher.real_quick_ratio() > best_ratio and \ + cruncher.quick_ratio() > best_ratio and \ + cruncher.ratio() > best_ratio: + best_ratio, best_i, best_j = cruncher.ratio(), i, j + if best_ratio < cutoff: + # no non-identical "pretty close" pair + if eqi is None: + # no identical pair either -- treat it as a straight replace + plain_replace(a, alo, ahi, b, blo, bhi) + return + # no close pair, but an identical pair -- synch up on that + best_i, best_j, best_ratio = eqi, eqj, 1.0 + else: + # there's a close pair, so forget the identical pair (if any) + eqi = None + + # a[best_i] very similar to b[best_j]; eqi is None iff they're not + # identical + if TRACE: + print '*** best_ratio', best_ratio, best_i, best_j + dump('>', a, best_i, best_i+1) + dump('<', b, best_j, best_j+1) + + # pump out diffs from before the synch point + fancy_helper(a, alo, best_i, b, blo, best_j) + + # do intraline marking on the synch pair + aelt, belt = a[best_i], b[best_j] + if eqi is None: + # pump out a '-', '?', '+', '?' quad for the synched lines + atags = btags = "" + cruncher.set_seqs(aelt, belt) + for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes(): + la, lb = ai2 - ai1, bj2 - bj1 + if tag == 'replace': + atags = atags + '^' * la + btags = btags + '^' * lb + elif tag == 'delete': + atags = atags + '-' * la + elif tag == 'insert': + btags = btags + '+' * lb + elif tag == 'equal': + atags = atags + ' ' * la + btags = btags + ' ' * lb + else: + raise ValueError('unknown tag ' + `tag`) + printq(aelt, belt, atags, btags) + else: + # the synch pair is identical + print ' ', aelt, + + # pump out diffs from after the synch point + fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi) + +def fancy_helper(a, alo, ahi, b, blo, bhi): + if alo < ahi: + if blo < bhi: + fancy_replace(a, alo, ahi, b, blo, bhi) + else: + dump('-', a, alo, ahi) + elif blo < bhi: + dump('+', b, blo, bhi) + +# Crap to deal with leading tabs in "?" output. Can hurt, but will +# probably help most of the time. + +def printq(aline, bline, atags, btags): + common = min(count_leading(aline, "\t"), + count_leading(bline, "\t")) + common = min(common, count_leading(atags[:common], " ")) + print "-", aline, + if count_leading(atags, " ") < len(atags): + print "?", "\t" * common + atags[common:] + print "+", bline, + if count_leading(btags, " ") < len(btags): + print "?", "\t" * common + btags[common:] + +def count_leading(line, ch): + i, n = 0, len(line) + while i < n and line[i] == ch: + i = i + 1 + return i + +def fail(msg): + import sys + out = sys.stderr.write + out(msg + "\n\n") + out(__doc__) + return 0 + +# open a file & return the file object; gripe and return 0 if it +# couldn't be opened +def fopen(fname): + try: + return open(fname, 'r') + except IOError, detail: + return fail("couldn't open " + fname + ": " + str(detail)) + +# open two files & spray the diff to stdout; return false iff a problem +def fcompare(f1name, f2name): + f1 = fopen(f1name) + f2 = fopen(f2name) + if not f1 or not f2: + return 0 + + a = f1.readlines(); f1.close() + b = f2.readlines(); f2.close() + + cruncher = SequenceMatcher(IS_LINE_JUNK, a, b) + for tag, alo, ahi, blo, bhi in cruncher.get_opcodes(): + if tag == 'replace': + fancy_replace(a, alo, ahi, b, blo, bhi) + elif tag == 'delete': + dump('-', a, alo, ahi) + elif tag == 'insert': + dump('+', b, blo, bhi) + elif tag == 'equal': + dump(' ', a, alo, ahi) + else: + raise ValueError('unknown tag ' + `tag`) + + return 1 + +# crack args (sys.argv[1:] is normal) & compare; +# return false iff a problem + +def main(args): + import getopt + try: + opts, args = getopt.getopt(args, "qr:") + except getopt.error, detail: + return fail(str(detail)) + noisy = 1 + qseen = rseen = 0 + for opt, val in opts: + if opt == "-q": + qseen = 1 + noisy = 0 + elif opt == "-r": + rseen = 1 + whichfile = val + if qseen and rseen: + return fail("can't specify both -q and -r") + if rseen: + if args: + return fail("no args allowed with -r option") + if whichfile in "12": + restore(whichfile) + return 1 + return fail("-r value must be 1 or 2") + if len(args) != 2: + return fail("need 2 filename args") + f1name, f2name = args + if noisy: + print '-:', f1name + print '+:', f2name + return fcompare(f1name, f2name) + +def restore(which): + import sys + tag = {"1": "- ", "2": "+ "}[which] + prefixes = (" ", tag) + for line in sys.stdin.readlines(): + if line[:2] in prefixes: + print line[2:], + +if __name__ == '__main__': + import sys + args = sys.argv[1:] + if "-profile" in args: + import profile, pstats + args.remove("-profile") + statf = "ndiff.pro" + profile.run("main(args)", statf) + stats = pstats.Stats(statf) + stats.strip_dirs().sort_stats('time').print_stats() + else: + main(args) diff -Nru zope3-3.4.0/src/zope/tal/runtest.py zope3-3.5~bzr18/src/zope/tal/runtest.py --- zope3-3.4.0/src/zope/tal/runtest.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/runtest.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,154 @@ +#! /usr/bin/env python +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Driver program to run METAL and TAL regression tests. + +$Id: runtest.py 29651 2005-03-23 12:56:35Z hdima $ +""" +import glob +import os +import sys +import traceback + +from cStringIO import StringIO + +if __name__ == "__main__": + import setpath # Local hack to tweak sys.path etc. + +import zope.tal.driver +import zope.tal.tests.utils + +def showdiff(a, b): + import ndiff + cruncher = ndiff.SequenceMatcher(ndiff.IS_LINE_JUNK, a, b) + for tag, alo, ahi, blo, bhi in cruncher.get_opcodes(): + if tag == "equal": + continue + print nicerange(alo, ahi) + tag[0] + nicerange(blo, bhi) + ndiff.dump('<', a, alo, ahi) + if a and b: + print '---' + ndiff.dump('>', b, blo, bhi) + +def nicerange(lo, hi): + if hi <= lo+1: + return str(lo+1) + else: + return "%d,%d" % (lo+1, hi) + +def main(): + opts = [] + args = sys.argv[1:] + quiet = 0 + unittesting = 0 + if args and args[0] == "-q": + quiet = 1 + del args[0] + if args and args[0] == "-Q": + unittesting = 1 + del args[0] + while args and args[0].startswith('-'): + opts.append(args[0]) + del args[0] + if not args: + prefix = os.path.join("tests", "input", "test*.") + if zope.tal.tests.utils.skipxml: + xmlargs = [] + else: + xmlargs = glob.glob(prefix + "xml") + xmlargs.sort() + htmlargs = glob.glob(prefix + "html") + htmlargs.sort() + args = xmlargs + htmlargs + if not args: + sys.stderr.write("No tests found -- please supply filenames\n") + sys.exit(1) + errors = 0 + for arg in args: + locopts = [] + if arg.find("metal") >= 0 and "-m" not in opts: + locopts.append("-m") + if arg.find("_sa") >= 0 and "-a" not in opts: + locopts.append("-a") + if not unittesting: + print arg, + sys.stdout.flush() + if zope.tal.tests.utils.skipxml and arg.endswith(".xml"): + print "SKIPPED (XML parser not available)" + continue + save = sys.stdout, sys.argv + try: + try: + sys.stdout = stdout = StringIO() + sys.argv = [""] + opts + locopts + [arg] + zope.tal.driver.main() + finally: + sys.stdout, sys.argv = save + except SystemExit: + raise + except: + errors = 1 + if quiet: + print sys.exc_type + sys.stdout.flush() + else: + if unittesting: + print + else: + print "Failed:" + sys.stdout.flush() + traceback.print_exc() + continue + head, tail = os.path.split(arg) + outfile = os.path.join( + head.replace("input", "output"), + tail) + try: + f = open(outfile) + except IOError: + expected = None + print "(missing file %s)" % outfile, + else: + expected = f.readlines() + f.close() + stdout.seek(0) + if hasattr(stdout, "readlines"): + actual = stdout.readlines() + else: + actual = readlines(stdout) + if actual == expected: + if not unittesting: + print "OK" + else: + if unittesting: + print + else: + print "not OK" + errors = 1 + if not quiet and expected is not None: + showdiff(expected, actual) + if errors: + sys.exit(1) + +def readlines(f): + L = [] + while 1: + line = f.readline() + if not line: + break + L.append(line) + return L + +if __name__ == "__main__": + main() diff -Nru zope3-3.4.0/src/zope/tal/setpath.py zope3-3.5~bzr18/src/zope/tal/setpath.py --- zope3-3.4.0/src/zope/tal/setpath.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/setpath.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Read a module search path from .path file. + +If .path file isn't found in the directory of the setpath.py module, then try +to import ZODB. If that succeeds, we assume the path is already set up +correctly. If that import fails, an IOError is raised. + +$Id: setpath.py 29651 2005-03-23 12:56:35Z hdima $ +""" + +# TODO: Why does this want to find ZODB ??? + +import os +import sys + +dir = os.path.dirname(__file__) +path = os.path.join(dir, ".path") +try: + f = open(path) +except IOError: + try: + # If we can import ZODB, our sys.path is set up well enough already + import ZODB + except ImportError: + raise IOError("Can't find ZODB package. Please edit %s to point to " + "your Zope's lib/python directory" % path) +else: + for line in f.readlines(): + line = line.strip() + if line and line[0] != '#': + for dir in line.split(os.pathsep): + dir = os.path.expanduser(os.path.expandvars(dir)) + if dir not in sys.path: + sys.path.append(dir) + # Must import this first to initialize Persistence properly + import ZODB diff -Nru zope3-3.4.0/src/zope/tal/taldefs.py zope3-3.5~bzr18/src/zope/tal/taldefs.py --- zope3-3.4.0/src/zope/tal/taldefs.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/taldefs.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,201 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Common definitions used by TAL and METAL compilation and transformation. + +$Id: taldefs.py 41477 2006-01-28 19:52:03Z hdima $ +""" +import re +from zope.tal.interfaces import ITALExpressionErrorInfo +from zope.interface import implements + + +TAL_VERSION = "1.6" + +XML_NS = "http://www.w3.org/XML/1998/namespace" # URI for XML namespace +XMLNS_NS = "http://www.w3.org/2000/xmlns/" # URI for XML NS declarations + +ZOPE_TAL_NS = "http://xml.zope.org/namespaces/tal" +ZOPE_METAL_NS = "http://xml.zope.org/namespaces/metal" +ZOPE_I18N_NS = "http://xml.zope.org/namespaces/i18n" + +# This RE must exactly match the expression of the same name in the +# zope.i18n.simpletranslationservice module: +NAME_RE = "[a-zA-Z_][-a-zA-Z0-9_]*" + +KNOWN_METAL_ATTRIBUTES = frozenset([ + "define-macro", + "extend-macro", + "use-macro", + "define-slot", + "fill-slot", + ]) + +KNOWN_TAL_ATTRIBUTES = frozenset([ + "define", + "condition", + "content", + "replace", + "repeat", + "attributes", + "on-error", + "omit-tag", + "script", + "tal tag", # a pseudo attribute that holds the namespace of elements + # like , , + ]) + +KNOWN_I18N_ATTRIBUTES = frozenset([ + "translate", + "domain", + "target", + "source", + "attributes", + "data", + "name", + ]) + +class TALError(Exception): + + def __init__(self, msg, position=(None, None)): + assert msg != "" + self.msg = msg + self.lineno = position[0] + self.offset = position[1] + self.filename = None + + def setFile(self, filename): + self.filename = filename + + def __str__(self): + result = self.msg + if self.lineno is not None: + result = result + ", at line %d" % self.lineno + if self.offset is not None: + result = result + ", column %d" % (self.offset + 1) + if self.filename is not None: + result = result + ', in file %s' % self.filename + return result + +class METALError(TALError): + pass + +class TALExpressionError(TALError): + pass + +class I18NError(TALError): + pass + + +class ErrorInfo(object): + implements(ITALExpressionErrorInfo) + + def __init__(self, err, position=(None, None)): + if isinstance(err, Exception): + self.type = err.__class__ + self.value = err + else: + self.type = err + self.value = None + self.lineno = position[0] + self.offset = position[1] + + +_attr_re = re.compile(r"\s*([^\s]+)\s+([^\s].*)\Z", re.S) +_subst_re = re.compile(r"\s*(?:(text|structure)\s+)?(.*)\Z", re.S) + +def parseAttributeReplacements(arg, xml): + dict = {} + for part in splitParts(arg): + m = _attr_re.match(part) + if not m: + raise TALError("Bad syntax in attributes: %r" % part) + name, expr = m.groups() + if not xml: + name = name.lower() + if name in dict: + raise TALError("Duplicate attribute name in attributes: %r" % part) + dict[name] = expr + return dict + +def parseSubstitution(arg, position=(None, None)): + m = _subst_re.match(arg) + if not m: + raise TALError("Bad syntax in substitution text: %r" % arg, position) + key, expr = m.groups() + if not key: + key = "text" + return key, expr + +def splitParts(arg): + # Break in pieces at undoubled semicolons and + # change double semicolons to singles: + arg = arg.replace(";;", "\0") + parts = arg.split(';') + parts = [p.replace("\0", ";") for p in parts] + if len(parts) > 1 and not parts[-1].strip(): + del parts[-1] # It ended in a semicolon + return parts + +def isCurrentVersion(program): + version = getProgramVersion(program) + return version == TAL_VERSION + +def isinstance_(ob, type): + # Proxy-friendly and faster isinstance_ check for new-style objects + try: + return type in ob.__class__.__mro__ + except AttributeError: + return False + + +def getProgramMode(program): + version = getProgramVersion(program) + if (version == TAL_VERSION and isinstance_(program[1], tuple) and + len(program[1]) == 2): + opcode, mode = program[1] + if opcode == "mode": + return mode + return None + +def getProgramVersion(program): + if (len(program) >= 2 and + isinstance_(program[0], tuple) and len(program[0]) == 2): + opcode, version = program[0] + if opcode == "version": + return version + return None + +_ent1_re = re.compile('&(?![A-Z#])', re.I) +_entch_re = re.compile('&([A-Z][A-Z0-9]*)(?![A-Z0-9;])', re.I) +_entn1_re = re.compile('&#(?![0-9X])', re.I) +_entnx_re = re.compile('&(#X[A-F0-9]*)(?![A-F0-9;])', re.I) +_entnd_re = re.compile('&(#[0-9][0-9]*)(?![0-9;])') + +def attrEscape(s): + """Replace special characters '&<>' by character entities, + except when '&' already begins a syntactically valid entity.""" + s = _ent1_re.sub('&', s) + s = _entch_re.sub(r'&\1', s) + s = _entn1_re.sub('&#', s) + s = _entnx_re.sub(r'&\1', s) + s = _entnd_re.sub(r'&\1', s) + s = s.replace('<', '<') + s = s.replace('>', '>') + s = s.replace('"', '"') + return s + +import cgi +def quote(s, escape=cgi.escape): + return '"%s"' % escape(s, 1) +del cgi diff -Nru zope3-3.4.0/src/zope/tal/talgenerator.py zope3-3.5~bzr18/src/zope/tal/talgenerator.py --- zope3-3.4.0/src/zope/tal/talgenerator.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/talgenerator.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,856 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Code generator for TALInterpreter intermediate code. + +$Id: talgenerator.py 39119 2005-10-13 19:20:18Z fdrake $ +""" +import cgi +import re + +from zope.tal import taldefs +from zope.tal.taldefs import NAME_RE, TAL_VERSION +from zope.tal.taldefs import I18NError, METALError, TALError +from zope.tal.taldefs import parseSubstitution +from zope.tal.translationcontext import TranslationContext, DEFAULT_DOMAIN + + +_name_rx = re.compile(NAME_RE) + +class TALGenerator(object): + + inMacroUse = 0 + inMacroDef = 0 + source_file = None + + def __init__(self, expressionCompiler=None, xml=1, source_file=None): + if not expressionCompiler: + from zope.tal.dummyengine import DummyEngine + expressionCompiler = DummyEngine() + self.expressionCompiler = expressionCompiler + self.CompilerError = expressionCompiler.getCompilerError() + # This holds the emitted opcodes representing the input + self.program = [] + # The program stack for when we need to do some sub-evaluation for an + # intermediate result. E.g. in an i18n:name tag for which the + # contents describe the ${name} value. + self.stack = [] + # Another stack of postponed actions. Elements on this stack are a + # dictionary; key/values contain useful information that + # emitEndElement needs to finish its calculations + self.todoStack = [] + self.macros = {} + # {slot-name --> default content program} + self.slots = {} + self.slotStack = [] + self.xml = xml # true --> XML, false --> HTML + self.emit("version", TAL_VERSION) + self.emit("mode", xml and "xml" or "html") + if source_file is not None: + self.source_file = source_file + self.emit("setSourceFile", source_file) + self.i18nContext = TranslationContext() + self.i18nLevel = 0 + + def getCode(self): + assert not self.stack + assert not self.todoStack + return self.optimize(self.program), self.macros + + def optimize(self, program): + output = [] + collect = [] + cursor = 0 + for cursor in xrange(len(program)+1): + try: + item = program[cursor] + except IndexError: + item = (None, None) + opcode = item[0] + if opcode == "rawtext": + collect.append(item[1]) + continue + if opcode == "endTag": + collect.append("" % item[1]) + continue + if opcode == "startTag": + if self.optimizeStartTag(collect, item[1], item[2], ">"): + continue + if opcode == "startEndTag": + endsep = self.xml and "/>" or " />" + if self.optimizeStartTag(collect, item[1], item[2], endsep): + continue + if opcode in ("beginScope", "endScope"): + # Push *Scope instructions in front of any text instructions; + # this allows text instructions separated only by *Scope + # instructions to be joined together. + output.append(self.optimizeArgsList(item)) + continue + if opcode == 'noop': + # This is a spacer for end tags in the face of i18n:name + # attributes. We can't let the optimizer collect immediately + # following end tags into the same rawtextOffset. + opcode = None + pass + text = "".join(collect) + if text: + i = text.rfind("\n") + if i >= 0: + i = len(text) - (i + 1) + output.append(("rawtextColumn", (text, i))) + else: + output.append(("rawtextOffset", (text, len(text)))) + if opcode != None: + output.append(self.optimizeArgsList(item)) + collect = [] + return self.optimizeCommonTriple(output) + + def optimizeArgsList(self, item): + if len(item) == 2: + return item + else: + return item[0], tuple(item[1:]) + + # These codes are used to indicate what sort of special actions + # are needed for each special attribute. (Simple attributes don't + # get action codes.) + # + # The special actions (which are modal) are handled by + # TALInterpreter.attrAction() and .attrAction_tal(). + # + # Each attribute is represented by a tuple: + # + # (name, value) -- a simple name/value pair, with + # no special processing + # + # (name, value, action, *extra) -- attribute with special + # processing needs, action is a + # code that indicates which + # branch to take, and *extra + # contains additional, + # action-specific information + # needed by the processing + # + def optimizeStartTag(self, collect, name, attrlist, end): + # return true if the tag can be converted to plain text + if not attrlist: + collect.append("<%s%s" % (name, end)) + return 1 + opt = 1 + new = ["<" + name] + for i in range(len(attrlist)): + item = attrlist[i] + if len(item) > 2: + opt = 0 + name, value, action = item[:3] + attrlist[i] = (name, value, action) + item[3:] + else: + if item[1] is None: + s = item[0] + else: + s = '%s="%s"' % (item[0], taldefs.attrEscape(item[1])) + attrlist[i] = item[0], s + new.append(" " + s) + # if no non-optimizable attributes were found, convert to plain text + if opt: + new.append(end) + collect.extend(new) + return opt + + def optimizeCommonTriple(self, program): + if len(program) < 3: + return program + output = program[:2] + prev2, prev1 = output + for item in program[2:]: + if ( item[0] == "beginScope" + and prev1[0] == "setPosition" + and prev2[0] == "rawtextColumn"): + position = output.pop()[1] + text, column = output.pop()[1] + prev1 = None, None + closeprev = 0 + if output and output[-1][0] == "endScope": + closeprev = 1 + output.pop() + item = ("rawtextBeginScope", + (text, column, position, closeprev, item[1])) + output.append(item) + prev2 = prev1 + prev1 = item + return output + + def todoPush(self, todo): + self.todoStack.append(todo) + + def todoPop(self): + return self.todoStack.pop() + + def compileExpression(self, expr): + try: + return self.expressionCompiler.compile(expr) + except self.CompilerError, err: + raise TALError('%s in expression %s' % (err.args[0], `expr`), + self.position) + + def pushProgram(self): + self.stack.append(self.program) + self.program = [] + + def popProgram(self): + program = self.program + self.program = self.stack.pop() + return self.optimize(program) + + def pushSlots(self): + self.slotStack.append(self.slots) + self.slots = {} + + def popSlots(self): + slots = self.slots + self.slots = self.slotStack.pop() + return slots + + def emit(self, *instruction): + self.program.append(instruction) + + def emitStartTag(self, name, attrlist, isend=0): + if isend: + opcode = "startEndTag" + else: + opcode = "startTag" + self.emit(opcode, name, attrlist) + + def emitEndTag(self, name): + if self.xml and self.program and self.program[-1][0] == "startTag": + # Minimize empty element + self.program[-1] = ("startEndTag",) + self.program[-1][1:] + else: + self.emit("endTag", name) + + def emitOptTag(self, name, optTag, isend): + program = self.popProgram() #block + start = self.popProgram() #start tag + if (isend or not program) and self.xml: + # Minimize empty element + start[-1] = ("startEndTag",) + start[-1][1:] + isend = 1 + cexpr = optTag[0] + if cexpr: + cexpr = self.compileExpression(optTag[0]) + self.emit("optTag", name, cexpr, optTag[1], isend, start, program) + + def emitRawText(self, text): + self.emit("rawtext", text) + + def emitText(self, text): + self.emitRawText(cgi.escape(text)) + + def emitDefines(self, defines): + for part in taldefs.splitParts(defines): + m = re.match( + r"(?s)\s*(?:(global|local)\s+)?(%s)\s+(.*)\Z" % NAME_RE, part) + if not m: + raise TALError("invalid define syntax: " + `part`, + self.position) + scope, name, expr = m.group(1, 2, 3) + scope = scope or "local" + cexpr = self.compileExpression(expr) + if scope == "local": + self.emit("setLocal", name, cexpr) + else: + self.emit("setGlobal", name, cexpr) + + def emitOnError(self, name, onError, TALtag, isend): + block = self.popProgram() + key, expr = parseSubstitution(onError) + cexpr = self.compileExpression(expr) + if key == "text": + self.emit("insertText", cexpr, []) + else: + assert key == "structure" + self.emit("insertStructure", cexpr, {}, []) + if TALtag: + self.emitOptTag(name, (None, 1), isend) + else: + self.emitEndTag(name) + handler = self.popProgram() + self.emit("onError", block, handler) + + def emitCondition(self, expr): + cexpr = self.compileExpression(expr) + program = self.popProgram() + self.emit("condition", cexpr, program) + + def emitRepeat(self, arg): + m = re.match("(?s)\s*(%s)\s+(.*)\Z" % NAME_RE, arg) + if not m: + raise TALError("invalid repeat syntax: " + `arg`, + self.position) + name, expr = m.group(1, 2) + cexpr = self.compileExpression(expr) + program = self.popProgram() + self.emit("loop", name, cexpr, program) + + def emitSubstitution(self, arg, attrDict={}): + key, expr = parseSubstitution(arg) + cexpr = self.compileExpression(expr) + program = self.popProgram() + if key == "text": + self.emit("insertText", cexpr, program) + else: + assert key == "structure" + self.emit("insertStructure", cexpr, attrDict, program) + + def emitI18nSubstitution(self, arg, attrDict={}): + # TODO: Code duplication is BAD, we need to fix it later + key, expr = parseSubstitution(arg) + cexpr = self.compileExpression(expr) + program = self.popProgram() + if key == "text": + self.emit("insertI18nText", cexpr, program) + else: + assert key == "structure" + self.emit("insertI18nStructure", cexpr, attrDict, program) + + def emitEvaluateCode(self, lang): + program = self.popProgram() + self.emit('evaluateCode', lang, program) + + def emitI18nVariable(self, varname): + # Used for i18n:name attributes. + m = _name_rx.match(varname) + if m is None or m.group() != varname: + raise TALError("illegal i18n:name: %r" % varname, self.position) + program = self.popProgram() + self.emit('i18nVariable', varname, program, None, False) + + def emitTranslation(self, msgid, i18ndata): + program = self.popProgram() + if i18ndata is None: + self.emit('insertTranslation', msgid, program) + else: + key, expr = parseSubstitution(i18ndata) + cexpr = self.compileExpression(expr) + assert key == 'text' + self.emit('insertTranslation', msgid, program, cexpr) + + def emitDefineMacro(self, macroName): + program = self.popProgram() + macroName = macroName.strip() + if self.macros.has_key(macroName): + raise METALError("duplicate macro definition: %s" % `macroName`, + self.position) + if not re.match('%s$' % NAME_RE, macroName): + raise METALError("invalid macro name: %s" % `macroName`, + self.position) + self.macros[macroName] = program + self.inMacroDef = self.inMacroDef - 1 + self.emit("defineMacro", macroName, program) + + def emitUseMacro(self, expr): + cexpr = self.compileExpression(expr) + program = self.popProgram() + self.inMacroUse = 0 + self.emit("useMacro", expr, cexpr, self.popSlots(), program) + + def emitExtendMacro(self, defineName, useExpr): + cexpr = self.compileExpression(useExpr) + program = self.popProgram() + self.inMacroUse = 0 + self.emit("extendMacro", useExpr, cexpr, self.popSlots(), program, + defineName) + self.emitDefineMacro(defineName) + + def emitDefineSlot(self, slotName): + program = self.popProgram() + slotName = slotName.strip() + if not re.match('%s$' % NAME_RE, slotName): + raise METALError("invalid slot name: %s" % `slotName`, + self.position) + self.emit("defineSlot", slotName, program) + + def emitFillSlot(self, slotName): + program = self.popProgram() + slotName = slotName.strip() + if self.slots.has_key(slotName): + raise METALError("duplicate fill-slot name: %s" % `slotName`, + self.position) + if not re.match('%s$' % NAME_RE, slotName): + raise METALError("invalid slot name: %s" % `slotName`, + self.position) + self.slots[slotName] = program + self.inMacroUse = 1 + self.emit("fillSlot", slotName, program) + + def unEmitWhitespace(self): + collect = [] + i = len(self.program) - 1 + while i >= 0: + item = self.program[i] + if item[0] != "rawtext": + break + text = item[1] + if not re.match(r"\A\s*\Z", text): + break + collect.append(text) + i = i-1 + del self.program[i+1:] + if i >= 0 and self.program[i][0] == "rawtext": + text = self.program[i][1] + m = re.search(r"\s+\Z", text) + if m: + self.program[i] = ("rawtext", text[:m.start()]) + collect.append(m.group()) + collect.reverse() + return "".join(collect) + + def unEmitNewlineWhitespace(self): + collect = [] + i = len(self.program) + while i > 0: + i = i-1 + item = self.program[i] + if item[0] != "rawtext": + break + text = item[1] + if re.match(r"\A[ \t]*\Z", text): + collect.append(text) + continue + m = re.match(r"(?s)^(.*)(\n[ \t]*)\Z", text) + if not m: + break + text, rest = m.group(1, 2) + collect.reverse() + rest = rest + "".join(collect) + del self.program[i:] + if text: + self.emit("rawtext", text) + return rest + return None + + def replaceAttrs(self, attrlist, repldict): + # Each entry in attrlist starts like (name, value). Result is + # (name, value, action, expr, xlat, msgid) if there is a + # tal:attributes entry for that attribute. Additional attrs + # defined only by tal:attributes are added here. + # + # (name, value, action, expr, xlat, msgid) + if not repldict: + return attrlist + newlist = [] + for item in attrlist: + key = item[0] + if repldict.has_key(key): + expr, xlat, msgid = repldict[key] + item = item[:2] + ("replace", expr, xlat, msgid) + del repldict[key] + newlist.append(item) + # Add dynamic-only attributes + for key, (expr, xlat, msgid) in repldict.items(): + newlist.append((key, None, "insert", expr, xlat, msgid)) + return newlist + + def emitStartElement(self, name, attrlist, taldict, metaldict, i18ndict, + position=(None, None), isend=0): + if not taldict and not metaldict and not i18ndict: + # Handle the simple, common case + self.emitStartTag(name, attrlist, isend) + self.todoPush({}) + if isend: + self.emitEndElement(name, isend) + return + self.position = position + + # TODO: Ugly hack to work around tal:replace and i18n:translate issue. + # I (DV) need to cleanup the code later. + replaced = False + if "replace" in taldict: + if "content" in taldict: + raise TALError( + "tal:content and tal:replace are mutually exclusive", + position) + taldict["omit-tag"] = taldict.get("omit-tag", "") + taldict["content"] = taldict.pop("replace") + replaced = True + + for key, value in taldict.items(): + if key not in taldefs.KNOWN_TAL_ATTRIBUTES: + raise TALError("bad TAL attribute: " + `key`, position) + if not (value or key == 'omit-tag'): + raise TALError("missing value for TAL attribute: " + + `key`, position) + for key, value in metaldict.items(): + if key not in taldefs.KNOWN_METAL_ATTRIBUTES: + raise METALError("bad METAL attribute: " + `key`, + position) + if not value: + raise TALError("missing value for METAL attribute: " + + `key`, position) + for key, value in i18ndict.items(): + if key not in taldefs.KNOWN_I18N_ATTRIBUTES: + raise I18NError("bad i18n attribute: " + `key`, position) + if not value and key in ("attributes", "data", "id"): + raise I18NError("missing value for i18n attribute: " + + `key`, position) + + todo = {} + defineMacro = metaldict.get("define-macro") + extendMacro = metaldict.get("extend-macro") + useMacro = metaldict.get("use-macro") + defineSlot = metaldict.get("define-slot") + fillSlot = metaldict.get("fill-slot") + define = taldict.get("define") + condition = taldict.get("condition") + repeat = taldict.get("repeat") + content = taldict.get("content") + script = taldict.get("script") + attrsubst = taldict.get("attributes") + onError = taldict.get("on-error") + omitTag = taldict.get("omit-tag") + TALtag = taldict.get("tal tag") + i18nattrs = i18ndict.get("attributes") + # Preserve empty string if implicit msgids are used. We'll generate + # code with the msgid='' and calculate the right implicit msgid during + # interpretation phase. + msgid = i18ndict.get("translate") + varname = i18ndict.get('name') + i18ndata = i18ndict.get('data') + + if varname and not self.i18nLevel: + raise I18NError( + "i18n:name can only occur inside a translation unit", + position) + + if i18ndata and not msgid: + raise I18NError("i18n:data must be accompanied by i18n:translate", + position) + + if extendMacro: + if useMacro: + raise METALError( + "extend-macro cannot be used with use-macro", position) + if not defineMacro: + raise METALError( + "extend-macro must be used with define-macro", position) + + if defineMacro or extendMacro or useMacro: + if fillSlot or defineSlot: + raise METALError( + "define-slot and fill-slot cannot be used with " + "define-macro, extend-macro, or use-macro", position) + if defineMacro and useMacro: + raise METALError( + "define-macro may not be used with use-macro", position) + + useMacro = useMacro or extendMacro + + if content and msgid: + raise I18NError( + "explicit message id and tal:content can't be used together", + position) + + repeatWhitespace = None + if repeat: + # Hack to include preceding whitespace in the loop program + repeatWhitespace = self.unEmitNewlineWhitespace() + if position != (None, None): + # TODO: at some point we should insist on a non-trivial position + self.emit("setPosition", position) + if self.inMacroUse: + if fillSlot: + self.pushProgram() + # generate a source annotation at the beginning of fill-slot + if self.source_file is not None: + if position != (None, None): + self.emit("setPosition", position) + self.emit("setSourceFile", self.source_file) + todo["fillSlot"] = fillSlot + self.inMacroUse = 0 + else: + if fillSlot: + raise METALError("fill-slot must be within a use-macro", + position) + if not self.inMacroUse: + if defineMacro: + self.pushProgram() + self.emit("version", TAL_VERSION) + self.emit("mode", self.xml and "xml" or "html") + # generate a source annotation at the beginning of the macro + if self.source_file is not None: + if position != (None, None): + self.emit("setPosition", position) + self.emit("setSourceFile", self.source_file) + todo["defineMacro"] = defineMacro + self.inMacroDef = self.inMacroDef + 1 + if useMacro: + self.pushSlots() + self.pushProgram() + todo["useMacro"] = useMacro + self.inMacroUse = 1 + if defineSlot: + if not self.inMacroDef: + raise METALError( + "define-slot must be within a define-macro", + position) + self.pushProgram() + todo["defineSlot"] = defineSlot + + if defineSlot or i18ndict: + + domain = i18ndict.get("domain") or self.i18nContext.domain + source = i18ndict.get("source") or self.i18nContext.source + target = i18ndict.get("target") or self.i18nContext.target + if ( domain != DEFAULT_DOMAIN + or source is not None + or target is not None): + self.i18nContext = TranslationContext(self.i18nContext, + domain=domain, + source=source, + target=target) + self.emit("beginI18nContext", + {"domain": domain, "source": source, + "target": target}) + todo["i18ncontext"] = 1 + if taldict or i18ndict: + dict = {} + for item in attrlist: + key, value = item[:2] + dict[key] = value + self.emit("beginScope", dict) + todo["scope"] = 1 + if onError: + self.pushProgram() # handler + if TALtag: + self.pushProgram() # start + self.emitStartTag(name, list(attrlist)) # Must copy attrlist! + if TALtag: + self.pushProgram() # start + self.pushProgram() # block + todo["onError"] = onError + if define: + self.emitDefines(define) + todo["define"] = define + if condition: + self.pushProgram() + todo["condition"] = condition + if repeat: + todo["repeat"] = repeat + self.pushProgram() + if repeatWhitespace: + self.emitText(repeatWhitespace) + if content: + if varname: + todo['i18nvar'] = varname + todo["content"] = content + self.pushProgram() + else: + todo["content"] = content + # i18n:name w/o tal:replace uses the content as the interpolation + # dictionary values + elif varname: + todo['i18nvar'] = varname + self.pushProgram() + if msgid is not None: + self.i18nLevel += 1 + todo['msgid'] = msgid + if i18ndata: + todo['i18ndata'] = i18ndata + optTag = omitTag is not None or TALtag + if optTag: + todo["optional tag"] = omitTag, TALtag + self.pushProgram() + if attrsubst or i18nattrs: + if attrsubst: + repldict = taldefs.parseAttributeReplacements(attrsubst, + self.xml) + else: + repldict = {} + if i18nattrs: + i18nattrs = _parseI18nAttributes(i18nattrs, self.position, + self.xml) + else: + i18nattrs = {} + # Convert repldict's name-->expr mapping to a + # name-->(compiled_expr, translate) mapping + for key, value in repldict.items(): + if i18nattrs.get(key, None): + raise I18NError( + "attribute [%s] cannot both be part of tal:attributes" + " and have a msgid in i18n:attributes" % key, + position) + ce = self.compileExpression(value) + repldict[key] = ce, key in i18nattrs, i18nattrs.get(key) + for key in i18nattrs: + if key not in repldict: + repldict[key] = None, 1, i18nattrs.get(key) + else: + repldict = {} + if replaced: + todo["repldict"] = repldict + repldict = {} + if script: + todo["script"] = script + self.emitStartTag(name, self.replaceAttrs(attrlist, repldict), isend) + if optTag: + self.pushProgram() + if content and not varname: + self.pushProgram() + if not content and msgid is not None: + self.pushProgram() + if content and varname: + self.pushProgram() + if script: + self.pushProgram() + if todo and position != (None, None): + todo["position"] = position + self.todoPush(todo) + if isend: + self.emitEndElement(name, isend, position=position) + + def emitEndElement(self, name, isend=0, implied=0, position=(None, None)): + todo = self.todoPop() + if not todo: + # Shortcut + if not isend: + self.emitEndTag(name) + return + + self.position = todo.get("position", (None, None)) + defineMacro = todo.get("defineMacro") + useMacro = todo.get("useMacro") + defineSlot = todo.get("defineSlot") + fillSlot = todo.get("fillSlot") + repeat = todo.get("repeat") + content = todo.get("content") + script = todo.get("script") + condition = todo.get("condition") + onError = todo.get("onError") + repldict = todo.get("repldict", {}) + scope = todo.get("scope") + optTag = todo.get("optional tag") + msgid = todo.get('msgid') + i18ncontext = todo.get("i18ncontext") + varname = todo.get('i18nvar') + i18ndata = todo.get('i18ndata') + + if implied > 0: + if defineMacro or useMacro or defineSlot or fillSlot: + exc = METALError + what = "METAL" + else: + exc = TALError + what = "TAL" + raise exc("%s attributes on <%s> require explicit " % + (what, name, name), self.position) + + if script: + self.emitEvaluateCode(script) + # If there's no tal:content or tal:replace in the tag with the + # i18n:name, tal:replace is the default. + if content: + if msgid is not None: + self.emitI18nSubstitution(content, repldict) + else: + self.emitSubstitution(content, repldict) + # If we're looking at an implicit msgid, emit the insertTranslation + # opcode now, so that the end tag doesn't become part of the implicit + # msgid. If we're looking at an explicit msgid, it's better to emit + # the opcode after the i18nVariable opcode so we can better handle + # tags with both of them in them (and in the latter case, the contents + # would be thrown away for msgid purposes). + # + # Still, we should emit insertTranslation opcode before i18nVariable + # in case tal:content, i18n:translate and i18n:name in the same tag + if not content and msgid is not None: + self.emitTranslation(msgid, i18ndata) + self.i18nLevel -= 1 + if optTag: + self.emitOptTag(name, optTag, isend) + elif not isend: + # If we're processing the end tag for a tag that contained + # i18n:name, we need to make sure that optimize() won't collect + # immediately following end tags into the same rawtextOffset, so + # put a spacer here that the optimizer will recognize. + if varname: + self.emit('noop') + self.emitEndTag(name) + if varname: + self.emitI18nVariable(varname) + if repeat: + self.emitRepeat(repeat) + if condition: + self.emitCondition(condition) + if onError: + self.emitOnError(name, onError, optTag and optTag[1], isend) + if scope: + self.emit("endScope") + if i18ncontext: + self.emit("endI18nContext") + assert self.i18nContext.parent is not None + self.i18nContext = self.i18nContext.parent + if defineSlot: + self.emitDefineSlot(defineSlot) + if fillSlot: + self.emitFillSlot(fillSlot) + if useMacro or defineMacro: + if useMacro and defineMacro: + self.emitExtendMacro(defineMacro, useMacro) + elif useMacro: + self.emitUseMacro(useMacro) + elif defineMacro: + self.emitDefineMacro(defineMacro) + if useMacro or defineSlot: + # generate a source annotation after define-slot or use-macro + # because the source file might have changed + if self.source_file is not None: + if position != (None, None): + self.emit("setPosition", position) + self.emit("setSourceFile", self.source_file) + + +def _parseI18nAttributes(i18nattrs, position, xml): + d = {} + # Filter out empty items, eg: + # i18n:attributes="value msgid; name msgid2;" + # would result in 3 items where the last one is empty + attrs = [spec for spec in i18nattrs.split(";") if spec] + for spec in attrs: + parts = spec.split() + if len(parts) == 2: + attr, msgid = parts + elif len(parts) == 1: + attr = parts[0] + msgid = None + else: + raise TALError("illegal i18n:attributes specification: %r" % spec, + position) + if not xml: + attr = attr.lower() + if attr in d: + raise TALError( + "attribute may only be specified once in i18n:attributes: %r" + % attr, + position) + d[attr] = msgid + return d + +def test(): + t = TALGenerator() + t.pushProgram() + t.emit("bar") + p = t.popProgram() + t.emit("foo", p) + +if __name__ == "__main__": + test() diff -Nru zope3-3.4.0/src/zope/tal/talgettext.py zope3-3.5~bzr18/src/zope/tal/talgettext.py --- zope3-3.4.0/src/zope/tal/talgettext.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/talgettext.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,326 @@ +#!/usr/bin/env python +############################################################################## +# +# Copyright (c) 2002 Zope Corporation 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. +# +############################################################################## +"""Program to extract internationalization markup from Page Templates. + +Once you have marked up a Page Template file with i18n: namespace tags, use +this program to extract GNU gettext .po file entries. + +Usage: talgettext.py [options] files +Options: + -h / --help + Print this message and exit. + -o / --output + Output the translation .po file to . + -u / --update + Update the existing translation with any new translation strings + found. + +$Id: talgettext.py 105021 2009-10-12 06:29:49Z fretin $ +""" +import sys +import time +import getopt +import traceback + +from zope.interface import implements +from zope.tal.htmltalparser import HTMLTALParser +from zope.tal.talinterpreter import TALInterpreter, normalize +from zope.tal.dummyengine import DummyEngine +from zope.tal.interfaces import ITALExpressionEngine +from zope.tal.taldefs import TALExpressionError +from zope.i18nmessageid import Message + +pot_header = '''\ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\\n" +"POT-Creation-Date: %(time)s\\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n" +"Last-Translator: FULL NAME \\n" +"Language-Team: LANGUAGE \\n" +"MIME-Version: 1.0\\n" +"Content-Type: text/plain; charset=CHARSET\\n" +"Content-Transfer-Encoding: ENCODING\\n" +"Generated-By: talgettext.py %(version)s\\n" +''' + +NLSTR = '"\n"' + +def usage(code, msg=''): + # Python 2.1 required + print >> sys.stderr, __doc__ + if msg: + print >> sys.stderr, msg + sys.exit(code) + + +class POTALInterpreter(TALInterpreter): + def translate(self, msgid, default=None, i18ndict=None, obj=None): + if default is None: + default = getattr(msgid, 'default', unicode(msgid)) + # If no i18n dict exists yet, create one. + if i18ndict is None: + i18ndict = {} + if obj: + i18ndict.update(obj) + # Mmmh, it seems that sometimes the msgid is None; is that really + # possible? + if msgid is None: + return None + # TODO: We need to pass in one of context or target_language + return self.engine.translate(msgid, self.i18nContext.domain, i18ndict, + default=default, position=self.position) + + +class POEngine(DummyEngine): + implements(ITALExpressionEngine) + + def __init__(self, macros=None): + self.catalog = {} + DummyEngine.__init__(self, macros) + + def evaluate(*args): + # If the result of evaluate ever gets into a message ID, we want + # to notice the fact in the .pot file. + return '${DYNAMIC_CONTENT}' + + def evaluatePathOrVar(*args): + # Actually this method is never called. + return 'XXX' + + def evaluateSequence(self, expr): + return (0,) # dummy + + def evaluateBoolean(self, expr): + return True # dummy + + def translate(self, msgid, domain=None, mapping=None, default=None, + # Position is not part of the ITALExpressionEngine + # interface + position=None): + + if default is not None: + default = normalize(default) + if msgid == default: + default = None + msgid = Message(msgid, default=default) + + if domain not in self.catalog: + self.catalog[domain] = {} + domain = self.catalog[domain] + + if msgid not in domain: + domain[msgid] = [] + else: + msgids = domain.keys() + idx = msgids.index(msgid) + existing_msgid = msgids[idx] + if msgid.default != existing_msgid.default: + references = '\n'.join([location[0]+':'+str(location[1]) for location in domain[msgid]]) + print >> sys.stderr, "Warning: msgid '%s' in %s already exists " \ + "with a different default (bad: %s, should be: %s)\n" \ + "The references for the existent value are:\n%s\n" % \ + (msgid, self.file+':'+str(position), msgid.default, existing_msgid.default, references) + domain[msgid].append((self.file, position)) + return 'x' + + +class UpdatePOEngine(POEngine): + """A slightly-less braindead POEngine which supports loading an existing + .po file first.""" + + def __init__ (self, macros=None, filename=None): + POEngine.__init__(self, macros) + + self._filename = filename + self._loadFile() + self.base = self.catalog + self.catalog = {} + + def __add(self, id, s, fuzzy): + "Add a non-fuzzy translation to the dictionary." + if not fuzzy and str: + # check for multi-line values and munge them appropriately + if '\n' in s: + lines = s.rstrip().split('\n') + s = NLSTR.join(lines) + self.catalog[id] = s + + def _loadFile(self): + # shamelessly cribbed from Python's Tools/i18n/msgfmt.py + # 25-Mar-2003 Nathan R. Yergler (nathan@zope.org) + # 14-Apr-2003 Hacked by Barry Warsaw (barry@zope.com) + + ID = 1 + STR = 2 + + try: + lines = open(self._filename).readlines() + except IOError, msg: + print >> sys.stderr, msg + sys.exit(1) + + section = None + fuzzy = False + + # Parse the catalog + lno = 0 + for l in lines: + lno += True + # If we get a comment line after a msgstr, this is a new entry + if l[0] == '#' and section == STR: + self.__add(msgid, msgstr, fuzzy) + section = None + fuzzy = False + # Record a fuzzy mark + if l[:2] == '#,' and l.find('fuzzy'): + fuzzy = True + # Skip comments + if l[0] == '#': + continue + # Now we are in a msgid section, output previous section + if l.startswith('msgid'): + if section == STR: + self.__add(msgid, msgstr, fuzzy) + section = ID + l = l[5:] + msgid = msgstr = '' + # Now we are in a msgstr section + elif l.startswith('msgstr'): + section = STR + l = l[6:] + # Skip empty lines + if not l.strip(): + continue + # TODO: Does this always follow Python escape semantics? + l = eval(l) + if section == ID: + msgid += l + elif section == STR: + msgstr += '%s\n' % l + else: + print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \ + 'before:' + print >> sys.stderr, l + sys.exit(1) + # Add last entry + if section == STR: + self.__add(msgid, msgstr, fuzzy) + + def evaluate(self, expression): + try: + return POEngine.evaluate(self, expression) + except TALExpressionError: + pass + + def evaluatePathOrVar(self, expr): + return 'who cares' + + def translate(self, msgid, domain=None, mapping=None, default=None, + position=None): + if msgid not in self.base: + POEngine.translate(self, msgid, domain, mapping, default, position) + return 'x' + + +def main(): + try: + opts, args = getopt.getopt( + sys.argv[1:], + 'ho:u:', + ['help', 'output=', 'update=']) + except getopt.error, msg: + usage(1, msg) + + outfile = None + engine = None + update_mode = False + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-o', '--output'): + outfile = arg + elif opt in ('-u', '--update'): + update_mode = True + if outfile is None: + outfile = arg + engine = UpdatePOEngine(filename=arg) + + if not args: + print 'nothing to do' + return + + # We don't care about the rendered output of the .pt file + class Devnull(object): + def write(self, s): + pass + + # check if we've already instantiated an engine; + # if not, use the stupidest one available + if not engine: + engine = POEngine() + + # process each file specified + for filename in args: + try: + engine.file = filename + p = HTMLTALParser() + p.parseFile(filename) + program, macros = p.getCode() + POTALInterpreter(program, macros, engine, stream=Devnull(), + metal=False)() + except: # Hee hee, I love bare excepts! + print 'There was an error processing', filename + traceback.print_exc() + + # Now output the keys in the engine. Write them to a file if --output or + # --update was specified; otherwise use standard out. + if (outfile is None): + outfile = sys.stdout + else: + outfile = file(outfile, update_mode and "a" or "w") + + catalog = {} + for domain in engine.catalog.keys(): + catalog.update(engine.catalog[domain]) + + messages = catalog.copy() + try: + messages.update(engine.base) + except AttributeError: + pass + if '' not in messages: + print >> outfile, pot_header % {'time': time.ctime(), + 'version': __version__} + + msgids = catalog.keys() + # TODO: You should not sort by msgid, but by filename and position. (SR) + msgids.sort() + for msgid in msgids: + positions = engine.catalog[msgid] + for filename, position in positions: + outfile.write('#: %s:%s\n' % (filename, position[0])) + + outfile.write('msgid "%s"\n' % msgid) + outfile.write('msgstr ""\n') + outfile.write('\n') + + +if __name__ == '__main__': + main() diff -Nru zope3-3.4.0/src/zope/tal/talinterpreter.py zope3-3.5~bzr18/src/zope/tal/talinterpreter.py --- zope3-3.4.0/src/zope/tal/talinterpreter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/talinterpreter.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1021 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Interpreter for a pre-compiled TAL program. + +$Id: talinterpreter.py 87220 2008-06-07 03:08:35Z fdrake $ +""" +import cgi +import operator +import sys +import warnings + +# Do not use cStringIO here! It's not unicode aware. :( +from StringIO import StringIO + +from zope.i18nmessageid import Message +from zope.tal.taldefs import quote, TAL_VERSION, METALError +from zope.tal.taldefs import isCurrentVersion +from zope.tal.taldefs import getProgramVersion, getProgramMode +from zope.tal.talgenerator import TALGenerator +from zope.tal.translationcontext import TranslationContext + + +# Avoid constructing this tuple over and over +I18nMessageTypes = (Message,) + +TypesToTranslate = I18nMessageTypes + (str, unicode) + +BOOLEAN_HTML_ATTRS = frozenset([ + # List of Boolean attributes in HTML that should be rendered in + # minimized form (e.g. rather than ) + # From http://www.w3.org/TR/xhtml1/#guidelines (C.10) + # TODO: The problem with this is that this is not valid XML and + # can't be parsed back! + "compact", "nowrap", "ismap", "declare", "noshade", "checked", + "disabled", "readonly", "multiple", "selected", "noresize", + "defer" +]) + +_nulljoin = ''.join +_spacejoin = ' '.join + +def normalize(text): + # Now we need to normalize the whitespace in implicit message ids and + # implicit $name substitution values by stripping leading and trailing + # whitespace, and folding all internal whitespace to a single space. + return _spacejoin(text.split()) + + +class AltTALGenerator(TALGenerator): + + def __init__(self, repldict, expressionCompiler=None, xml=0): + self.repldict = repldict + self.enabled = 1 + TALGenerator.__init__(self, expressionCompiler, xml) + + def enable(self, enabled): + self.enabled = enabled + + def emit(self, *args): + if self.enabled: + TALGenerator.emit(self, *args) + + def emitStartElement(self, name, attrlist, taldict, metaldict, i18ndict, + position=(None, None), isend=0): + metaldict = {} + taldict = {} + i18ndict = {} + if self.enabled and self.repldict: + taldict["attributes"] = "x x" + TALGenerator.emitStartElement(self, name, attrlist, + taldict, metaldict, i18ndict, + position, isend) + + def replaceAttrs(self, attrlist, repldict): + if self.enabled and self.repldict: + repldict = self.repldict + self.repldict = None + return TALGenerator.replaceAttrs(self, attrlist, repldict) + + + +class MacroStackItem(list): + # This is a `list` subclass for backward compability. + """Stack entry for the TALInterpreter.macroStack. + + This offers convenience attributes for more readable access. + + """ + __slots__ = () + + # These would be nicer using @syntax, but that would require + # Python 2.4.x; this will do for now. + + macroName = property(lambda self: self[0]) + slots = property(lambda self: self[1]) + definingName = property(lambda self: self[2]) + extending = property(lambda self: self[3]) + entering = property(lambda self: self[4], + lambda self, value: operator.setitem(self, 4, value)) + i18nContext = property(lambda self: self[5]) + + +class TALInterpreter(object): + """TAL interpreter. + + Some notes on source annotations. They are HTML/XML comments added to the + output whenever sourceFile is changed by a setSourceFile bytecode. Source + annotations are disabled by default, but you can turn them on by passing a + sourceAnnotations argument to the constructor. You can change the format + of the annotations by overriding formatSourceAnnotation in a subclass. + + The output of the annotation is delayed until some actual text is output + for two reasons: + + 1. setPosition bytecode follows setSourceFile, and we need position + information to output the line number. + 2. Comments are not allowed in XML documents before the + declaration. + + For performance reasons (TODO: premature optimization?) instead of checking + the value of _pending_source_annotation on every write to the output + stream, the _stream_write attribute is changed to point to + _annotated_stream_write method whenever _pending_source_annotation is + set to True, and to _stream.write when it is False. The following + invariant always holds: + + if self._pending_source_annotation: + assert self._stream_write is self._annotated_stream_write + else: + assert self._stream_write is self.stream.write + + """ + + def __init__(self, program, macros, engine, stream=None, + debug=0, wrap=60, metal=1, tal=1, showtal=-1, + strictinsert=1, stackLimit=100, i18nInterpolate=1, + sourceAnnotations=0): + """Create a TAL interpreter. + + Optional arguments: + + stream -- output stream (defaults to sys.stdout). + + debug -- enable debugging output to sys.stderr (off by default). + + wrap -- try to wrap attributes on opening tags to this number of + column (default: 60). + + metal -- enable METAL macro processing (on by default). + + tal -- enable TAL processing (on by default). + + showtal -- do not strip away TAL directives. A special value of + -1 (which is the default setting) enables showtal when TAL + processing is disabled, and disables showtal when TAL processing is + enabled. Note that you must use 0, 1, or -1; true boolean values + are not supported (TODO: why?). + + strictinsert -- enable TAL processing and stricter HTML/XML + checking on text produced by structure inserts (on by default). + Note that Zope turns this value off by default. + + stackLimit -- set macro nesting limit (default: 100). + + i18nInterpolate -- enable i18n translations (default: on). + + sourceAnnotations -- enable source annotations with HTML comments + (default: off). + + """ + self.program = program + self.macros = macros + self.engine = engine # Execution engine (aka context) + self.Default = engine.getDefault() + self._pending_source_annotation = False + self._currentTag = "" + self._stream_stack = [stream or sys.stdout] + self.popStream() + self.debug = debug + self.wrap = wrap + self.metal = metal + self.tal = tal + if tal: + self.dispatch = self.bytecode_handlers_tal + else: + self.dispatch = self.bytecode_handlers + assert showtal in (-1, 0, 1) + if showtal == -1: + showtal = (not tal) + self.showtal = showtal + self.strictinsert = strictinsert + self.stackLimit = stackLimit + self.html = 0 + self.endsep = "/>" + self.endlen = len(self.endsep) + # macroStack entries are MacroStackItem instances; + # the entries are mutated while on the stack + self.macroStack = [] + # `inUseDirective` is set iff we're handling either a + # metal:use-macro or a metal:extend-macro + self.inUseDirective = False + self.position = None, None # (lineno, offset) + self.col = 0 + self.level = 0 + self.scopeLevel = 0 + self.sourceFile = None + self.i18nStack = [] + self.i18nInterpolate = i18nInterpolate + self.i18nContext = TranslationContext() + self.sourceAnnotations = sourceAnnotations + + def StringIO(self): + # Third-party products wishing to provide a full Unicode-aware + # StringIO can do so by monkey-patching this method. + return FasterStringIO() + + def saveState(self): + return (self.position, self.col, self.stream, self._stream_stack, + self.scopeLevel, self.level, self.i18nContext) + + def restoreState(self, state): + (self.position, self.col, self.stream, + self._stream_stack, scopeLevel, level, i18n) = state + if self._pending_source_annotation: + self._stream_write = self._annotated_stream_write + else: + self._stream_write = self.stream.write + assert self.level == level + while self.scopeLevel > scopeLevel: + self.engine.endScope() + self.scopeLevel = self.scopeLevel - 1 + self.engine.setPosition(self.position) + self.i18nContext = i18n + + def restoreOutputState(self, state): + (dummy, self.col, self.stream, + self._stream_stack, scopeLevel, level, i18n) = state + if self._pending_source_annotation: + self._stream_write = self._annotated_stream_write + else: + self._stream_write = self.stream.write + assert self.level == level + assert self.scopeLevel == scopeLevel + + def pushMacro(self, macroName, slots, definingName, extending): + if len(self.macroStack) >= self.stackLimit: + raise METALError("macro nesting limit (%d) exceeded " + "by %s" % (self.stackLimit, `macroName`)) + self.macroStack.append( + MacroStackItem((macroName, slots, definingName, extending, + True, self.i18nContext))) + + def popMacro(self): + return self.macroStack.pop() + + def __call__(self): + assert self.level == 0 + assert self.scopeLevel == 0 + assert self.i18nContext.parent is None + self.interpret(self.program) + assert self.level == 0 + assert self.scopeLevel == 0 + assert self.i18nContext.parent is None + + def pushStream(self, newstream): + self._stream_stack.append(self.stream) + self.stream = newstream + if self._pending_source_annotation: + self._stream_write = self._annotated_stream_write + else: + self._stream_write = self.stream.write + + def popStream(self): + self.stream = self._stream_stack.pop() + if self._pending_source_annotation: + self._stream_write = self._annotated_stream_write + else: + self._stream_write = self.stream.write + + def _annotated_stream_write(self, s): + idx = s.find('= 0 or s.isspace(): + # Do not preprend comments in front of the declaration. + end_of_doctype = s.find('?>', idx) + if end_of_doctype > idx: + self.stream.write(s[:end_of_doctype+2]) + s = s[end_of_doctype+2:] + # continue + else: + self.stream.write(s) + return + self._pending_source_annotation = False + self._stream_write = self.stream.write + self._stream_write(self.formatSourceAnnotation()) + self._stream_write(s) + + def formatSourceAnnotation(self): + lineno = self.position[0] + if lineno is None: + location = self.sourceFile + else: + location = '%s (line %s)' % (self.sourceFile, lineno) + sep = '=' * 78 + return '' % (sep, location, sep) + + def stream_write(self, s, + len=len): + self._stream_write(s) + i = s.rfind('\n') + if i < 0: + self.col = self.col + len(s) + else: + self.col = len(s) - (i + 1) + + bytecode_handlers = {} + + def interpret(self, program): + oldlevel = self.level + self.level = oldlevel + 1 + handlers = self.dispatch + try: + if self.debug: + for (opcode, args) in program: + s = "%sdo_%s(%s)\n" % (" "*self.level, opcode, + repr(args)) + if len(s) > 80: + s = s[:76] + "...\n" + sys.stderr.write(s) + handlers[opcode](self, args) + else: + for (opcode, args) in program: + handlers[opcode](self, args) + finally: + self.level = oldlevel + + def do_version(self, version): + assert version == TAL_VERSION + bytecode_handlers["version"] = do_version + + def do_mode(self, mode): + assert mode in ("html", "xml") + self.html = (mode == "html") + if self.html: + self.endsep = " />" + else: + self.endsep = "/>" + self.endlen = len(self.endsep) + bytecode_handlers["mode"] = do_mode + + def do_setSourceFile(self, source_file): + self.sourceFile = source_file + self.engine.setSourceFile(source_file) + if self.sourceAnnotations: + self._pending_source_annotation = True + self._stream_write = self._annotated_stream_write + + bytecode_handlers["setSourceFile"] = do_setSourceFile + + def do_setPosition(self, position): + self.position = position + self.engine.setPosition(position) + bytecode_handlers["setPosition"] = do_setPosition + + def do_startEndTag(self, stuff): + self.do_startTag(stuff, self.endsep, self.endlen) + bytecode_handlers["startEndTag"] = do_startEndTag + + def do_startTag(self, (name, attrList), + end=">", endlen=1, _len=len): + # The bytecode generator does not cause calls to this method + # for start tags with no attributes; those are optimized down + # to rawtext events. Hence, there is no special "fast path" + # for that case. + self._currentTag = name + L = ["<", name] + append = L.append + col = self.col + _len(name) + 1 + wrap = self.wrap + align = col + 1 + if align >= wrap/2: + align = 4 # Avoid a narrow column far to the right + attrAction = self.dispatch[""] + try: + for item in attrList: + if _len(item) == 2: + rendered = item[1:] + else: + # item[2] is the 'action' field: + if item[2] in ('metal', 'tal', 'xmlns', 'i18n'): + if not self.showtal: + continue + rendered = self.attrAction(item) + else: + rendered = attrAction(self, item) + if not rendered: + continue + for s in rendered: + slen = _len(s) + if (wrap and + col >= align and + col + 1 + slen > wrap): + append("\n") + append(" "*align) + col = align + slen + else: + append(" ") + col = col + 1 + slen + append(s) + append(end) + col = col + endlen + finally: + self._stream_write(_nulljoin(L)) + self.col = col + bytecode_handlers["startTag"] = do_startTag + + def attrAction(self, item): + name, value, action = item[:3] + if action == 'insert': + return () + macs = self.macroStack + if action == 'metal' and self.metal and macs: + # Drop all METAL attributes at a use-depth beyond the first + # use-macro and its extensions + if len(macs) > 1: + for macro in macs[1:]: + if not macro.extending: + return () + if not macs[-1].entering: + return () + macs[-1].entering = False + # Convert or drop depth-one METAL attributes. + i = name.rfind(":") + 1 + prefix, suffix = name[:i], name[i:] + if suffix == "define-macro": + # Convert define-macro as we enter depth one. + useName = macs[0].macroName + defName = macs[0].definingName + res = [] + if defName: + res.append('%sdefine-macro=%s' % (prefix, quote(defName))) + if useName: + res.append('%suse-macro=%s' % (prefix, quote(useName))) + return res + elif suffix == "define-slot": + name = prefix + "fill-slot" + elif suffix == "fill-slot": + pass + else: + return () + + if value is None: + value = name + else: + value = "%s=%s" % (name, quote(value)) + return [value] + + def attrAction_tal(self, item): + name, value, action = item[:3] + ok = 1 + expr, xlat, msgid = item[3:] + if self.html and name.lower() in BOOLEAN_HTML_ATTRS: + evalue = self.engine.evaluateBoolean(item[3]) + if evalue is self.Default: + if action == 'insert': # Cancelled insert + ok = 0 + elif evalue: + value = None + else: + ok = 0 + elif expr is not None: + evalue = self.engine.evaluateText(item[3]) + if evalue is self.Default: + if action == 'insert': # Cancelled insert + ok = 0 + else: + if evalue is None: + ok = 0 + value = evalue + + if ok: + if xlat: + translated = self.translate(msgid or value, value) + if translated is not None: + value = translated + elif isinstance(value, I18nMessageTypes): + translated = self.translate(value) + if translated is not None: + value = translated + if value is None: + value = name + return ["%s=%s" % (name, quote(value))] + else: + return () + bytecode_handlers[""] = attrAction + + def no_tag(self, start, program): + state = self.saveState() + self.stream = stream = self.StringIO() + self._stream_write = stream.write + self.interpret(start) + self.restoreOutputState(state) + self.interpret(program) + + def do_optTag(self, (name, cexpr, tag_ns, isend, start, program), + omit=0): + if tag_ns and not self.showtal: + return self.no_tag(start, program) + + self.interpret(start) + if not isend: + self.interpret(program) + s = '' % name + self._stream_write(s) + self.col = self.col + len(s) + + def do_optTag_tal(self, stuff): + cexpr = stuff[1] + if cexpr is not None and (cexpr == '' or + self.engine.evaluateBoolean(cexpr)): + self.no_tag(stuff[-2], stuff[-1]) + else: + self.do_optTag(stuff) + bytecode_handlers["optTag"] = do_optTag + + def do_rawtextBeginScope(self, (s, col, position, closeprev, dict)): + self._stream_write(s) + self.col = col + self.do_setPosition(position) + if closeprev: + engine = self.engine + engine.endScope() + engine.beginScope() + else: + self.engine.beginScope() + self.scopeLevel = self.scopeLevel + 1 + + def do_rawtextBeginScope_tal(self, (s, col, position, closeprev, dict)): + self._stream_write(s) + self.col = col + engine = self.engine + self.position = position + engine.setPosition(position) + if closeprev: + engine.endScope() + engine.beginScope() + else: + engine.beginScope() + self.scopeLevel = self.scopeLevel + 1 + engine.setLocal("attrs", dict) + bytecode_handlers["rawtextBeginScope"] = do_rawtextBeginScope + + def do_beginScope(self, dict): + self.engine.beginScope() + self.scopeLevel = self.scopeLevel + 1 + + def do_beginScope_tal(self, dict): + engine = self.engine + engine.beginScope() + engine.setLocal("attrs", dict) + self.scopeLevel = self.scopeLevel + 1 + bytecode_handlers["beginScope"] = do_beginScope + + def do_endScope(self, notused=None): + self.engine.endScope() + self.scopeLevel = self.scopeLevel - 1 + bytecode_handlers["endScope"] = do_endScope + + def do_setLocal(self, notused): + pass + + def do_setLocal_tal(self, (name, expr)): + self.engine.setLocal(name, self.engine.evaluateValue(expr)) + bytecode_handlers["setLocal"] = do_setLocal + + def do_setGlobal_tal(self, (name, expr)): + self.engine.setGlobal(name, self.engine.evaluateValue(expr)) + bytecode_handlers["setGlobal"] = do_setLocal + + def do_beginI18nContext(self, settings): + get = settings.get + self.i18nContext = TranslationContext(self.i18nContext, + domain=get("domain"), + source=get("source"), + target=get("target")) + bytecode_handlers["beginI18nContext"] = do_beginI18nContext + + def do_endI18nContext(self, notused=None): + self.i18nContext = self.i18nContext.parent + assert self.i18nContext is not None + bytecode_handlers["endI18nContext"] = do_endI18nContext + + def do_insertText(self, stuff): + self.interpret(stuff[1]) + bytecode_handlers["insertText"] = do_insertText + bytecode_handlers["insertI18nText"] = do_insertText + + def _writeText(self, text): + # '&' must be done first! + s = text.replace( + "&", "&").replace("<", "<").replace(">", ">") + self._stream_write(s) + i = s.rfind('\n') + if i < 0: + self.col += len(s) + else: + self.col = len(s) - (i + 1) + + def do_insertText_tal(self, stuff): + text = self.engine.evaluateText(stuff[0]) + if text is None: + return + if text is self.Default: + self.interpret(stuff[1]) + return + if isinstance(text, I18nMessageTypes): + # Translate this now. + text = self.translate(text) + self._writeText(text) + + def do_insertI18nText_tal(self, stuff): + # TODO: Code duplication is BAD, we need to fix it later + text = self.engine.evaluateText(stuff[0]) + if text is not None: + if text is self.Default: + self.interpret(stuff[1]) + else: + if isinstance(text, TypesToTranslate): + text = self.translate(text) + self._writeText(text) + + def do_i18nVariable(self, stuff): + varname, program, expression, structure = stuff + if expression is None: + # The value is implicitly the contents of this tag, so we have to + # evaluate the mini-program to get the value of the variable. + state = self.saveState() + try: + tmpstream = self.StringIO() + self.pushStream(tmpstream) + try: + self.interpret(program) + finally: + self.popStream() + if self.html and self._currentTag == "pre": + value = tmpstream.getvalue() + else: + value = normalize(tmpstream.getvalue()) + finally: + self.restoreState(state) + else: + # TODO: Seems like this branch not used anymore, we + # need to remove it + + # Evaluate the value to be associated with the variable in the + # i18n interpolation dictionary. + if structure: + value = self.engine.evaluateStructure(expression) + else: + value = self.engine.evaluate(expression) + + # evaluate() does not do any I18n, so we do it here. + if isinstance(value, I18nMessageTypes): + # Translate this now. + value = self.translate(value) + + if not structure: + value = cgi.escape(unicode(value)) + + # Either the i18n:name tag is nested inside an i18n:translate in which + # case the last item on the stack has the i18n dictionary and string + # representation, or the i18n:name and i18n:translate attributes are + # in the same tag, in which case the i18nStack will be empty. In that + # case we can just output the ${name} to the stream + i18ndict, srepr = self.i18nStack[-1] + i18ndict[varname] = value + placeholder = '${%s}' % varname + srepr.append(placeholder) + self._stream_write(placeholder) + bytecode_handlers['i18nVariable'] = do_i18nVariable + + def do_insertTranslation(self, stuff): + i18ndict = {} + srepr = [] + obj = None + self.i18nStack.append((i18ndict, srepr)) + msgid = stuff[0] + # We need to evaluate the content of the tag because that will give us + # several useful pieces of information. First, the contents will + # include an implicit message id, if no explicit one was given. + # Second, it will evaluate any i18nVariable definitions in the body of + # the translation (necessary for $varname substitutions). + # + # Use a temporary stream to capture the interpretation of the + # subnodes, which should /not/ go to the output stream. + currentTag = self._currentTag + tmpstream = self.StringIO() + self.pushStream(tmpstream) + try: + self.interpret(stuff[1]) + finally: + self.popStream() + # We only care about the evaluated contents if we need an implicit + # message id. All other useful information will be in the i18ndict on + # the top of the i18nStack. + default = tmpstream.getvalue() + if not msgid: + if self.html and currentTag == "pre": + msgid = default + else: + msgid = normalize(default) + self.i18nStack.pop() + # See if there is was an i18n:data for msgid + if len(stuff) > 2: + obj = self.engine.evaluate(stuff[2]) + xlated_msgid = self.translate(msgid, default, i18ndict, obj) + # TODO: I can't decide whether we want to cgi escape the translated + # string or not. OTOH not doing this could introduce a cross-site + # scripting vector by allowing translators to sneak JavaScript into + # translations. OTOH, for implicit interpolation values, we don't + # want to escape stuff like ${name} <= "Timmy". + assert xlated_msgid is not None + self._stream_write(xlated_msgid) + bytecode_handlers['insertTranslation'] = do_insertTranslation + + def do_insertStructure(self, stuff): + self.interpret(stuff[2]) + bytecode_handlers["insertStructure"] = do_insertStructure + bytecode_handlers["insertI18nStructure"] = do_insertStructure + + def do_insertStructure_tal(self, (expr, repldict, block)): + structure = self.engine.evaluateStructure(expr) + if structure is None: + return + if structure is self.Default: + self.interpret(block) + return + if isinstance(structure, I18nMessageTypes): + text = self.translate(structure) + else: + text = unicode(structure) + if not (repldict or self.strictinsert): + # Take a shortcut, no error checking + self.stream_write(text) + return + if self.html: + self.insertHTMLStructure(text, repldict) + else: + self.insertXMLStructure(text, repldict) + + def do_insertI18nStructure_tal(self, (expr, repldict, block)): + # TODO: Code duplication is BAD, we need to fix it later + structure = self.engine.evaluateStructure(expr) + if structure is not None: + if structure is self.Default: + self.interpret(block) + else: + if not isinstance(structure, TypesToTranslate): + structure = unicode(structure) + text = self.translate(structure) + if not (repldict or self.strictinsert): + # Take a shortcut, no error checking + self.stream_write(text) + elif self.html: + self.insertHTMLStructure(text, repldict) + else: + self.insertXMLStructure(text, repldict) + + def insertHTMLStructure(self, text, repldict): + from zope.tal.htmltalparser import HTMLTALParser + gen = AltTALGenerator(repldict, self.engine, 0) + p = HTMLTALParser(gen) # Raises an exception if text is invalid + p.parseString(text) + program, macros = p.getCode() + self.interpret(program) + + def insertXMLStructure(self, text, repldict): + from zope.tal.talparser import TALParser + gen = AltTALGenerator(repldict, self.engine, 0) + p = TALParser(gen) + gen.enable(0) + p.parseFragment('') + gen.enable(1) + p.parseFragment(text) # Raises an exception if text is invalid + gen.enable(0) + p.parseFragment('', 1) + program, macros = gen.getCode() + self.interpret(program) + + def do_evaluateCode(self, stuff): + lang, program = stuff + # Use a temporary stream to capture the interpretation of the + # subnodes, which should /not/ go to the output stream. + tmpstream = self.StringIO() + self.pushStream(tmpstream) + try: + self.interpret(program) + finally: + self.popStream() + code = tmpstream.getvalue() + output = self.engine.evaluateCode(lang, code) + self._stream_write(output) + bytecode_handlers["evaluateCode"] = do_evaluateCode + + def do_loop(self, (name, expr, block)): + self.interpret(block) + + def do_loop_tal(self, (name, expr, block)): + iterator = self.engine.setRepeat(name, expr) + while iterator.next(): + self.interpret(block) + bytecode_handlers["loop"] = do_loop + + def translate(self, msgid, default=None, i18ndict=None, + obj=None, domain=None): + if default is None: + default = getattr(msgid, 'default', unicode(msgid)) + if i18ndict is None: + i18ndict = {} + if domain is None: + domain = getattr(msgid, 'domain', self.i18nContext.domain) + if obj: + i18ndict.update(obj) + if not self.i18nInterpolate: + return msgid + # TODO: We need to pass in one of context or target_language + return self.engine.translate(msgid, self.i18nContext.domain, + i18ndict, default=default) + + def do_rawtextColumn(self, (s, col)): + self._stream_write(s) + self.col = col + bytecode_handlers["rawtextColumn"] = do_rawtextColumn + + def do_rawtextOffset(self, (s, offset)): + self._stream_write(s) + self.col = self.col + offset + bytecode_handlers["rawtextOffset"] = do_rawtextOffset + + def do_condition(self, (condition, block)): + if not self.tal or self.engine.evaluateBoolean(condition): + self.interpret(block) + bytecode_handlers["condition"] = do_condition + + def do_defineMacro(self, (macroName, macro)): + wasInUse = self.inUseDirective + self.inUseDirective = False + self.interpret(macro) + self.inUseDirective = wasInUse + bytecode_handlers["defineMacro"] = do_defineMacro + + def do_useMacro(self, (macroName, macroExpr, compiledSlots, block), + definingName=None, extending=False): + if not self.metal: + self.interpret(block) + return + macro = self.engine.evaluateMacro(macroExpr) + if macro is self.Default: + macro = block + else: + if not isCurrentVersion(macro): + raise METALError("macro %s has incompatible version %s" % + (`macroName`, `getProgramVersion(macro)`), + self.position) + mode = getProgramMode(macro) + if mode != (self.html and "html" or "xml"): + raise METALError("macro %s has incompatible mode %s" % + (`macroName`, `mode`), self.position) + self.pushMacro(macroName, compiledSlots, definingName, extending) + + # We want 'macroname' name to be always available as a variable + outer = self.engine.getValue('macroname') + self.engine.setLocal('macroname', macroName.rsplit('/', 1)[-1]) + + prev_source = self.sourceFile + wasInUse = self.inUseDirective + self.inUseDirective = True + self.interpret(macro) + self.inUseDirective = wasInUse + + if self.sourceFile != prev_source: + self.engine.setSourceFile(prev_source) + self.sourceFile = prev_source + self.popMacro() + # Push the outer macroname again. + self.engine.setLocal('macroname', outer) + bytecode_handlers["useMacro"] = do_useMacro + + def do_extendMacro(self, (macroName, macroExpr, compiledSlots, block, + definingName)): + # extendMacro results from a combination of define-macro and + # use-macro. definingName has the value of the + # metal:define-macro attribute. + extending = self.metal and self.inUseDirective + self.do_useMacro((macroName, macroExpr, compiledSlots, block), + definingName, extending) + bytecode_handlers["extendMacro"] = do_extendMacro + + def do_fillSlot(self, (slotName, block)): + # This is only executed if the enclosing 'use-macro' evaluates + # to 'default'. + self.interpret(block) + bytecode_handlers["fillSlot"] = do_fillSlot + + def do_defineSlot(self, (slotName, block)): + if not self.metal: + self.interpret(block) + return + macs = self.macroStack + if macs: + len_macs = len(macs) + # Measure the extension depth of this use-macro + depth = 1 + while depth < len_macs: + if macs[-depth].extending: + depth += 1 + else: + break + # Search for a slot filler from the most specific to the + # most general macro. The most general is at the top of + # the stack. + slot = None + i = len_macs - 1 + while i >= (len_macs - depth): + slot = macs[i].slots.get(slotName) + if slot is not None: + break + i -= 1 + if slot is not None: + # Found a slot filler. Temporarily chop the macro + # stack starting at the macro that filled the slot and + # render the slot filler. + chopped = macs[i:] + del macs[i:] + try: + self.interpret(slot) + finally: + # Restore the stack entries. + for mac in chopped: + mac.entering = False # Not entering + macs.extend(chopped) + return + # Falling out of the 'if' allows the macro to be interpreted. + self.interpret(block) + bytecode_handlers["defineSlot"] = do_defineSlot + + def do_onError(self, (block, handler)): + self.interpret(block) + + def do_onError_tal(self, (block, handler)): + state = self.saveState() + self.stream = stream = self.StringIO() + self._stream_write = stream.write + try: + self.interpret(block) + # TODO: this should not catch ZODB.POSException.ConflictError. + # The ITALExpressionEngine interface should provide a way of + # getting the set of exception types that should not be + # handled. + except: + exc = sys.exc_info()[1] + self.restoreState(state) + engine = self.engine + engine.beginScope() + error = engine.createErrorInfo(exc, self.position) + engine.setLocal('error', error) + try: + self.interpret(handler) + finally: + engine.endScope() + else: + self.restoreOutputState(state) + self.stream_write(stream.getvalue()) + bytecode_handlers["onError"] = do_onError + + bytecode_handlers_tal = bytecode_handlers.copy() + bytecode_handlers_tal["rawtextBeginScope"] = do_rawtextBeginScope_tal + bytecode_handlers_tal["beginScope"] = do_beginScope_tal + bytecode_handlers_tal["setLocal"] = do_setLocal_tal + bytecode_handlers_tal["setGlobal"] = do_setGlobal_tal + bytecode_handlers_tal["insertStructure"] = do_insertStructure_tal + bytecode_handlers_tal["insertI18nStructure"] = do_insertI18nStructure_tal + bytecode_handlers_tal["insertText"] = do_insertText_tal + bytecode_handlers_tal["insertI18nText"] = do_insertI18nText_tal + bytecode_handlers_tal["loop"] = do_loop_tal + bytecode_handlers_tal["onError"] = do_onError_tal + bytecode_handlers_tal[""] = attrAction_tal + bytecode_handlers_tal["optTag"] = do_optTag_tal + + +class FasterStringIO(StringIO): + """Append-only version of StringIO. + + This let's us have a much faster write() method. + """ + def close(self): + if not self.closed: + self.write = _write_ValueError + StringIO.close(self) + + def seek(self, pos, mode=0): + raise RuntimeError("FasterStringIO.seek() not allowed") + + def write(self, s): + #assert self.pos == self.len + self.buflist.append(s) + self.len = self.pos = self.pos + len(s) + + +def _write_ValueError(s): + raise ValueError("I/O operation on closed file") diff -Nru zope3-3.4.0/src/zope/tal/talparser.py zope3-3.5~bzr18/src/zope/tal/talparser.py --- zope3-3.4.0/src/zope/tal/talparser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/talparser.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,142 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Parse XML and compile to TALInterpreter intermediate code. + +$Id: talparser.py 76887 2007-06-21 10:08:47Z hdima $ +""" +from zope.tal.taldefs import XML_NS, ZOPE_I18N_NS, ZOPE_METAL_NS, ZOPE_TAL_NS +from zope.tal.talgenerator import TALGenerator +from zope.tal.xmlparser import XMLParser + + +class TALParser(XMLParser): + + ordered_attributes = 1 + + def __init__(self, gen=None, encoding=None): # Override + XMLParser.__init__(self, encoding) + if gen is None: + gen = TALGenerator() + self.gen = gen + self.nsStack = [] + self.nsDict = {XML_NS: 'xml'} + self.nsNew = [] + + def getCode(self): + return self.gen.getCode() + + def StartNamespaceDeclHandler(self, prefix, uri): + self.nsStack.append(self.nsDict.copy()) + self.nsDict[uri] = prefix + self.nsNew.append((prefix, uri)) + + def EndNamespaceDeclHandler(self, prefix): + self.nsDict = self.nsStack.pop() + + def StartElementHandler(self, name, attrs): + if self.ordered_attributes: + # attrs is a list of alternating names and values + attrlist = [] + for i in range(0, len(attrs), 2): + key = attrs[i] + value = attrs[i + 1] + attrlist.append((key, value)) + else: + # attrs is a dict of {name: value} + attrlist = attrs.items() + attrlist.sort() # For definiteness + name, attrlist, taldict, metaldict, i18ndict \ + = self.process_ns(name, attrlist) + attrlist = self.xmlnsattrs() + attrlist + self.gen.emitStartElement(name, attrlist, taldict, metaldict, i18ndict, + self.getpos()) + + def process_ns(self, name, attrlist): + taldict = {} + metaldict = {} + i18ndict = {} + fixedattrlist = [] + name, namebase, namens = self.fixname(name) + for key, value in attrlist: + key, keybase, keyns = self.fixname(key) + ns = keyns or namens # default to tag namespace + item = key, value + if ns == 'metal': + metaldict[keybase] = value + item = item + ("metal",) + elif ns == 'tal': + taldict[keybase] = value + item = item + ("tal",) + elif ns == 'i18n': + i18ndict[keybase] = value + item = item + ('i18n',) + fixedattrlist.append(item) + if namens in ('metal', 'tal', 'i18n'): + taldict['tal tag'] = namens + return name, fixedattrlist, taldict, metaldict, i18ndict + + _namespaces = { + ZOPE_TAL_NS: "tal", + ZOPE_METAL_NS: "metal", + ZOPE_I18N_NS: "i18n", + } + + def xmlnsattrs(self): + newlist = [] + for prefix, uri in self.nsNew: + if prefix: + key = "xmlns:" + prefix + else: + key = "xmlns" + if uri in self._namespaces: + item = (key, uri, "xmlns") + else: + item = (key, uri) + newlist.append(item) + self.nsNew = [] + return newlist + + def fixname(self, name): + if ' ' in name: + uri, name = name.split(' ', 1) + prefix = self.nsDict[uri] + prefixed = name + if prefix: + prefixed = "%s:%s" % (prefix, name) + ns = self._namespaces.get(uri, "x") + return (prefixed, name, ns) + return (name, name, None) + + def EndElementHandler(self, name): + name = self.fixname(name)[0] + self.gen.emitEndElement(name, position=self.getpos()) + + def DefaultHandler(self, text): + self.gen.emitRawText(text) + +def test(): + import sys + p = TALParser() + file = "tests/input/test01.xml" + if sys.argv[1:]: + file = sys.argv[1] + p.parseFile(file) + program, macros = p.getCode() + from zope.tal.talinterpreter import TALInterpreter + from zope.tal.dummyengine import DummyEngine + engine = DummyEngine(macros) + TALInterpreter(program, macros, engine, sys.stdout, wrap=0)() + +if __name__ == "__main__": + test() diff -Nru zope3-3.4.0/src/zope/tal/tests/__init__.py zope3-3.5~bzr18/src/zope/tal/tests/__init__.py --- zope3-3.4.0/src/zope/tal/tests/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/tal/tests/input/acme_template.pt zope3-3.5~bzr18/src/zope/tal/tests/input/acme_template.pt --- zope3-3.4.0/src/zope/tal/tests/input/acme_template.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/acme_template.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,15 @@ + + + +ACME Look and Feel + + +

+Copyright 2004 Acme Inc. +
+Standard disclaimers apply. +
+
+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/document_list.pt zope3-3.5~bzr18/src/zope/tal/tests/input/document_list.pt --- zope3-3.4.0/src/zope/tal/tests/input/document_list.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/document_list.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,21 @@ + + + +Acme Document List + + + +
+

Documents

+
    +
  • Rocket Science for Dummies
  • +
  • Birds for the Gourmet Chef
  • +
+
+
+This document list is classified. +
+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/__init__.py zope3-3.5~bzr18/src/zope/tal/tests/input/__init__.py --- zope3-3.4.0/src/zope/tal/tests/input/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/tal/tests/input/pnome_template.pt zope3-3.5~bzr18/src/zope/tal/tests/input/pnome_template.pt --- zope3-3.4.0/src/zope/tal/tests/input/pnome_template.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/pnome_template.pt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,23 @@ + + + +Title here + + + + +
+
+ "The early bird gets the worm, but the second mouse gets the cheese." +
+ Preferences... +
+
+ Content here +
+
+ page footer +
+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test01.html zope3-3.5~bzr18/src/zope/tal/tests/input/test01.html --- zope3-3.4.0/src/zope/tal/tests/input/test01.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test01.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,56 @@ + + + + dadada + + +

This title is not displayed

+

Title

+ + +  &HarryPotter; + + + + + +

+ +

+ +

+ +

foo bar

+ + + +
    + +
  • Car Name
  • +
    +
+ + + + python + python + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test01.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test01.xml --- zope3-3.4.0/src/zope/tal/tests/input/test01.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test01.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,57 @@ + + + + + dadada + + +

This title is not displayed

+

Title

+ + +  &HarryPotter; + + + + + +

+ +

+ +

+ +

foo bar

+ + + +
    + +
  • Car Name
  • +
    +
+ + + + python + python + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test02.html zope3-3.5~bzr18/src/zope/tal/tests/input/test02.html --- zope3-3.4.0/src/zope/tal/tests/input/test02.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test02.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,118 @@ + + + + + + sample1 + a simple invoice + + + + + + + + + +
+ 01786 + 2000-03-17 + 55377 + 2000-03-15 + GJ03405 + DAVE 1 + 2000-03-17 + K5211(34) + 23 + 23 +
+ + SHIPWRIGHT RESTAURANTS LIMITED + 125 NORTH SERVICE ROAD W + WESTLAKE ACCESS + NORTH BAY + L8B1O5 + ONTARIO + CANADA + + + + ATTN: PAULINE DEGRASSI + + + + + + + + 1 + CS + DM 5309 + #1013 12 OZ.MUNICH STEIN + 37.72 + 37.72 + + + 6 + DZ + ON 6420 + PROVINCIAL DINNER FORK + 17.98 + 107.88 + + + 72 + EA + JR20643 + PLASTIC HANDLED STEAK KNIFE + .81 + 58.32 + + + 6 + DZ + ON 6410 + PROVINCIAL TEASPOONS + 12.16 + 72.96 + + + 0 + DZ + ON 6411 + PROVINCIAL RD BOWL SPOON + 6 + 17.98 + 0.00 + + + 1 + EA + DO 3218 + 34 OZ DUAL DIAL SCALE AM3218 + 70.00 + 5.0 + 66.50 + + + 1 + CS + DM 195 + 20 OZ.BEER PUB GLASS + 55.90 + 55.90 + + + + 399.28 + 3.50 + 23.75 + 29.61 + 33.84 + 33.84 + 486.48 + +
+ + +
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test02.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test02.xml --- zope3-3.4.0/src/zope/tal/tests/input/test02.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test02.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,119 @@ + + + + + + + sample1 + a simple invoice + + + + + + + + + +
+ 01786 + 2000-03-17 + 55377 + 2000-03-15 + GJ03405 + DAVE 1 + 2000-03-17 + K5211(34) + 23 + 23 +
+ + SHIPWRIGHT RESTAURANTS LIMITED + 125 NORTH SERVICE ROAD W + WESTLAKE ACCESS + NORTH BAY + L8B1O5 + ONTARIO + CANADA + + + + ATTN: PAULINE DEGRASSI + + + + + + + + 1 + CS + DM 5309 + #1013 12 OZ.MUNICH STEIN + 37.72 + 37.72 + + + 6 + DZ + ON 6420 + PROVINCIAL DINNER FORK + 17.98 + 107.88 + + + 72 + EA + JR20643 + PLASTIC HANDLED STEAK KNIFE + .81 + 58.32 + + + 6 + DZ + ON 6410 + PROVINCIAL TEASPOONS + 12.16 + 72.96 + + + 0 + DZ + ON 6411 + PROVINCIAL RD BOWL SPOON + 6 + 17.98 + 0.00 + + + 1 + EA + DO 3218 + 34 OZ DUAL DIAL SCALE AM3218 + 70.00 + 5.0 + 66.50 + + + 1 + CS + DM 195 + 20 OZ.BEER PUB GLASS + 55.90 + 55.90 + + + + 399.28 + 3.50 + 23.75 + 29.61 + 33.84 + 33.84 + 486.48 + +
+ + +
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test03.html zope3-3.5~bzr18/src/zope/tal/tests/input/test03.html --- zope3-3.4.0/src/zope/tal/tests/input/test03.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test03.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ +

+ + outer variable x, first appearance + + inner variable x + + outer variable x, second appearance + +

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test03.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test03.xml --- zope3-3.4.0/src/zope/tal/tests/input/test03.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test03.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + +

+ + outer variable x, first appearance + + inner variable x + + outer variable x, second appearance + +

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test04.html zope3-3.5~bzr18/src/zope/tal/tests/input/test04.html --- zope3-3.4.0/src/zope/tal/tests/input/test04.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test04.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,26 @@ + + + + +
    +
  • + 1 + +
  • +
+ + + +

use-macro + fill-slot +

+ + + +

use-macro

+ +

define-slot

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test04.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test04.xml --- zope3-3.4.0/src/zope/tal/tests/input/test04.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test04.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ + + + + + +
    +
  • + 1 + +
  • +
+ + + +

use-macro + fill-slot +

+ + + +

use-macro

+ +

define-slot

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test05.html zope3-3.5~bzr18/src/zope/tal/tests/input/test05.html --- zope3-3.4.0/src/zope/tal/tests/input/test05.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test05.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ + + + + +

This is the body of test5

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test05.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test05.xml --- zope3-3.4.0/src/zope/tal/tests/input/test05.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test05.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + +

This is the body of test5

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test06.html zope3-3.5~bzr18/src/zope/tal/tests/input/test06.html --- zope3-3.4.0/src/zope/tal/tests/input/test06.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test06.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + dummy body in test6 + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test06.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test06.xml --- zope3-3.4.0/src/zope/tal/tests/input/test06.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test06.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + + + dummy body in test6 + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test07.html zope3-3.5~bzr18/src/zope/tal/tests/input/test07.html --- zope3-3.4.0/src/zope/tal/tests/input/test07.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test07.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + + + + + + +
Top LeftTop Right
Bottom leftBottom Right
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test07.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test07.xml --- zope3-3.4.0/src/zope/tal/tests/input/test07.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test07.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,12 @@ + + + + + + + + + + + +
Top LeftTop Right
Bottom leftBottom Right
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test08.html zope3-3.5~bzr18/src/zope/tal/tests/input/test08.html --- zope3-3.4.0/src/zope/tal/tests/input/test08.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test08.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,44 @@ + + + + + +
+ +

Some headline

+

This is the real contents of the bottom right slot.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+
+
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test08.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test08.xml --- zope3-3.4.0/src/zope/tal/tests/input/test08.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test08.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,45 @@ + + + + + + +
+ +

Some headline

+

This is the real contents of the bottom right slot.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+
+
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test09.html zope3-3.5~bzr18/src/zope/tal/tests/input/test09.html --- zope3-3.4.0/src/zope/tal/tests/input/test09.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test09.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,30 @@ + + +

+ Just a bunch of text. +

more text... +

    +
  • first item +
  • second item + +
      +
    1. second list, first item +
    2. second list, second item +
      +
      term 1 +
      term 2 +
      definition +
      +
    + +
  • Now let's have a paragraph... +

    My Paragraph +

  • + +
  • And a table in a list item: + +
    +
+ + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test09.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test09.xml --- zope3-3.4.0/src/zope/tal/tests/input/test09.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test09.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,30 @@ + + +

+ Just a bunch of text.

+

more text...

+
    +
  • first item
  • +
  • second item + +
      +
    1. second list, first item
    2. +
    3. second list, second item +
      +
      term 1
      +
      term 2
      +
      definition
      +
    4. +
  • + +
  • Now let's have a paragraph... +

    My Paragraph

    +
  • + +
  • And a table in a list item: + +
  • +
+ + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test10.html zope3-3.5~bzr18/src/zope/tal/tests/input/test10.html --- zope3-3.4.0/src/zope/tal/tests/input/test10.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test10.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ + + + + + + +
+ +

Some headline

+

This is the real contents of the bottom right slot.

+
+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

+
+
+ diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test11.html zope3-3.5~bzr18/src/zope/tal/tests/input/test11.html --- zope3-3.4.0/src/zope/tal/tests/input/test11.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test11.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,19 @@ + +

dummy text

+

+ + + + + +

+

+ +

+ + +

p

+
+
rule
+ diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test11.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test11.xml --- zope3-3.4.0/src/zope/tal/tests/input/test11.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test11.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,14 @@ + +

dummy text

+

+ + + + + +

+

+ +

+ diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test12.html zope3-3.5~bzr18/src/zope/tal/tests/input/test12.html --- zope3-3.4.0/src/zope/tal/tests/input/test12.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test12.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test13.html zope3-3.5~bzr18/src/zope/tal/tests/input/test13.html --- zope3-3.4.0/src/zope/tal/tests/input/test13.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test13.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ +Here's a stray greater than: > + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test14.html zope3-3.5~bzr18/src/zope/tal/tests/input/test14.html --- zope3-3.4.0/src/zope/tal/tests/input/test14.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test14.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + +
+
+ +

+ +

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test14.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test14.xml --- zope3-3.4.0/src/zope/tal/tests/input/test14.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test14.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,15 @@ + + + + + + + +
+
+ +

+ +

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test15.html zope3-3.5~bzr18/src/zope/tal/tests/input/test15.html --- zope3-3.4.0/src/zope/tal/tests/input/test15.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test15.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,26 @@ + + INNERSLOT + + + + inner-argument + + +
+
+ + OUTERSLOT + +
+
+ +
+ + +
outer-argument
+
+
+
+ +
+
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test16.html zope3-3.5~bzr18/src/zope/tal/tests/input/test16.html --- zope3-3.4.0/src/zope/tal/tests/input/test16.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test16.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ +blah, blah diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test16.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test16.xml --- zope3-3.4.0/src/zope/tal/tests/input/test16.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test16.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + + +bar + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test17.html zope3-3.5~bzr18/src/zope/tal/tests/input/test17.html --- zope3-3.4.0/src/zope/tal/tests/input/test17.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test17.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ +No +No +Yes + +No +Yes diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test17.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test17.xml --- zope3-3.4.0/src/zope/tal/tests/input/test17.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test17.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + +No +No +Yes + +No +Yes + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test18.html zope3-3.5~bzr18/src/zope/tal/tests/input/test18.html --- zope3-3.4.0/src/zope/tal/tests/input/test18.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test18.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,16 @@ +

Content

+

+ + +

Content

+

+ + +

Content

+

+ + +

No

+

No

+

Yes

+

Yes

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test18.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test18.xml --- zope3-3.4.0/src/zope/tal/tests/input/test18.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test18.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,20 @@ + + +

Content

+

+ + +

Content

+

+ + +

Content

+

+ + +

No

+

No

+

Yes

+

Yes

+ diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test19.html zope3-3.5~bzr18/src/zope/tal/tests/input/test19.html --- zope3-3.4.0/src/zope/tal/tests/input/test19.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test19.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ +Replace this +This is a +translated string +And another +translated string diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test19.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test19.xml --- zope3-3.4.0/src/zope/tal/tests/input/test19.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test19.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ + + +Replace this +This is a +translated string +And another +translated string + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test20.html zope3-3.5~bzr18/src/zope/tal/tests/input/test20.html --- zope3-3.4.0/src/zope/tal/tests/input/test20.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test20.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +replaceable

content

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test20.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test20.xml --- zope3-3.4.0/src/zope/tal/tests/input/test20.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test20.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + +replaceable

content

+ diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test21.html zope3-3.5~bzr18/src/zope/tal/tests/input/test21.html --- zope3-3.4.0/src/zope/tal/tests/input/test21.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test21.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + was born in + . + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test21.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test21.xml --- zope3-3.4.0/src/zope/tal/tests/input/test21.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test21.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ + + + + was born in + . + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test22.html zope3-3.5~bzr18/src/zope/tal/tests/input/test22.html --- zope3-3.4.0/src/zope/tal/tests/input/test22.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test22.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + Jim was born in + the USA. + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test22.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test22.xml --- zope3-3.4.0/src/zope/tal/tests/input/test22.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test22.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + content + omit + replace + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test23.html zope3-3.5~bzr18/src/zope/tal/tests/input/test23.html --- zope3-3.4.0/src/zope/tal/tests/input/test23.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test23.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ +2:32 pm diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test24.html zope3-3.5~bzr18/src/zope/tal/tests/input/test24.html --- zope3-3.4.0/src/zope/tal/tests/input/test24.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test24.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,12 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test25.html zope3-3.5~bzr18/src/zope/tal/tests/input/test25.html --- zope3-3.4.0/src/zope/tal/tests/input/test25.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test25.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test26.html zope3-3.5~bzr18/src/zope/tal/tests/input/test26.html --- zope3-3.4.0/src/zope/tal/tests/input/test26.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test26.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,3 @@ + + Job #NN diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test27.html zope3-3.5~bzr18/src/zope/tal/tests/input/test27.html --- zope3-3.4.0/src/zope/tal/tests/input/test27.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test27.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ +

Your contact email address is recorded as + user@host.com +

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test28.html zope3-3.5~bzr18/src/zope/tal/tests/input/test28.html --- zope3-3.4.0/src/zope/tal/tests/input/test28.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test28.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ +

Your contact email address is recorded as + + user@host.com +

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test29.html zope3-3.5~bzr18/src/zope/tal/tests/input/test29.html --- zope3-3.4.0/src/zope/tal/tests/input/test29.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test29.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ +
At the tone the time will be +2:32 pm... beep!
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test30.html zope3-3.5~bzr18/src/zope/tal/tests/input/test30.html --- zope3-3.4.0/src/zope/tal/tests/input/test30.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test30.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ +

Your contact email address is recorded as +user@host.com +

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test31.html zope3-3.5~bzr18/src/zope/tal/tests/input/test31.html --- zope3-3.4.0/src/zope/tal/tests/input/test31.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test31.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ +

Your contact email address is recorded as + + + user@host.com +

diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test32.html zope3-3.5~bzr18/src/zope/tal/tests/input/test32.html --- zope3-3.4.0/src/zope/tal/tests/input/test32.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test32.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + was born in + . + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test33.html zope3-3.5~bzr18/src/zope/tal/tests/input/test33.html --- zope3-3.4.0/src/zope/tal/tests/input/test33.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test33.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +don't translate me diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test34.html zope3-3.5~bzr18/src/zope/tal/tests/input/test34.html --- zope3-3.4.0/src/zope/tal/tests/input/test34.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test34.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ + + stuff + + more stuff + + + + stuff + + more stuff + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test35.html zope3-3.5~bzr18/src/zope/tal/tests/input/test35.html --- zope3-3.4.0/src/zope/tal/tests/input/test35.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test35.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + +

+ + + +

name

+
\ No newline at end of file diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test36.html zope3-3.5~bzr18/src/zope/tal/tests/input/test36.html --- zope3-3.4.0/src/zope/tal/tests/input/test36.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test36.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + some text + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test37.html zope3-3.5~bzr18/src/zope/tal/tests/input/test37.html --- zope3-3.4.0/src/zope/tal/tests/input/test37.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test37.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,3 @@ + + Test + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_domain.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_domain.html --- zope3-3.4.0/src/zope/tal/tests/input/test_domain.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_domain.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ +
+Replace this +This is a +translated string +And another +translated string +
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_failed_attr_translation.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_failed_attr_translation.html --- zope3-3.4.0/src/zope/tal/tests/input/test_failed_attr_translation.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_failed_attr_translation.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal1.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal1.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal1.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,61 @@ + + AAA + INNER + BBB + + + + + + + + + + AAA + + INNER + + BBB + + + + + + + + + + OUTERSLOT + + + + AAA + + INNER + INNERSLOT + + + BBB + + + + + + + OUTERSLOT + + + + + + + INNERSLOT + + + + + INSLOT + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal2.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal2.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal2.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal2.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ +
+ OUTER + INNER + OUTER +
+ +
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal3.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal3.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal3.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal3.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +Should not get attr in metal diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal4.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal4.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal4.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal4.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + + Z3 UI + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal5.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal5.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal5.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal5.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + + Z3 UI + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal6.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal6.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal6.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal6.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ + + + Z3 UI + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal7.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal7.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal7.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal7.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal8.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal8.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal8.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal8.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,15 @@ + + +
+
+Default body +
+
+ + + + +
+Filled-in body +
+ diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_metal9.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal9.html --- zope3-3.4.0/src/zope/tal/tests/input/test_metal9.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_metal9.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,23 @@ +
+ +Default for macro1 + +
+ +
+ +Macro 2's slot 1 decoration + +Default for macro2 + + +
+ +
+
+ +
+ +Custom slot1 + +
diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_sa1.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa1.html --- zope3-3.4.0/src/zope/tal/tests/input/test_sa1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa1.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + +Simple test of source annotations + +

Foo!

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_sa1.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa1.xml --- zope3-3.4.0/src/zope/tal/tests/input/test_sa1.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa1.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + +Simple test of source annotations + +

Foo!

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_sa2.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa2.html --- zope3-3.4.0/src/zope/tal/tests/input/test_sa2.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa2.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ + + +Simple test of source annotations + +

Foo!

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_sa2.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa2.xml --- zope3-3.4.0/src/zope/tal/tests/input/test_sa2.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa2.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + +Simple test of source annotations + +

Foo!

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_sa3.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa3.html --- zope3-3.4.0/src/zope/tal/tests/input/test_sa3.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa3.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,15 @@ + + +
This is macro1 on sa3 line 3. + This is slot1 on sa3 line 4. + This is the end of macro1 on sa3 line 5. +
+

Some text on sa3 line 7.

+

+ This text on sa3 line 9 will disappear. + Text from sa3 line 10 is filled into slot1. + This text on sa3 line 11 will disappear. +

+

This is some text on sa3 line 13.

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_sa3.xml zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa3.xml --- zope3-3.4.0/src/zope/tal/tests/input/test_sa3.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa3.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,16 @@ + + + +
This is macro1 on sa3 line 4. + This is slot1 on sa3 line 5. + This is the end of macro1 on sa3 line 6. +
+

Some text on sa3 line 8.

+

+ This text on sa3 line 10 will disappear. + Text from sa3 line 11 is filled into slot1. + This text on sa3 line 12 will disappear. +

+

This is some text on sa3 line 14.

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/input/test_sa4.html zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa4.html --- zope3-3.4.0/src/zope/tal/tests/input/test_sa4.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/input/test_sa4.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ + + +

Some text on sa4 line 3.

+

+ This text on sa4 line 5 will disappear. + Text from sa4 line 6 is filled into slot1. + This text on sa4 line 7 will disappear. +

+

This is some text on sa4 line 9.

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/markbench.py zope3-3.5~bzr18/src/zope/tal/tests/markbench.py --- zope3-3.4.0/src/zope/tal/tests/markbench.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/markbench.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,187 @@ +#! /usr/bin/env python +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Run benchmarks of TAL vs. DTML + +$Id: markbench.py 30139 2005-04-24 07:01:43Z hdima $ +""" + +import warnings +warnings.filterwarnings("ignore", category=DeprecationWarning) + +import os +os.environ['NO_SECURITY'] = 'true' + +import getopt +import sys +import time + +from cStringIO import StringIO + +#from zope.documenttemplate.dt_html import HTMLFile + +from zope.tal.htmltalparser import HTMLTALParser +from zope.tal.talinterpreter import TALInterpreter +from zope.tal.dummyengine import DummyEngine + + +def time_apply(f, args, kwargs, count): + r = [None] * count + for i in range(4): + f(*args, **kwargs) + t0 = time.clock() + for i in r: + pass + t1 = time.clock() + for i in r: + f(*args, **kwargs) + t = time.clock() - t1 - (t1 - t0) + return t / count + +def time_zpt(fn, count): + from zope.pagetemplate.pagetemplate import PageTemplate + pt = PageTemplate() + pt.write(open(fn).read()) + return time_apply(pt.pt_render, (data,), {}, count) + +def time_tal(fn, count): + p = HTMLTALParser() + p.parseFile(fn) + program, macros = p.getCode() + engine = DummyEngine(macros) + engine.globals = data + tal = TALInterpreter(program, macros, engine, StringIO(), wrap=0, + tal=1, strictinsert=0) + return time_apply(tal, (), {}, count) + +def time_dtml(fn, count): + html = HTMLFile(fn) + return time_apply(html, (), data, count) + +def profile_zpt(fn, count, profiler): + from zope.pagetemplate.pagetemplate import PageTemplate + pt = PageTemplate() + pt.write(open(fn).read()) + for i in range(4): + pt.pt_render(extra_context=data) + r = [None] * count + for i in r: + profiler.runcall(pt.pt_render, 0, data) + +def profile_tal(fn, count, profiler): + p = HTMLTALParser() + p.parseFile(fn) + program, macros = p.getCode() + engine = DummyEngine(macros) + engine.globals = data + tal = TALInterpreter(program, macros, engine, StringIO(), wrap=0, + tal=1, strictinsert=0) + for i in range(4): + tal() + r = [None] * count + for i in r: + profiler.runcall(tal) + +# Figure out where the benchmark files are: +try: + fname = __file__ +except NameError: + fname = sys.argv[0] +taldir = os.path.dirname(os.path.dirname(os.path.abspath(fname))) +benchdir = os.path.join(taldir, 'benchmark') + +# Construct templates for the filenames: +tal_fn = os.path.join(benchdir, 'tal%.2d.html') +dtml_fn = os.path.join(benchdir, 'dtml%.2d.html') + +def compare(n, count, profiler=None, verbose=1): + if verbose: + t1 = int(time_zpt(tal_fn % n, count) * 1000 + 0.5) + t2 = int(time_tal(tal_fn % n, count) * 1000 + 0.5) + t3 = 'n/a' # int(time_dtml(dtml_fn % n, count) * 1000 + 0.5) + print '%.2d: %10s %10s %10s' % (n, t1, t2, t3) + if profiler: + profile_tal(tal_fn % n, count, profiler) + +def main(count, profiler=None, verbose=1): + n = 1 + if verbose: + print '##: %10s %10s %10s' % ('ZPT', 'TAL', 'DTML') + while os.path.isfile(tal_fn % n) and os.path.isfile(dtml_fn % n): + compare(n, count, profiler, verbose) + n = n + 1 + +def get_signal_name(sig): + import signal + for name in dir(signal): + if getattr(signal, name) == sig: + return name + return None + +data = {'x':'X', 'r2': range(2), 'r8': range(8), 'r64': range(64)} +for i in range(10): + data['x%s' % i] = 'X%s' % i + +if __name__ == "__main__": + filename = "markbench.prof" + profiler = None + runtests = False + verbose = True + + opts, args = getopt.getopt(sys.argv[1:], "pqt") + for opt, arg in opts: + if opt == "-p": + import profile + profiler = profile.Profile() + elif opt == "-q": + verbose = False + elif opt == "-t": + runtests = True + + if runtests: + srcdir = os.path.dirname(os.path.dirname(taldir)) + topdir = os.path.dirname(srcdir) + pwd = os.getcwd() + os.chdir(topdir) + rc = os.spawnl(os.P_WAIT, sys.executable, + sys.executable, "test.py", "zope.tal.tests") + if rc > 0: + # TODO: Failing tests don't cause test.py to report an + # error; not sure why. ;-( + sys.exit(rc) + elif rc < 0: + sig = -rc + print >>sys.stderr, ( + "Process exited, signal %d (%s)." + % (sig, get_signal_name(sig) or "")) + sys.exit(1) + os.chdir(pwd) + + if len(args) >= 1: + for arg in args: + compare(int(arg), 25, profiler, verbose) + else: + main(25, profiler, verbose) + + if profiler is not None: + profiler.dump_stats(filename) + import pstats + p = pstats.Stats(filename) + p.strip_dirs() + p.sort_stats('time', 'calls') + try: + p.print_stats(20) + except IOError, e: + if e.errno != errno.EPIPE: + raise diff -Nru zope3-3.4.0/src/zope/tal/tests/output/acme_template.html zope3-3.5~bzr18/src/zope/tal/tests/output/acme_template.html --- zope3-3.4.0/src/zope/tal/tests/output/acme_template.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/acme_template.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,26 @@ + + + +ACME Look and Feel + + + + +
+
+ "The early bird gets the worm, but the second mouse gets the cheese." +
+ Preferences... +
+
+ Content here +
+
+Copyright 2004 Acme Inc. +
+Standard disclaimers apply. +
+
+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/acme_template_source.html zope3-3.5~bzr18/src/zope/tal/tests/output/acme_template_source.html --- zope3-3.4.0/src/zope/tal/tests/output/acme_template_source.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/acme_template_source.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,27 @@ + + + +ACME Look and Feel + + + + +
+
+ "The early bird gets the worm, but the second mouse gets the cheese." +
+ Preferences... +
+
+ Content here +
+
+Copyright 2004 Acme Inc. +
+Standard disclaimers apply. +
+
+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/document_list.html zope3-3.5~bzr18/src/zope/tal/tests/output/document_list.html --- zope3-3.4.0/src/zope/tal/tests/output/document_list.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/document_list.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,30 @@ + + + +Acme Document List + + + +
+
+ "The early bird gets the worm, but the second mouse gets the cheese." +
+ Preferences... +
+
+

Documents

+
    +
  • Rocket Science for Dummies
  • +
  • Birds for the Gourmet Chef
  • +
+
+
+Copyright 2004 Acme Inc. +
+This document list is classified. +
+
+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/document_list_source.html zope3-3.5~bzr18/src/zope/tal/tests/output/document_list_source.html --- zope3-3.4.0/src/zope/tal/tests/output/document_list_source.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/document_list_source.html 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,30 @@ + + + +Acme Document List + + + +
+
+ "The early bird gets the worm, but the second mouse gets the cheese." +
+ Preferences... +
+
+

Documents

+
    +
  • Rocket Science for Dummies
  • +
  • Birds for the Gourmet Chef
  • +
+
+
+Copyright 2004 Acme Inc. +
+This document list is classified. +
+
+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/__init__.py zope3-3.5~bzr18/src/zope/tal/tests/output/__init__.py --- zope3-3.4.0/src/zope/tal/tests/output/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/__init__.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ +# +# This file is necessary to make this directory a package. diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test01.html zope3-3.5~bzr18/src/zope/tal/tests/output/test01.html --- zope3-3.4.0/src/zope/tal/tests/output/test01.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test01.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,68 @@ + + + + dadada + + + +

This +Is +The +Replaced +Title

+ + +  &HarryPotter; + + + + here/id + +

5

+ +

+ honda +

+

+ subaru +

+

+ acura +

+ +

foo bar

+ + + +
    + +
  • honda
  • +
    + +
  • subaru
  • +
    + +
  • acura
  • +
    +
+ + + + python + python + + + + + + + + +  

Header Level 3

+  

Header Level 3

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test01.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test01.xml --- zope3-3.4.0/src/zope/tal/tests/output/test01.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test01.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,65 @@ + + + + + dadada + + + +

This Is The Replaced Title

+ + +  &HarryPotter; + + + + here/id + +

5

+ +

+ honda +

+

+ subaru +

+

+ acura +

+ +

foo bar

+ + + +
    + +
  • honda
  • +
    + +
  • subaru
  • +
    + +
  • acura
  • +
    +
+ + + + python + python + + + + + + + + +  

Header Level 3

+  

Header Level 3

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test02.html zope3-3.5~bzr18/src/zope/tal/tests/output/test02.html --- zope3-3.4.0/src/zope/tal/tests/output/test02.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test02.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,118 @@ + + + + + + sample1 + a simple invoice + + + + + + + + + +
+ 01786 + 2000-03-17 + 55377 + 2000-03-15 + GJ03405 + DAVE 1 + 2000-03-17 + K5211(34) + 23 + 23 +
+ + SHIPWRIGHT RESTAURANTS LIMITED + 125 NORTH SERVICE ROAD W + WESTLAKE ACCESS + NORTH BAY + L8B1O5 + ONTARIO + CANADA + + + + ATTN: PAULINE DEGRASSI + + + + + + + + 1 + CS + DM 5309 + #1013 12 OZ.MUNICH STEIN + 37.72 + 37.72 + + + 6 + DZ + ON 6420 + PROVINCIAL DINNER FORK + 17.98 + 107.88 + + + 72 + EA + JR20643 + PLASTIC HANDLED STEAK KNIFE + .81 + 58.32 + + + 6 + DZ + ON 6410 + PROVINCIAL TEASPOONS + 12.16 + 72.96 + + + 0 + DZ + ON 6411 + PROVINCIAL RD BOWL SPOON + 6 + 17.98 + 0.00 + + + 1 + EA + DO 3218 + 34 OZ DUAL DIAL SCALE AM3218 + 70.00 + 5.0 + 66.50 + + + 1 + CS + DM 195 + 20 OZ.BEER PUB GLASS + 55.90 + 55.90 + + + + 399.28 + 3.50 + 23.75 + 29.61 + 33.84 + 33.84 + 486.48 + +
+ + +
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test02.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test02.xml --- zope3-3.4.0/src/zope/tal/tests/output/test02.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test02.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,119 @@ + + + + + + + sample1 + a simple invoice + + + + + + + + + +
+ 01786 + 2000-03-17 + 55377 + 2000-03-15 + GJ03405 + DAVE 1 + 2000-03-17 + K5211(34) + 23 + 23 +
+ + SHIPWRIGHT RESTAURANTS LIMITED + 125 NORTH SERVICE ROAD W + WESTLAKE ACCESS + NORTH BAY + L8B1O5 + ONTARIO + CANADA + + + + ATTN: PAULINE DEGRASSI + + + + + + + + 1 + CS + DM 5309 + #1013 12 OZ.MUNICH STEIN + 37.72 + 37.72 + + + 6 + DZ + ON 6420 + PROVINCIAL DINNER FORK + 17.98 + 107.88 + + + 72 + EA + JR20643 + PLASTIC HANDLED STEAK KNIFE + .81 + 58.32 + + + 6 + DZ + ON 6410 + PROVINCIAL TEASPOONS + 12.16 + 72.96 + + + 0 + DZ + ON 6411 + PROVINCIAL RD BOWL SPOON + 6 + 17.98 + 0.00 + + + 1 + EA + DO 3218 + 34 OZ DUAL DIAL SCALE AM3218 + 70.00 + 5.0 + 66.50 + + + 1 + CS + DM 195 + 20 OZ.BEER PUB GLASS + 55.90 + 55.90 + + + + 399.28 + 3.50 + 23.75 + 29.61 + 33.84 + 33.84 + 486.48 + +
+ + +
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test03.html zope3-3.5~bzr18/src/zope/tal/tests/output/test03.html --- zope3-3.4.0/src/zope/tal/tests/output/test03.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test03.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ +

+ + hello brave new world + + goodbye cruel world + + hello brave new world + +

diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test03.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test03.xml --- zope3-3.4.0/src/zope/tal/tests/output/test03.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test03.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + +

+ + hello brave new world + + goodbye cruel world + + hello brave new world + +

diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test04.html zope3-3.5~bzr18/src/zope/tal/tests/output/test04.html --- zope3-3.4.0/src/zope/tal/tests/output/test04.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test04.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,38 @@ + + + + +
    +
+ + + +
    +
  • + 0 + hello world +
  • +
  • + 1 + hello world +
  • +
+ + + +
    +
  • + 0 + goodbye cruel world +
  • +
  • + 1 + goodbye cruel world +
  • +
+ +

define-slot

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test04.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test04.xml --- zope3-3.4.0/src/zope/tal/tests/output/test04.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test04.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,39 @@ + + + + + +
    +
+ + + +
    +
  • + 0 + hello world +
  • +
  • + 1 + hello world +
  • +
+ + + +
    +
  • + 0 + goodbye cruel world +
  • +
  • + 1 + goodbye cruel world +
  • +
+ +

define-slot

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test05.html zope3-3.5~bzr18/src/zope/tal/tests/output/test05.html --- zope3-3.4.0/src/zope/tal/tests/output/test05.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test05.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ + + + + +

This is the body of test5

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test05.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test05.xml --- zope3-3.4.0/src/zope/tal/tests/output/test05.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test05.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + + + + + +

This is the body of test5

+ + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test06.html zope3-3.5~bzr18/src/zope/tal/tests/output/test06.html --- zope3-3.4.0/src/zope/tal/tests/output/test06.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test06.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + + +

This is the body of test5

+ + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test06.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test06.xml --- zope3-3.4.0/src/zope/tal/tests/output/test06.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test06.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ + + + + +

This is the body of test5

+ + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test07.html zope3-3.5~bzr18/src/zope/tal/tests/output/test07.html --- zope3-3.4.0/src/zope/tal/tests/output/test07.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test07.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ + + + + + + + + + + +
Top LeftTop Right
Bottom leftBottom Right
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test07.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test07.xml --- zope3-3.4.0/src/zope/tal/tests/output/test07.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test07.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,12 @@ + + + + + + + + + + + +
Top LeftTop Right
Bottom leftBottom Right
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test08.html zope3-3.5~bzr18/src/zope/tal/tests/output/test08.html --- zope3-3.4.0/src/zope/tal/tests/output/test08.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test08.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,47 @@ + + + + + + + + + + +
Top LeftTop Right
Bottom left +

Some headline

+

This is the real contents of the bottom right slot.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test08.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test08.xml --- zope3-3.4.0/src/zope/tal/tests/output/test08.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test08.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,48 @@ + + + + + + + + + + + +
Top LeftTop Right
Bottom left +

Some headline

+

This is the real contents of the bottom right slot.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test09.html zope3-3.5~bzr18/src/zope/tal/tests/output/test09.html --- zope3-3.4.0/src/zope/tal/tests/output/test09.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test09.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,30 @@ + + +

+ Just a bunch of text.

+

more text...

+
    +
  • first item
  • +
  • second item + +
      +
    1. second list, first item
    2. +
    3. second list, second item +
      +
      term 1
      +
      term 2
      +
      definition
      +
    4. +
  • + +
  • Now let's have a paragraph... +

    My Paragraph

    +
  • + +
  • And a table in a list item: + +
  • +
+ + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test09.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test09.xml --- zope3-3.4.0/src/zope/tal/tests/output/test09.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test09.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,30 @@ + + +

+ Just a bunch of text.

+

more text...

+
    +
  • first item
  • +
  • second item + +
      +
    1. second list, first item
    2. +
    3. second list, second item +
      +
      term 1
      +
      term 2
      +
      definition
      +
    4. +
  • + +
  • Now let's have a paragraph... +

    My Paragraph

    +
  • + +
  • And a table in a list item: + +
  • +
+ + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test10.html zope3-3.5~bzr18/src/zope/tal/tests/output/test10.html --- zope3-3.4.0/src/zope/tal/tests/output/test10.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test10.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,51 @@ + + + + + + + + + + + +
Top LeftTop Right
Bottom left +

Some headline

+

This is the real contents of the bottom right slot.

+
+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

It is supposed to contain a lot of text. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab. + Blabber, blabber, blah. Baah, baah, barb.

+

+
+ diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test11.html zope3-3.5~bzr18/src/zope/tal/tests/output/test11.html --- zope3-3.4.0/src/zope/tal/tests/output/test11.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test11.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,8 @@ + + bar +

bad boy!

+

x undefined

+ x undefined + x undefined +
+ diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test11.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test11.xml --- zope3-3.4.0/src/zope/tal/tests/output/test11.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test11.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ + + bar +

bad boy!

+

x undefined

+ diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test12.html zope3-3.5~bzr18/src/zope/tal/tests/output/test12.html --- zope3-3.4.0/src/zope/tal/tests/output/test12.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test12.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test13.html zope3-3.5~bzr18/src/zope/tal/tests/output/test13.html --- zope3-3.4.0/src/zope/tal/tests/output/test13.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test13.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ +Here's a stray greater than: > + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test14.html zope3-3.5~bzr18/src/zope/tal/tests/output/test14.html --- zope3-3.4.0/src/zope/tal/tests/output/test14.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test14.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,13 @@ + + + + + + +
carbikebroomstick
+ +

+ Harry + Ron + Hermione +

diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test14.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test14.xml --- zope3-3.4.0/src/zope/tal/tests/output/test14.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test14.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,18 @@ + + + + + + + + + +
carbikebroomstick
+ +

+ Harry + Ron + Hermione +

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test15.html zope3-3.5~bzr18/src/zope/tal/tests/output/test15.html --- zope3-3.4.0/src/zope/tal/tests/output/test15.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test15.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,29 @@ + + INNERSLOT + + + + inner-argument + + +
+ + + OUTERSLOT + + +
+ +
+ +
outer-argument
+
+
+ +
+ + + OUTERSLOT + + +
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test16.html zope3-3.5~bzr18/src/zope/tal/tests/output/test16.html --- zope3-3.4.0/src/zope/tal/tests/output/test16.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test16.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +blah, blah diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test16.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test16.xml --- zope3-3.4.0/src/zope/tal/tests/output/test16.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test16.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + +bar + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test17.html zope3-3.5~bzr18/src/zope/tal/tests/output/test17.html --- zope3-3.4.0/src/zope/tal/tests/output/test17.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test17.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ +Yes +Yes +Yes + +Yes +Yes diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test17.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test17.xml --- zope3-3.4.0/src/zope/tal/tests/output/test17.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test17.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,9 @@ + + +Yes +Yes +Yes + +Yes +Yes + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test18.html zope3-3.5~bzr18/src/zope/tal/tests/output/test18.html --- zope3-3.4.0/src/zope/tal/tests/output/test18.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test18.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,16 @@ +Content + + + +Content + + + +

Content

+

+ + +Yes +Yes +Yes +Yes diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test18.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test18.xml --- zope3-3.4.0/src/zope/tal/tests/output/test18.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test18.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,19 @@ + + +Content + + + +Content + + + +

Content

+

+ + +Yes +Yes +Yes +Yes + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test19.html zope3-3.5~bzr18/src/zope/tal/tests/output/test19.html --- zope3-3.4.0/src/zope/tal/tests/output/test19.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test19.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,3 @@ +REPLACE THIS +MSGID +AND ANOTHER TRANSLATED STRING diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test19.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test19.xml --- zope3-3.4.0/src/zope/tal/tests/output/test19.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test19.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + +REPLACE THIS +MSGID +AND ANOTHER TRANSLATED STRING + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test20.html zope3-3.5~bzr18/src/zope/tal/tests/output/test20.html --- zope3-3.4.0/src/zope/tal/tests/output/test20.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test20.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +REPLACEABLE HERE diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test20.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test20.xml --- zope3-3.4.0/src/zope/tal/tests/output/test20.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test20.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + +REPLACEABLE HERE + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test21.html zope3-3.5~bzr18/src/zope/tal/tests/output/test21.html --- zope3-3.4.0/src/zope/tal/tests/output/test21.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test21.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +Lomax WAS BORN IN Antarctica. diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test21.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test21.xml --- zope3-3.4.0/src/zope/tal/tests/output/test21.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test21.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + +Lomax WAS BORN IN Antarctica. + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test22.html zope3-3.5~bzr18/src/zope/tal/tests/output/test22.html --- zope3-3.4.0/src/zope/tal/tests/output/test22.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test22.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +Jim WAS BORN IN the USA. diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test22.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test22.xml --- zope3-3.4.0/src/zope/tal/tests/output/test22.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test22.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + content + omit + replace + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test23.html zope3-3.5~bzr18/src/zope/tal/tests/output/test23.html --- zope3-3.4.0/src/zope/tal/tests/output/test23.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test23.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +59 minutes after 6 PM diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test24.html zope3-3.5~bzr18/src/zope/tal/tests/output/test24.html --- zope3-3.4.0/src/zope/tal/tests/output/test24.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test24.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test25.html zope3-3.5~bzr18/src/zope/tal/tests/output/test25.html --- zope3-3.4.0/src/zope/tal/tests/output/test25.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test25.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test26.html zope3-3.5~bzr18/src/zope/tal/tests/output/test26.html --- zope3-3.4.0/src/zope/tal/tests/output/test26.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test26.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +7 is the JOB NUMBER diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test27.html zope3-3.5~bzr18/src/zope/tal/tests/output/test27.html --- zope3-3.4.0/src/zope/tal/tests/output/test27.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test27.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

Your contact email address is recorded as aperson@dom.ain

diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test28.html zope3-3.5~bzr18/src/zope/tal/tests/output/test28.html --- zope3-3.4.0/src/zope/tal/tests/output/test28.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test28.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

Your contact email address is recorded as aperson@dom.ain

diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test29.html zope3-3.5~bzr18/src/zope/tal/tests/output/test29.html --- zope3-3.4.0/src/zope/tal/tests/output/test29.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test29.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +
AT THE TONE THE TIME WILL BE 59 minutes after 6 PM... BEEP!
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test30.html zope3-3.5~bzr18/src/zope/tal/tests/output/test30.html --- zope3-3.4.0/src/zope/tal/tests/output/test30.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test30.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

Your contact email address is recorded as aperson@dom.ain

diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test31.html zope3-3.5~bzr18/src/zope/tal/tests/output/test31.html --- zope3-3.4.0/src/zope/tal/tests/output/test31.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test31.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +

Your contact email address is recorded as aperson@dom.ain

diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test32.html zope3-3.5~bzr18/src/zope/tal/tests/output/test32.html --- zope3-3.4.0/src/zope/tal/tests/output/test32.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test32.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +Lomax was born in Antarctica diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test33.html zope3-3.5~bzr18/src/zope/tal/tests/output/test33.html --- zope3-3.4.0/src/zope/tal/tests/output/test33.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test33.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +don't translate me diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test34.html zope3-3.5~bzr18/src/zope/tal/tests/output/test34.html --- zope3-3.4.0/src/zope/tal/tests/output/test34.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test34.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,7 @@ + + stuff + foobar + more stuff + + +STUFF foobar MORE STUFF diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test35.html zope3-3.5~bzr18/src/zope/tal/tests/output/test35.html --- zope3-3.4.0/src/zope/tal/tests/output/test35.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test35.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + +

page

diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test36.html zope3-3.5~bzr18/src/zope/tal/tests/output/test36.html --- zope3-3.4.0/src/zope/tal/tests/output/test36.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test36.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,2 @@ +<foo> +<foo> some text diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test37.html zope3-3.5~bzr18/src/zope/tal/tests/output/test37.html --- zope3-3.4.0/src/zope/tal/tests/output/test37.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test37.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,3 @@ + + TEST + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_domain.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_domain.html --- zope3-3.4.0/src/zope/tal/tests/output/test_domain.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_domain.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ +
+replace this +msgid +and another translated string +
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_failed_attr_translation.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_failed_attr_translation.html --- zope3-3.4.0/src/zope/tal/tests/output/test_failed_attr_translation.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_failed_attr_translation.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal1.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal1.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal1.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,79 @@ + + AAA + INNER + BBB + + + + AAA + INNER + BBB + + +INNER + + + AAA + + INNER + + BBB + + + + AAA + + INNER + + BBB + + +INNER + + + AAA + OUTERSLOT + BBB + + + + AAA + + INNER + INNERSLOT + + + BBB + + + + AAA + + INNER + INNERSLOT + + + BBB + + + + AAA + OUTERSLOT + BBB + + +INNER + INNERSLOT + + +INNER + INNERSLOT + + +INNER + + INSLOT + + + +INSLOT diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal2.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal2.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal2.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal2.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ +
+ OUTER + INNER + OUTER +
+ +
+ OUTER + INNER + OUTER +
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal3.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal3.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal3.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal3.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1 @@ +Should not get attr in metal diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal4.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal4.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal4.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal4.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + + Z3 UI + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal5.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal5.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal5.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal5.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,4 @@ + + + Z3 UI + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal6.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal6.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal6.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal6.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,5 @@ + + + Z3 UI + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal7.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal7.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal7.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal7.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal8.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal8.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal8.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal8.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,19 @@ + + +
+
+Default body +
+
+ + + + + +
+
+Filled-in body +
+
+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_metal9.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal9.html --- zope3-3.4.0/src/zope/tal/tests/output/test_metal9.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_metal9.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,32 @@ +
+ +Default for macro1 + +
+ +
+ +Macro 2's slot 1 decoration + +Default for macro2 + + +
+ +
+ +Macro 2's slot 1 decoration + +Default for macro2 + + +
+ +
+ +Macro 2's slot 1 decoration + +Custom slot1 + + +
diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_sa1.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa1.html --- zope3-3.4.0/src/zope/tal/tests/output/test_sa1.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa1.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,10 @@ + +Simple test of source annotations + +

Foo!

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_sa1.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa1.xml --- zope3-3.4.0/src/zope/tal/tests/output/test_sa1.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa1.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,11 @@ + + +Simple test of source annotations + +

Foo!

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_sa2.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa2.html --- zope3-3.4.0/src/zope/tal/tests/output/test_sa2.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa2.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,13 @@ + + +Simple test of source annotations + +

Foo!

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_sa2.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa2.xml --- zope3-3.4.0/src/zope/tal/tests/output/test_sa2.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa2.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,14 @@ + + + +Simple test of source annotations + +

Foo!

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_sa3.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa3.html --- zope3-3.4.0/src/zope/tal/tests/output/test_sa3.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa3.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,42 @@ + + +
This is macro1 on sa3 line 3. + This is slot1 on sa3 line 4. + This is the end of macro1 on sa3 line 5. +
+

Some text on sa3 line 7.

+
This is macro1 on sa3 line 3. + Text from sa3 line 10 is filled into slot1. + This is the end of macro1 on sa3 line 5. +
+

This is some text on sa3 line 13.

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_sa3.xml zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa3.xml --- zope3-3.4.0/src/zope/tal/tests/output/test_sa3.xml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa3.xml 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,43 @@ + + + +
This is macro1 on sa3 line 4. + This is slot1 on sa3 line 5. + This is the end of macro1 on sa3 line 6. +
+

Some text on sa3 line 8.

+
This is macro1 on sa3 line 4. + Text from sa3 line 11 is filled into slot1. + This is the end of macro1 on sa3 line 6. +
+

This is some text on sa3 line 14.

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/output/test_sa4.html zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa4.html --- zope3-3.4.0/src/zope/tal/tests/output/test_sa4.html 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/output/test_sa4.html 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,30 @@ + + +

Some text on sa4 line 3.

+
This is macro1 on sa3 line 3. + Text from sa4 line 6 is filled into slot1. + This is the end of macro1 on sa3 line 5. +
+

This is some text on sa4 line 9.

+ + diff -Nru zope3-3.4.0/src/zope/tal/tests/run.py zope3-3.5~bzr18/src/zope/tal/tests/run.py --- zope3-3.4.0/src/zope/tal/tests/run.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/run.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,45 @@ +#! /usr/bin/env python +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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. +# +############################################################################## +"""Run all tests. + +$Id: run.py 37557 2005-07-29 18:19:31Z benji_york $ +""" +import sys +import unittest + +from zope.tal.tests import utils +from zope.tal.tests import test_htmltalparser +from zope.tal.tests import test_talinterpreter +from zope.tal.tests import test_files +from zope.tal.tests import test_sourcepos + +# TODO this code isn't picked up by the Zope 3 test framework.. +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(test_htmltalparser.test_suite()) + if not utils.skipxml: + import test_xmlparser + suite.addTest(test_xmlparser.test_suite()) + suite.addTest(test_talinterpreter.test_suite()) + suite.addTest(test_files.test_suite()) + suite.addTest(test_sourcepos.test_suite()) + return suite + +def main(): + return utils.run_suite(test_suite()) + +if __name__ == "__main__": + errs = main() + sys.exit(errs and 1 or 0) diff -Nru zope3-3.4.0/src/zope/tal/tests/test_files.py zope3-3.5~bzr18/src/zope/tal/tests/test_files.py --- zope3-3.4.0/src/zope/tal/tests/test_files.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/test_files.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,90 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 that run driver.py over input files comparing to output files. + +$Id: test_files.py 30452 2005-05-20 05:13:10Z fdrake $ +""" + +import glob +import os +import sys +import unittest + +import zope.tal.runtest + +from zope.tal.tests import utils + + +class FileTestCase(unittest.TestCase): + + def __init__(self, file, dir): + self.__file = file + self.__dir = dir + unittest.TestCase.__init__(self) + + # For unittest. + def shortDescription(self): + path = os.path.basename(self.__file) + return '%s (%s)' % (path, self.__class__) + + def runTest(self): + basename = os.path.basename(self.__file) + #sys.stdout.write(basename + " ") + sys.stdout.flush() + if basename.startswith('test_sa'): + sys.argv = ["", "-Q", "-a", self.__file] + elif basename.startswith('test_metal'): + sys.argv = ["", "-Q", "-m", self.__file] + else: + sys.argv = ["", "-Q", self.__file] + pwd = os.getcwd() + try: + try: + os.chdir(self.__dir) + zope.tal.runtest.main() + finally: + os.chdir(pwd) + except SystemExit, what: + if what.code: + self.fail("output for %s didn't match" % self.__file) + +try: + script = __file__ +except NameError: + script = sys.argv[0] + +def test_suite(): + suite = unittest.TestSuite() + dir = os.path.dirname(script) + dir = os.path.abspath(dir) + parentdir = os.path.dirname(dir) + prefix = os.path.join(dir, "input", "test*.") + if utils.skipxml: + xmlargs = [] + else: + xmlargs = glob.glob(prefix + "xml") + xmlargs.sort() + htmlargs = glob.glob(prefix + "html") + htmlargs.sort() + args = xmlargs + htmlargs + if not args: + sys.stderr.write("Warning: no test input files found!!!\n") + for arg in args: + case = FileTestCase(arg, parentdir) + suite.addTest(case) + return suite + +if __name__ == "__main__": + errs = utils.run_suite(test_suite()) + sys.exit(errs and 1 or 0) diff -Nru zope3-3.4.0/src/zope/tal/tests/test_htmltalparser.py zope3-3.5~bzr18/src/zope/tal/tests/test_htmltalparser.py --- zope3-3.4.0/src/zope/tal/tests/test_htmltalparser.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/test_htmltalparser.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1021 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 HTMLTALParser code generator. + +$Id: test_htmltalparser.py 38009 2005-08-19 20:29:35Z fdrake $ +""" +import pprint +import sys +import unittest + +from zope.tal import htmltalparser, taldefs +from zope.tal.tests import utils + + +class TestCaseBase(unittest.TestCase): + + prologue = "" + epilogue = "" + initial_program = [('version', taldefs.TAL_VERSION), ('mode', 'html')] + final_program = [] + + def _merge(self, p1, p2): + if p1 and p2: + op1, args1 = p1[-1] + op2, args2 = p2[0] + if op1.startswith('rawtext') and op2.startswith('rawtext'): + return (p1[:-1] + + [rawtext(args1[0] + args2[0])] + + p2[1:]) + return p1+p2 + + def _run_check(self, source, program, macros={}): + parser = htmltalparser.HTMLTALParser() + parser.parseString(self.prologue + source + self.epilogue) + got_program, got_macros = parser.getCode() + program = self._merge(self.initial_program, program) + program = self._merge(program, self.final_program) + self.assert_(got_program == program, + "Program:\n" + pprint.pformat(got_program) + + "\nExpected:\n" + pprint.pformat(program)) + self.assert_(got_macros == macros, + "Macros:\n" + pprint.pformat(got_macros) + + "\nExpected:\n" + pprint.pformat(macros)) + + def _should_error(self, source, exc=taldefs.TALError): + def parse(self=self, source=source): + parser = htmltalparser.HTMLTALParser() + parser.parseString(self.prologue + source + self.epilogue) + self.assertRaises(exc, parse) + + +def rawtext(s): + """Compile raw text to the appropriate instruction.""" + if "\n" in s: + return ("rawtextColumn", (s, len(s) - (s.rfind("\n") + 1))) + else: + return ("rawtextOffset", (s, len(s))) + + +class HTMLTALParserTestCases(TestCaseBase): + + def test_code_simple_identity(self): + self._run_check("""My Title</html>""", [ + rawtext('<html a="b" b="c" c="d">' + '<title>My Title'), + ]) + + def test_code_implied_list_closings(self): + self._run_check("""
""", [ + rawtext('
'), + ]) + self._run_check("""
""", [ + rawtext('
' + '
'), + ]) + + def test_code_implied_table_closings(self): + self._run_check("""

text
head\t
cell\t""" + """""", [ + rawtext('

text

cell \n \t \n
' + '\t
head
cell\t' + ' \n \t \n
cell
'), + ]) + self._run_check("""
cell """ + """
cell
""", [ + rawtext('
cell ' + '
cell
'), + ]) + + def test_code_bad_nesting(self): + def check(self=self): + self._run_check("", []) + self.assertRaises(htmltalparser.NestingError, check) + + def test_code_attr_syntax(self): + output = [ + rawtext(''), + ] + self._run_check("""""", output) + self._run_check("""""", output) + self._run_check("""""", output) + self._run_check("""""", output) + + def test_code_attr_values(self): + self._run_check( + """""", [ + rawtext('')]) + self._run_check("""""", [ + rawtext(''), + ]) + + def test_code_attr_entity_replacement(self): + # we expect entities *not* to be replaced by HTLMParser! + self._run_check("""""", [ + rawtext(''), + ]) + self._run_check("""""", [ + rawtext(''), + ]) + self._run_check("""""", [ + rawtext(''), + ]) + self._run_check("""""", [ + rawtext(''), + ]) + + def test_code_attr_funky_names(self): + self._run_check("""""", [ + rawtext(''), + ]) + + def test_code_pcdata_entityref(self): + self._run_check(""" """, [ + rawtext(' '), + ]) + + def test_code_short_endtags(self): + self._run_check("""""", [ + rawtext(''), + ]) + + +class METALGeneratorTestCases(TestCaseBase): + + def test_null(self): + self._run_check("", []) + + def test_define_macro(self): + macro = self.initial_program + [ + ('startTag', ('p', [('metal:define-macro', 'M', 'metal')])), + rawtext('booh

'), + ] + program = [ + ('setPosition', (1, 0)), + ('defineMacro', ('M', macro)), + ] + macros = {'M': macro} + self._run_check('

booh

', program, macros) + + def test_use_macro(self): + self._run_check('

booh

', [ + ('setPosition', (1, 0)), + ('useMacro', + ('M', '$M$', {}, + [('startTag', ('p', [('metal:use-macro', 'M', 'metal')])), + rawtext('booh

')])), + ]) + + def test_define_slot(self): + macro = self.initial_program + [ + ('startTag', ('p', [('metal:define-macro', 'M', 'metal')])), + rawtext('foo'), + ('setPosition', (1, 29)), + ('defineSlot', ('S', + [('startTag', ('span', [('metal:define-slot', 'S', 'metal')])), + rawtext('spam')])), + rawtext('bar

'), + ] + program = [('setPosition', (1, 0)), + ('defineMacro', ('M', macro))] + macros = {'M': macro} + self._run_check('

foo' + 'spambar

', + program, macros) + + def test_fill_slot(self): + self._run_check('

foo' + 'spambar

', [ + ('setPosition', (1, 0)), + ('useMacro', + ('M', '$M$', + {'S': [('startTag', ('span', + [('metal:fill-slot', 'S', 'metal')])), + rawtext('spam')]}, + [('startTag', ('p', [('metal:use-macro', 'M', 'metal')])), + rawtext('foo'), + ('setPosition', (1, 26)), + ('fillSlot', ('S', + [('startTag', ('span', [('metal:fill-slot', 'S', 'metal')])), + rawtext('spam')])), + rawtext('bar

')])), + ]) + + +class TALGeneratorTestCases(TestCaseBase): + + def test_null(self): + self._run_check("", []) + + def test_define_1(self): + self._run_check("

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:define': 'xyzzy string:spam'}), + ('setLocal', ('xyzzy', '$string:spam$')), + ('startTag', ('p', [('tal:define', 'xyzzy string:spam', 'tal')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_define_2(self): + self._run_check("

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:define': 'local xyzzy string:spam'}), + ('setLocal', ('xyzzy', '$string:spam$')), + ('startTag', ('p', + [('tal:define', 'local xyzzy string:spam', 'tal')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_define_3(self): + self._run_check("

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:define': 'global xyzzy string:spam'}), + ('setGlobal', ('xyzzy', '$string:spam$')), + ('startTag', ('p', + [('tal:define', 'global xyzzy string:spam', 'tal')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_define_4(self): + self._run_check("

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:define': 'x string:spam; y x'}), + ('setLocal', ('x', '$string:spam$')), + ('setLocal', ('y', '$x$')), + ('startTag', ('p', [('tal:define', 'x string:spam; y x', 'tal')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_define_5(self): + self._run_check("

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:define': 'x string:;;;;; y x'}), + ('setLocal', ('x', '$string:;;$')), + ('setLocal', ('y', '$x$')), + ('startTag', ('p', [('tal:define', 'x string:;;;;; y x', 'tal')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_define_6(self): + self._run_check( + "

", [ + ('setPosition', (1, 0)), + ('beginScope', + {'tal:define': 'x string:spam; global y x; local z y'}), + ('setLocal', ('x', '$string:spam$')), + ('setGlobal', ('y', '$x$')), + ('setLocal', ('z', '$y$')), + ('startTag', ('p', + [('tal:define', 'x string:spam; global y x; local z y', 'tal')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_condition(self): + self._run_check( + "

foo

", [ + rawtext('

'), + ('setPosition', (1, 3)), + ('beginScope', {'tal:condition': 'python:1'}), + ('condition', ('$python:1$', + [('startTag', ('span', [('tal:condition', 'python:1', 'tal')])), + rawtext('foo')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_content_1(self): + self._run_check("

bar

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:content': 'string:foo'}), + ('startTag', ('p', [('tal:content', 'string:foo', 'tal')])), + ('insertText', ('$string:foo$', [rawtext('bar')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_content_2(self): + self._run_check("

bar

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:content': 'text string:foo'}), + ('startTag', ('p', [('tal:content', 'text string:foo', 'tal')])), + ('insertText', ('$string:foo$', [rawtext('bar')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_content_3(self): + self._run_check("

bar

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:content': 'structure string:
'}), + ('startTag', ('p', + [('tal:content', 'structure string:
', 'tal')])), + ('insertStructure', + ('$string:
$', {}, [rawtext('bar')])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_replace_1(self): + self._run_check("

bar

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:replace': 'string:foo'}), + ('optTag', + ('p', + '', + None, + 0, + [('startTag', ('p', [('tal:replace', 'string:foo', 'tal')]))], + [('insertText', ('$string:foo$', [('rawtextOffset', ('bar', 3))]))])), + ('endScope', ()), + ]) + + def test_replace_2(self): + self._run_check("

bar

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:replace': 'text string:foo'}), + ('optTag', + ('p', + '', + None, + 0, + [('startTag', ('p', [('tal:replace', 'text string:foo', 'tal')]))], + [('insertText', ('$string:foo$', [('rawtextOffset', ('bar', 3))]))])), + ('endScope', ()), + ]) + + def test_replace_3(self): + self._run_check("

bar

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:replace': 'structure string:
'}), + ('optTag', + ('p', + '', + None, + 0, + [('startTag', ('p', [('tal:replace', 'structure string:
', 'tal')]))], + [('insertStructure', + ('$string:
$', {}, [('rawtextOffset', ('bar', 3))]))])), + ('endScope', ()), + ]) + + def test_repeat(self): + self._run_check("

" + "dummy

", [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:repeat': 'x python:(1,2,3)'}), + ('loop', ('x', '$python:(1,2,3)$', + [('startTag', ('p', + [('tal:repeat', 'x python:(1,2,3)', 'tal')])), + ('setPosition', (1, 33)), + ('beginScope', {'tal:replace': 'x'}), + ('optTag', + ('span', + '', + None, + 0, + [('startTag', ('span', [('tal:replace', 'x', 'tal')]))], + [('insertText', ('$x$', [('rawtextOffset', ('dummy', 5))]))])), + ('endScope', ()), + rawtext('

')])), + ('endScope', ()), + ]) + + def test_script_1(self): + self._run_check('

code

', [ + ('setPosition', (1, 0)), + ('beginScope', {'tal:script': 'text/server-python'}), + ('startTag', ('p', + [('tal:script', 'text/server-python', 'tal')])), + ('evaluateCode', ('text/server-python', + [('rawtextOffset', ('code', 4))])), + ('endScope', ()), + rawtext('

'), + ]) + + def test_script_2(self): + self._run_check('' + 'code' + '', [ + ('setPosition', (1, 0)), + ('beginScope', {'script': 'text/server-python'}), + ('optTag', + ('tal:block', + None, + 'tal', + 0, + [('startTag', ('tal:block', + [('script', 'text/server-python', 'tal')]))], + [('evaluateCode', + ('text/server-python', + [('rawtextOffset', ('code', 4))]))])), + ('endScope', ()) + ]) + + def test_script_3(self): + self._run_check('', [ + ('setPosition', (1, 0)), + ('beginScope', {}), + ('optTag', + ('script', + '', + None, + 0, + [('rawtextOffset', ('', [ + ('rawtextOffset', + ('', 44)) + ]) + + def test_attributes_1(self): + self._run_check("" + "link", [ + ('setPosition', (1, 0)), + ('beginScope', + {'tal:attributes': 'href string:http://www.zope.org; x string:y', + 'name': 'bar', 'href': 'foo'}), + ('startTag', ('a', + [('href', 'foo', 'replace', '$string:http://www.zope.org$', 0, None), + ('name', 'name="bar"'), + ('tal:attributes', + 'href string:http://www.zope.org; x string:y', 'tal'), + ('x', None, 'insert', '$string:y$', 0, None)])), + ('endScope', ()), + rawtext('link'), + ]) + + def test_attributes_2(self): + self._run_check("

duh

", [ + ('setPosition', (1, 0)), + ('beginScope', + {'tal:attributes': 'src string:foo.png', + 'tal:replace': 'structure string:'}), + ('optTag', + ('p', + '', + None, + 0, + [('startTag', + ('p', + [('tal:replace', 'structure string:', 'tal'), + ('tal:attributes', 'src string:foo.png', 'tal')]))], + [('insertStructure', + ('$string:$', + {'src': ('$string:foo.png$', False, None)}, + [('rawtextOffset', ('duh', 3))]))])), + ('endScope', ())]) + + def test_on_error_1(self): + self._run_check("

okay

", [ + ('setPosition', (1, 0)), + ('beginScope', + {'tal:content': 'notHere', 'tal:on-error': 'string:error'}), + ('onError', + ([('startTag', ('p', + [('tal:on-error', 'string:error', 'tal'), + ('tal:content', 'notHere', 'tal')])), + ('insertText', ('$notHere$', [rawtext('okay')])), + rawtext('

')], + [('startTag', ('p', + [('tal:on-error', 'string:error', 'tal'), + ('tal:content', 'notHere', 'tal')])), + ('insertText', ('$string:error$', [])), + rawtext('

')])), + ('endScope', ()), + ]) + + def test_on_error_2(self): + self._run_check("

okay

", [ + ('setPosition', (1, 0)), + ('beginScope', + {'tal:replace': 'notHere', 'tal:on-error': 'string:error'}), + ('onError', + ([('optTag', + ('p', + '', + None, + 0, + [('startTag', + ('p', + [('tal:on-error', 'string:error', 'tal'), + ('tal:replace', 'notHere', 'tal')]))], + [('insertText', ('$notHere$', [('rawtextOffset', ('okay', 4))]))]))], + [('startTag', + ('p', + [('tal:on-error', 'string:error', 'tal'), + ('tal:replace', 'notHere', 'tal')])), + ('insertText', ('$string:error$', [])), + ('rawtextOffset', ('

', 4))])), + ('endScope', ()), + ]) + + def test_dup_attr(self): + self._should_error("") + self._should_error("", taldefs.METALError) + + def test_tal_errors(self): + self._should_error("

") + self._should_error("

") + self._should_error("

") + self._should_error("

") + self._should_error("

") + for tag in htmltalparser.EMPTY_HTML_TAGS: + self._should_error("<%s tal:content='string:foo'>" % tag) + + def test_metal_errors(self): + exc = taldefs.METALError + self._should_error(2*"

xxx

", exc) + self._should_error("" + + 2*"

" + "", exc) + self._should_error("

", exc) + self._should_error("

", exc) + + def test_extend_macro_errors(self): + exc = taldefs.METALError + # extend-macro requires define-macro: + self._should_error("

xxx

", exc) + # extend-macro prevents use-macro: + self._should_error("

xxx

", exc) + # use-macro doesn't co-exist with define-macro: + self._should_error("

xxx

", exc) + + # + # I18N test cases + # + + def test_i18n_attributes(self): + self._run_check("foo", [ + ('setPosition', (1, 0)), + ('beginScope', {'alt': 'foo', 'i18n:attributes': 'alt'}), + ('startTag', ('img', + [('alt', 'foo', 'replace', None, 1, None), + ('i18n:attributes', 'alt', 'i18n')])), + ('endScope', ()), + ]) + self._run_check("foo", [ + ('setPosition', (1, 0)), + ('beginScope', {'alt': 'foo', 'i18n:attributes': 'alt foo ; bar'}), + ('startTag', ('img', + [('alt', 'foo', 'replace', None, 1, 'foo'), + ('i18n:attributes', 'alt foo ; bar', 'i18n'), + ('bar', None, 'insert', None, 1, None)])), + ('endScope', ()), + ]) + + def test_i18n_name_bad_name(self): + self._should_error("") + self._should_error("") + + def test_i18n_attributes_repeated_attr(self): + self._should_error("") + self._should_error("") + + def test_i18n_translate(self): + # input/test19.html + self._run_check('''\ +Replace this +This is a +translated string +And another +translated string +''', [ + ('setPosition', (1, 0)), + ('beginScope', {'i18n:translate': ''}), + ('startTag', ('span', [('i18n:translate', '', 'i18n')])), + ('insertTranslation', ('', [('rawtextOffset', ('Replace this', 12))])), + ('rawtextBeginScope', + ('\n', 0, (2, 0), 1, {'i18n:translate': 'msgid'})), + ('startTag', ('span', [('i18n:translate', 'msgid', 'i18n')])), + ('insertTranslation', + ('msgid', [('rawtextColumn', ('This is a\ntranslated string', 17))])), + ('rawtextBeginScope', ('\n', 0, (4, 0), 1, {'i18n:translate': ''})), + ('startTag', ('span', [('i18n:translate', '', 'i18n')])), + ('insertTranslation', + ('', [('rawtextColumn', ('And another\ntranslated string', 17))])), + ('endScope', ()), + ('rawtextColumn', ('\n', 0))]) + + def test_i18n_translate_with_nested_tal(self): + self._run_check('''\ +replaceable

content

+''', [ + ('setPosition', (1, 0)), + ('beginScope', {'i18n:translate': ''}), + ('startTag', ('span', [('i18n:translate', '', 'i18n')])), + ('insertTranslation', + ('', + [('rawtextOffset', ('replaceable ', 12)), + ('setPosition', (1, 36)), + ('beginScope', {'tal:replace': 'str:here'}), + ('optTag', + ('p', + '', + None, + 0, + [('startTag', ('p', [('tal:replace', 'str:here', 'tal')]))], + [('insertText', + ('$str:here$', [('rawtextOffset', ('content', 7))]))])), + ('endScope', ())])), + ('endScope', ()), + ('rawtextColumn', ('\n', 0)) + ]) + + def test_i18n_name(self): + # input/test21.html + self._run_check('''\ + + was born in + . + +''', [ + ('setPosition', (1, 0)), + ('beginScope', {'i18n:translate': ''}), + ('startTag', ('span', [('i18n:translate', '', 'i18n')])), + ('insertTranslation', + ('', + [('rawtextBeginScope', + ('\n ', + 2, + (2, 2), + 0, + {'i18n:name': 'name', 'tal:replace': 'str:Lomax'})), + ('i18nVariable', + ('name', + [('optTag', + ('span', + '', + None, + 1, + [('startEndTag', + ('span', + [('tal:replace', 'str:Lomax', 'tal'), + ('i18n:name', 'name', 'i18n')]))], + [('insertText', ('$str:Lomax$', []))]))], + None, + 0)), + ('rawtextBeginScope', + (' was born in\n ', + 2, + (3, 2), + 1, + {'i18n:name': 'country', 'tal:replace': 'str:Antarctica'})), + ('i18nVariable', + ('country', + [('optTag', + ('span', + '', + None, + 1, + [('startEndTag', + ('span', + [('tal:replace', 'str:Antarctica', 'tal'), + ('i18n:name', 'country', 'i18n')]))], + [('insertText', ('$str:Antarctica$', []))]))], + None, + 0)), + ('endScope', ()), + ('rawtextColumn', ('.\n', 0))])), + ('endScope', ()), + ('rawtextColumn', ('\n', 0)) + ]) + + def test_i18n_name_with_content(self): + self._run_check('
This is text for ' + '.' + '
', [ +('setPosition', (1, 0)), +('beginScope', {'i18n:translate': ''}), +('startTag', ('div', [('i18n:translate', '', 'i18n')])), +('insertTranslation', + ('', + [('rawtextOffset', ('This is text for ', 17)), + ('setPosition', (1, 40)), + ('beginScope', + {'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}), + ('i18nVariable', + ('bar_name', + [('startTag', + ('span', + [('i18n:translate', '', 'i18n'), + ('tal:content', 'bar', 'tal'), + ('i18n:name', 'bar_name', 'i18n')])), + ('insertI18nText', ('$bar$', [])), + ('rawtextOffset', ('
', 7))], + None, + 0)), + ('endScope', ()), + ('rawtextOffset', ('.', 1))])), +('endScope', ()), +('rawtextOffset', ('', 6)) + ]) + + def test_i18n_name_implicit_value(self): + # input/test22.html + self._run_check('''\ + + Jim was born in + the USA. + +''', [('setPosition', (1, 0)), + ('beginScope', {'i18n:translate': ''}), + ('startTag', ('span', [('i18n:translate', '', 'i18n')])), + ('insertTranslation', + ('', + [('rawtextBeginScope', + ('\n ', 2, (2, 2), 0, {'i18n:name': 'name', 'tal:omit-tag': ''})), + ('i18nVariable', + ('name', + [('optTag', + ('span', + '', + None, + 0, + [('startTag', + ('span', + [('tal:omit-tag', '', 'tal'), + ('i18n:name', 'name', 'i18n')]))], + [('rawtextOffset', ('Jim', 10))]))], + None, + 0)), + ('rawtextBeginScope', + (' was born in\n ', + 2, + (3, 2), + 1, + {'i18n:name': 'country', 'tal:omit-tag': ''})), + ('i18nVariable', + ('country', + [('optTag', + ('span', + '', + None, + 0, + [('startTag', + ('span', + [('tal:omit-tag', '', 'tal'), + ('i18n:name', 'country', 'i18n')]))], + [('rawtextOffset', ('the USA', 7))]))], + None, + 0)), + ('endScope', ()), + ('rawtextColumn', ('.\n', 0))])), + ('endScope', ()), + ('rawtextColumn', ('\n', 0)) + ]) + + def test_i18n_context_domain(self): + self._run_check("", [ + ('setPosition', (1, 0)), + ('beginI18nContext', {'domain': 'mydomain', + 'source': None, 'target': None}), + ('beginScope', {'i18n:domain': 'mydomain'}), + ('startEndTag', ('span', [('i18n:domain', 'mydomain', 'i18n')])), + ('endScope', ()), + ('endI18nContext', ()), + ]) + + def test_i18n_context_source(self): + self._run_check("", [ + ('setPosition', (1, 0)), + ('beginI18nContext', {'source': 'en', + 'domain': 'default', 'target': None}), + ('beginScope', {'i18n:source': 'en'}), + ('startEndTag', ('span', [('i18n:source', 'en', 'i18n')])), + ('endScope', ()), + ('endI18nContext', ()), + ]) + + def test_i18n_context_source_target(self): + self._run_check("", [ + ('setPosition', (1, 0)), + ('beginI18nContext', {'source': 'en', 'target': 'ru', + 'domain': 'default'}), + ('beginScope', {'i18n:source': 'en', 'i18n:target': 'ru'}), + ('startEndTag', ('span', [('i18n:source', 'en', 'i18n'), + ('i18n:target', 'ru', 'i18n')])), + ('endScope', ()), + ('endI18nContext', ()), + ]) + + def test_i18n_context_in_define_slot(self): + text = ("
" + "
spam
" + "
") + self._run_check(text, [ + ('setPosition', (1, 0)), + ('useMacro', + ('M', '$M$', + {'S': [('startTag', ('div', + [('metal:fill-slot', 'S', 'metal')])), + rawtext('spam')]}, + [('beginI18nContext', {'domain': 'mydomain', + 'source': None, 'target': None}), + ('beginScope', + {'i18n:domain': 'mydomain', 'metal:use-macro': 'M'}), + ('startTag', ('div', [('metal:use-macro', 'M', 'metal'), + ('i18n:domain', 'mydomain', 'i18n')])), + ('setPosition', (1, 48)), + ('fillSlot', ('S', + [('startTag', + ('div', [('metal:fill-slot', 'S', 'metal')])), + rawtext('spam')])), + ('endScope', ()), + rawtext(''), + ('endI18nContext', ())])), + ]) + + def test_i18n_data(self): + # input/test23.html + self._run_check('''\ +2:32 pm +''', [ + ('setPosition', (1, 0)), + ('beginScope', + {'i18n:translate': 'timefmt', 'i18n:data': 'here/currentTime'}), + ('startTag', + ('span', + [('i18n:data', 'here/currentTime', 'i18n'), + ('i18n:translate', 'timefmt', 'i18n')])), + ('insertTranslation', + ('timefmt', [('rawtextOffset', ('2:32 pm', 7))], '$here/currentTime$')), + ('endScope', ()), + ('rawtextColumn', ('
\n', 0)) + ]) + + def test_i18n_data_with_name(self): + # input/test29.html + self._run_check('''\ +
At the tone the time will be +2:32 pm... beep!
+''', [('setPosition', (1, 0)), + ('beginScope', {'i18n:translate': ''}), + ('startTag', ('div', [('i18n:translate', '', 'i18n')])), + ('insertTranslation', + ('', + [('rawtextBeginScope', + ('At the tone the time will be\n', + 0, + (2, 0), + 0, + {'i18n:data': 'here/currentTime', + 'i18n:name': 'time', + 'i18n:translate': 'timefmt'})), + ('i18nVariable', + ('time', + [('startTag', + ('span', + [('i18n:data', 'here/currentTime', 'i18n'), + ('i18n:translate', 'timefmt', 'i18n'), + ('i18n:name', 'time', 'i18n')])), + ('insertTranslation', + ('timefmt', + [('rawtextOffset', ('2:32 pm', 7))], + '$here/currentTime$')), + ('rawtextOffset', ('
', 7))], + None, + 0)), + ('endScope', ()), + ('rawtextOffset', ('... beep!', 9))])), + ('endScope', ()), + ('rawtextColumn', ('\n', 0)) + ]) + + def test_i18n_name_around_tal_content(self): + # input/test28.html + self._run_check('''\ +

Your contact email address is recorded as + + user@host.com +

+''', [('setPosition', (1, 0)), + ('beginScope', {'i18n:translate': 'verify'}), + ('startTag', ('p', [('i18n:translate', 'verify', 'i18n')])), + ('insertTranslation', + ('verify', + [('rawtextBeginScope', + ('Your contact email address is recorded as\n ', + 4, + (2, 4), + 0, + {'i18n:name': 'email', 'tal:omit-tag': ''})), + ('i18nVariable', + ('email', + [('optTag', + ('span', + '', + None, + 0, + [('startTag', + ('span', + [('tal:omit-tag', '', 'tal'), + ('i18n:name', 'email', 'i18n')]))], + [('rawtextBeginScope', + ('\n ', + 4, + (3, 4), + 0, + {'href': 'mailto:user@example.com', + 'tal:content': 'request/submitter'})), + ('startTag', + ('a', + [('href', 'href="mailto:user@example.com"'), + ('tal:content', 'request/submitter', 'tal')])), + ('insertText', + ('$request/submitter$', + [('rawtextOffset', ('user@host.com', 13))])), + ('endScope', ()), + ('rawtextOffset', ('', 4))]))], + None, + 0)), + ('endScope', ()), + ('rawtextColumn', ('\n', 0))])), + ('endScope', ()), + ('rawtextColumn', ('

\n', 0)) + ]) + + def test_i18n_name_with_tal_content(self): + # input/test27.html + self._run_check('''\ +

Your contact email address is recorded as + user@host.com +

+''', [ + ('setPosition', (1, 0)), + ('beginScope', {'i18n:translate': 'verify'}), + ('startTag', ('p', [('i18n:translate', 'verify', 'i18n')])), + ('insertTranslation', + ('verify', + [('rawtextBeginScope', + ('Your contact email address is recorded as\n ', + 4, + (2, 4), + 0, + {'href': 'mailto:user@example.com', + 'i18n:name': 'email', + 'tal:content': 'request/submitter'})), + ('i18nVariable', + ('email', + [('startTag', + ('a', + [('href', 'href="mailto:user@example.com"'), + ('tal:content', 'request/submitter', 'tal'), + ('i18n:name', 'email', 'i18n')])), + ('insertText', + ('$request/submitter$', + [('rawtextOffset', ('user@host.com', 13))])), + ('rawtextOffset', ('', 4))], + None, + 0)), + ('endScope', ()), + ('rawtextColumn', ('\n', 0))])), + ('endScope', ()), + ('rawtextColumn', ('

\n', 0)) + ]) + + +def test_suite(): + suite = unittest.makeSuite(HTMLTALParserTestCases) + suite.addTest(unittest.makeSuite(METALGeneratorTestCases)) + suite.addTest(unittest.makeSuite(TALGeneratorTestCases)) + return suite + + +if __name__ == "__main__": + errs = utils.run_suite(test_suite()) + sys.exit(errs and 1 or 0) diff -Nru zope3-3.4.0/src/zope/tal/tests/test_sourcepos.py zope3-3.5~bzr18/src/zope/tal/tests/test_sourcepos.py --- zope3-3.4.0/src/zope/tal/tests/test_sourcepos.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/test_sourcepos.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,93 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 TALInterpreter. + +$Id: test_sourcepos.py 30452 2005-05-20 05:13:10Z fdrake $ +""" +import unittest + +from StringIO import StringIO + +from zope.tal.htmltalparser import HTMLTALParser +from zope.tal.talinterpreter import TALInterpreter +from zope.tal.talgenerator import TALGenerator +from zope.tal.dummyengine import DummyEngine + + +page1 = ''' +
+page1= +
+''' + +main_template = ''' +main_template= +
+main_template= +
+main_template= +''' + +footer = '''
+footer= +
''' + +expected = ''' +main_template=main_template (2,14) +
+page1=page1 (3,6) +
+main_template=main_template (4,14) +
+footer=footer (2,7) +
+main_template=main_template (6,14) +''' + + + +class SourcePosTestCase(unittest.TestCase): + + def parse(self, eng, s, fn): + gen = TALGenerator(expressionCompiler=eng, xml=0, source_file=fn) + parser = HTMLTALParser(gen) + parser.parseString(s) + program, macros = parser.getCode() + return program, macros + + def test_source_positions(self): + # Ensure source file and position are set correctly by TAL + macros = {} + eng = DummyEngine(macros) + page1_program, page1_macros = self.parse(eng, page1, 'page1') + main_template_program, main_template_macros = self.parse( + eng, main_template, 'main_template') + footer_program, footer_macros = self.parse(eng, footer, 'footer') + + macros['main'] = main_template_macros['main'] + macros['foot'] = footer_macros['foot'] + + stream = StringIO() + interp = TALInterpreter(page1_program, macros, eng, stream) + interp() + self.assertEqual(stream.getvalue().strip(), expected.strip(), + "Got result:\n%s\nExpected:\n%s" + % (stream.getvalue(), expected)) + + +def test_suite(): + return unittest.makeSuite(SourcePosTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/tal/tests/test_talgettext.py zope3-3.5~bzr18/src/zope/tal/tests/test_talgettext.py --- zope3-3.4.0/src/zope/tal/tests/test_talgettext.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/test_talgettext.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,78 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 talgettext utility. + +$Id: test_talgettext.py 30452 2005-05-20 05:13:10Z fdrake $ +""" +import sys +import unittest +from StringIO import StringIO + +from zope.tal.htmltalparser import HTMLTALParser +from zope.tal.talgettext import POTALInterpreter +from zope.tal.talgettext import POEngine +from zope.tal.tests import utils + +class test_POEngine(unittest.TestCase): + """Test the PO engine functionality, which simply adds items to a catalog + as .translate is called + """ + + def test_translate(self): + test_keys = ['foo', 'bar', 'blarf', 'washington'] + + engine = POEngine() + engine.file = 'foo.pt' + for key in test_keys: + engine.translate(key, 'domain') + + for key in test_keys: + self.failIf(key not in engine.catalog['domain'], + "POEngine catalog does not properly store message ids" + ) + + def test_dynamic_msgids(self): + sample_source = """ +

+ Some + dynamic + text. +

+

+ A link. +

+ """ + p = HTMLTALParser() + p.parseString(sample_source) + program, macros = p.getCode() + engine = POEngine() + engine.file = 'sample_source' + POTALInterpreter(program, macros, engine, stream=StringIO(), + metal=False)() + msgids = [] + for domain in engine.catalog.values(): + msgids += domain.keys() + msgids.sort() + self.assertEquals(msgids, + ['A link.', + 'Some ${DYNAMIC_CONTENT} text.']) + + +def test_suite(): + suite = unittest.makeSuite(test_POEngine) + return suite + +if __name__ == "__main__": + errs = utils.run_suite(test_suite()) + sys.exit(errs and 1 or 0) diff -Nru zope3-3.4.0/src/zope/tal/tests/test_talinterpreter.py zope3-3.5~bzr18/src/zope/tal/tests/test_talinterpreter.py --- zope3-3.4.0/src/zope/tal/tests/test_talinterpreter.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/tal/tests/test_talinterpreter.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,859 @@ +# -*- coding: ISO-8859-1 -*- +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Corporation 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 TALInterpreter. + +$Id: test_talinterpreter.py 99007 2009-04-08 12:35:42Z tseaver $ +""" +import os +import sys +import unittest + +from StringIO import StringIO + +from zope.tal.taldefs import METALError, I18NError, TAL_VERSION +from zope.tal.taldefs import TALExpressionError +from zope.tal.htmltalparser import HTMLTALParser +from zope.tal.talparser import TALParser +from zope.tal.talinterpreter import TALInterpreter +from zope.tal.talgenerator import TALGenerator +from zope.tal.dummyengine import DummyEngine +from zope.tal.dummyengine import MultipleDomainsDummyEngine +from zope.tal.dummyengine import DummyTranslationDomain +from zope.tal.tests import utils +from zope.i18nmessageid import Message + + +class TestCaseBase(unittest.TestCase): + + def _compile(self, source, source_file=None): + generator = TALGenerator(xml=0, source_file=source_file) + parser = HTMLTALParser(generator) + parser.parseString(source) + program, macros = parser.getCode() + return program, macros + + +class MacroErrorsTestCase(TestCaseBase): + + def setUp(self): + dummy, macros = self._compile('

Booh

') + self.macro = macros['M'] + self.engine = DummyEngine(macros) + program, dummy = self._compile('

Bah

') + self.interpreter = TALInterpreter(program, {}, self.engine) + + def tearDown(self): + try: + self.interpreter() + except METALError: + pass + else: + self.fail("Expected METALError") + + def test_mode_error(self): + self.macro[1] = ("mode", "duh") + + def test_version_error(self): + self.macro[0] = ("version", "duh") + + +class MacroFunkyErrorTest(TestCaseBase): + + def test_div_in_p_using_macro(self): + dummy, macros = self._compile('

Booh

') + engine = DummyEngine(macros) + program, dummy = self._compile( + '

foo

') + interpreter = TALInterpreter(program, {}, engine) + + output = interpreter() + self.assertEqual(output, '

foo

') + + +class MacroExtendTestCase(TestCaseBase): + + def setUp(self): + s = self._read(('input', 'pnome_template.pt')) + self.pnome_program, pnome_macros = self._compile(s) + s = self._read(('input', 'acme_template.pt')) + self.acme_program, acme_macros = self._compile(s) + s = self._read(('input', 'document_list.pt')) + self.doclist_program, doclist_macros = self._compile(s) + macros = { + 'pnome_macros_page': pnome_macros['page'], + 'acme_macros_page': acme_macros['page'], + } + self.engine = DummyEngine(macros) + + def _read(self, path): + dir = os.path.dirname(__file__) + fn = os.path.join(dir, *path) + f = open(fn) + data = f.read() + f.close() + return data + + def test_preview_acme_template(self): + # An ACME designer is previewing the ACME design. For the + # purposes of this use case, extending a macro should act the + # same as using a macro. + result = StringIO() + interpreter = TALInterpreter( + self.acme_program, {}, self.engine, stream=result) + interpreter() + actual = result.getvalue().strip() + expected = self._read(('output', 'acme_template.html')).strip() + self.assertEqual(actual, expected) + + def test_preview_acme_template_source(self): + # Render METAL attributes in acme_template + result = StringIO() + interpreter = TALInterpreter( + self.acme_program, {}, self.engine, stream=result, tal=False) + interpreter() + actual = result.getvalue().strip() + expected = self._read(('output', 'acme_template_source.html')).strip() + self.assertEqual(actual, expected) + + +class I18NCornerTestCaseBase(TestCaseBase): + + def factory(self, msgid, default, mapping={}): + raise NotImplementedError("abstract method") + + def setUp(self): + self.engine = DummyEngine() + # Make sure we'll translate the msgid not its unicode representation + self.engine.setLocal('foo', + self.factory('FoOvAlUe${empty}', 'default', {'empty': ''})) + self.engine.setLocal('bar', 'BaRvAlUe') + + def _check(self, program, expected): + result = StringIO() + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + self.assertEqual(expected, result.getvalue()) + + def test_simple_messageid_translate(self): + # This test is mainly here to make sure our DummyEngine works + # correctly. + program, macros = self._compile( + '') + self._check(program, 'FOOVALUE') + + program, macros = self._compile( + '') + self._check(program, 'FOOVALUE') + + # i18n messages defined in Python are translated automatically + # (no i18n:translate necessary) + program, macros = self._compile( + '') + self._check(program, 'FOOVALUE') + + program, macros = self._compile( + '') + self._check(program, 'FOOVALUE') + + def test_attributes_translation(self): + program, macros = self._compile( + '') + self._check(program, '') + + program, macros = self._compile( + '') + self._check(program, '') + + program, macros = self._compile( + '') + self._check(program, '') + + # i18n messages defined in Python are translated automatically + # (no i18n:attributes necessary) + program, macros = self._compile( + '') + self._check(program, '') + + def test_text_variable_translate(self): + program, macros = self._compile( + '') + self._check(program, 'BaRvAlUe') + + program, macros = self._compile( + '') + self._check(program, 'BARVALUE') + + program, macros = self._compile( + '') + self._check(program, 'BARVALUE') + + def test_text_translate(self): + program, macros = self._compile( + '') + self._check(program, 'BaR') + + program, macros = self._compile( + '') + self._check(program, 'BAR') + + program, macros = self._compile( + '') + self._check(program, 'BAR') + + def test_structure_text_variable_translate(self): + program, macros = self._compile( + '') + self._check(program, 'BaRvAlUe') + + program, macros = self._compile( + '') + self._check(program, 'BARVALUE') + + program, macros = self._compile( + '') + self._check(program, 'BARVALUE') + + # i18n messages defined in Python are translated automatically + # (no i18n:translate necessary) + program, macros = self._compile( + '') + self._check(program, 'FOOVALUE') + + program, macros = self._compile( + '') + self._check(program, 'FOOVALUE') + + def test_structure_text_translate(self): + program, macros = self._compile( + '') + self._check(program, 'BaR') + + program, macros = self._compile( + '') + self._check(program, 'BAR') + + program, macros = self._compile( + '') + self._check(program, 'BAR') + + def test_replace_with_messageid_and_i18nname(self): + program, macros = self._compile( + '
' + '' + '
') + self._check(program, '
FOOVALUE
') + + def test_pythonexpr_replace_with_messageid_and_i18nname(self): + program, macros = self._compile( + '
' + '' + '
') + self._check(program, '
FOOVALUE
') + + def test_structure_replace_with_messageid_and_i18nname(self): + program, macros = self._compile( + '
' + '' + '
') + self._check(program, '
FOOVALUE
') + + def test_complex_replace_with_messageid_and_i18nname(self): + program, macros = self._compile( + '
' + '' + '' + '' + '
') + self._check(program, '
FOOVALUE
') + + def test_content_with_messageid_and_i18nname(self): + program, macros = self._compile( + '
' + '' + '
') + self._check(program, '
FOOVALUE
') + + def test_content_with_messageid_and_i18nname_and_i18ntranslate(self): + # Let's tell the user this is incredibly silly! + self.assertRaises( + I18NError, self._compile, + '') + + def test_content_with_explicit_messageid(self): + # Let's tell the user this is incredibly silly! + self.assertRaises( + I18NError, self._compile, + '') + + def test_content_with_plaintext_and_i18nname_and_i18ntranslate(self): + # Let's tell the user this is incredibly silly! + self.assertRaises( + I18NError, self._compile, + 'green') + + def test_translate_static_text_as_dynamic(self): + program, macros = self._compile( + '
This is text for ' + '.' + '
') + self._check(program, + '
THIS IS TEXT FOR BaRvAlUe.
') + program, macros = self._compile( + '
This is text for ' + '.' + '
') + self._check(program, + '
THIS IS TEXT FOR BARVALUE.
') + + def test_translate_static_text_as_dynamic_from_bytecode(self): + program = [('version', TAL_VERSION), + ('mode', 'html'), +('setPosition', (1, 0)), +('beginScope', {'i18n:translate': ''}), +('startTag', ('div', [('i18n:translate', '', 'i18n')])), +('insertTranslation', + ('', + [('rawtextOffset', ('This is text for ', 17)), + ('setPosition', (1, 40)), + ('beginScope', + {'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}), + ('i18nVariable', + ('bar_name', + [('startTag', + ('span', + [('i18n:translate', '', 'i18n'), + ('tal:content', 'bar', 'tal'), + ('i18n:name', 'bar_name', 'i18n')])), + ('insertTranslation', + ('', + [('insertText', ('$bar$', []))])), + ('rawtextOffset', ('
', 7))], + None, + 0)), + ('endScope', ()), + ('rawtextOffset', ('.', 1))])), +('endScope', ()), +('rawtextOffset', ('
', 6)) +] + self._check(program, + '
THIS IS TEXT FOR BARVALUE.
') + + def test_for_correct_msgids(self): + self.engine.translationDomain.clearMsgids() + result = StringIO() + #GChapelle: + #I have the feeling the i18n:translate with the i18n:name is wrong + # + #program, macros = self._compile( + # '
This is text for ' + # '.
') + program, macros = self._compile( + '
This is text for ' + '.
') + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = self.engine.translationDomain.getMsgids('default') + msgids.sort() + self.assertEqual(1, len(msgids)) + self.assertEqual('This is text for ${bar_name}.', msgids[0][0]) + self.assertEqual({'bar_name': 'BaRvAlUe'}, msgids[0][1]) + self.assertEqual( + '
THIS IS TEXT FOR BaRvAlUe.
', + result.getvalue()) + + def test_for_correct_msgids_translate_name(self): + self.engine.translationDomain.clearMsgids() + result = StringIO() + program, macros = self._compile( + '
This is text for ' + '.
') + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = self.engine.translationDomain.getMsgids('default') + msgids.sort() + self.assertEqual(2, len(msgids)) + self.assertEqual('This is text for ${bar_name}.', msgids[1][0]) + self.assertEqual({'bar_name': 'BARVALUE'}, msgids[1][1]) + self.assertEqual( + '
THIS IS TEXT FOR BARVALUE.
', + result.getvalue()) + + def test_i18ntranslate_i18nname_and_attributes(self): + # Test for Issue 301: Bug with i18n:name and i18n:translate + # on the same element + self.engine.translationDomain.clearMsgids() + result = StringIO() + program, macros = self._compile( + '

' + 'Some static text and a link text.

') + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = self.engine.translationDomain.getMsgids('default') + msgids.sort() + self.assertEqual(2, len(msgids)) + self.assertEqual('Some static text and a ${link}.', msgids[0][0]) + self.assertEqual({'link': 'LINK TEXT'}, msgids[0][1]) + self.assertEqual('link text', msgids[1][0]) + self.assertEqual( + '

SOME STATIC TEXT AND A LINK TEXT.

', + result.getvalue()) + + def test_for_raw_msgids(self): + # Test for Issue 314: i18n:translate removes line breaks from + #
...
contents + # HTML mode + self.engine.translationDomain.clearMsgids() + result = StringIO() + program, macros = self._compile( + '
This is text\n' + ' \tfor\n div.
' + '
 This is text\n'
+            ' \tfor\n pre. 
') + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = self.engine.translationDomain.getMsgids('default') + msgids.sort() + self.assertEqual(2, len(msgids)) + self.assertEqual(' This is text\n \tfor\n pre. ', msgids[0][0]) + self.assertEqual('This is text for div.', msgids[1][0]) + self.assertEqual( + '
THIS IS TEXT FOR DIV.
' + '
 THIS IS TEXT\n \tFOR\n PRE. 
', + result.getvalue()) + + # XML mode + self.engine.translationDomain.clearMsgids() + result = StringIO() + parser = TALParser() + parser.parseString( + '\n' + '
 This is text\n'
+            ' \tfor\n barvalue. 
') + program, macros = parser.getCode() + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = self.engine.translationDomain.getMsgids('default') + msgids.sort() + self.assertEqual(1, len(msgids)) + self.assertEqual('This is text for barvalue.', msgids[0][0]) + self.assertEqual( + '\n' + '
THIS IS TEXT  FOR BARVALUE.
', + result.getvalue()) + + def test_raw_msgids_and_i18ntranslate_i18nname(self): + self.engine.translationDomain.clearMsgids() + result = StringIO() + program, macros = self._compile( + '
This is text\n \tfor\n' + '
 \tbar\n 
.
') + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + msgids = self.engine.translationDomain.getMsgids('default') + msgids.sort() + self.assertEqual(2, len(msgids)) + self.assertEqual(' \tbar\n ', msgids[0][0]) + self.assertEqual('This is text for ${bar}.', msgids[1][0]) + self.assertEqual({'bar': '
 \tBAR\n 
'}, msgids[1][1]) + self.assertEqual( + u'
THIS IS TEXT FOR
 \tBAR\n 
.
', + result.getvalue()) + + def test_for_handling_unicode_vars(self): + # Make sure that non-ASCII Unicode is substituted correctly. + # http://collector.zope.org/Zope3-dev/264 + program, macros = self._compile( + "
" + "Foo
") + self._check(program, u"
FOO \u00C0
") + +class I18NCornerTestCaseMessage(I18NCornerTestCaseBase): + + def factory(self, msgid, default=None, mapping={}, domain=None): + return Message(msgid, domain=domain, default=default, mapping=mapping) + +class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage): + + def setUp(self): + # MultipleDomainsDummyEngine is a Engine + # where default domain transforms to uppercase + self.engine = MultipleDomainsDummyEngine() + self.engine.setLocal('foo', + self.factory('FoOvAlUe${empty}', 'default', {'empty': ''})) + self.engine.setLocal('bar', 'BaRvAlUe') + self.engine.setLocal('baz', + self.factory('BaZvAlUe', 'default', {})) + # Message ids with different domains + self.engine.setLocal('toupper', + self.factory('ToUpper', 'default', {})) + self.engine.setLocal('tolower', + self.factory('ToLower', 'default', {}, domain='lower')) + + def test_multiple_domains(self): + program, macros = self._compile( + '
') + self._check(program, '
TOUPPER
') + program, macros = self._compile( + '
') + self._check(program, '
tolower
') + program, macros = self._compile( + '
') + self._check(program, '
TOUPPER
') + program, macros = self._compile( + '
') + self._check(program, '
tolower
') + program, macros = self._compile( + '
') + self._check(program, '
TOUPPER
') + program, macros = self._compile( + '
') + self._check(program, '
tolower
') + + def test_unused_explicit_domain(self): + #a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine + #is a domain that transforms to lowercase + self.engine.setLocal('othertolower', + self.factory('OtherToLower', 'a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine', {}, domain='lower')) + program, macros = self._compile( + '
') + self._check(program, '
othertolower
') + #takes domain into account for strings + program, macros = self._compile( + '
') + self._check(program, '
tolower
') + #but not for messageids + program, macros = self._compile( + '
') + self._check(program, '
BAZVALUE
') + +class ScriptTestCase(TestCaseBase): + + def setUp(self): + self.engine = DummyEngine() + + def _check(self, program, expected): + result = StringIO() + self.interpreter = TALInterpreter(program, {}, self.engine, + stream=result) + self.interpreter() + self.assertEqual(expected, result.getvalue()) + + def test_simple(self): + program, macros = self._compile( + '

print "hello"

') + self._check(program, '

hello\n

') + + def test_script_and_tal_block(self): + program, macros = self._compile( + '\n' + ' global x\n' + ' x = 1\n' + '\n' + '') + self._check(program, '\n1') + self.assertEqual(self.engine.codeGlobals['x'], 1) + + def test_script_and_tal_block_having_inside_print(self): + program, macros = self._compile( + '\n' + ' print "hello"' + '') + self._check(program, 'hello\n') + + def test_script_and_omittag(self): + program, macros = self._compile( + '

\n' + ' print "hello"' + '

') + self._check(program, 'hello\n') + + def test_script_and_inside_tags(self): + program, macros = self._compile( + '

\n' + ' print "hello"' + '

') + self._check(program, 'hello\n') + + def test_script_and_inside_tags_with_tal(self): + program, macros = self._compile( + '

') + self._check(program, 'hello\n') + + def test_html_script(self): + program, macros = self._compile( + '') + self._check(program, 'Hello world!\n') + + def test_html_script_and_javascript(self): + program, macros = self._compile( + '') + self._check(program, + ' + diff -Nru zope3-3.4.0/src/zope/viewlet/javascript_viewlet.pt zope3-3.5~bzr18/src/zope/viewlet/javascript_viewlet.pt --- zope3-3.4.0/src/zope/viewlet/javascript_viewlet.pt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/viewlet/javascript_viewlet.pt 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,2 @@ + diff -Nru zope3-3.4.0/src/zope/viewlet/manager.py zope3-3.5~bzr18/src/zope/viewlet/manager.py --- zope3-3.4.0/src/zope/viewlet/manager.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/viewlet/manager.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,189 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Content Provider Manager implementation + +$Id: manager.py 112059 2010-05-05 19:40:35Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import zope.component +import zope.interface +import zope.security +import zope.event +from zope.browserpage import ViewPageTemplateFile + +from zope.viewlet import interfaces +from zope.location.interfaces import ILocation +from zope.contentprovider.interfaces import BeforeUpdateEvent + +class ViewletManagerBase(object): + """The Viewlet Manager Base + + A generic manager class which can be instantiated + """ + zope.interface.implements(interfaces.IViewletManager) + template = None + + def __init__(self, context, request, view): + self.__updated = False + self.__parent__ = view + self.context = context + self.request = request + + def __getitem__(self, name): + """See zope.interface.common.mapping.IReadMapping""" + # Find the viewlet + viewlet = zope.component.queryMultiAdapter( + (self.context, self.request, self.__parent__, self), + interfaces.IViewlet, name=name) + + # If the viewlet was not found, then raise a lookup error + if viewlet is None: + raise zope.component.interfaces.ComponentLookupError( + 'No provider with name `%s` found.' %name) + + # If the viewlet cannot be accessed, then raise an + # unauthorized error + if not zope.security.canAccess(viewlet, 'render'): + raise zope.security.interfaces.Unauthorized( + 'You are not authorized to access the provider ' + 'called `%s`.' %name) + + # Return the viewlet. + return viewlet + + def get(self, name, default=None): + """See zope.interface.common.mapping.IReadMapping""" + try: + return self[name] + except (zope.component.interfaces.ComponentLookupError, + zope.security.interfaces.Unauthorized): + return default + + def __contains__(self, name): + """See zope.interface.common.mapping.IReadMapping""" + return bool(self.get(name, False)) + + def filter(self, viewlets): + """Sort out all content providers + + ``viewlets`` is a list of tuples of the form (name, viewlet). + """ + # Only return viewlets accessible to the principal + return [(name, viewlet) for name, viewlet in viewlets + if zope.security.canAccess(viewlet, 'render')] + + def sort(self, viewlets): + """Sort the viewlets. + + ``viewlets`` is a list of tuples of the form (name, viewlet). + """ + # By default, use the standard Python way of doing sorting. + return sorted(viewlets, lambda x, y: cmp(x[1], y[1])) + + def update(self): + """See zope.contentprovider.interfaces.IContentProvider""" + self.__updated = True + + # Find all content providers for the region + viewlets = zope.component.getAdapters( + (self.context, self.request, self.__parent__, self), + interfaces.IViewlet) + + viewlets = self.filter(viewlets) + viewlets = self.sort(viewlets) + # Just use the viewlets from now on + self.viewlets=[] + for name, viewlet in viewlets: + if ILocation.providedBy(viewlet): + viewlet.__name__ = name + self.viewlets.append(viewlet) + self._updateViewlets() + + def _updateViewlets(self): + """Calls update on all viewlets and fires events""" + for viewlet in self.viewlets: + zope.event.notify(BeforeUpdateEvent(viewlet, self.request)) + viewlet.update() + + def render(self): + """See zope.contentprovider.interfaces.IContentProvider""" + # Now render the view + if self.template: + return self.template(viewlets=self.viewlets) + else: + return u'\n'.join([viewlet.render() for viewlet in self.viewlets]) + + +def ViewletManager(name, interface, template=None, bases=()): + + attrDict = {'__name__' : name} + if template is not None: + attrDict['template'] = ViewPageTemplateFile(template) + + if ViewletManagerBase not in bases: + # Make sure that we do not get a default viewlet manager mixin, if the + # provided base is already a full viewlet manager implementation. + if not (len(bases) == 1 and + interfaces.IViewletManager.implementedBy(bases[0])): + bases = bases + (ViewletManagerBase,) + + ViewletManager = type( + '' % interface.getName(), bases, attrDict) + zope.interface.classImplements(ViewletManager, interface) + return ViewletManager + + +def getWeight((name, viewlet)): + try: + return int(viewlet.weight) + except AttributeError: + return 0 + + +class WeightOrderedViewletManager(ViewletManagerBase): + """Weight ordered viewlet managers.""" + + def sort(self, viewlets): + return sorted(viewlets, key=getWeight) + + def render(self): + """See zope.contentprovider.interfaces.IContentProvider""" + # do not render a manager template if no viewlets are avaiable + if not self.viewlets: + return u'' + elif self.template: + return self.template(viewlets=self.viewlets) + else: + return u'\n'.join([viewlet.render() for viewlet in self.viewlets]) + + +def isAvailable(viewlet): + try: + return zope.security.canAccess(viewlet, 'render') and viewlet.available + except AttributeError: + return True + + +class ConditionalViewletManager(WeightOrderedViewletManager): + """Conditional weight ordered viewlet managers.""" + + def filter(self, viewlets): + """Sort out all viewlets which are explicit not available + + ``viewlets`` is a list of tuples of the form (name, viewlet). + """ + return [(name, viewlet) for name, viewlet in viewlets + if isAvailable(viewlet)] + diff -Nru zope3-3.4.0/src/zope/viewlet/metaconfigure.py zope3-3.5~bzr18/src/zope/viewlet/metaconfigure.py --- zope3-3.4.0/src/zope/viewlet/metaconfigure.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/viewlet/metaconfigure.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,224 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Viewlet metadconfigure + +$Id: metaconfigure.py 112059 2010-05-05 19:40:35Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import os + +from zope.security import checker +from zope.configuration.exceptions import ConfigurationError +from zope.interface import Interface, classImplements +from zope.publisher.interfaces.browser import IDefaultBrowserLayer +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.publisher.interfaces.browser import IBrowserView +from zope.component import zcml +from zope.component.interface import provideInterface +from zope.viewlet import viewlet, manager, interfaces + +def viewletManagerDirective( + _context, name, permission, + for_=Interface, layer=IDefaultBrowserLayer, view=IBrowserView, + provides=interfaces.IViewletManager, class_=None, template=None, + allowed_interface=None, allowed_attributes=None): + + # A list of attributes available under the provided permission + required = {} + + # Get the permission; mainly to correctly handle CheckerPublic. + permission = _handle_permission(_context, permission) + + # If class is not given we use the basic viewlet manager. + if class_ is None: + class_ = manager.ViewletManagerBase + + # Make sure that the template exists and that all low-level API methods + # have the right permission. + if template: + template = os.path.abspath(str(_context.path(template))) + if not os.path.isfile(template): + raise ConfigurationError("No such file", template) + required['__getitem__'] = permission + + # Create a new class based on the template and class. + new_class = manager.ViewletManager( + name, provides, template=template, bases=(class_, )) + else: + # Create a new class based on the class. + new_class = manager.ViewletManager(name, provides, bases=(class_, )) + + # Register some generic attributes with the security dictionary + for attr_name in ('browserDefault', 'update', 'render', 'publishTraverse'): + required[attr_name] = permission + + # Register the ``provides`` interface and register fields in the security + # dictionary + _handle_allowed_interface( + _context, (provides,), permission, required) + + # Register the allowed interface and add the field's security entries + _handle_allowed_interface( + _context, allowed_interface, permission, required) + + # Register single allowed attributes in the security dictionary + _handle_allowed_attributes( + _context, allowed_attributes, permission, required) + + # Register interfaces + _handle_for(_context, for_) + zcml.interface(_context, view) + + # Create a checker for the viewlet manager + checker.defineChecker(new_class, checker.Checker(required)) + + # register a viewlet manager + _context.action( + discriminator = ('viewletManager', for_, layer, view, name), + callable = zcml.handler, + args = ('registerAdapter', + new_class, (for_, layer, view), provides, name, + _context.info),) + + +def viewletDirective( + _context, name, permission, + for_=Interface, layer=IDefaultBrowserLayer, view=IBrowserView, + manager=interfaces.IViewletManager, class_=None, template=None, + attribute='render', allowed_interface=None, allowed_attributes=None, + **kwargs): + + # Security map dictionary + required = {} + + # Get the permission; mainly to correctly handle CheckerPublic. + permission = _handle_permission(_context, permission) + + # Either the class or template must be specified. + if not (class_ or template): + raise ConfigurationError("Must specify a class or template") + + # Make sure that all the non-default attribute specifications are correct. + if attribute != 'render': + if template: + raise ConfigurationError( + "Attribute and template cannot be used together.") + + # Note: The previous logic forbids this condition to evere occur. + if not class_: + raise ConfigurationError( + "A class must be provided if attribute is used") + + # Make sure that the template exists and that all low-level API methods + # have the right permission. + if template: + template = os.path.abspath(str(_context.path(template))) + if not os.path.isfile(template): + raise ConfigurationError("No such file", template) + required['__getitem__'] = permission + + # Make sure the has the right form, if specified. + if class_: + if attribute != 'render': + if not hasattr(class_, attribute): + raise ConfigurationError( + "The provided class doesn't have the specified attribute " + ) + if template: + # Create a new class for the viewlet template and class. + new_class = viewlet.SimpleViewletClass( + template, bases=(class_, ), attributes=kwargs, name=name) + else: + if not hasattr(class_, 'browserDefault'): + cdict = {'browserDefault': + lambda self, request: (getattr(self, attribute), ())} + else: + cdict = {} + + cdict['__name__'] = name + cdict['__page_attribute__'] = attribute + cdict.update(kwargs) + new_class = type(class_.__name__, + (class_, viewlet.SimpleAttributeViewlet), cdict) + + if hasattr(class_, '__implements__'): + classImplements(new_class, IBrowserPublisher) + + else: + # Create a new class for the viewlet template alone. + new_class = viewlet.SimpleViewletClass(template, name=name, + attributes=kwargs) + + # Set up permission mapping for various accessible attributes + _handle_allowed_interface( + _context, allowed_interface, permission, required) + _handle_allowed_attributes( + _context, allowed_attributes, permission, required) + _handle_allowed_attributes( + _context, kwargs.keys(), permission, required) + _handle_allowed_attributes( + _context, + (attribute, 'browserDefault', 'update', 'render', 'publishTraverse'), + permission, required) + + # Register the interfaces. + _handle_for(_context, for_) + zcml.interface(_context, view) + + # Create the security checker for the new class + checker.defineChecker(new_class, checker.Checker(required)) + + # register viewlet + _context.action( + discriminator = ('viewlet', for_, layer, view, manager, name), + callable = zcml.handler, + args = ('registerAdapter', + new_class, (for_, layer, view, manager), interfaces.IViewlet, + name, _context.info),) + +def _handle_permission(_context, permission): + if permission == 'zope.Public': + permission = checker.CheckerPublic + + return permission + +def _handle_allowed_interface(_context, allowed_interface, permission, + required): + # Allow access for all names defined by named interfaces + if allowed_interface: + for i in allowed_interface: + _context.action( + discriminator = None, + callable = provideInterface, + args = (None, i) + ) + + for name in i: + required[name] = permission + +def _handle_allowed_attributes(_context, allowed_attributes, permission, + required): + # Allow access for all named attributes + if allowed_attributes: + for name in allowed_attributes: + required[name] = permission + +def _handle_for(_context, for_): + if for_ is not None: + _context.action( + discriminator = None, + callable = provideInterface, + args = ('', for_) + ) diff -Nru zope3-3.4.0/src/zope/viewlet/metadirectives.py zope3-3.5~bzr18/src/zope/viewlet/metadirectives.py --- zope3-3.4.0/src/zope/viewlet/metadirectives.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/viewlet/metadirectives.py 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,160 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Viewlet metadirective + +$Id: metadirectives.py 112059 2010-05-05 19:40:35Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import zope.configuration.fields +import zope.schema +from zope.publisher.interfaces import browser +from zope.security.zcml import Permission +from zope.i18nmessageid import MessageFactory +from zope.interface import Interface +_ = MessageFactory('zope') + +from zope.viewlet import interfaces + + +class IContentProvider(Interface): + """A directive to register a simple content provider. + + Content providers are registered by their context (`for` attribute), the + request (`layer` attribute) and the view (`view` attribute). They also + must provide a name, so that they can be found using the TALES + ``provider`` namespace. Other than that, content providers are just like + any other views. + """ + + view = zope.configuration.fields.GlobalObject( + title=_("The view the content provider is registered for."), + description=_("The view can either be an interface or a class. By " + "default the provider is registered for all views, " + "the most common case."), + required=False, + default=browser.IBrowserView) + + name = zope.schema.TextLine( + title=_("The name of the content provider."), + description=_("The name of the content provider is used in the TALES " + "``provider`` namespace to look up the content " + "provider."), + required=True) + + for_ = zope.configuration.fields.GlobalObject( + title=u"The interface or class this view is for.", + required=False + ) + + permission = Permission( + title=u"Permission", + description=u"The permission needed to use the view.", + required=True + ) + + class_ = zope.configuration.fields.GlobalObject( + title=_("Class"), + description=_("A class that provides attributes used by the view."), + required=False, + ) + + layer = zope.configuration.fields.GlobalInterface( + title=_("The layer the view is in."), + description=_(""" + A skin is composed of layers. It is common to put skin + specific views in a layer named after the skin. If the 'layer' + attribute is not supplied, it defaults to 'default'."""), + required=False, + ) + + allowed_interface = zope.configuration.fields.Tokens( + title=_("Interface that is also allowed if user has permission."), + description=_(""" + By default, 'permission' only applies to viewing the view and + any possible sub views. By specifying this attribute, you can + make the permission also apply to everything described in the + supplied interface. + + Multiple interfaces can be provided, separated by + whitespace."""), + required=False, + value_type=zope.configuration.fields.GlobalInterface(), + ) + + allowed_attributes = zope.configuration.fields.Tokens( + title=_("View attributes that are also allowed if the user" + " has permission."), + description=_(""" + By default, 'permission' only applies to viewing the view and + any possible sub views. By specifying 'allowed_attributes', + you can make the permission also apply to the extra attributes + on the view object."""), + required=False, + value_type=zope.configuration.fields.PythonIdentifier(), + ) + + +class ITemplatedContentProvider(IContentProvider): + """A directive for registering a content provider that uses a page + template to provide its content.""" + + template = zope.configuration.fields.Path( + title=_("Content-generating template."), + description=_("Refers to a file containing a page template (should " + "end in extension ``.pt`` or ``.html``)."), + required=False) + + +class IViewletManagerDirective(ITemplatedContentProvider): + """A directive to register a new viewlet manager. + + Viewlet manager registrations are very similar to content provider + registrations, since they are just a simple extension of content + providers. However, viewlet managers commonly have a specific provided + interface, which is used to discriminate the viewlets they are providing. + """ + + provides = zope.configuration.fields.GlobalInterface( + title=_("The interface this viewlet manager provides."), + description=_("A viewlet manager can provide an interface, which " + "is used to lookup its contained viewlets."), + required=False, + default=interfaces.IViewletManager, + ) + + +class IViewletDirective(ITemplatedContentProvider): + """A directive to register a new viewlet. + + Viewlets are content providers that can only be displayed inside a viewlet + manager. Thus they are additionally discriminated by the manager. Viewlets + can rely on the specified viewlet manager interface to provide their + content. + + The viewlet directive also supports an undefined set of keyword arguments + that are set as attributes on the viewlet after creation. Those attributes + can then be used to implement sorting and filtering, for example. + """ + + manager = zope.configuration.fields.GlobalObject( + title=_("view"), + description=u"The interface of the view this viewlet is for. " + u"(default IBrowserView)", + required=False, + default=interfaces.IViewletManager) + + +# Arbitrary keys and values are allowed to be passed to the viewlet. +IViewletDirective.setTaggedValue('keyword_arguments', True) diff -Nru zope3-3.4.0/src/zope/viewlet/meta.zcml zope3-3.5~bzr18/src/zope/viewlet/meta.zcml --- zope3-3.4.0/src/zope/viewlet/meta.zcml 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/viewlet/meta.zcml 2010-07-27 10:57:45.000000000 +0000 @@ -0,0 +1,20 @@ + + + + + + + + + + + diff -Nru zope3-3.4.0/src/zope/viewlet/README.txt zope3-3.5~bzr18/src/zope/viewlet/README.txt --- zope3-3.4.0/src/zope/viewlet/README.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/viewlet/README.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,1206 @@ +============================= +Viewlets and Viewlet Managers +============================= + +Let's start with some motivation. Using content providers allows us to insert +one piece of HTML content. In most Web development, however, you are often +interested in defining some sort of region and then allow developers to +register content for those regions. + + >>> from zope.viewlet import interfaces + + +Design Notes +------------ + +As mentioned above, besides inserting snippets of HTML at places, we more +frequently want to define a region in our page and allow specialized content +providers to be inserted based on configuration. Those specialized content +providers are known as viewlets and are only available inside viewlet +managers, which are just a more complex example of content providers. + +Unfortunately, the Java world does not implement this layer separately. The +viewlet manager is most similar to a Java "channel", but we decided against +using this name, since it is very generic and not very meaningful. The viewlet +has no Java counterpart, since Java does not implement content providers using +a component architecture and thus does not register content providers +specifically for viewlet managers, which I believe makes the Java +implementation less useful as a generic concept. In fact, the main design +goal in the Java world is the implementation of reusable and sharable +portlets. The scope for Zope 3 is larger, since we want to provide a generic +framework for building pluggable user interfaces. + + +The Viewlet Manager +------------------- + +In this implementation of viewlets, those regions are just content providers +called viewlet managers that manage a special type of content providers known +as viewlets. Every viewlet manager handles the viewlets registered for it: + + >>> class ILeftColumn(interfaces.IViewletManager): + ... """Viewlet manager located in the left column.""" + +You can then create a viewlet manager using this interface now: + + >>> from zope.viewlet import manager + >>> LeftColumn = manager.ViewletManager('left', ILeftColumn) + +Now we have to instantiate it: + + >>> import zope.interface + >>> class Content(object): + ... zope.interface.implements(zope.interface.Interface) + >>> content = Content() + + >>> from zope.publisher.browser import TestRequest + >>> request = TestRequest() + + >>> from zope.publisher.interfaces.browser import IBrowserView + >>> class View(object): + ... zope.interface.implements(IBrowserView) + ... def __init__(self, context, request): + ... pass + >>> view = View(content, request) + + >>> leftColumn = LeftColumn(content, request, view) + +So initially nothing gets rendered: + + >>> leftColumn.update() + >>> leftColumn.render() + u'' + +But now we register some viewlets for the manager + + >>> import zope.component + >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer + + >>> class WeatherBox(object): + ... zope.interface.implements(interfaces.IViewlet) + ... + ... def __init__(self, context, request, view, manager): + ... self.__parent__ = view + ... + ... def update(self): + ... pass + ... + ... def render(self): + ... return u'
It is sunny today!
' + + >>> # Create a security checker for viewlets. + >>> from zope.security.checker import NamesChecker, defineChecker + >>> viewletChecker = NamesChecker(('update', 'render')) + >>> defineChecker(WeatherBox, viewletChecker) + + >>> zope.component.provideAdapter( + ... WeatherBox, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, ILeftColumn), + ... interfaces.IViewlet, name='weather') + + >>> from zope.location.interfaces import ILocation + >>> class SportBox(object): + ... zope.interface.implements(interfaces.IViewlet, + ... ILocation) + ... + ... def __init__(self, context, request, view, manager): + ... self.__parent__ = view + ... + ... def update(self): + ... pass + ... + ... def render(self): + ... return u'
Patriots (23) : Steelers (7)
' + + >>> defineChecker(SportBox, viewletChecker) + + >>> zope.component.provideAdapter( + ... SportBox, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, ILeftColumn), + ... interfaces.IViewlet, name='sport') + +and thus the left column is filled. Note that also events get fired +before viewlets are updated. We register a simple handler to +demonstrate this behaviour. + + >>> from zope.contentprovider.interfaces import IBeforeUpdateEvent + >>> events = [] + >>> def handler(ev): + ... events.append(ev) + >>> zope.component.provideHandler(handler, (IBeforeUpdateEvent,)) + >>> leftColumn.update() + >>> [(ev, ev.object.__class__.__name__) for ev in events] + [(, 'SportBox'), + (, 'WeatherBox')] + + >>> print leftColumn.render() +
Patriots (23) : Steelers (7)
+
It is sunny today!
+ +But this is of course pretty lame, since there is no way of specifying how the +viewlets are put together. But we have a solution. The second argument of the +``ViewletManager()`` function is a template in which we can specify how the +viewlets are put together: + + >>> import os, tempfile + >>> temp_dir = tempfile.mkdtemp() + >>> leftColTemplate = os.path.join(temp_dir, 'leftCol.pt') + >>> open(leftColTemplate, 'w').write(''' + ...
+ ... + ...
+ ... ''') + + >>> LeftColumn = manager.ViewletManager('left', ILeftColumn, + ... template=leftColTemplate) + >>> leftColumn = LeftColumn(content, request, view) + +TODO: Fix this silly thing; viewlets should be directly available. + +As you can see, the viewlet manager provides a global ``options/viewlets`` +variable that is an iterable of all the available viewlets in the correct +order: + + >>> leftColumn.update() + >>> print leftColumn.render().strip() +
+
Patriots (23) : Steelers (7)
+
It is sunny today!
+
+ +If a viewlet provides ILocation the ``__name__`` attribute of the +viewlet is set to the name under which the viewlet is registered. + + >>> [getattr(viewlet, '__name__', None) for viewlet in leftColumn.viewlets] + [u'sport', None] + + +You can also lookup the viewlets directly for management purposes: + + >>> leftColumn['weather'] + + >>> leftColumn.get('weather') + + +The viewlet manager also provides the __contains__ method defined in +IReadMapping: + + >>> 'weather' in leftColumn + True + + >>> 'unknown' in leftColumn + False + +If the viewlet is not found, then the expected behavior is provided: + + >>> leftColumn['stock'] + Traceback (most recent call last): + ... + ComponentLookupError: No provider with name `stock` found. + + >>> leftColumn.get('stock') is None + True + +Customizing the default Viewlet Manager +--------------------------------------- + +One important feature of any viewlet manager is to be able to filter and sort +the viewlets it is displaying. The default viewlet manager that we have been +using in the tests above, supports filtering by access availability and +sorting via the viewlet's ``__cmp__()`` method (default). You can easily +override this default policy by providing a base viewlet manager class. + +In our case we will manage the viewlets using a global list: + + >>> shown = ['weather', 'sport'] + +The viewlet manager base class now uses this list: + + >>> class ListViewletManager(object): + ... + ... def filter(self, viewlets): + ... viewlets = super(ListViewletManager, self).filter(viewlets) + ... return [(name, viewlet) + ... for name, viewlet in viewlets + ... if name in shown] + ... + ... def sort(self, viewlets): + ... viewlets = dict(viewlets) + ... return [(name, viewlets[name]) for name in shown] + +Let's now create a new viewlet manager: + + >>> LeftColumn = manager.ViewletManager( + ... 'left', ILeftColumn, bases=(ListViewletManager,), + ... template=leftColTemplate) + >>> leftColumn = LeftColumn(content, request, view) + +So we get the weather box first and the sport box second: + + >>> leftColumn.update() + >>> print leftColumn.render().strip() +
+
It is sunny today!
+
Patriots (23) : Steelers (7)
+
+ +Now let's change the order... + + >>> shown.reverse() + +and the order should switch as well: + + >>> leftColumn.update() + >>> print leftColumn.render().strip() +
+
Patriots (23) : Steelers (7)
+
It is sunny today!
+
+ +Of course, we also can remove a shown viewlet: + + >>> weather = shown.pop() + >>> leftColumn.update() + >>> print leftColumn.render().strip() +
+
Patriots (23) : Steelers (7)
+
+ + +WeightOrderedViewletManager +--------------------------- + +The weight ordered viewlet manager offers ordering viewlets by a additional +weight argument. Viewlets which doesn't provide a weight attribute will get +a weight of 0 (zero). + +Let's define a new column: + + >>> class IWeightedColumn(interfaces.IViewletManager): + ... """Column with weighted viewlet manager.""" + +First register a template for the weight ordered viewlet manager: + + >>> weightedColTemplate = os.path.join(temp_dir, 'weightedColTemplate.pt') + >>> open(weightedColTemplate, 'w').write(''' + ...
+ ... + ...
+ ... ''') + +And create a new weight ordered viewlet manager: + + >>> from zope.viewlet.manager import WeightOrderedViewletManager + >>> WeightedColumn = manager.ViewletManager( + ... 'left', IWeightedColumn, bases=(WeightOrderedViewletManager,), + ... template=weightedColTemplate) + >>> weightedColumn = WeightedColumn(content, request, view) + +Let's create some viewlets: + + >>> from zope.viewlet import viewlet + >>> class FirstViewlet(viewlet.ViewletBase): + ... + ... weight = 1 + ... + ... def render(self): + ... return u'
first
' + + >>> class SecondViewlet(viewlet.ViewletBase): + ... + ... weight = 2 + ... + ... def render(self): + ... return u'
second
' + + >>> class ThirdViewlet(viewlet.ViewletBase): + ... + ... weight = 3 + ... + ... def render(self): + ... return u'
third
' + + >>> class UnWeightedViewlet(viewlet.ViewletBase): + ... + ... def render(self): + ... return u'
unweighted
' + + >>> defineChecker(FirstViewlet, viewletChecker) + >>> defineChecker(SecondViewlet, viewletChecker) + >>> defineChecker(ThirdViewlet, viewletChecker) + >>> defineChecker(UnWeightedViewlet, viewletChecker) + + >>> zope.component.provideAdapter( + ... ThirdViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IWeightedColumn), + ... interfaces.IViewlet, name='third') + + >>> zope.component.provideAdapter( + ... FirstViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IWeightedColumn), + ... interfaces.IViewlet, name='first') + + >>> zope.component.provideAdapter( + ... SecondViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IWeightedColumn), + ... interfaces.IViewlet, name='second') + + >>> zope.component.provideAdapter( + ... UnWeightedViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IWeightedColumn), + ... interfaces.IViewlet, name='unweighted') + +And check the order: + + >>> weightedColumn.update() + >>> print weightedColumn.render().strip() +
+
unweighted
+
first
+
second
+
third
+
+ + +ConditionalViewletManager +------------------------- + +The conditional ordered viewlet manager offers ordering viewlets by a +additional weight argument and filters by the available attribute if a +supported by the viewlet. Viewlets which doesn't provide a available attribute +will not get skipped. The default weight value for viewlets which doesn't +provide a weight attribute is 0 (zero). + +Let's define a new column: + + >>> class IConditionalColumn(interfaces.IViewletManager): + ... """Column with weighted viewlet manager.""" + +First register a template for the weight ordered viewlet manager: + + >>> conditionalColTemplate = os.path.join(temp_dir, + ... 'conditionalColTemplate.pt') + >>> open(conditionalColTemplate, 'w').write(''' + ...
+ ... + ...
+ ... ''') + +And create a new conditional viewlet manager: + + >>> from zope.viewlet.manager import ConditionalViewletManager + >>> ConditionalColumn = manager.ViewletManager( + ... 'left', IConditionalColumn, bases=(ConditionalViewletManager,), + ... template=conditionalColTemplate) + >>> conditionalColumn = ConditionalColumn(content, request, view) + +Let's create some viewlets. We also use the previous viewlets supporting no +weight and or no available attribute: + + >>> from zope.viewlet import viewlet + >>> class AvailableViewlet(viewlet.ViewletBase): + ... + ... weight = 4 + ... + ... available = True + ... + ... def render(self): + ... return u'
available
' + + >>> class UnAvailableViewlet(viewlet.ViewletBase): + ... + ... weight = 5 + ... + ... available = False + ... + ... def render(self): + ... return u'
not available
' + + >>> defineChecker(AvailableViewlet, viewletChecker) + >>> defineChecker(UnAvailableViewlet, viewletChecker) + + >>> zope.component.provideAdapter( + ... ThirdViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IConditionalColumn), + ... interfaces.IViewlet, name='third') + + >>> zope.component.provideAdapter( + ... FirstViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IConditionalColumn), + ... interfaces.IViewlet, name='first') + + >>> zope.component.provideAdapter( + ... SecondViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IConditionalColumn), + ... interfaces.IViewlet, name='second') + + >>> zope.component.provideAdapter( + ... UnWeightedViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IConditionalColumn), + ... interfaces.IViewlet, name='unweighted') + + >>> zope.component.provideAdapter( + ... AvailableViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IConditionalColumn), + ... interfaces.IViewlet, name='available') + + >>> zope.component.provideAdapter( + ... UnAvailableViewlet, + ... (zope.interface.Interface, IDefaultBrowserLayer, + ... IBrowserView, IConditionalColumn), + ... interfaces.IViewlet, name='unavailable') + +And check the order: + + >>> conditionalColumn.update() + >>> print conditionalColumn.render().strip() +
+
unweighted
+
first
+
second
+
third
+
available
+
+ + +Viewlet Base Classes +-------------------- + +To make the creation of viewlets simpler, a set of useful base classes and +helper functions are provided. + +The first class is a base class that simply defines the constructor: + + >>> base = viewlet.ViewletBase('context', 'request', 'view', 'manager') + >>> base.context + 'context' + >>> base.request + 'request' + >>> base.__parent__ + 'view' + >>> base.manager + 'manager' + +But a default ``render()`` method implementation is not provided: + + >>> base.render() + Traceback (most recent call last): + ... + NotImplementedError: `render` method must be implemented by subclass. + +If you have already an existing class that produces the HTML content in some +method, then the ``SimpleAttributeViewlet`` might be for you, since it can be +used to convert any class quickly into a viewlet: + + >>> class FooViewlet(viewlet.SimpleAttributeViewlet): + ... __page_attribute__ = 'foo' + ... + ... def foo(self): + ... return 'output' + +The `__page_attribute__` attribute provides the name of the function to call for +rendering. + + >>> foo = FooViewlet('context', 'request', 'view', 'manager') + >>> foo.foo() + 'output' + >>> foo.render() + 'output' + +If you specify `render` as the attribute an error is raised to prevent +infinite recursion: + + >>> foo.__page_attribute__ = 'render' + >>> foo.render() + Traceback (most recent call last): + ... + AttributeError: render + +The same is true if the specified attribute does not exist: + + >>> foo.__page_attribute__ = 'bar' + >>> foo.render() + Traceback (most recent call last): + ... + AttributeError: 'FooViewlet' object has no attribute 'bar' + +To create simple template-based viewlets you can use the +``SimpleViewletClass()`` function. This function is very similar to its view +equivalent and is used by the ZCML directives to create viewlets. The result +of this function call will be a fully functional viewlet class. Let's start by +simply specifying a template only: + + >>> template = os.path.join(temp_dir, 'demoTemplate.pt') + >>> open(template, 'w').write('''
contents
''') + + >>> Demo = viewlet.SimpleViewletClass(template) + >>> print Demo(content, request, view, manager).render() +
contents
+ +Now let's additionally specify a class that can provide additional features: + + >>> class MyViewlet(object): + ... myAttribute = 8 + + >>> Demo = viewlet.SimpleViewletClass(template, bases=(MyViewlet,)) + >>> MyViewlet in Demo.__bases__ + True + >>> Demo(content, request, view, manager).myAttribute + 8 + +The final important feature is the ability to pass in further attributes to +the class: + + >>> Demo = viewlet.SimpleViewletClass( + ... template, attributes={'here': 'now', 'lucky': 3}) + >>> demo = Demo(content, request, view, manager) + >>> demo.here + 'now' + >>> demo.lucky + 3 + +As for all views, they must provide a name that can also be passed to the +function: + + >>> Demo = viewlet.SimpleViewletClass(template, name='demoViewlet') + >>> demo = Demo(content, request, view, manager) + >>> demo.__name__ + 'demoViewlet' + +In addition to the the generic viewlet code above, the package comes with two +viewlet base classes and helper functions for inserting CSS and Javascript +links into HTML headers, since those two are so very common. I am only going +to demonstrate the helper functions here, since those demonstrations will +fully demonstrate the functionality of the base classes as well. + +The viewlet will look up the resource it was given and tries to produce the +absolute URL for it: + + >>> class JSResource(object): + ... def __init__(self, request): + ... self.request = request + ... + ... def __call__(self): + ... return '/@@/resource.js' + + >>> zope.component.provideAdapter( + ... JSResource, + ... (IDefaultBrowserLayer,), + ... zope.interface.Interface, name='resource.js') + + >>> JSViewlet = viewlet.JavaScriptViewlet('resource.js') + >>> print JSViewlet(content, request, view, manager).render().strip() + + + +There is also a javascript viewlet base class which knows how to render more +then one javascript resource file: + + >>> class JSSecondResource(object): + ... def __init__(self, request): + ... self.request = request + ... + ... def __call__(self): + ... return '/@@/second-resource.js' + + >>> zope.component.provideAdapter( + ... JSSecondResource, + ... (IDefaultBrowserLayer,), + ... zope.interface.Interface, name='second-resource.js') + + >>> JSBundleViewlet = viewlet.JavaScriptBundleViewlet(('resource.js', + ... 'second-resource.js')) + >>> print JSBundleViewlet(content, request, view, manager).render().strip() + + + + +The same works for the CSS resource viewlet: + + >>> class CSSResource(object): + ... def __init__(self, request): + ... self.request = request + ... + ... def __call__(self): + ... return '/@@/resource.css' + + >>> zope.component.provideAdapter( + ... CSSResource, + ... (IDefaultBrowserLayer,), + ... zope.interface.Interface, name='resource.css') + + >>> CSSViewlet = viewlet.CSSViewlet('resource.css') + >>> print CSSViewlet(content, request, view, manager).render().strip() + + +You can also change the media type and the rel attribute: + + >>> CSSViewlet = viewlet.CSSViewlet('resource.css', media='print', rel='css') + >>> print CSSViewlet(content, request, view, manager).render().strip() + + +There is also a bundle viewlet for CSS links: + + >>> class CSSPrintResource(object): + ... def __init__(self, request): + ... self.request = request + ... + ... def __call__(self): + ... return '/@@/print-resource.css' + + >>> zope.component.provideAdapter( + ... CSSPrintResource, + ... (IDefaultBrowserLayer,), + ... zope.interface.Interface, name='print-resource.css') + + >>> items = [] + >>> items.append({'path':'resource.css', 'rel':'stylesheet', 'media':'all'}) + >>> items.append({'path':'print-resource.css', 'media':'print'}) + >>> CSSBundleViewlet = viewlet.CSSBundleViewlet(items) + >>> print CSSBundleViewlet(content, request, view, manager).render().strip() + + + + +A Complex Example +----------------- + +The Data +~~~~~~~~ + +So far we have only demonstrated simple (maybe overly trivial) use cases of +the viewlet system. In the following example, we are going to develop a +generic contents view for files. The step is to create a file component: + + >>> class IFile(zope.interface.Interface): + ... data = zope.interface.Attribute('Data of file.') + + >>> class File(object): + ... zope.interface.implements(IFile) + ... def __init__(self, data=''): + ... self.__name__ = '' + ... self.data = data + +Since we want to also provide the size of a file, here a simple implementation +of the ``ISized`` interface: + + >>> from zope import size + >>> class FileSized(object): + ... zope.interface.implements(size.interfaces.ISized) + ... zope.component.adapts(IFile) + ... + ... def __init__(self, file): + ... self.file = file + ... + ... def sizeForSorting(self): + ... return 'byte', len(self.file.data) + ... + ... def sizeForDisplay(self): + ... return '%i bytes' %len(self.file.data) + + >>> zope.component.provideAdapter(FileSized) + +We also need a container to which we can add files: + + >>> class Container(dict): + ... def __setitem__(self, name, value): + ... value.__name__ = name + ... super(Container, self).__setitem__(name, value) + +Here is some sample data: + + >>> container = Container() + >>> container['test.txt'] = File('Hello World!') + >>> container['mypage.html'] = File('Hello World!') + >>> container['data.xml'] = File('Hello World!') + + +The View +~~~~~~~~ + +The contents view of the container should iterate through the container and +represent the files in a table: + + >>> contentsTemplate = os.path.join(temp_dir, 'contents.pt') + >>> open(contentsTemplate, 'w').write(''' + ... + ... + ...

Contents

+ ...
+ ... + ... + ... ''') + + >>> from zope.browserpage.simpleviewclass import SimpleViewClass + >>> Contents = SimpleViewClass(contentsTemplate, name='contents.html') + + +The Viewlet Manager +~~~~~~~~~~~~~~~~~~~ + +Now we have to write our own viewlet manager. In this case we cannot use the +default implementation, since the viewlets will be looked up for each +different item: + + >>> shownColumns = [] + + >>> class ContentsViewletManager(object): + ... zope.interface.implements(interfaces.IViewletManager) + ... index = None + ... + ... def __init__(self, context, request, view): + ... self.context = context + ... self.request = request + ... self.__parent__ = view + ... + ... def update(self): + ... rows = [] + ... for name, value in self.context.items(): + ... rows.append( + ... [zope.component.getMultiAdapter( + ... (value, self.request, self.__parent__, self), + ... interfaces.IViewlet, name=colname) + ... for colname in shownColumns]) + ... [entry.update() for entry in rows[-1]] + ... self.rows = rows + ... + ... def render(self, *args, **kw): + ... return self.index(*args, **kw) + +Now we need a template to produce the contents table: + + >>> tableTemplate = os.path.join(temp_dir, 'table.pt') + >>> open(tableTemplate, 'w').write(''' + ... + ... + ... + ... + ...
+ ... + ...
+ ... ''') + +From the two pieces above, we can generate the final viewlet manager class and +register it (it's a bit tedious, I know): + + >>> from zope.browserpage import ViewPageTemplateFile + >>> ContentsViewletManager = type( + ... 'ContentsViewletManager', (ContentsViewletManager,), + ... {'index': ViewPageTemplateFile(tableTemplate)}) + + >>> zope.component.provideAdapter( + ... ContentsViewletManager, + ... (Container, IDefaultBrowserLayer, zope.interface.Interface), + ... interfaces.IViewletManager, name='contents') + +Since we have not defined any viewlets yet, the table is totally empty: + + >>> contents = Contents(container, request) + >>> print contents().strip() + + +

Contents

+
+ + + + + + + +
+
+ + + + +The Viewlets and the Final Result +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now let's create a first viewlet for the manager... + + >>> class NameViewlet(object): + ... + ... def __init__(self, context, request, view, manager): + ... self.__parent__ = view + ... self.context = context + ... + ... def update(self): + ... pass + ... + ... def render(self): + ... return self.context.__name__ + +and register it: + + >>> zope.component.provideAdapter( + ... NameViewlet, + ... (IFile, IDefaultBrowserLayer, + ... zope.interface.Interface, interfaces.IViewletManager), + ... interfaces.IViewlet, name='name') + +Note how you register the viewlet on ``IFile`` and not on the container. Now +we should be able to see the name for each file in the container: + + >>> print contents().strip() + + +

Contents

+
+ + + + + + + +
+
+ + + +Waaa, nothing there! What happened? Well, we have to tell our user preferences +that we want to see the name as a column in the table: + + >>> shownColumns = ['name'] + + >>> print contents().strip() + + +

Contents

+
+ + + + + + + + + + +
+ mypage.html +
+ data.xml +
+ test.txt +
+
+ + + +Let's now write a second viewlet that will display the size of the object for +us: + + >>> class SizeViewlet(object): + ... + ... def __init__(self, context, request, view, manager): + ... self.__parent__ = view + ... self.context = context + ... + ... def update(self): + ... pass + ... + ... def render(self): + ... return size.interfaces.ISized(self.context).sizeForDisplay() + + >>> zope.component.provideAdapter( + ... SizeViewlet, + ... (IFile, IDefaultBrowserLayer, + ... zope.interface.Interface, interfaces.IViewletManager), + ... interfaces.IViewlet, name='size') + +After we added it to the list of shown columns, + + >>> shownColumns = ['name', 'size'] + +we can see an entry for it: + + >>> print contents().strip() + + +

Contents

+
+ + + + + + + + + + + + + +
+ mypage.html + + 38 bytes +
+ data.xml + + 31 bytes +
+ test.txt + + 12 bytes +
+
+ + + +If we switch the two columns around, + + >>> shownColumns = ['size', 'name'] + +the result will be + + >>> print contents().strip() + + +

Contents

+
+ + + + + + + + + + + + + +
+ 38 bytes + + mypage.html +
+ 31 bytes + + data.xml +
+ 12 bytes + + test.txt +
+
+ + + + +Supporting Sorting +~~~~~~~~~~~~~~~~~~ + +Oftentimes you also want to batch and sort the entries in a table. Since those +two features are not part of the view logic, they should be treated with +independent components. In this example, we are going to only implement +sorting using a simple utility: + + >>> class ISorter(zope.interface.Interface): + ... + ... def sort(values): + ... """Sort the values.""" + + >>> class SortByName(object): + ... zope.interface.implements(ISorter) + ... + ... def sort(self, values): + ... return sorted(values, lambda x, y: cmp(x.__name__, y.__name__)) + + >>> zope.component.provideUtility(SortByName(), name='name') + + >>> class SortBySize(object): + ... zope.interface.implements(ISorter) + ... + ... def sort(self, values): + ... return sorted( + ... values, + ... lambda x, y: cmp(size.interfaces.ISized(x).sizeForSorting(), + ... size.interfaces.ISized(y).sizeForSorting())) + + >>> zope.component.provideUtility(SortBySize(), name='size') + +Note that we decided to give the sorter utilities the same name as the +corresponding viewlet. This convention will make our implementation of the +viewlet manager much simpler: + + >>> sortByColumn = '' + + >>> class SortedContentsViewletManager(object): + ... zope.interface.implements(interfaces.IViewletManager) + ... index = None + ... + ... def __init__(self, context, request, view): + ... self.context = context + ... self.request = request + ... self.__parent__ = view + ... + ... def update(self): + ... values = self.context.values() + ... + ... if sortByColumn: + ... sorter = zope.component.queryUtility(ISorter, sortByColumn) + ... if sorter: + ... values = sorter.sort(values) + ... + ... rows = [] + ... for value in values: + ... rows.append( + ... [zope.component.getMultiAdapter( + ... (value, self.request, self.__parent__, self), + ... interfaces.IViewlet, name=colname) + ... for colname in shownColumns]) + ... [entry.update() for entry in rows[-1]] + ... self.rows = rows + ... + ... def render(self, *args, **kw): + ... return self.index(*args, **kw) + +As you can see, the concern of sorting is cleanly separated from generating +the view code. In MVC terms that means that the controller (sort) is logically +separated from the view (viewlets). Let's now do the registration dance for +the new viewlet manager. We simply override the existing registration: + + >>> SortedContentsViewletManager = type( + ... 'SortedContentsViewletManager', (SortedContentsViewletManager,), + ... {'index': ViewPageTemplateFile(tableTemplate)}) + + >>> zope.component.provideAdapter( + ... SortedContentsViewletManager, + ... (Container, IDefaultBrowserLayer, zope.interface.Interface), + ... interfaces.IViewletManager, name='contents') + +Finally we sort the contents by name: + + >>> shownColumns = ['name', 'size'] + >>> sortByColumn = 'name' + + >>> print contents().strip() + + +

Contents

+
+ + + + + + + + + + + + + +
+ data.xml + + 31 bytes +
+ mypage.html + + 38 bytes +
+ test.txt + + 12 bytes +
+
+ + + +Now let's sort by size: + + >>> sortByColumn = 'size' + + >>> print contents().strip() + + +

Contents

+
+ + + + + + + + + + + + + +
+ test.txt + + 12 bytes +
+ data.xml + + 31 bytes +
+ mypage.html + + 38 bytes +
+
+ + + +That's it! As you can see, in a few steps we have built a pretty flexible +contents view with selectable columns and sorting. However, there is a lot of +room for extending this example: + +- Table Header: The table header cell for each column should be a different + type of viewlet, but registered under the same name. The column header + viewlet also adapts the container not the item. The header column should + also be able to control the sorting. + +- Batching: A simple implementation of batching should work very similar to + the sorting feature. Of course, efficient implementations should somehow + combine batching and sorting more effectively. + +- Sorting in ascending and descending order: Currently, you can only sort from + the smallest to the highest value; however, this limitation is almost + superficial and can easily be removed by making the sorters a bit more + flexible. + +- Further Columns: For a real application, you would want to implement other + columns, of course. You would also probably want some sort of fallback for + the case that a viewlet is not found for a particular container item and + column. + + +Cleanup +------- + + >>> import shutil + >>> shutil.rmtree(temp_dir) diff -Nru zope3-3.4.0/src/zope/viewlet/tests.py zope3-3.5~bzr18/src/zope/viewlet/tests.py --- zope3-3.4.0/src/zope/viewlet/tests.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/viewlet/tests.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,89 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Viewlet tests + +$Id: tests.py 112059 2010-05-05 19:40:35Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import os +import doctest +import sys +import unittest + +import zope.component +from zope.testing import cleanup +from zope.traversing.testing import setUp as traversingSetUp +from zope.component import eventtesting + +def setUp(test): + cleanup.setUp() + eventtesting.setUp() + traversingSetUp() + + # resource namespace setup + from zope.traversing.interfaces import ITraversable + from zope.traversing.namespace import resource + zope.component.provideAdapter( + resource, (None,), ITraversable, name = "resource") + zope.component.provideAdapter( + resource, (None, None), ITraversable, name = "resource") + + from zope.browserpage import metaconfigure + from zope.contentprovider import tales + metaconfigure.registerType('provider', tales.TALESProviderExpression) + +def tearDown(test): + cleanup.tearDown() + +class FakeModule(object): + """A fake module.""" + + def __init__(self, dict): + self.__dict = dict + + def __getattr__(self, name): + try: + return self.__dict[name] + except KeyError: + raise AttributeError(name) + +def directivesSetUp(test): + setUp(test) + test.globs['__name__'] = 'zope.viewlet.directives' + sys.modules['zope.viewlet.directives'] = FakeModule(test.globs) + +def directivesTearDown(test): + tearDown(test) + del sys.modules[test.globs['__name__']] + test.globs.clear() + +def test_suite(): + return unittest.TestSuite(( + doctest.DocFileSuite('README.txt', + setUp=setUp, tearDown=tearDown, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, + globs = {'__file__': os.path.join( + os.path.dirname(__file__), 'README.txt')} + ), + doctest.DocFileSuite('directives.txt', + setUp=directivesSetUp, tearDown=directivesTearDown, + optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, + globs = {'__file__': os.path.join( + os.path.dirname(__file__), 'directives.txt')} + ), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff -Nru zope3-3.4.0/src/zope/viewlet/viewlet.py zope3-3.5~bzr18/src/zope/viewlet/viewlet.py --- zope3-3.4.0/src/zope/viewlet/viewlet.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope/viewlet/viewlet.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,222 @@ +############################################################################## +# +# 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. +# +############################################################################## +"""Viewlet implementation + +$Id: viewlet.py 112059 2010-05-05 19:40:35Z tseaver $ +""" +__docformat__ = 'restructuredtext' + +import os +import sys +import zope.interface +from zope.traversing import api +from zope.publisher.browser import BrowserView +from zope.viewlet import interfaces + +from zope.browserpage import simpleviewclass +from zope.browserpage import ViewPageTemplateFile + +class ViewletBase(BrowserView): + """Viewlet adapter class used in meta directive as a mixin class.""" + + zope.interface.implements(interfaces.IViewlet) + + def __init__(self, context, request, view, manager): + super(ViewletBase, self).__init__(context, request) + self.__parent__ = view + self.context = context + self.request = request + self.manager = manager + + def update(self): + pass + + def render(self): + raise NotImplementedError( + '`render` method must be implemented by subclass.') + + +class SimpleAttributeViewlet(ViewletBase): + """A viewlet that uses a specified method to produce its content.""" + + def render(self, *args, **kw): + # If a class doesn't provide it's own call, then get the attribute + # given by the browser default. + + attr = self.__page_attribute__ + if attr == 'render': + raise AttributeError("render") + + meth = getattr(self, attr) + return meth(*args, **kw) + + +class simple(simpleviewclass.simple): + """Simple viewlet class supporting the ``render()`` method.""" + + render = simpleviewclass.simple.__call__ + + +def SimpleViewletClass(template, offering=None, bases=(), attributes=None, + name=u''): + """A function that can be used to generate a viewlet from a set of + information. + """ + # Get the current frame + if offering is None: + offering = sys._getframe(1).f_globals + + # Create the base class hierarchy + bases += (simple, ViewletBase) + + attrs = {'index' : ViewPageTemplateFile(template, offering), + '__name__' : name} + if attributes: + attrs.update(attributes) + + # Generate a derived view class. + class_ = type("SimpleViewletClass from %s" % template, bases, attrs) + + return class_ + + +class ResourceViewletBase(object): + """A simple viewlet for inserting references to resources. + + This is an abstract class that is expected to be used as a base only. + """ + _path = None + + def getURL(self): + resource = api.traverse(self.context, '++resource++' + self._path, + request=self.request) + return resource() + + def render(self, *args, **kw): + return self.index(*args, **kw) + + +def JavaScriptViewlet(path): + """Create a viewlet that can simply insert a javascript link.""" + src = os.path.join(os.path.dirname(__file__), 'javascript_viewlet.pt') + + klass = type('JavaScriptViewlet', + (ResourceViewletBase, ViewletBase), + {'index': ViewPageTemplateFile(src), + '_path': path}) + + return klass + + +class CSSResourceViewletBase(ResourceViewletBase): + + _media = 'all' + _rel = 'stylesheet' + + def getMedia(self): + return self._media + + def getRel(self): + return self._rel + + +def CSSViewlet(path, media="all", rel="stylesheet"): + """Create a viewlet that can simply insert a CSS link.""" + src = os.path.join(os.path.dirname(__file__), 'css_viewlet.pt') + + klass = type('CSSViewlet', + (CSSResourceViewletBase, ViewletBase), + {'index': ViewPageTemplateFile(src), + '_path': path, + '_media':media, + '_rel':rel}) + + return klass + + +class ResourceBundleViewletBase(object): + """A simple viewlet for inserting references to different resources. + + This is an abstract class that is expected to be used as a base only. + """ + _paths = None + + def getResources(self): + resources = [] + append = resources.append + for path in self._paths: + append(api.traverse(self.context, '++resource++' + path, + request=self.request)) + return resources + + def render(self, *args, **kw): + return self.index(*args, **kw) + + +def JavaScriptBundleViewlet(paths): + """Create a viewlet that can simply insert javascript links.""" + src = os.path.join(os.path.dirname(__file__), + 'javascript_bundle_viewlet.pt') + + klass = type('JavaScriptBundleViewlet', + (ResourceBundleViewletBase, ViewletBase), + {'index': ViewPageTemplateFile(src), + '_paths': paths}) + + return klass + + +class CSSResourceBundleViewletBase(object): + """A simple viewlet for inserting css references to different resources. + + There is a tuple or list of dict used for the different resource + descriptions. The list of dict uses the following format: + + ({path:'the path', media:'all', rel:'stylesheet'},...) + + The default values for media is ``all`` and the default value for rel is + ``stylesheet``. The path must be set there is no default value for the + path attribute. + + This is an abstract class that is expected to be used as a base only. + """ + _items = None + + def getResources(self): + resources = [] + append = resources.append + for item in self._items: + info = {} + info['url'] = api.traverse(self.context, + '++resource++' + item.get('path'), request=self.request) + info['media'] = item.get('media', 'all') + info['rel'] = item.get('rel', 'stylesheet') + append(info) + return resources + + def render(self, *args, **kw): + return self.index(*args, **kw) + + +def CSSBundleViewlet(items): + """Create a viewlet that can simply insert css links.""" + src = os.path.join(os.path.dirname(__file__), + 'css_bundle_viewlet.pt') + + klass = type('CSSBundleViewlet', + (CSSResourceBundleViewletBase, ViewletBase), + {'index': ViewPageTemplateFile(src), + '_items': items}) + + return klass diff -Nru zope3-3.4.0/src/Zope3.egg-info/dependency_links.txt zope3-3.5~bzr18/src/Zope3.egg-info/dependency_links.txt --- zope3-3.4.0/src/Zope3.egg-info/dependency_links.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/Zope3.egg-info/dependency_links.txt 2010-07-27 11:03:08.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/Zope3.egg-info/namespace_packages.txt zope3-3.5~bzr18/src/Zope3.egg-info/namespace_packages.txt --- zope3-3.4.0/src/Zope3.egg-info/namespace_packages.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/Zope3.egg-info/namespace_packages.txt 2010-07-27 11:03:08.000000000 +0000 @@ -0,0 +1,3 @@ +zope +zope.app +zc diff -Nru zope3-3.4.0/src/Zope3.egg-info/not-zip-safe zope3-3.5~bzr18/src/Zope3.egg-info/not-zip-safe --- zope3-3.4.0/src/Zope3.egg-info/not-zip-safe 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/Zope3.egg-info/not-zip-safe 2010-07-27 11:02:41.000000000 +0000 @@ -0,0 +1 @@ + diff -Nru zope3-3.4.0/src/Zope3.egg-info/PKG-INFO zope3-3.5~bzr18/src/Zope3.egg-info/PKG-INFO --- zope3-3.4.0/src/Zope3.egg-info/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/Zope3.egg-info/PKG-INFO 2010-07-27 11:03:08.000000000 +0000 @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: Zope3 +Version: 3.5dev +Summary: Zope3 +Home-page: UNKNOWN +Author: Zope Corporation and Contributors +Author-email: zope-dev@zope.org +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff -Nru zope3-3.4.0/src/Zope3.egg-info/SOURCES.txt zope3-3.5~bzr18/src/Zope3.egg-info/SOURCES.txt --- zope3-3.4.0/src/Zope3.egg-info/SOURCES.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/Zope3.egg-info/SOURCES.txt 2010-07-27 11:03:09.000000000 +0000 @@ -0,0 +1,1609 @@ +README.txt +setup.py +src/BTrees/IFBTree.py +src/BTrees/IIBTree.py +src/BTrees/IOBTree.py +src/BTrees/Interfaces.py +src/BTrees/LFBTree.py +src/BTrees/LLBTree.py +src/BTrees/LOBTree.py +src/BTrees/Length.py +src/BTrees/OIBTree.py +src/BTrees/OLBTree.py +src/BTrees/OOBTree.py +src/BTrees/_IFBTree.c +src/BTrees/_IFBTree.py +src/BTrees/_IIBTree.c +src/BTrees/_IIBTree.py +src/BTrees/_IOBTree.c +src/BTrees/_IOBTree.py +src/BTrees/_LFBTree.c +src/BTrees/_LFBTree.py +src/BTrees/_LLBTree.c +src/BTrees/_LLBTree.py +src/BTrees/_LOBTree.c +src/BTrees/_LOBTree.py +src/BTrees/_OIBTree.c +src/BTrees/_OIBTree.py +src/BTrees/_OLBTree.c +src/BTrees/_OLBTree.py +src/BTrees/_OOBTree.c +src/BTrees/_OOBTree.py +src/BTrees/__init__.py +src/BTrees/_fsBTree.c +src/BTrees/_fsBTree.py +src/BTrees/check.py +src/BTrees/fsBTree.py +src/BTrees/tests/__init__.py +src/BTrees/tests/testBTrees.py +src/BTrees/tests/testBTreesUnicode.py +src/BTrees/tests/testConflict.py +src/BTrees/tests/testLength.py +src/BTrees/tests/testSetOps.py +src/BTrees/tests/test_btreesubclass.py +src/BTrees/tests/test_check.py +src/BTrees/tests/test_compare.py +src/RestrictedPython/Eval.py +src/RestrictedPython/Guards.py +src/RestrictedPython/Limits.py +src/RestrictedPython/MutatingWalker.py +src/RestrictedPython/PrintCollector.py +src/RestrictedPython/RCompile.py +src/RestrictedPython/RestrictionMutator.py +src/RestrictedPython/SelectCompiler.py +src/RestrictedPython/Utilities.py +src/RestrictedPython/__init__.py +src/RestrictedPython/tests/__init__.py +src/RestrictedPython/tests/before_and_after.py +src/RestrictedPython/tests/before_and_after24.py +src/RestrictedPython/tests/before_and_after25.py +src/RestrictedPython/tests/before_and_after26.py +src/RestrictedPython/tests/class.py +src/RestrictedPython/tests/lambda.py +src/RestrictedPython/tests/restricted_module.py +src/RestrictedPython/tests/security_in_syntax.py +src/RestrictedPython/tests/security_in_syntax26.py +src/RestrictedPython/tests/testCompile.py +src/RestrictedPython/tests/testREADME.py +src/RestrictedPython/tests/testRestrictions.py +src/RestrictedPython/tests/testUtiliities.py +src/RestrictedPython/tests/unpack.py +src/RestrictedPython/tests/verify.py +src/ZConfig/__init__.py +src/ZConfig/cfgparser.py +src/ZConfig/cmdline.py +src/ZConfig/datatypes.py +src/ZConfig/info.py +src/ZConfig/loader.py +src/ZConfig/matcher.py +src/ZConfig/schema.py +src/ZConfig/schemaless.py +src/ZConfig/substitution.py +src/ZConfig/url.py +src/ZConfig/components/__init__.py +src/ZConfig/components/basic/__init__.py +src/ZConfig/components/basic/mapping.py +src/ZConfig/components/basic/tests/__init__.py +src/ZConfig/components/basic/tests/test_mapping.py +src/ZConfig/components/logger/__init__.py +src/ZConfig/components/logger/datatypes.py +src/ZConfig/components/logger/factory.py +src/ZConfig/components/logger/handlers.py +src/ZConfig/components/logger/logger.py +src/ZConfig/components/logger/loghandler.py +src/ZConfig/components/logger/tests/__init__.py +src/ZConfig/components/logger/tests/test_logger.py +src/ZConfig/tests/__init__.py +src/ZConfig/tests/support.py +src/ZConfig/tests/test_cfgimports.py +src/ZConfig/tests/test_cmdline.py +src/ZConfig/tests/test_config.py +src/ZConfig/tests/test_cookbook.py +src/ZConfig/tests/test_datatypes.py +src/ZConfig/tests/test_loader.py +src/ZConfig/tests/test_readme.py +src/ZConfig/tests/test_schema.py +src/ZConfig/tests/test_schemaless.py +src/ZConfig/tests/test_subst.py +src/ZConfig/tests/library/__init__.py +src/ZConfig/tests/library/thing/__init__.py +src/ZConfig/tests/library/widget/__init__.py +src/ZEO/ClientStorage.py +src/ZEO/CommitLog.py +src/ZEO/Exceptions.py +src/ZEO/ServerStub.py +src/ZEO/StorageServer.py +src/ZEO/TransactionBuffer.py +src/ZEO/__init__.py +src/ZEO/cache.py +src/ZEO/hash.py +src/ZEO/interfaces.py +src/ZEO/mkzeoinst.py +src/ZEO/monitor.py +src/ZEO/runzeo.py +src/ZEO/util.py +src/ZEO/zeoctl.py +src/ZEO/zeopasswd.py +src/ZEO/auth/__init__.py +src/ZEO/auth/auth_digest.py +src/ZEO/auth/base.py +src/ZEO/auth/hmac.py +src/ZEO/scripts/__init__.py +src/ZEO/scripts/parsezeolog.py +src/ZEO/scripts/tests.py +src/ZEO/scripts/timeout.py +src/ZEO/scripts/zeopack.py +src/ZEO/scripts/zeoqueue.py +src/ZEO/scripts/zeoreplay.py +src/ZEO/scripts/zeoserverlog.py +src/ZEO/scripts/zeoup.py +src/ZEO/tests/Cache.py +src/ZEO/tests/CommitLockTests.py +src/ZEO/tests/ConnectionTests.py +src/ZEO/tests/InvalidationTests.py +src/ZEO/tests/IterationTests.py +src/ZEO/tests/TestThread.py +src/ZEO/tests/ThreadTests.py +src/ZEO/tests/__init__.py +src/ZEO/tests/auth_plaintext.py +src/ZEO/tests/deadlock.py +src/ZEO/tests/forker.py +src/ZEO/tests/servertesting.py +src/ZEO/tests/speed.py +src/ZEO/tests/stress.py +src/ZEO/tests/testAuth.py +src/ZEO/tests/testConnection.py +src/ZEO/tests/testConversionSupport.py +src/ZEO/tests/testMonitor.py +src/ZEO/tests/testTransactionBuffer.py +src/ZEO/tests/testZEO.py +src/ZEO/tests/testZEO2.py +src/ZEO/tests/testZEOOptions.py +src/ZEO/tests/test_cache.py +src/ZEO/tests/zeoserver.py +src/ZEO/zrpc/__init__.py +src/ZEO/zrpc/_hmac.py +src/ZEO/zrpc/client.py +src/ZEO/zrpc/connection.py +src/ZEO/zrpc/error.py +src/ZEO/zrpc/log.py +src/ZEO/zrpc/marshal.py +src/ZEO/zrpc/server.py +src/ZEO/zrpc/smac.py +src/ZEO/zrpc/trigger.py +src/ZODB/ActivityMonitor.py +src/ZODB/BaseStorage.py +src/ZODB/ConflictResolution.py +src/ZODB/Connection.py +src/ZODB/DB.py +src/ZODB/DemoStorage.py +src/ZODB/ExportImport.py +src/ZODB/MappingStorage.py +src/ZODB/POSException.py +src/ZODB/UndoLogCompatible.py +src/ZODB/__init__.py +src/ZODB/blob.py +src/ZODB/broken.py +src/ZODB/config.py +src/ZODB/conversionhack.py +src/ZODB/dbmStorage.py +src/ZODB/fsIndex.py +src/ZODB/fsrecover.py +src/ZODB/fstools.py +src/ZODB/interfaces.py +src/ZODB/loglevels.py +src/ZODB/persistentclass.py +src/ZODB/serialize.py +src/ZODB/transact.py +src/ZODB/utils.py +src/ZODB/FileStorage/FileStorage.py +src/ZODB/FileStorage/__init__.py +src/ZODB/FileStorage/format.py +src/ZODB/FileStorage/fsdump.py +src/ZODB/FileStorage/fsoids.py +src/ZODB/FileStorage/fspack.py +src/ZODB/FileStorage/interfaces.py +src/ZODB/FileStorage/tests.py +src/ZODB/scripts/__init__.py +src/ZODB/scripts/analyze.py +src/ZODB/scripts/checkbtrees.py +src/ZODB/scripts/fsoids.py +src/ZODB/scripts/fsrefs.py +src/ZODB/scripts/fsstats.py +src/ZODB/scripts/fstail.py +src/ZODB/scripts/fstest.py +src/ZODB/scripts/migrate.py +src/ZODB/scripts/migrateblobs.py +src/ZODB/scripts/netspace.py +src/ZODB/scripts/referrers.py +src/ZODB/scripts/repozo.py +src/ZODB/scripts/simul.py +src/ZODB/scripts/space.py +src/ZODB/scripts/stats.py +src/ZODB/scripts/zodbload.py +src/ZODB/scripts/tests/__init__.py +src/ZODB/scripts/tests/test_doc.py +src/ZODB/scripts/tests/test_repozo.py +src/ZODB/tests/BasicStorage.py +src/ZODB/tests/ConflictResolution.py +src/ZODB/tests/Corruption.py +src/ZODB/tests/HistoryStorage.py +src/ZODB/tests/IteratorStorage.py +src/ZODB/tests/MTStorage.py +src/ZODB/tests/MVCCMappingStorage.py +src/ZODB/tests/MinPO.py +src/ZODB/tests/PackableStorage.py +src/ZODB/tests/PersistentStorage.py +src/ZODB/tests/ReadOnlyStorage.py +src/ZODB/tests/RecoveryStorage.py +src/ZODB/tests/RevisionStorage.py +src/ZODB/tests/StorageTestBase.py +src/ZODB/tests/Synchronization.py +src/ZODB/tests/TransactionalUndoStorage.py +src/ZODB/tests/__init__.py +src/ZODB/tests/dangle.py +src/ZODB/tests/loggingsupport.py +src/ZODB/tests/sampledm.py +src/ZODB/tests/speed.py +src/ZODB/tests/testActivityMonitor.py +src/ZODB/tests/testBroken.py +src/ZODB/tests/testCache.py +src/ZODB/tests/testConfig.py +src/ZODB/tests/testConnection.py +src/ZODB/tests/testConnectionSavepoint.py +src/ZODB/tests/testDB.py +src/ZODB/tests/testDemoStorage.py +src/ZODB/tests/testFileStorage.py +src/ZODB/tests/testMVCCMappingStorage.py +src/ZODB/tests/testMappingStorage.py +src/ZODB/tests/testPersistentList.py +src/ZODB/tests/testPersistentMapping.py +src/ZODB/tests/testRecover.py +src/ZODB/tests/testSerialize.py +src/ZODB/tests/testTimeStamp.py +src/ZODB/tests/testUtils.py +src/ZODB/tests/testZODB.py +src/ZODB/tests/test_cache.py +src/ZODB/tests/test_datamanageradapter.py +src/ZODB/tests/test_doctest_files.py +src/ZODB/tests/test_fsdump.py +src/ZODB/tests/test_storage.py +src/ZODB/tests/testblob.py +src/ZODB/tests/testconflictresolution.py +src/ZODB/tests/testcrossdatabasereferences.py +src/ZODB/tests/testfsIndex.py +src/ZODB/tests/testfsoids.py +src/ZODB/tests/testhistoricalconnections.py +src/ZODB/tests/testmvcc.py +src/ZODB/tests/testpersistentclass.py +src/ZODB/tests/util.py +src/ZODB/tests/warnhook.py +src/Zope3.egg-info/PKG-INFO +src/Zope3.egg-info/SOURCES.txt +src/Zope3.egg-info/dependency_links.txt +src/Zope3.egg-info/namespace_packages.txt +src/Zope3.egg-info/not-zip-safe +src/Zope3.egg-info/top_level.txt +src/docutils/__init__.py +src/docutils/_compat.py +src/docutils/_string_template_compat.py +src/docutils/core.py +src/docutils/examples.py +src/docutils/frontend.py +src/docutils/io.py +src/docutils/nodes.py +src/docutils/statemachine.py +src/docutils/urischemes.py +src/docutils/utils.py +src/docutils/languages/__init__.py +src/docutils/languages/af.py +src/docutils/languages/ca.py +src/docutils/languages/cs.py +src/docutils/languages/de.py +src/docutils/languages/en.py +src/docutils/languages/eo.py +src/docutils/languages/es.py +src/docutils/languages/fi.py +src/docutils/languages/fr.py +src/docutils/languages/gl.py +src/docutils/languages/he.py +src/docutils/languages/it.py +src/docutils/languages/ja.py +src/docutils/languages/nl.py +src/docutils/languages/pl.py +src/docutils/languages/pt_br.py +src/docutils/languages/ru.py +src/docutils/languages/sk.py +src/docutils/languages/sv.py +src/docutils/languages/zh_cn.py +src/docutils/languages/zh_tw.py +src/docutils/parsers/__init__.py +src/docutils/parsers/null.py +src/docutils/parsers/rst/__init__.py +src/docutils/parsers/rst/roles.py +src/docutils/parsers/rst/states.py +src/docutils/parsers/rst/tableparser.py +src/docutils/parsers/rst/directives/__init__.py +src/docutils/parsers/rst/directives/admonitions.py +src/docutils/parsers/rst/directives/body.py +src/docutils/parsers/rst/directives/html.py +src/docutils/parsers/rst/directives/images.py +src/docutils/parsers/rst/directives/misc.py +src/docutils/parsers/rst/directives/parts.py +src/docutils/parsers/rst/directives/references.py +src/docutils/parsers/rst/directives/tables.py +src/docutils/parsers/rst/languages/__init__.py +src/docutils/parsers/rst/languages/af.py +src/docutils/parsers/rst/languages/ca.py +src/docutils/parsers/rst/languages/cs.py +src/docutils/parsers/rst/languages/de.py +src/docutils/parsers/rst/languages/en.py +src/docutils/parsers/rst/languages/eo.py +src/docutils/parsers/rst/languages/es.py +src/docutils/parsers/rst/languages/fi.py +src/docutils/parsers/rst/languages/fr.py +src/docutils/parsers/rst/languages/gl.py +src/docutils/parsers/rst/languages/he.py +src/docutils/parsers/rst/languages/it.py +src/docutils/parsers/rst/languages/ja.py +src/docutils/parsers/rst/languages/nl.py +src/docutils/parsers/rst/languages/pl.py +src/docutils/parsers/rst/languages/pt_br.py +src/docutils/parsers/rst/languages/ru.py +src/docutils/parsers/rst/languages/sk.py +src/docutils/parsers/rst/languages/sv.py +src/docutils/parsers/rst/languages/zh_cn.py +src/docutils/parsers/rst/languages/zh_tw.py +src/docutils/readers/__init__.py +src/docutils/readers/doctree.py +src/docutils/readers/pep.py +src/docutils/readers/standalone.py +src/docutils/readers/python/__init__.py +src/docutils/readers/python/moduleparser.py +src/docutils/readers/python/pynodes.py +src/docutils/transforms/__init__.py +src/docutils/transforms/components.py +src/docutils/transforms/frontmatter.py +src/docutils/transforms/misc.py +src/docutils/transforms/parts.py +src/docutils/transforms/peps.py +src/docutils/transforms/references.py +src/docutils/transforms/universal.py +src/docutils/transforms/writer_aux.py +src/docutils/writers/__init__.py +src/docutils/writers/docutils_xml.py +src/docutils/writers/manpage.py +src/docutils/writers/null.py +src/docutils/writers/pseudoxml.py +src/docutils/writers/html4css1/__init__.py +src/docutils/writers/latex2e/__init__.py +src/docutils/writers/newlatex2e/__init__.py +src/docutils/writers/newlatex2e/unicode_map.py +src/docutils/writers/odf_odt/__init__.py +src/docutils/writers/odf_odt/pygmentsformatter.py +src/docutils/writers/pep_html/__init__.py +src/docutils/writers/s5_html/__init__.py +src/mechanize/__init__.py +src/mechanize/_auth.py +src/mechanize/_beautifulsoup.py +src/mechanize/_clientcookie.py +src/mechanize/_debug.py +src/mechanize/_firefox3cookiejar.py +src/mechanize/_form.py +src/mechanize/_gzip.py +src/mechanize/_headersutil.py +src/mechanize/_html.py +src/mechanize/_http.py +src/mechanize/_lwpcookiejar.py +src/mechanize/_markupbase.py +src/mechanize/_mechanize.py +src/mechanize/_mozillacookiejar.py +src/mechanize/_msiecookiejar.py +src/mechanize/_opener.py +src/mechanize/_pullparser.py +src/mechanize/_request.py +src/mechanize/_response.py +src/mechanize/_rfc3986.py +src/mechanize/_sgmllib_copy.py +src/mechanize/_sockettimeout.py +src/mechanize/_testcase.py +src/mechanize/_urllib2.py +src/mechanize/_urllib2_fork.py +src/mechanize/_useragent.py +src/mechanize/_util.py +src/mechanize/_version.py +src/persistent/TimeStamp.c +src/persistent/TimeStamp.py +src/persistent/__init__.py +src/persistent/cPersistence.c +src/persistent/cPersistence.py +src/persistent/cPickleCache.c +src/persistent/cPickleCache.py +src/persistent/dict.py +src/persistent/interfaces.py +src/persistent/list.py +src/persistent/mapping.py +src/persistent/ring.c +src/persistent/wref.py +src/persistent/tests/__init__.py +src/persistent/tests/testPersistent.py +src/persistent/tests/test_PickleCache.py +src/persistent/tests/test_list.py +src/persistent/tests/test_mapping.py +src/persistent/tests/test_overriding_attrs.py +src/persistent/tests/test_persistent.py +src/persistent/tests/test_pickle.py +src/persistent/tests/test_wref.py +src/persistent/tests/utils.py +src/transaction/__init__.py +src/transaction/_manager.py +src/transaction/_transaction.py +src/transaction/interfaces.py +src/transaction/weakset.py +src/transaction/tests/__init__.py +src/transaction/tests/sampledm.py +src/transaction/tests/savepointsample.py +src/transaction/tests/test_SampleDataManager.py +src/transaction/tests/test_SampleResourceManager.py +src/transaction/tests/test_register_compat.py +src/transaction/tests/test_savepoint.py +src/transaction/tests/test_transaction.py +src/transaction/tests/test_weakset.py +src/transaction/tests/warnhook.py +src/wsgi_intercept/__init__.py +src/wsgi_intercept/httplib2_intercept.py +src/wsgi_intercept/mock_http.py +src/wsgi_intercept/test_wsgi_app.py +src/wsgi_intercept/mechanize_intercept/__init__.py +src/wsgi_intercept/mechanize_intercept/wsgi_browser.py +src/wsgi_intercept/mechanoid_intercept/__init__.py +src/wsgi_intercept/mechanoid_intercept/wsgi_browser.py +src/wsgi_intercept/setup_cmd/__init__.py +src/wsgi_intercept/setup_cmd/build_docs.py +src/wsgi_intercept/setup_cmd/publish_docs.py +src/wsgi_intercept/test/__init__.py +src/wsgi_intercept/test/test_httplib2.py +src/wsgi_intercept/test/test_mechanize.py +src/wsgi_intercept/test/test_mechanoid.py +src/wsgi_intercept/test/test_webtest.py +src/wsgi_intercept/test/test_webunit.py +src/wsgi_intercept/test/test_wsgi_compliance.py +src/wsgi_intercept/test/test_wsgi_urllib2.py +src/wsgi_intercept/test/test_zope_testbrowser.py +src/wsgi_intercept/urllib2_intercept/__init__.py +src/wsgi_intercept/urllib2_intercept/wsgi_urllib2.py +src/wsgi_intercept/webtest_intercept/__init__.py +src/wsgi_intercept/webtest_intercept/webtest.py +src/wsgi_intercept/webunit_intercept/HTMLParser.py +src/wsgi_intercept/webunit_intercept/IMGSucker.py +src/wsgi_intercept/webunit_intercept/SimpleDOM.py +src/wsgi_intercept/webunit_intercept/__init__.py +src/wsgi_intercept/webunit_intercept/config.py +src/wsgi_intercept/webunit_intercept/cookie.py +src/wsgi_intercept/webunit_intercept/utility.py +src/wsgi_intercept/webunit_intercept/webunittest.py +src/wsgi_intercept/zope_testbrowser/__init__.py +src/wsgi_intercept/zope_testbrowser/wsgi_testbrowser.py +src/zc/__init__.py +src/zc/lockfile/__init__.py +src/zc/lockfile/tests.py +src/zdaemon/__init__.py +src/zdaemon/zdctl.py +src/zdaemon/zdoptions.py +src/zdaemon/zdrun.py +src/zdaemon/tests/__init__.py +src/zdaemon/tests/nokill.py +src/zdaemon/tests/parent.py +src/zdaemon/tests/tests.py +src/zdaemon/tests/testzdoptions.py +src/zdaemon/tests/testzdrun.py +src/zope/__init__.py +src/zope/annotation/__init__.py +src/zope/annotation/attribute.py +src/zope/annotation/factory.py +src/zope/annotation/interfaces.py +src/zope/annotation/tests/__init__.py +src/zope/annotation/tests/annotations.py +src/zope/annotation/tests/test_attributeannotations.py +src/zope/app/__init__.py +src/zope/app/applicationcontrol/__init__.py +src/zope/app/applicationcontrol/applicationcontrol.py +src/zope/app/applicationcontrol/i18n.py +src/zope/app/applicationcontrol/interfaces.py +src/zope/app/applicationcontrol/runtimeinfo.py +src/zope/app/applicationcontrol/testing.py +src/zope/app/applicationcontrol/zopeversion.py +src/zope/app/applicationcontrol/browser/__init__.py +src/zope/app/applicationcontrol/browser/runtimeinfo.py +src/zope/app/applicationcontrol/browser/servercontrol.py +src/zope/app/applicationcontrol/browser/translationdomaincontrol.py +src/zope/app/applicationcontrol/browser/zodbcontrol.py +src/zope/app/applicationcontrol/browser/tests/__init__.py +src/zope/app/applicationcontrol/browser/tests/test_errorredirect.py +src/zope/app/applicationcontrol/browser/tests/test_runtimeinfoview.py +src/zope/app/applicationcontrol/browser/tests/test_servercontrolview.py +src/zope/app/applicationcontrol/browser/tests/test_translationdomaincontrol.py +src/zope/app/applicationcontrol/browser/tests/test_translationdomaincontrol_functional.py +src/zope/app/applicationcontrol/browser/tests/test_zodbcontrol.py +src/zope/app/applicationcontrol/tests/__init__.py +src/zope/app/applicationcontrol/tests/test_zopeversion.py +src/zope/app/appsetup/__init__.py +src/zope/app/appsetup/appsetup.py +src/zope/app/appsetup/bootstrap.py +src/zope/app/appsetup/controller.py +src/zope/app/appsetup/debug.py +src/zope/app/appsetup/errorlog.py +src/zope/app/appsetup/interfaces.py +src/zope/app/appsetup/product.py +src/zope/app/appsetup/session.py +src/zope/app/appsetup/testlayer.py +src/zope/app/appsetup/tests.py +src/zope/app/appsetup/testpackage/__init__.py +src/zope/app/appsetup/testpackage/testobject.py +src/zope/app/appsetup/testproduct/__init__.py +src/zope/app/authentication/__init__.py +src/zope/app/authentication/authentication.py +src/zope/app/authentication/ftpplugins.py +src/zope/app/authentication/generic.py +src/zope/app/authentication/groupfolder.py +src/zope/app/authentication/httpplugins.py +src/zope/app/authentication/i18n.py +src/zope/app/authentication/idpicker.py +src/zope/app/authentication/interfaces.py +src/zope/app/authentication/password.py +src/zope/app/authentication/placelesssetup.py +src/zope/app/authentication/principalfolder.py +src/zope/app/authentication/session.py +src/zope/app/authentication/testing.py +src/zope/app/authentication/tests.py +src/zope/app/authentication/vocabulary.py +src/zope/app/authentication/browser/__init__.py +src/zope/app/authentication/browser/adding.py +src/zope/app/authentication/browser/loginform.py +src/zope/app/authentication/browser/register.py +src/zope/app/authentication/browser/schemasearch.py +src/zope/app/authentication/browser/tests.py +src/zope/app/component/__init__.py +src/zope/app/component/contentdirective.py +src/zope/app/component/hooks.py +src/zope/app/component/i18n.py +src/zope/app/component/metaconfigure.py +src/zope/app/component/metadirectives.py +src/zope/app/component/site.py +src/zope/app/component/testing.py +src/zope/app/component/vocabulary.py +src/zope/app/component/browser/__init__.py +src/zope/app/component/browser/registration.py +src/zope/app/component/browser/tests.py +src/zope/app/component/interfaces/__init__.py +src/zope/app/component/interfaces/registration.py +src/zope/app/container/__init__.py +src/zope/app/container/btree.py +src/zope/app/container/constraints.py +src/zope/app/container/contained.py +src/zope/app/container/dependency.py +src/zope/app/container/directory.py +src/zope/app/container/find.py +src/zope/app/container/i18n.py +src/zope/app/container/interfaces.py +src/zope/app/container/ordered.py +src/zope/app/container/sample.py +src/zope/app/container/size.py +src/zope/app/container/testing.py +src/zope/app/container/traversal.py +src/zope/app/container/browser/__init__.py +src/zope/app/container/browser/adding.py +src/zope/app/container/browser/contents.py +src/zope/app/container/browser/find.py +src/zope/app/container/browser/metaconfigure.py +src/zope/app/container/browser/tests/__init__.py +src/zope/app/container/browser/tests/test_adding.py +src/zope/app/container/browser/tests/test_contents.py +src/zope/app/container/browser/tests/test_contents_functional.py +src/zope/app/container/browser/tests/test_directive.py +src/zope/app/container/browser/tests/test_view_permissions.py +src/zope/app/container/tests/__init__.py +src/zope/app/container/tests/placelesssetup.py +src/zope/app/debug/__init__.py +src/zope/app/debug/debug.py +src/zope/app/dependable/__init__.py +src/zope/app/dependable/dependency.py +src/zope/app/dependable/interfaces.py +src/zope/app/dependable/tests.py +src/zope/app/exception/__init__.py +src/zope/app/exception/interfaces.py +src/zope/app/exception/systemerror.py +src/zope/app/exception/testing.py +src/zope/app/exception/browser/__init__.py +src/zope/app/exception/browser/notfound.py +src/zope/app/exception/browser/unauthorized.py +src/zope/app/exception/browser/user.py +src/zope/app/exception/browser/tests/__init__.py +src/zope/app/exception/browser/tests/test_error.py +src/zope/app/exception/browser/tests/test_unauthorized.py +src/zope/app/form/__init__.py +src/zope/app/form/interfaces.py +src/zope/app/form/testing.py +src/zope/app/form/utility.py +src/zope/app/form/browser/__init__.py +src/zope/app/form/browser/add.py +src/zope/app/form/browser/boolwidgets.py +src/zope/app/form/browser/editview.py +src/zope/app/form/browser/exception.py +src/zope/app/form/browser/formview.py +src/zope/app/form/browser/i18n.py +src/zope/app/form/browser/interfaces.py +src/zope/app/form/browser/itemswidgets.py +src/zope/app/form/browser/macros.py +src/zope/app/form/browser/metaconfigure.py +src/zope/app/form/browser/metadirectives.py +src/zope/app/form/browser/objectwidget.py +src/zope/app/form/browser/schemadisplay.py +src/zope/app/form/browser/sequencewidget.py +src/zope/app/form/browser/source.py +src/zope/app/form/browser/submit.py +src/zope/app/form/browser/textwidgets.py +src/zope/app/form/browser/widget.py +src/zope/app/form/browser/tests/__init__.py +src/zope/app/form/browser/tests/support.py +src/zope/app/form/browser/tests/test_add.py +src/zope/app/form/browser/tests/test_browserwidget.py +src/zope/app/form/browser/tests/test_directives.py +src/zope/app/form/browser/tests/test_editview.py +src/zope/app/form/browser/tests/test_form.py +src/zope/app/form/browser/tests/test_functional_editview.py +src/zope/app/form/browser/tests/test_functional_i18n.py +src/zope/app/form/browser/tests/test_widgetdirective.py +src/zope/app/form/tests/__init__.py +src/zope/app/form/tests/test_utility.py +src/zope/app/form/tests/utils.py +src/zope/app/http/__init__.py +src/zope/app/http/delete.py +src/zope/app/http/httpdate.py +src/zope/app/http/interfaces.py +src/zope/app/http/options.py +src/zope/app/http/put.py +src/zope/app/http/traversal.py +src/zope/app/http/exception/__init__.py +src/zope/app/http/exception/methodnotallowed.py +src/zope/app/http/exception/notfound.py +src/zope/app/http/exception/unauthorized.py +src/zope/app/http/exception/tests/__init__.py +src/zope/app/http/exception/tests/test_methodnotallowed.py +src/zope/app/http/exception/tests/test_unauthorized.py +src/zope/app/http/tests/__init__.py +src/zope/app/http/tests/test_delete.py +src/zope/app/http/tests/test_functional_put.py +src/zope/app/http/tests/test_options.py +src/zope/app/http/tests/test_put.py +src/zope/app/http/tests/test_traversers.py +src/zope/app/pagetemplate/__init__.py +src/zope/app/pagetemplate/engine.py +src/zope/app/pagetemplate/i18n.py +src/zope/app/pagetemplate/interfaces.py +src/zope/app/pagetemplate/metaconfigure.py +src/zope/app/pagetemplate/namedtemplate.py +src/zope/app/pagetemplate/simpleviewclass.py +src/zope/app/pagetemplate/talesapi.py +src/zope/app/pagetemplate/urlquote.py +src/zope/app/pagetemplate/viewpagetemplatefile.py +src/zope/app/pagetemplate/tests/__init__.py +src/zope/app/pagetemplate/tests/test_binding.py +src/zope/app/pagetemplate/tests/test_engine.py +src/zope/app/pagetemplate/tests/test_namedtemplate.py +src/zope/app/pagetemplate/tests/test_nested.py +src/zope/app/pagetemplate/tests/test_talesapi.py +src/zope/app/pagetemplate/tests/test_urlquote.py +src/zope/app/pagetemplate/tests/testpackage/__init__.py +src/zope/app/pagetemplate/tests/testpackage/content.py +src/zope/app/publication/__init__.py +src/zope/app/publication/browser.py +src/zope/app/publication/ftp.py +src/zope/app/publication/http.py +src/zope/app/publication/httpfactory.py +src/zope/app/publication/interfaces.py +src/zope/app/publication/metaconfigure.py +src/zope/app/publication/metadirectives.py +src/zope/app/publication/publicationtraverse.py +src/zope/app/publication/requestpublicationfactories.py +src/zope/app/publication/requestpublicationregistry.py +src/zope/app/publication/soap.py +src/zope/app/publication/testing.py +src/zope/app/publication/traversers.py +src/zope/app/publication/xmlrpc.py +src/zope/app/publication/zopepublication.py +src/zope/app/publication/tests/__init__.py +src/zope/app/publication/tests/support.py +src/zope/app/publication/tests/test_browserpublication.py +src/zope/app/publication/tests/test_dependencies.py +src/zope/app/publication/tests/test_functional.py +src/zope/app/publication/tests/test_http.py +src/zope/app/publication/tests/test_httpfactory.py +src/zope/app/publication/tests/test_proxycontrol.py +src/zope/app/publication/tests/test_requestpublicationfactories.py +src/zope/app/publication/tests/test_requestpublicationregistry.py +src/zope/app/publication/tests/test_simplecomponenttraverser.py +src/zope/app/publication/tests/test_xmlrpcpublication.py +src/zope/app/publication/tests/test_zopepublication.py +src/zope/app/publisher/__init__.py +src/zope/app/publisher/fieldconverters.py +src/zope/app/publisher/fileresource.py +src/zope/app/publisher/i18n.py +src/zope/app/publisher/pagetemplateresource.py +src/zope/app/publisher/testing.py +src/zope/app/publisher/browser/__init__.py +src/zope/app/publisher/browser/directoryresource.py +src/zope/app/publisher/browser/fields.py +src/zope/app/publisher/browser/fileresource.py +src/zope/app/publisher/browser/i18nfileresource.py +src/zope/app/publisher/browser/i18nresourcemeta.py +src/zope/app/publisher/browser/icon.py +src/zope/app/publisher/browser/managementviewselector.py +src/zope/app/publisher/browser/menu.py +src/zope/app/publisher/browser/menumeta.py +src/zope/app/publisher/browser/metaconfigure.py +src/zope/app/publisher/browser/metadirectives.py +src/zope/app/publisher/browser/pagetemplateresource.py +src/zope/app/publisher/browser/resource.py +src/zope/app/publisher/browser/resourcemeta.py +src/zope/app/publisher/browser/resources.py +src/zope/app/publisher/browser/viewmeta.py +src/zope/app/publisher/browser/vocabulary.py +src/zope/app/publisher/interfaces/__init__.py +src/zope/app/publisher/interfaces/browser.py +src/zope/app/publisher/interfaces/ftp.py +src/zope/app/publisher/interfaces/http.py +src/zope/app/publisher/interfaces/xmlrpc.py +src/zope/app/publisher/tests/__init__.py +src/zope/app/publisher/tests/test_fieldconverters.py +src/zope/app/publisher/xmlrpc/__init__.py +src/zope/app/publisher/xmlrpc/metaconfigure.py +src/zope/app/publisher/xmlrpc/metadirectives.py +src/zope/app/publisher/xmlrpc/tests/__init__.py +src/zope/app/publisher/xmlrpc/tests/test_directives.py +src/zope/app/publisher/xmlrpc/tests/test_functional.py +src/zope/app/schema/__init__.py +src/zope/app/schema/tests.py +src/zope/app/schema/vocabulary.py +src/zope/app/server/__init__.py +src/zope/app/server/accesslog.py +src/zope/app/server/ftp.py +src/zope/app/server/main.py +src/zope/app/server/mkzopeinstance.py +src/zope/app/server/server.py +src/zope/app/server/servercontrol.py +src/zope/app/server/servertype.py +src/zope/app/server/wsgi.py +src/zope/app/server/zpasswd.py +src/zope/app/server/tests/__init__.py +src/zope/app/server/tests/test_accesslog.py +src/zope/app/server/tests/test_ftp.py +src/zope/app/server/tests/test_mkzopeinstance.py +src/zope/app/server/tests/test_schema.py +src/zope/app/server/tests/test_server.py +src/zope/app/server/tests/test_servertype.py +src/zope/app/server/tests/test_zpasswd.py +src/zope/app/testing/__init__.py +src/zope/app/testing/dochttp.py +src/zope/app/testing/functional.py +src/zope/app/testing/placelesssetup.py +src/zope/app/testing/setup.py +src/zope/app/testing/testing.py +src/zope/app/testing/tests.py +src/zope/app/testing/xmlrpc.py +src/zope/app/testing/ztapi.py +src/zope/app/wsgi/__init__.py +src/zope/app/wsgi/fileresult.py +src/zope/app/wsgi/interfaces.py +src/zope/app/wsgi/paste.py +src/zope/app/wsgi/testing.py +src/zope/app/wsgi/testlayer.py +src/zope/app/wsgi/tests.py +src/zope/applicationcontrol/__init__.py +src/zope/applicationcontrol/applicationcontrol.py +src/zope/applicationcontrol/interfaces.py +src/zope/applicationcontrol/runtimeinfo.py +src/zope/applicationcontrol/tests/__init__.py +src/zope/applicationcontrol/tests/test_applicationcontrol.py +src/zope/applicationcontrol/tests/test_runtimeinfo.py +src/zope/authentication/__init__.py +src/zope/authentication/interfaces.py +src/zope/authentication/loginpassword.py +src/zope/authentication/logout.py +src/zope/authentication/principal.py +src/zope/authentication/tests/__init__.py +src/zope/authentication/tests/test_loginpassword.py +src/zope/authentication/tests/test_logout.py +src/zope/authentication/tests/test_principal.py +src/zope/broken/__init__.py +src/zope/broken/interfaces.py +src/zope/browser/__init__.py +src/zope/browser/interfaces.py +src/zope/browser/tests.py +src/zope/browsermenu/__init__.py +src/zope/browsermenu/field.py +src/zope/browsermenu/interfaces.py +src/zope/browsermenu/menu.py +src/zope/browsermenu/metaconfigure.py +src/zope/browsermenu/metadirectives.py +src/zope/browsermenu/tests/__init__.py +src/zope/browsermenu/tests/test_addMenuItem.py +src/zope/browsermenu/tests/test_directives.py +src/zope/browsermenu/tests/test_fields.py +src/zope/browsermenu/tests/test_menu.py +src/zope/browsermenu/tests/test_menudirectives.py +src/zope/browserpage/__init__.py +src/zope/browserpage/metaconfigure.py +src/zope/browserpage/metadirectives.py +src/zope/browserpage/namedtemplate.py +src/zope/browserpage/simpleviewclass.py +src/zope/browserpage/viewpagetemplatefile.py +src/zope/browserpage/tests/__init__.py +src/zope/browserpage/tests/sample.py +src/zope/browserpage/tests/simpletestview.py +src/zope/browserpage/tests/test_boundpagetemplate.py +src/zope/browserpage/tests/test_expressiontype.py +src/zope/browserpage/tests/test_namedtemplate.py +src/zope/browserpage/tests/test_page.py +src/zope/browserpage/tests/test_simpleviewclass.py +src/zope/browserpage/tests/test_viewzpt.py +src/zope/browserresource/__init__.py +src/zope/browserresource/directory.py +src/zope/browserresource/file.py +src/zope/browserresource/i18nfile.py +src/zope/browserresource/icon.py +src/zope/browserresource/interfaces.py +src/zope/browserresource/metaconfigure.py +src/zope/browserresource/metadirectives.py +src/zope/browserresource/resource.py +src/zope/browserresource/resources.py +src/zope/browserresource/tests/__init__.py +src/zope/browserresource/tests/support.py +src/zope/browserresource/tests/test_directives.py +src/zope/browserresource/tests/test_directory.py +src/zope/browserresource/tests/test_file.py +src/zope/browserresource/tests/test_i18nfile.py +src/zope/browserresource/tests/test_icondirective.py +src/zope/browserresource/tests/test_resource.py +src/zope/browserresource/tests/test_resources.py +src/zope/cachedescriptors/__init__.py +src/zope/cachedescriptors/method.py +src/zope/cachedescriptors/property.py +src/zope/cachedescriptors/tests.py +src/zope/component/__init__.py +src/zope/component/_api.py +src/zope/component/_declaration.py +src/zope/component/event.py +src/zope/component/eventtesting.py +src/zope/component/factory.py +src/zope/component/globalregistry.py +src/zope/component/hookable.py +src/zope/component/hooks.py +src/zope/component/interface.py +src/zope/component/interfaces.py +src/zope/component/nexttesting.py +src/zope/component/persistentregistry.py +src/zope/component/registry.py +src/zope/component/security.py +src/zope/component/standalonetests.py +src/zope/component/testing.py +src/zope/component/testlayer.py +src/zope/component/tests.py +src/zope/component/zcml.py +src/zope/component/testfiles/__init__.py +src/zope/component/testfiles/adapter.py +src/zope/component/testfiles/components.py +src/zope/component/testfiles/views.py +src/zope/componentvocabulary/__init__.py +src/zope/componentvocabulary/i18n.py +src/zope/componentvocabulary/vocabulary.py +src/zope/componentvocabulary/tests/__init__.py +src/zope/componentvocabulary/tests/test_vocabulary.py +src/zope/configuration/__init__.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/stxdocs.py +src/zope/configuration/xmlconfig.py +src/zope/configuration/zopeconfigure.py +src/zope/configuration/tests/__init__.py +src/zope/configuration/tests/bad.py +src/zope/configuration/tests/directives.py +src/zope/configuration/tests/test_conditions.py +src/zope/configuration/tests/test_config.py +src/zope/configuration/tests/test_docutils.py +src/zope/configuration/tests/test_nested.py +src/zope/configuration/tests/test_simple.py +src/zope/configuration/tests/test_xmlconfig.py +src/zope/configuration/tests/victim.py +src/zope/configuration/tests/excludedemo/__init__.py +src/zope/configuration/tests/excludedemo/sub/__init__.py +src/zope/configuration/tests/samplepackage/__init__.py +src/zope/configuration/tests/samplepackage/foo.py +src/zope/container/__init__.py +src/zope/container/_zope_container_contained.c +src/zope/container/_zope_container_contained.py +src/zope/container/btree.py +src/zope/container/constraints.py +src/zope/container/contained.py +src/zope/container/dependency.py +src/zope/container/directory.py +src/zope/container/find.py +src/zope/container/folder.py +src/zope/container/i18n.py +src/zope/container/interfaces.py +src/zope/container/ordered.py +src/zope/container/sample.py +src/zope/container/size.py +src/zope/container/testing.py +src/zope/container/traversal.py +src/zope/container/tests/__init__.py +src/zope/container/tests/test_btree.py +src/zope/container/tests/test_constraints.py +src/zope/container/tests/test_contained.py +src/zope/container/tests/test_containertraversable.py +src/zope/container/tests/test_containertraverser.py +src/zope/container/tests/test_dependencies.py +src/zope/container/tests/test_directory.py +src/zope/container/tests/test_find.py +src/zope/container/tests/test_folder.py +src/zope/container/tests/test_icontainer.py +src/zope/container/tests/test_ordered.py +src/zope/container/tests/test_size.py +src/zope/contentprovider/__init__.py +src/zope/contentprovider/interfaces.py +src/zope/contentprovider/provider.py +src/zope/contentprovider/tales.py +src/zope/contentprovider/tests.py +src/zope/contenttype/__init__.py +src/zope/contenttype/parse.py +src/zope/contenttype/tests/__init__.py +src/zope/contenttype/tests/testContentTypes.py +src/zope/contenttype/tests/test_parse.py +src/zope/copy/__init__.py +src/zope/copy/interfaces.py +src/zope/copy/tests.py +src/zope/copypastemove/__init__.py +src/zope/copypastemove/interfaces.py +src/zope/copypastemove/tests/__init__.py +src/zope/copypastemove/tests/test_clipboard.py +src/zope/copypastemove/tests/test_rename.py +src/zope/datetime/__init__.py +src/zope/datetime/timezones.py +src/zope/datetime/tests/__init__.py +src/zope/datetime/tests/test_datetimeparse.py +src/zope/datetime/tests/test_standard_dates.py +src/zope/datetime/tests/test_tzinfo.py +src/zope/deferredimport/__init__.py +src/zope/deferredimport/deferredmodule.py +src/zope/deferredimport/tests.py +src/zope/deprecation/__init__.py +src/zope/deprecation/deprecation.py +src/zope/deprecation/tests.py +src/zope/dottedname/__init__.py +src/zope/dottedname/resolve.py +src/zope/dottedname/tests.py +src/zope/dublincore/__init__.py +src/zope/dublincore/annotatableadapter.py +src/zope/dublincore/creatorannotator.py +src/zope/dublincore/dcsv.py +src/zope/dublincore/dcterms.py +src/zope/dublincore/interfaces.py +src/zope/dublincore/property.py +src/zope/dublincore/testing.py +src/zope/dublincore/timeannotators.py +src/zope/dublincore/xmlmetadata.py +src/zope/dublincore/zopedublincore.py +src/zope/dublincore/browser/__init__.py +src/zope/dublincore/browser/metadataedit.py +src/zope/dublincore/tests/__init__.py +src/zope/dublincore/tests/test_annotatableadapter.py +src/zope/dublincore/tests/test_creatorannotator.py +src/zope/dublincore/tests/test_dcsv.py +src/zope/dublincore/tests/test_partialannotatable.py +src/zope/dublincore/tests/test_property.py +src/zope/dublincore/tests/test_xmlmetadata.py +src/zope/dublincore/tests/test_zdcannotatableadapter.py +src/zope/dublincore/tests/test_zopedublincore.py +src/zope/error/__init__.py +src/zope/error/error.py +src/zope/error/interfaces.py +src/zope/error/tests.py +src/zope/event/__init__.py +src/zope/event/tests.py +src/zope/exceptions/__init__.py +src/zope/exceptions/exceptionformatter.py +src/zope/exceptions/interfaces.py +src/zope/exceptions/log.py +src/zope/exceptions/tests/__init__.py +src/zope/exceptions/tests/test_exceptionformatter.py +src/zope/filerepresentation/__init__.py +src/zope/filerepresentation/interfaces.py +src/zope/formlib/__init__.py +src/zope/formlib/boolwidgets.py +src/zope/formlib/errors.py +src/zope/formlib/exception.py +src/zope/formlib/form.py +src/zope/formlib/i18n.py +src/zope/formlib/interfaces.py +src/zope/formlib/itemswidgets.py +src/zope/formlib/namedtemplate.py +src/zope/formlib/objectwidget.py +src/zope/formlib/sequencewidget.py +src/zope/formlib/source.py +src/zope/formlib/textwidgets.py +src/zope/formlib/utility.py +src/zope/formlib/widget.py +src/zope/formlib/widgets.py +src/zope/formlib/tests/__init__.py +src/zope/formlib/tests/functionalsupport.py +src/zope/formlib/tests/support.py +src/zope/formlib/tests/test_browserwidget.py +src/zope/formlib/tests/test_checkboxwidget.py +src/zope/formlib/tests/test_choicecollections.py +src/zope/formlib/tests/test_choicewidget.py +src/zope/formlib/tests/test_datetimewidget.py +src/zope/formlib/tests/test_datewidget.py +src/zope/formlib/tests/test_decimalwidget.py +src/zope/formlib/tests/test_displaywidget.py +src/zope/formlib/tests/test_exception.py +src/zope/formlib/tests/test_filewidget.py +src/zope/formlib/tests/test_floatwidget.py +src/zope/formlib/tests/test_formlib.py +src/zope/formlib/tests/test_functional_booleanradiowidget.py +src/zope/formlib/tests/test_functional_checkboxwidget.py +src/zope/formlib/tests/test_functional_datetimewidget.py +src/zope/formlib/tests/test_functional_decimalwidget.py +src/zope/formlib/tests/test_functional_filewidget.py +src/zope/formlib/tests/test_functional_floatwidget.py +src/zope/formlib/tests/test_functional_intwidget.py +src/zope/formlib/tests/test_functional_objectwidget.py +src/zope/formlib/tests/test_functional_selectwidget.py +src/zope/formlib/tests/test_functional_textareawidget.py +src/zope/formlib/tests/test_functional_textwidget.py +src/zope/formlib/tests/test_intwidget.py +src/zope/formlib/tests/test_itemswidget.py +src/zope/formlib/tests/test_multicheckboxwidget.py +src/zope/formlib/tests/test_objectwidget.py +src/zope/formlib/tests/test_passwordwidget.py +src/zope/formlib/tests/test_radiowidget.py +src/zope/formlib/tests/test_registrations.py +src/zope/formlib/tests/test_selectwidget.py +src/zope/formlib/tests/test_sequencewidget.py +src/zope/formlib/tests/test_setprefix.py +src/zope/formlib/tests/test_source.py +src/zope/formlib/tests/test_textareawidget.py +src/zope/formlib/tests/test_textwidget.py +src/zope/formlib/tests/test_widget.py +src/zope/formlib/tests/test_widgetdocs.py +src/zope/hookable/__init__.py +src/zope/hookable/_zope_hookable.c +src/zope/hookable/_zope_hookable.py +src/zope/hookable/tests/__init__.py +src/zope/hookable/tests/test_hookable.py +src/zope/i18n/__init__.py +src/zope/i18n/compile.py +src/zope/i18n/config.py +src/zope/i18n/format.py +src/zope/i18n/gettextmessagecatalog.py +src/zope/i18n/negotiator.py +src/zope/i18n/simpletranslationdomain.py +src/zope/i18n/testing.py +src/zope/i18n/testmessagecatalog.py +src/zope/i18n/translationdomain.py +src/zope/i18n/zcml.py +src/zope/i18n/interfaces/__init__.py +src/zope/i18n/interfaces/locales.py +src/zope/i18n/locales/__init__.py +src/zope/i18n/locales/fallbackcollator.py +src/zope/i18n/locales/inheritance.py +src/zope/i18n/locales/provider.py +src/zope/i18n/locales/xmlfactory.py +src/zope/i18n/locales/tests/__init__.py +src/zope/i18n/locales/tests/test_docstrings.py +src/zope/i18n/locales/tests/test_fallbackcollator.py +src/zope/i18n/locales/tests/test_locales.py +src/zope/i18n/locales/tests/test_xmlfactory.py +src/zope/i18n/tests/__init__.py +src/zope/i18n/tests/test.py +src/zope/i18n/tests/test_formats.py +src/zope/i18n/tests/test_gettextmessagecatalog.py +src/zope/i18n/tests/test_imessagecatalog.py +src/zope/i18n/tests/test_itranslationdomain.py +src/zope/i18n/tests/test_negotiator.py +src/zope/i18n/tests/test_simpletranslationdomain.py +src/zope/i18n/tests/test_testmessagecatalog.py +src/zope/i18n/tests/test_translationdomain.py +src/zope/i18n/tests/test_zcml.py +src/zope/i18n/tests/testi18nawareobject.py +src/zope/i18n/tests/testii18naware.py +src/zope/i18n/tests/locale2/__init__.py +src/zope/i18nmessageid/__init__.py +src/zope/i18nmessageid/_zope_i18nmessageid_message.c +src/zope/i18nmessageid/_zope_i18nmessageid_message.py +src/zope/i18nmessageid/message.py +src/zope/i18nmessageid/tests.py +src/zope/interface/__init__.py +src/zope/interface/_flatten.py +src/zope/interface/_zope_interface_coptimizations.c +src/zope/interface/_zope_interface_coptimizations.py +src/zope/interface/adapter.py +src/zope/interface/advice.py +src/zope/interface/declarations.py +src/zope/interface/document.py +src/zope/interface/exceptions.py +src/zope/interface/interface.py +src/zope/interface/interfaces.py +src/zope/interface/ro.py +src/zope/interface/verify.py +src/zope/interface/common/__init__.py +src/zope/interface/common/idatetime.py +src/zope/interface/common/interfaces.py +src/zope/interface/common/mapping.py +src/zope/interface/common/sequence.py +src/zope/interface/common/tests/__init__.py +src/zope/interface/common/tests/basemapping.py +src/zope/interface/common/tests/test_idatetime.py +src/zope/interface/common/tests/test_import_interfaces.py +src/zope/interface/tests/__init__.py +src/zope/interface/tests/dummy.py +src/zope/interface/tests/ifoo.py +src/zope/interface/tests/m1.py +src/zope/interface/tests/m2.py +src/zope/interface/tests/odd.py +src/zope/interface/tests/test_adapter.py +src/zope/interface/tests/test_advice.py +src/zope/interface/tests/test_declarations.py +src/zope/interface/tests/test_document.py +src/zope/interface/tests/test_element.py +src/zope/interface/tests/test_interface.py +src/zope/interface/tests/test_odd_declarations.py +src/zope/interface/tests/test_sorting.py +src/zope/interface/tests/test_verify.py +src/zope/interface/tests/unitfixtures.py +src/zope/lifecycleevent/__init__.py +src/zope/lifecycleevent/interfaces.py +src/zope/lifecycleevent/tests.py +src/zope/location/__init__.py +src/zope/location/interfaces.py +src/zope/location/location.py +src/zope/location/pickling.py +src/zope/location/tests.py +src/zope/location/traversing.py +src/zope/minmax/__init__.py +src/zope/minmax/_minmax.py +src/zope/minmax/interfaces.py +src/zope/minmax/tests.py +src/zope/pagetemplate/__init__.py +src/zope/pagetemplate/engine.py +src/zope/pagetemplate/i18n.py +src/zope/pagetemplate/interfaces.py +src/zope/pagetemplate/pagetemplate.py +src/zope/pagetemplate/pagetemplatefile.py +src/zope/pagetemplate/tests/__init__.py +src/zope/pagetemplate/tests/batch.py +src/zope/pagetemplate/tests/test_basictemplate.py +src/zope/pagetemplate/tests/test_engine.py +src/zope/pagetemplate/tests/test_htmltests.py +src/zope/pagetemplate/tests/test_ptfile.py +src/zope/pagetemplate/tests/trusted.py +src/zope/pagetemplate/tests/util.py +src/zope/pagetemplate/tests/input/__init__.py +src/zope/pagetemplate/tests/output/__init__.py +src/zope/pagetemplate/tests/testpackage/__init__.py +src/zope/pagetemplate/tests/testpackage/content.py +src/zope/password/__init__.py +src/zope/password/interfaces.py +src/zope/password/password.py +src/zope/password/testing.py +src/zope/password/tests.py +src/zope/password/vocabulary.py +src/zope/pluggableauth/__init__.py +src/zope/pluggableauth/authentication.py +src/zope/pluggableauth/factories.py +src/zope/pluggableauth/interfaces.py +src/zope/pluggableauth/tests.py +src/zope/pluggableauth/plugins/__init__.py +src/zope/pluggableauth/plugins/ftpplugins.py +src/zope/pluggableauth/plugins/generic.py +src/zope/pluggableauth/plugins/httpplugins.py +src/zope/pluggableauth/plugins/session.py +src/zope/principalregistry/__init__.py +src/zope/principalregistry/metaconfigure.py +src/zope/principalregistry/metadirectives.py +src/zope/principalregistry/principalregistry.py +src/zope/principalregistry/tests/__init__.py +src/zope/principalregistry/tests/test_doc.py +src/zope/principalregistry/tests/test_principalregistry.py +src/zope/processlifetime/__init__.py +src/zope/processlifetime/tests.py +src/zope/proxy/__init__.py +src/zope/proxy/_zope_proxy_proxy.c +src/zope/proxy/_zope_proxy_proxy.py +src/zope/proxy/decorator.py +src/zope/proxy/interfaces.py +src/zope/proxy/tests/__init__.py +src/zope/proxy/tests/test_decorator.py +src/zope/proxy/tests/test_proxy.py +src/zope/ptresource/__init__.py +src/zope/ptresource/ptresource.py +src/zope/ptresource/tests.py +src/zope/publisher/__init__.py +src/zope/publisher/base.py +src/zope/publisher/browser.py +src/zope/publisher/contenttype.py +src/zope/publisher/defaultview.py +src/zope/publisher/ftp.py +src/zope/publisher/http.py +src/zope/publisher/paste.py +src/zope/publisher/principallogging.py +src/zope/publisher/publish.py +src/zope/publisher/skinnable.py +src/zope/publisher/xmlrpc.py +src/zope/publisher/zcml.py +src/zope/publisher/interfaces/__init__.py +src/zope/publisher/interfaces/browser.py +src/zope/publisher/interfaces/ftp.py +src/zope/publisher/interfaces/http.py +src/zope/publisher/interfaces/logginginfo.py +src/zope/publisher/interfaces/xmlrpc.py +src/zope/publisher/tests/__init__.py +src/zope/publisher/tests/basetestiapplicationrequest.py +src/zope/publisher/tests/basetestipublicationrequest.py +src/zope/publisher/tests/basetestipublisherrequest.py +src/zope/publisher/tests/httprequest.py +src/zope/publisher/tests/publication.py +src/zope/publisher/tests/test_baserequest.py +src/zope/publisher/tests/test_baseresponse.py +src/zope/publisher/tests/test_browser.py +src/zope/publisher/tests/test_browserlanguages.py +src/zope/publisher/tests/test_browserrequest.py +src/zope/publisher/tests/test_browserresponse.py +src/zope/publisher/tests/test_defaultview.py +src/zope/publisher/tests/test_ftp.py +src/zope/publisher/tests/test_http.py +src/zope/publisher/tests/test_httpcharsets.py +src/zope/publisher/tests/test_ipublication.py +src/zope/publisher/tests/test_mapply.py +src/zope/publisher/tests/test_paste.py +src/zope/publisher/tests/test_principallogging.py +src/zope/publisher/tests/test_publisher.py +src/zope/publisher/tests/test_requestdataproperty.py +src/zope/publisher/tests/test_skinnable.py +src/zope/publisher/tests/test_xmlrpc.py +src/zope/publisher/tests/test_xmlrpcrequest.py +src/zope/publisher/tests/test_zcml.py +src/zope/publisher/tests/views.py +src/zope/publisher/tests/xmlrpcviews.py +src/zope/schema/__init__.py +src/zope/schema/_bootstrapfields.py +src/zope/schema/_bootstrapinterfaces.py +src/zope/schema/_field.py +src/zope/schema/_messageid.py +src/zope/schema/_schema.py +src/zope/schema/accessors.py +src/zope/schema/fieldproperty.py +src/zope/schema/interfaces.py +src/zope/schema/vocabulary.py +src/zope/schema/tests/__init__.py +src/zope/schema/tests/states.py +src/zope/schema/tests/test_accessors.py +src/zope/schema/tests/test_boolfield.py +src/zope/schema/tests/test_choice.py +src/zope/schema/tests/test_containerfield.py +src/zope/schema/tests/test_date.py +src/zope/schema/tests/test_datetime.py +src/zope/schema/tests/test_decimalfield.py +src/zope/schema/tests/test_dictfield.py +src/zope/schema/tests/test_docs.py +src/zope/schema/tests/test_equality.py +src/zope/schema/tests/test_field.py +src/zope/schema/tests/test_fieldproperty.py +src/zope/schema/tests/test_floatfield.py +src/zope/schema/tests/test_interfacefield.py +src/zope/schema/tests/test_intfield.py +src/zope/schema/tests/test_iterablefield.py +src/zope/schema/tests/test_listfield.py +src/zope/schema/tests/test_objectfield.py +src/zope/schema/tests/test_schema.py +src/zope/schema/tests/test_setfield.py +src/zope/schema/tests/test_states.py +src/zope/schema/tests/test_strfield.py +src/zope/schema/tests/test_timedelta.py +src/zope/schema/tests/test_tuplefield.py +src/zope/schema/tests/test_vocabulary.py +src/zope/security/__init__.py +src/zope/security/_definitions.py +src/zope/security/_proxy.c +src/zope/security/_proxy.py +src/zope/security/_zope_security_checker.c +src/zope/security/_zope_security_checker.py +src/zope/security/adapter.py +src/zope/security/checker.py +src/zope/security/decorator.py +src/zope/security/i18n.py +src/zope/security/interfaces.py +src/zope/security/management.py +src/zope/security/metaconfigure.py +src/zope/security/metadirectives.py +src/zope/security/permission.py +src/zope/security/protectclass.py +src/zope/security/proxy.py +src/zope/security/setup.py +src/zope/security/simplepolicies.py +src/zope/security/testing.py +src/zope/security/zcml.py +src/zope/security/tests/__init__.py +src/zope/security/tests/adapter.py +src/zope/security/tests/components.py +src/zope/security/tests/emptymodule.py +src/zope/security/tests/exampleclass.py +src/zope/security/tests/module.py +src/zope/security/tests/modulehookup.py +src/zope/security/tests/test_adapter.py +src/zope/security/tests/test_checker.py +src/zope/security/tests/test_contentdirective.py +src/zope/security/tests/test_decorator.py +src/zope/security/tests/test_directives.py +src/zope/security/tests/test_location.py +src/zope/security/tests/test_management.py +src/zope/security/tests/test_module_directives.py +src/zope/security/tests/test_permission.py +src/zope/security/tests/test_protectclass.py +src/zope/security/tests/test_protectsubclass.py +src/zope/security/tests/test_proxy.py +src/zope/security/tests/test_set_checkers.py +src/zope/security/tests/test_simpleinteraction.py +src/zope/security/tests/test_standard_checkers.py +src/zope/security/untrustedpython/__init__.py +src/zope/security/untrustedpython/builtins.py +src/zope/security/untrustedpython/interpreter.py +src/zope/security/untrustedpython/rcompile.py +src/zope/security/untrustedpython/tests.py +src/zope/securitypolicy/__init__.py +src/zope/securitypolicy/grantinfo.py +src/zope/securitypolicy/interfaces.py +src/zope/securitypolicy/metaconfigure.py +src/zope/securitypolicy/metadirectives.py +src/zope/securitypolicy/principalpermission.py +src/zope/securitypolicy/principalrole.py +src/zope/securitypolicy/role.py +src/zope/securitypolicy/rolepermission.py +src/zope/securitypolicy/securitymap.py +src/zope/securitypolicy/settings.py +src/zope/securitypolicy/vocabulary.py +src/zope/securitypolicy/zopepolicy.py +src/zope/securitypolicy/tests/__init__.py +src/zope/securitypolicy/tests/test_annotationprincipalpermissionmanager.py +src/zope/securitypolicy/tests/test_annotationprincipalrolemanager.py +src/zope/securitypolicy/tests/test_annotationrolepermissionmanager.py +src/zope/securitypolicy/tests/test_principalpermissionmanager.py +src/zope/securitypolicy/tests/test_principalrolemanager.py +src/zope/securitypolicy/tests/test_role.py +src/zope/securitypolicy/tests/test_rolepermissionmanager.py +src/zope/securitypolicy/tests/test_securitydirectives.py +src/zope/securitypolicy/tests/test_securitymap.py +src/zope/securitypolicy/tests/test_settings.py +src/zope/securitypolicy/tests/test_vocabulary.py +src/zope/securitypolicy/tests/test_zopepolicy.py +src/zope/sendmail/__init__.py +src/zope/sendmail/delivery.py +src/zope/sendmail/event.py +src/zope/sendmail/interfaces.py +src/zope/sendmail/maildir.py +src/zope/sendmail/mailer.py +src/zope/sendmail/queue.py +src/zope/sendmail/vocabulary.py +src/zope/sendmail/zcml.py +src/zope/sendmail/tests/__init__.py +src/zope/sendmail/tests/test_delivery.py +src/zope/sendmail/tests/test_directives.py +src/zope/sendmail/tests/test_event.py +src/zope/sendmail/tests/test_maildir.py +src/zope/sendmail/tests/test_mailer.py +src/zope/sendmail/tests/test_queue.py +src/zope/sendmail/tests/test_vocabulary.py +src/zope/server/__init__.py +src/zope/server/adjustments.py +src/zope/server/buffers.py +src/zope/server/dualmodechannel.py +src/zope/server/fixedstreamreceiver.py +src/zope/server/maxsockets.py +src/zope/server/serverbase.py +src/zope/server/serverchannelbase.py +src/zope/server/taskthreads.py +src/zope/server/trigger.py +src/zope/server/utilities.py +src/zope/server/zlogintegration.py +src/zope/server/ftp/__init__.py +src/zope/server/ftp/logger.py +src/zope/server/ftp/publisher.py +src/zope/server/ftp/server.py +src/zope/server/ftp/tests/__init__.py +src/zope/server/ftp/tests/demofs.py +src/zope/server/ftp/tests/fstests.py +src/zope/server/ftp/tests/test_demofs.py +src/zope/server/ftp/tests/test_ftpserver.py +src/zope/server/ftp/tests/test_publisher.py +src/zope/server/http/__init__.py +src/zope/server/http/chunking.py +src/zope/server/http/commonaccesslogger.py +src/zope/server/http/http_date.py +src/zope/server/http/httprequestparser.py +src/zope/server/http/httpserver.py +src/zope/server/http/httpserverchannel.py +src/zope/server/http/httptask.py +src/zope/server/http/publisherhttpserver.py +src/zope/server/http/wsgihttpserver.py +src/zope/server/http/tests/__init__.py +src/zope/server/http/tests/test_commonaccesslogger.py +src/zope/server/http/tests/test_httpdate.py +src/zope/server/http/tests/test_httprequestparser.py +src/zope/server/http/tests/test_httpserver.py +src/zope/server/http/tests/test_wsgiserver.py +src/zope/server/http/tests/wsgi_app.py +src/zope/server/interfaces/__init__.py +src/zope/server/interfaces/ftp.py +src/zope/server/interfaces/logger.py +src/zope/server/linereceiver/__init__.py +src/zope/server/linereceiver/linecommandparser.py +src/zope/server/linereceiver/lineserverchannel.py +src/zope/server/linereceiver/linetask.py +src/zope/server/logger/__init__.py +src/zope/server/logger/filelogger.py +src/zope/server/logger/m_syslog.py +src/zope/server/logger/pythonlogger.py +src/zope/server/logger/resolvinglogger.py +src/zope/server/logger/rotatingfilelogger.py +src/zope/server/logger/socketlogger.py +src/zope/server/logger/sysloglogger.py +src/zope/server/logger/taillogger.py +src/zope/server/logger/unresolvinglogger.py +src/zope/server/logger/tests/__init__.py +src/zope/server/logger/tests/test_pythonlogger.py +src/zope/server/tests/__init__.py +src/zope/server/tests/asyncerror.py +src/zope/server/tests/test_serverbase.py +src/zope/session/__init__.py +src/zope/session/http.py +src/zope/session/interfaces.py +src/zope/session/session.py +src/zope/session/tests.py +src/zope/site/__init__.py +src/zope/site/folder.py +src/zope/site/hooks.py +src/zope/site/interfaces.py +src/zope/site/next.py +src/zope/site/site.py +src/zope/site/testing.py +src/zope/site/tests/__init__.py +src/zope/site/tests/test_folder.py +src/zope/site/tests/test_localsitemanager.py +src/zope/site/tests/test_registration.py +src/zope/site/tests/test_site.py +src/zope/site/tests/test_sitemanagercontainer.py +src/zope/size/__init__.py +src/zope/size/interfaces.py +src/zope/size/tests.py +src/zope/tal/__init__.py +src/zope/tal/driver.py +src/zope/tal/dummyengine.py +src/zope/tal/htmltalparser.py +src/zope/tal/interfaces.py +src/zope/tal/ndiff.py +src/zope/tal/runtest.py +src/zope/tal/setpath.py +src/zope/tal/taldefs.py +src/zope/tal/talgenerator.py +src/zope/tal/talgettext.py +src/zope/tal/talinterpreter.py +src/zope/tal/talparser.py +src/zope/tal/timer.py +src/zope/tal/translationcontext.py +src/zope/tal/xmlparser.py +src/zope/tal/benchmark/__init__.py +src/zope/tal/tests/__init__.py +src/zope/tal/tests/markbench.py +src/zope/tal/tests/run.py +src/zope/tal/tests/test_files.py +src/zope/tal/tests/test_htmltalparser.py +src/zope/tal/tests/test_sourcepos.py +src/zope/tal/tests/test_talgettext.py +src/zope/tal/tests/test_talinterpreter.py +src/zope/tal/tests/test_talparser.py +src/zope/tal/tests/test_xmlparser.py +src/zope/tal/tests/utils.py +src/zope/tal/tests/input/__init__.py +src/zope/tal/tests/output/__init__.py +src/zope/tales/__init__.py +src/zope/tales/engine.py +src/zope/tales/expressions.py +src/zope/tales/interfaces.py +src/zope/tales/pythonexpr.py +src/zope/tales/tales.py +src/zope/tales/tests/__init__.py +src/zope/tales/tests/simpleexpr.py +src/zope/tales/tests/test_expressions.py +src/zope/tales/tests/test_tales.py +src/zope/tales/tests/test_traverser.py +src/zope/testbrowser/__init__.py +src/zope/testbrowser/browser.py +src/zope/testbrowser/cookies.py +src/zope/testbrowser/interfaces.py +src/zope/testbrowser/testing.py +src/zope/testbrowser/tests.py +src/zope/testbrowser/ftests/__init__.py +src/zope/testing/__init__.py +src/zope/testing/cleanup.py +src/zope/testing/doctestunit.py +src/zope/testing/exceptions.py +src/zope/testing/formparser.py +src/zope/testing/loggingsupport.py +src/zope/testing/loghandler.py +src/zope/testing/module.py +src/zope/testing/server.py +src/zope/testing/setupstack.py +src/zope/testing/tests.py +src/zope/testing/doctest/__init__.py +src/zope/testing/renormalizing/__init__.py +src/zope/testing/testrunner/__init__.py +src/zope/testing/testrunner/_doctest.py +src/zope/testing/testrunner/coverage.py +src/zope/testing/testrunner/debug.py +src/zope/testing/testrunner/eggsupport.py +src/zope/testing/testrunner/feature.py +src/zope/testing/testrunner/filter.py +src/zope/testing/testrunner/find.py +src/zope/testing/testrunner/formatter.py +src/zope/testing/testrunner/garbagecollection.py +src/zope/testing/testrunner/interfaces.py +src/zope/testing/testrunner/layer.py +src/zope/testing/testrunner/listing.py +src/zope/testing/testrunner/logsupport.py +src/zope/testing/testrunner/options.py +src/zope/testing/testrunner/process.py +src/zope/testing/testrunner/profiling.py +src/zope/testing/testrunner/refcount.py +src/zope/testing/testrunner/runner.py +src/zope/testing/testrunner/selftest.py +src/zope/testing/testrunner/shuffle.py +src/zope/testing/testrunner/statistics.py +src/zope/testing/testrunner/tb_format.py +src/zope/testing/testrunner/tests.py +src/zope/testing/testrunner/testrunner-ex-pp-products/__init__.py +src/zope/testing/testrunner/testrunner-ex-pp-products/sampletests.py +src/zope/testing/testrunner/testrunner-ex-pp-products/more/__init__.py +src/zope/testing/testrunner/testrunner-ex-pp-products/more/sampletests.py +src/zope/traversing/__init__.py +src/zope/traversing/adapters.py +src/zope/traversing/api.py +src/zope/traversing/interfaces.py +src/zope/traversing/namespace.py +src/zope/traversing/publicationtraverse.py +src/zope/traversing/testing.py +src/zope/traversing/browser/__init__.py +src/zope/traversing/browser/absoluteurl.py +src/zope/traversing/browser/interfaces.py +src/zope/traversing/browser/tests.py +src/zope/traversing/tests/__init__.py +src/zope/traversing/tests/test_conveniencefunctions.py +src/zope/traversing/tests/test_dependencies.py +src/zope/traversing/tests/test_lang.py +src/zope/traversing/tests/test_namespacetrversal.py +src/zope/traversing/tests/test_physicallocationadapters.py +src/zope/traversing/tests/test_presentation.py +src/zope/traversing/tests/test_publicationtraverse.py +src/zope/traversing/tests/test_skin.py +src/zope/traversing/tests/test_traverser.py +src/zope/traversing/tests/test_vh.py +src/zope/traversing/tests/test_vhosting.py +src/zope/viewlet/__init__.py +src/zope/viewlet/interfaces.py +src/zope/viewlet/manager.py +src/zope/viewlet/metaconfigure.py +src/zope/viewlet/metadirectives.py +src/zope/viewlet/tests.py +src/zope/viewlet/viewlet.py \ No newline at end of file diff -Nru zope3-3.4.0/src/Zope3.egg-info/top_level.txt zope3-3.5~bzr18/src/Zope3.egg-info/top_level.txt --- zope3-3.4.0/src/Zope3.egg-info/top_level.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/Zope3.egg-info/top_level.txt 2010-07-27 11:03:08.000000000 +0000 @@ -0,0 +1,13 @@ +BTrees +RestrictedPython +zdaemon +persistent +ZEO +ZConfig +docutils +ZODB +wsgi_intercept +zope +mechanize +zc +transaction diff -Nru zope3-3.4.0/src/zope.proxy/proxy.h zope3-3.5~bzr18/src/zope.proxy/proxy.h --- zope3-3.4.0/src/zope.proxy/proxy.h 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/src/zope.proxy/proxy.h 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,65 @@ +#ifndef _proxy_H_ +#define _proxy_H_ 1 + +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +typedef Py_ssize_t (*lenfunc)(PyObject *); +typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t); +typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t); +typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *); +typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *); +#endif + +typedef struct { + PyObject_HEAD + PyObject *proxy_object; +} ProxyObject; + +#define Proxy_GET_OBJECT(ob) (((ProxyObject *)(ob))->proxy_object) + +typedef struct { + PyTypeObject *proxytype; + int (*check)(PyObject *obj); + PyObject *(*create)(PyObject *obj); + PyObject *(*getobject)(PyObject *proxy); +} ProxyInterface; + + +#ifndef PROXY_MODULE + +/* These are only defined in the public interface, and are not + * available within the module implementation. There we use the + * classic Python/C API only. + */ + +static ProxyInterface *_proxy_api = NULL; + +static int +Proxy_Import(void) +{ + if (_proxy_api == NULL) { + PyObject *m = PyImport_ImportModule("zope.proxy"); + if (m != NULL) { + PyObject *tmp = PyObject_GetAttrString(m, "_CAPI"); + if (tmp != NULL) { + if (PyCObject_Check(tmp)) + _proxy_api = (ProxyInterface *) + PyCObject_AsVoidPtr(tmp); + Py_DECREF(tmp); + } + } + } + return (_proxy_api == NULL) ? -1 : 0; +} + +#define ProxyType (*_proxy_api->proxytype) +#define Proxy_Check(obj) (_proxy_api->check((obj))) +#define Proxy_CheckExact(obj) ((obj)->ob_type == ProxyType) +#define Proxy_New(obj) (_proxy_api->create((obj))) +#define Proxy_GetObject(proxy) (_proxy_api->getobject((proxy))) + +#endif /* PROXY_MODULE */ + +#endif /* _proxy_H_ */ diff -Nru zope3-3.4.0/test.config zope3-3.5~bzr18/test.config --- zope3-3.4.0/test.config 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/test.config 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,113 @@ +# test.config: options for Zope Test Runner. +# Use this file to set Test Runner options that you would +# otherwise set at the command line. + +# LEVEL= +# Run the tests at the given level. See -a, --all. +# LEVEL = 1 + +# BUILD= +# Build extensions before testing. See -b. +# BUILD = False + +# BUILD= +# Build extensions before testing. See -b. +# BUILD = False + +# BUILD_INPLACE= +# With BUILD==True, tests will be run from src directory. +# See -B. +# BUILD_INPLACE = False + +# PYCHECKER= +# Use pychecker. See -c. +# PYCHECKER = False + +# DEBUG= +# Run a debug version of test harness. +# See -d. +# DEBUG = False + +# DEBUGGER= +# whether to post-mortem with pdb. +# See -D. +# DEBUGGER = False + +# RUN_UNIT= +# RUN_FUNCTIONAL= +# Whether to run functional tests and/or unit tests. +# Default behaviour is RUN_UNIT and RUN_FUNCTIONAL. +# RUN_UNIT = True +# RUN_FUNCTIONAL = True + +# GC_THRESHOLD=| +# Set the garbage collection threshold. +# See -g. +# GC_THRESHOLD = None + +# GC_FLAGS= +# list of garbage collection flags to set. +# See -G. +# GC_FLAGS = [] + +# KEEP_STALE_BYTECODE= +# Do not delete all stale bytecode before running tests. +# See -k. + +# LIBDIR= +# Search for tests starting in the specified start directory. +# See -l. +# LIBDIR = '/path/to/dir/' + +# LOOP= +# Keep running the selected tests in a loop. +# See -L. +# LOOP = False + +# PROGRESS= +# Show running PROGRESS. It can be combined with -v or -vv. +# See -p. +# PROGRESS = False + +# REFCOUNT= +# Look for refcount problems. +# See -r. +# REFCOUNT = False + +# TRACE= +# Use the trace module from Python for code coverage. +# See -T. +# TRACE = False + +# TIMETESTS= +# Time the individual tests and print a list of the top , sorted from +# longest to shortest. +# See -t (which sets TIMETESTS=50). +# TIMETESTS = 0 + +# TIMESFN= +# File to which to write timing information. +# See -t. +# TIMESFN = '/path/to/file' + +# VERBOSE= +# Verbosity level. Set VERBOSE=0 for quiet. +# VERBOSE = 0 + +# TEST_DIRS= +# List of paths, limiting where tests are searched for. +# See --dir. +# TEST_DIRS = ['/path/to/tests/'] + +# GUI=|"minimal" +# Whether to run unittest GUI. Use string value "minimal" for the minimal GUI. +# GUI = False + + +# MODULE_FILTERS=[, ...] +# List of regular expressions (in string format) for matching test module paths +# MODULE_FILTERS = [] + +# TEST_FILTERS=[, ...] +# List of regular expressions (in string format) for matching test names +# TEST_FILTERS = [] diff -Nru zope3-3.4.0/test.py zope3-3.5~bzr18/test.py --- zope3-3.4.0/test.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/test.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,103 @@ +#!/usr/bin/env python2.5 +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 script + +$Id: test.py 74746 2007-04-25 11:26:02Z ctheune $ +""" +import logging, os, sys, warnings + +here = os.path.abspath(os.path.dirname(sys.argv[0])) + +# Remove this directory from path: +sys.path[:] = [p for p in sys.path if os.path.abspath(p) != here] + +# add src to path +src = os.path.join(here, 'src') +sys.path.insert(0, src) # put at beginning to avoid one in site_packages + +from zope.testing import testrunner + +defaults = ['--tests-pattern', '^f?tests$', '--test-path', src] +defaults += ['-m', + '!^(' + 'ZConfig' + '|' + 'BTrees' + '|' + 'persistent' + '|' + 'ThreadedAsync' + '|' + 'transaction' + '|' + 'ZEO' + '|' + 'ZODB' + '|' + 'zodbcode' + '|' + 'twisted' + '|' + 'zdaemon' + '|' + 'zope[.]app[.]authentication' + '|' + 'zope[.]app[.]applicationcontrol' + '|' + 'zope[.]app[.]container' + '|' + 'zope[.]app[.]component[.]browser' + '|' + 'zope[.]app[.]exception' + '|' + 'zope[.]app[.]publication' + '|' + 'zope[.]app[.]publisher' + '|' + 'zope[.]app[.]form' + '|' + 'zope[.]app[.]http' + '|' + 'zope[.]app[.]wsgi' + '|' + 'zope[.]app[.]testing' + '|' + 'zope[.]app[.]publisher[.]xmlrpc' + '|' + 'zope[.]configuration' + '|' + 'zope[.]testing' + '|' + 'zope[.]copypastemove' + '|' + 'zope[.]principalannotation' + '|' + 'zope[.]publisher' + '|' + 'zc[.]sourcefactory' + '|' + ')[.]'] + +# Get rid of twisted.conch.ssh warning +warnings.filterwarnings( + 'ignore', 'PyCrypto', RuntimeWarning, 'twisted[.]conch[.]ssh') + +result = testrunner.run(defaults) + +# Avoid spurious error during exit. Some thing is trying to log +# something after the files used by the logger have been closed. +logging.disable(999999999) + +os._exit(result) diff -Nru zope3-3.4.0/trial.py zope3-3.5~bzr18/trial.py --- zope3-3.4.0/trial.py 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/trial.py 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,27 @@ +#!/usr/bin/env python2.5 +############################################################################## +# +# Copyright (c) 2004 Zope Corporation 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 script + +$Id: test.py 29143 2005-02-14 22:43:16Z srichter $ +""" +import sys, os + +here = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, os.path.join(here, 'src')) + +from twisted.scripts.trial import run + +if __name__ == '__main__': + run() Binary files /tmp/fmVPiGRk0Y/zope3-3.4.0/Zope-3.4.0.tgz and /tmp/LVXw1_J8ci/zope3-3.5~bzr18/Zope-3.4.0.tgz differ diff -Nru zope3-3.4.0/ZopePublicLicense.txt zope3-3.5~bzr18/ZopePublicLicense.txt --- zope3-3.4.0/ZopePublicLicense.txt 1970-01-01 00:00:00.000000000 +0000 +++ zope3-3.5~bzr18/ZopePublicLicense.txt 2010-07-27 10:57:46.000000000 +0000 @@ -0,0 +1,54 @@ +Zope 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.