[Zope3-checkins] SVN: Zope3/branches/srichter-blow-services/ Added some bbb and updated the final packages to the new layout. Merged

Stephan Richter srichter at cosmos.phy.tufts.edu
Mon Jan 17 10:30:11 EST 2005


Log message for revision 28853:
  Added some bbb and updated the final packages to the new layout. Merged 
  trunk.
  

Changed:
  D   Zope3/branches/srichter-blow-services/package-includes/zope.app.registration.fssync-configure.zcml
  D   Zope3/branches/srichter-blow-services/package-includes/zope.app.site.fssync-configure.zcml
  D   Zope3/branches/srichter-blow-services/package-includes/zope.app.utility.fssync-configure.zcml
  U   Zope3/branches/srichter-blow-services/src/BTrees/Interfaces.py
  U   Zope3/branches/srichter-blow-services/src/BTrees/check.py
  U   Zope3/branches/srichter-blow-services/src/BTrees/floatvaluemacros.h
  U   Zope3/branches/srichter-blow-services/src/BTrees/tests/testBTrees.py
  U   Zope3/branches/srichter-blow-services/src/ZEO/ClientStorage.py
  U   Zope3/branches/srichter-blow-services/src/ZEO/__init__.py
  U   Zope3/branches/srichter-blow-services/src/ZEO/cache.py
  U   Zope3/branches/srichter-blow-services/src/ZEO/stats.py
  U   Zope3/branches/srichter-blow-services/src/ZEO/tests/ConnectionTests.py
  U   Zope3/branches/srichter-blow-services/src/ZEO/tests/test_cache.py
  U   Zope3/branches/srichter-blow-services/src/ZEO/version.txt
  U   Zope3/branches/srichter-blow-services/src/ZODB/BaseStorage.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/ConflictResolution.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/Connection.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/DB.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/DemoStorage.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/ExportImport.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/FileStorage.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fsdump.py
  A   Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fsoids.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fspack.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/MappingStorage.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/Mount.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/POSException.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/__init__.py
  A   Zope3/branches/srichter-blow-services/src/ZODB/collaborations.txt
  U   Zope3/branches/srichter-blow-services/src/ZODB/dbmStorage.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/interfaces.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/serialize.py
  A   Zope3/branches/srichter-blow-services/src/ZODB/tests/dbopen.txt
  D   Zope3/branches/srichter-blow-services/src/ZODB/tests/loggingsupport.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/tests/testConnection.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/tests/testDB.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/tests/testFileStorage.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/tests/testUtils.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/tests/testZODB.py
  A   Zope3/branches/srichter-blow-services/src/ZODB/tests/test_doctest_files.py
  A   Zope3/branches/srichter-blow-services/src/ZODB/tests/testfsoids.py
  U   Zope3/branches/srichter-blow-services/src/ZODB/utils.py
  U   Zope3/branches/srichter-blow-services/src/bugtracker/browser/bug.py
  U   Zope3/branches/srichter-blow-services/src/bugtracker/browser/bug_edit.pt
  U   Zope3/branches/srichter-blow-services/src/bugtracker/browser/comment.py
  U   Zope3/branches/srichter-blow-services/src/bugtracker/tests/placelesssetup.py
  U   Zope3/branches/srichter-blow-services/src/bugtracker/tests/test_mail.py
  U   Zope3/branches/srichter-blow-services/src/bugtracker/tests/test_vocabularies.py
  U   Zope3/branches/srichter-blow-services/src/bugtracker/vocabulary.py
  U   Zope3/branches/srichter-blow-services/src/persistent/cPersistence.c
  U   Zope3/branches/srichter-blow-services/src/persistent/interfaces.py
  U   Zope3/branches/srichter-blow-services/src/persistent/tests/test_persistent.py
  U   Zope3/branches/srichter-blow-services/src/transaction/_manager.py
  U   Zope3/branches/srichter-blow-services/src/transaction/_transaction.py
  U   Zope3/branches/srichter-blow-services/src/transaction/interfaces.py
  A   Zope3/branches/srichter-blow-services/src/transaction/tests/test_SampleResourceManager.py
  _U  Zope3/branches/srichter-blow-services/src/z3checkins/__init__.py
  A   Zope3/branches/srichter-blow-services/src/z3checkins/browser.py
  U   Zope3/branches/srichter-blow-services/src/z3checkins/configure.zcml
  UU  Zope3/branches/srichter-blow-services/src/z3checkins/folder.py
  UU  Zope3/branches/srichter-blow-services/src/z3checkins/interfaces.py
  UU  Zope3/branches/srichter-blow-services/src/z3checkins/message.py
  D   Zope3/branches/srichter-blow-services/src/z3checkins/message_plain.pt
  U   Zope3/branches/srichter-blow-services/src/z3checkins/tests/svn_msg4.txt
  A   Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_browser.py
  A   Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_folder.py
  U   Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_message.py
  A   Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_timeutils.py
  A   Zope3/branches/srichter-blow-services/src/z3checkins/timeutils.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/apidoc/classmodule/menu.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/applicationcontrol/browser/zodbcontrol.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/catalog/browser/advanced.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/__init__.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/
  A   Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/__init__.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/localservice.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/component/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/container/browser/contents.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/container/browser/metaconfigure.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/dav/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/dublincore/browser/edit.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/event/dispatching.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/exception/browser/tests/test_unauthorized.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/file/browser/file_add.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/file/browser/file_upload.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/form/browser/edit.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/form/browser/editwizard.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/homefolder/homefolder.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/introspector/introspector.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/introspector/marker.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/meta.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/observable/README.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/pau/README.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/pau/browser/groupfolder.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/pau/browser/principalfolder.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/pau/groupfolder.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/pau/groupfolder.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/pau/pau.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/pau/principalfolder.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/__init__.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/interfaces.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/tests/authsetup.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/tests/test_pluggableauth.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/presentation/__init__.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/presentation/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/presentation/browser/zpt.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/presentation/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/presentation/presentation.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/presentation/xxx_Tests/test_presentation.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/publication/httpfactory.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/publication/interfaces.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/publication/soap.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/publication/zopepublication.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/pythonpage/edit.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/rdb/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/schema/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/schema/browser/schema_edit.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/schema/fields.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/schema/tests/test_fieldfactory.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/schemacontent/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/schemacontent/browser/permission_edit.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/browser/auth.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/browser/principalterms.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/interfaces.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/principal.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/principalregistry.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/tests/test_securitydirectives.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/security/vocabulary.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/browser/granting.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/browser/granting.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_principalpermissionmanager.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_principalrolemanager.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_securitydirectives.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/zopepolicy.txt
  A   Zope3/branches/srichter-blow-services/src/zope/app/site/
  A   Zope3/branches/srichter-blow-services/src/zope/app/site/__init__.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/site/interfaces.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/site/meta.zcml
  A   Zope3/branches/srichter-blow-services/src/zope/app/site/metaconfigure.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/site/tests/
  A   Zope3/branches/srichter-blow-services/src/zope/app/site/tests/__init__.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/site/tests/placefulsetup.py
  A   Zope3/branches/srichter-blow-services/src/zope/app/table/
  U   Zope3/branches/srichter-blow-services/src/zope/app/workflow/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/configure.zcml
  U   Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/contentworkflow_registry.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/definition_edit.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/instance_manage.pt
  U   Zope3/branches/srichter-blow-services/src/zope/app/zapi/README.txt
  U   Zope3/branches/srichter-blow-services/src/zope/app/zapi/__init__.py
  U   Zope3/branches/srichter-blow-services/src/zope/app/zptpage/browser/inlinecode.pt
  U   Zope3/branches/srichter-blow-services/src/zope/component/README.txt
  U   Zope3/branches/srichter-blow-services/src/zope/component/bbb/service.py
  U   Zope3/branches/srichter-blow-services/src/zope/event/README.txt
  U   Zope3/branches/srichter-blow-services/src/zope/interface/README.txt
  U   Zope3/branches/srichter-blow-services/src/zope/interface/adapter.py
  U   Zope3/branches/srichter-blow-services/src/zope/schema/README.txt
  A   Zope3/branches/srichter-blow-services/src/zope/testing/formparser.py
  A   Zope3/branches/srichter-blow-services/src/zope/testing/formparser.txt
  U   Zope3/branches/srichter-blow-services/src/zope/testing/tests.py
  U   Zope3/branches/srichter-blow-services/src/zwiki/browser/add.pt
  U   Zope3/branches/srichter-blow-services/src/zwiki/browser/wikipage.py
  U   Zope3/branches/srichter-blow-services/src/zwiki/traversal.py
  U   Zope3/branches/srichter-blow-services/zopeskel/bin/debugzope.in
  U   Zope3/branches/srichter-blow-services/zopeskel/bin/i18nextract.in
  U   Zope3/branches/srichter-blow-services/zopeskel/bin/pyskel.in
  U   Zope3/branches/srichter-blow-services/zopeskel/bin/test.in
  U   Zope3/branches/srichter-blow-services/zopeskel/bin/zopectl.in

-=-
Deleted: Zope3/branches/srichter-blow-services/package-includes/zope.app.registration.fssync-configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/package-includes/zope.app.registration.fssync-configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/package-includes/zope.app.registration.fssync-configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,3 +0,0 @@
-<configure>
-<!-- XXX: include package="zope.app.registration.fssync" / -->
-</configure>

Deleted: Zope3/branches/srichter-blow-services/package-includes/zope.app.site.fssync-configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/package-includes/zope.app.site.fssync-configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/package-includes/zope.app.site.fssync-configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,3 +0,0 @@
-<configure>
-<!-- XXX: include package="zope.app.site.fssync" / -->
-</configure>

Deleted: Zope3/branches/srichter-blow-services/package-includes/zope.app.utility.fssync-configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/package-includes/zope.app.utility.fssync-configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/package-includes/zope.app.utility.fssync-configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,3 +0,0 @@
-<configure>
-<!-- XXX: include package="zope.app.utility.fssync" /-->
-</configure>

Modified: Zope3/branches/srichter-blow-services/src/BTrees/Interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/BTrees/Interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/BTrees/Interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -12,8 +12,7 @@
 #
 ##############################################################################
 
-import OOBTree, Interface
-from Interface import Interface
+from zope.interface import Interface
 
 class ICollection(Interface):
 
@@ -390,11 +389,3 @@
 # Eventually, I need to express this through the interfaces.
 #
 ################################################################
-
-# XXX Need to use the new declaration syntax once it is available
-# for Zope 2.
-
-## OOBTree.OOSet.__implements__=ISet
-## OOBTree.OOTreeSet.__implements__=ITreeSet
-## OOBTree.OOBucket.__implements__=IDictionaryIsh
-## OOBTree.OOBTree.__implements__=IBTree

Modified: Zope3/branches/srichter-blow-services/src/BTrees/check.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/BTrees/check.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/BTrees/check.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -40,7 +40,7 @@
 from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet
 from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet
 
-from ZODB.utils import positive_id
+from ZODB.utils import positive_id, oid_repr
 
 TYPE_UNKNOWN, TYPE_BTREE, TYPE_BUCKET = range(3)
 
@@ -208,7 +208,11 @@
     return keys, values
 
 def type_and_adr(obj):
-    return "%s (0x%x)" % (type(obj).__name__, positive_id(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()

Modified: Zope3/branches/srichter-blow-services/src/BTrees/floatvaluemacros.h
===================================================================
--- Zope3/branches/srichter-blow-services/src/BTrees/floatvaluemacros.h	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/BTrees/floatvaluemacros.h	2005-01-17 15:30:10 UTC (rev 28853)
@@ -3,21 +3,21 @@
 
 #define VALUE_TYPE float
 #undef VALUE_TYPE_IS_PYOBJECT
-#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0)) 
+#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_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; } 
-  
+      (STATUS)=0; (TARGET)=0; }
+
 #define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0
 
 #define MERGE_DEFAULT 1.0f

Modified: Zope3/branches/srichter-blow-services/src/BTrees/tests/testBTrees.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/BTrees/tests/testBTrees.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/BTrees/tests/testBTrees.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -206,7 +206,7 @@
             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:
@@ -235,7 +235,7 @@
             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:
@@ -267,7 +267,7 @@
 
         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:

Modified: Zope3/branches/srichter-blow-services/src/ZEO/ClientStorage.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZEO/ClientStorage.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZEO/ClientStorage.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -309,7 +309,7 @@
             cache_path = os.path.join(dir, "%s-%s.zec" % (client, storage))
         else:
             cache_path = None
-        self._cache = self.ClientCacheClass(cache_path)
+        self._cache = self.ClientCacheClass(cache_path, size=cache_size)
         # XXX When should it be opened?
         self._cache.open()
 
@@ -353,21 +353,29 @@
             self._wait_sync(deadline)
 
     def _wait_sync(self, deadline=None):
-        # If there is no mainloop running, this code needs
-        # to call poll() to cause asyncore to handle events.
-        while 1:
-            if self._ready.isSet():
-                break
-            if deadline and time.time() > deadline:
+        # Log no more than one "waiting" message per LOG_THROTTLE seconds.
+        LOG_THROTTLE = 300 # 5 minutes
+        next_log_time = time.time()
+
+        while not self._ready.isSet():
+            now = time.time()
+            if deadline and now > deadline:
                 log2("Timed out waiting for connection", level=logging.WARNING)
                 break
-            log2("Waiting for cache verification to finish")
+            if now >= next_log_time:
+                log2("Waiting for cache verification to finish")
+                next_log_time = now + LOG_THROTTLE
             if self._connection is None:
                 # If the connection was closed while we were
                 # waiting for it to become ready, start over.
-                return self._wait(deadline - time.time())
-            else:
-                self._connection.pending(30)
+                if deadline is None:
+                    timeout = None
+                else:
+                    timeout = deadline - now
+                return self._wait(timeout)
+            # No mainloop ia running, so we need to call something fancy to
+            # handle asyncore events.
+            self._connection.pending(30)
 
     def close(self):
         """Storage API: finalize the storage, releasing external resources."""

Modified: Zope3/branches/srichter-blow-services/src/ZEO/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZEO/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZEO/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -22,4 +22,4 @@
 """
 
 # The next line must use double quotes, so release.py recognizes it.
-version = "2.3"
+version = "2.4a0"

Modified: Zope3/branches/srichter-blow-services/src/ZEO/cache.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZEO/cache.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZEO/cache.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -13,13 +13,13 @@
 ##############################################################################
 """Disk-based client cache for ZEO.
 
-ClientCache exposes an API used by the ZEO client storage.  FileCache
-stores objects one disk using a 2-tuple of oid and tid as key.
+ClientCache exposes an API used by the ZEO client storage.  FileCache stores
+objects on disk using a 2-tuple of oid and tid as key.
 
-The upper cache's API is similar to a storage API with methods like
-load(), store(), and invalidate().  It manages in-memory data
-structures that allow it to map this richer API onto the simple
-key-based API of the lower-level cache.
+ClientCaches API is similar to a storage API with methods like load(),
+store(), and invalidate().  It manages in-memory data structures that allow
+it to map this richer API onto the simple key-based API of the lower-level
+FileCache.
 """
 
 import bisect
@@ -31,6 +31,8 @@
 
 from ZODB.utils import z64, u64
 
+logger = logging.getLogger("zeo.cache")
+
 ##
 # A disk-based cache for ZEO clients.
 # <p>
@@ -54,9 +56,9 @@
 # <h3>Cache verification</h3>
 # <p>
 # When the client is connected to the server, it receives
-# invalidations every time an object is modified.  Whe the client is
-# disconnected, it must perform cache verification to make sure its
-# cached data is synchronized with the storage's current state.
+# invalidations every time an object is modified.  When the client is
+# disconnected then reconnects, it must perform cache verification to make
+# sure its cached data is synchronized with the storage's current state.
 # <p>
 # quick verification
 # full verification
@@ -67,23 +69,22 @@
 
     ##
     # Do we put the constructor here?
-    # @param path path of persistent snapshot of cache state
-    # @param size maximum size of object data, in bytes
+    # @param path path of persistent snapshot of cache state (a file path)
+    # @param size size of cache file, in bytes
 
-    def __init__(self, path=None, size=None, trace=False):
+    # The default size of 200MB makes a lot more sense than the traditional
+    # default of 20MB.  The default here is misleading, though, since
+    # ClientStorage is the only user of ClientCache, and it always passes an
+    # explicit size of its own choosing.
+    def __init__(self, path=None, size=200*1024**2, trace=False):
         self.path = path
         self.size = size
-        self.log = logging.getLogger("zeo.cache")
 
         if trace and path:
             self._setup_trace()
         else:
             self._trace = self._notrace
 
-        # Last transaction seen by the cache, either via setLastTid()
-        # or by invalidate().
-        self.tid = None
-
         # The cache stores objects in a dict mapping (oid, tid) pairs
         # to Object() records (see below).  The tid is the transaction
         # id that wrote the object.  An object record includes data,
@@ -95,25 +96,32 @@
         # records.  The in-memory form can be reconstructed from these
         # records.
 
-        # Maps oid to current tid.  Used to find compute key for objects.
+        # Maps oid to current tid.  Used to compute key for objects.
         self.current = {}
+
         # Maps oid to list of (start_tid, end_tid) pairs in sorted order.
         # Used to find matching key for load of non-current data.
         self.noncurrent = {}
-        # Map oid to version, tid pair.  If there is no entry, the object
+
+        # Map oid to (version, tid) pair.  If there is no entry, the object
         # is not modified in a version.
         self.version = {}
 
-        # A double-linked list is used to manage the cache.  It makes
-        # decisions about which objects to keep and which to evict.
-        self.fc = FileCache(size or 10**6, self.path, self)
+        # A FileCache instance does all the low-level work of storing
+        # and retrieving objects to/from the cache file.
+        self.fc = FileCache(size, self.path, self)
 
     def open(self):
         self.fc.scan(self.install)
 
+    ##
+    # Callback for FileCache.scan(), when a pre-existing file cache is
+    # used.  For each object in the file, `install()` is invoked.  `f`
+    # is the file object, positioned at the start of the serialized Object.
+    # `ent` is an Entry giving the object's key ((oid, start_tid) pair).
     def install(self, f, ent):
-        # Called by cache storage layer to insert object
-        o = Object.fromFile(f, ent.key, header_only=True)
+        # Called by cache storage layer to insert object.
+        o = Object.fromFile(f, ent.key, skip_data=True)
         if o is None:
             return
         oid = o.key[0]
@@ -122,8 +130,13 @@
         elif o.end_tid is None:
             self.current[oid] = o.start_tid
         else:
-            L = self.noncurrent.setdefault(oid, [])
-            bisect.insort_left(L, (o.start_tid, o.end_tid))
+            assert o.start_tid < o.end_tid
+            this_span = o.start_tid, o.end_tid
+            span_list = self.noncurrent.get(oid)
+            if span_list:
+                bisect.insort_left(span_list, this_span)
+            else:
+                self.noncurrent[oid] = [this_span]
 
     def close(self):
         self.fc.close()
@@ -139,7 +152,7 @@
     ##
     # Return the last transaction seen by the cache.
     # @return a transaction id
-    # @defreturn string
+    # @defreturn string, or None if no transaction is yet known
 
     def getLastTid(self):
         if self.fc.tid == z64:
@@ -151,7 +164,7 @@
     # Return the current data record for oid and version.
     # @param oid object id
     # @param version a version string
-    # @return data record, serial number, tid or None if the object is not
+    # @return (data record, serial number, tid), or None if the object is not
     #         in the cache
     # @defreturn 3-tuple: (string, string, string)
 
@@ -207,7 +220,7 @@
         return o.data, o.start_tid, o.end_tid
 
     ##
-    # Return the version an object is modified in or None for an
+    # Return the version an object is modified in, or None for an
     # object that is not modified in a version.
     # @param oid object id
     # @return name of version in which the object is modified
@@ -225,7 +238,7 @@
     # @param oid object id
     # @param version name of version that oid was modified in.  The cache
     #                only stores current version data, so end_tid should
-    #                be None.
+    #                be None if version is not the empty string.
     # @param start_tid the id of the transaction that wrote this revision
     # @param end_tid the id of the transaction that created the next
     #                revision of oid.  If end_tid is None, the data is
@@ -235,11 +248,11 @@
 
     def store(self, oid, version, start_tid, end_tid, data):
         # It's hard for the client to avoid storing the same object
-        # more than once.  One case is whether the client requests
+        # more than once.  One case is when the client requests
         # version data that doesn't exist.  It checks the cache for
         # the requested version, doesn't find it, then asks the server
         # for that data.  The server returns the non-version data,
-        # which may already by in the cache.
+        # which may already be in the cache.
         if (oid, start_tid) in self.fc:
             return
         o = Object((oid, start_tid), version, data, start_tid, end_tid)
@@ -274,48 +287,97 @@
         self.fc.add(o)
 
     ##
-    # Mark the current data for oid as non-current.  If there is no
-    # current data for oid, do nothing.
+    # Remove all knowledge of noncurrent revisions of oid, both in
+    # self.noncurrent and in our FileCache.  `version` and `tid` are used
+    # only for trace records.
+    def _remove_noncurrent_revisions(self, oid, version, tid):
+        noncurrent_list = self.noncurrent.get(oid)
+        if noncurrent_list:
+            # Note:  must iterate over a copy of noncurrent_list.  The
+            # FileCache remove() calls our _evicted() method, and that
+            # mutates the list.
+            for old_tid, dummy in noncurrent_list[:]:
+                # 0x1E = invalidate (hit, discarding current or non-current)
+                self._trace(0x1E, oid, version, tid)
+                self.fc.remove((oid, old_tid))
+            del self.noncurrent[oid]
+
+    ##
+    # If `tid` is None, or we have data for `oid` in a (non-empty) version,
+    # forget all knowledge of `oid`.  (`tid` can be None only for
+    # invalidations generated by startup cache verification.)  If `tid`
+    # isn't None, we don't have version data for `oid`, and we had current
+    # data for `oid`, stop believing we have current data, and mark the
+    # data we had as being valid only up to `tid`.  In all other cases, do
+    # nothing.
     # @param oid object id
     # @param version name of version to invalidate.
-    # @param tid the id of the transaction that wrote a new revision of oid
-
+    # @param tid the id of the transaction that wrote a new revision of oid,
+    #        or None to forget all cached info about oid (version, current
+    #        revision, and non-current revisions)
     def invalidate(self, oid, version, tid):
-        if tid > self.fc.tid:
+        if tid > self.fc.tid and tid is not None:
             self.fc.settid(tid)
+
+        remove_all_knowledge_of_oid = tid is None
+
         if oid in self.version:
+            # Forget we know about the version data.
+            # 0x1A = invalidate (hit, version)
             self._trace(0x1A, oid, version, tid)
             dllversion, dlltid = self.version[oid]
             assert not version or version == dllversion, (version, dllversion)
-            # remove() will call unlink() to delete from self.version
             self.fc.remove((oid, dlltid))
-            # And continue on, we must also remove any non-version data
-            # from the cache.  This is a bit of a failure of the current
-            # cache consistency approach as the new tid of the version
-            # data gets confused with the old tid of the non-version data.
-            # I could sort this out, but it seems simpler to punt and
-            # have the cache invalidation too much for versions.
+            assert oid not in self.version # .remove() got rid of it
+            # And continue:  we must also remove any non-version data from
+            # the cache.  Or, at least, I have such a poor understanding of
+            # versions that anything less drastic would probably be wrong.
+            remove_all_knowledge_of_oid = True
 
-        if oid not in self.current:
+        if remove_all_knowledge_of_oid:
+            self._remove_noncurrent_revisions(oid, version, tid)
+
+        # Only current, non-version data remains to be handled.
+
+        cur_tid = self.current.get(oid)
+        if not cur_tid:
+            # 0x10 == invalidate (miss)
             self._trace(0x10, oid, version, tid)
             return
-        cur_tid = self.current.pop(oid)
+
+        # We had current data for oid, but no longer.
+
+        if remove_all_knowledge_of_oid:
+            # 0x1E = invalidate (hit, discarding current or non-current)
+            self._trace(0x1E, oid, version, tid)
+            self.fc.remove((oid, cur_tid))
+            assert cur_tid not in self.current  # .remove() got rid of it
+            return
+
+        # Add the data we have to the list of non-current data for oid.
+        assert tid is not None and cur_tid < tid
+        # 0x1C = invalidate (hit, saving non-current)
+        self._trace(0x1C, oid, version, tid)
+        del self.current[oid]   # because we no longer have current data
+
+        # Update the end_tid half of oid's validity range on disk.
         # XXX Want to fetch object without marking it as accessed
         o = self.fc.access((oid, cur_tid))
+        assert o is not None
+        assert o.end_tid is None  # i.e., o was current
         if o is None:
-            # XXX is this possible?
-            return None
+            # XXX is this possible? (doubt it; added an assert just above)
+            return
         o.end_tid = tid
-        self.fc.update(o)
-        self._trace(0x1C, oid, version, tid)
+        self.fc.update(o)   # record the new end_tid on disk
+        # Add to oid's list of non-current data.
         L = self.noncurrent.setdefault(oid, [])
         bisect.insort_left(L, (cur_tid, tid))
 
     ##
     # Return the number of object revisions in the cache.
-
+    #
     # XXX just return len(self.cache)?
-
     def __len__(self):
         n = len(self.current) + len(self.version)
         if self.noncurrent:
@@ -323,7 +385,7 @@
         return n
 
     ##
-    # Generates over, version, serial triples for all objects in the
+    # Generates (oid, serial, version) triples for all objects in the
     # cache.  This generator is used by cache verification.
 
     def contents(self):
@@ -346,14 +408,14 @@
             print oid_repr(oid), oid_repr(tid), repr(version)
         print "dll contents"
         L = list(self.fc)
-        L.sort(lambda x,y:cmp(x.key, y.key))
+        L.sort(lambda x, y: cmp(x.key, y.key))
         for x in L:
             end_tid = x.end_tid or z64
             print oid_repr(x.key[0]), oid_repr(x.key[1]), oid_repr(end_tid)
         print
 
     def _evicted(self, o):
-        # Called by Object o to signal its eviction
+        # Called by the FileCache to signal that Object o has been evicted.
         oid, tid = o.key
         if o.end_tid is None:
             if o.version:
@@ -361,7 +423,7 @@
             else:
                 del self.current[oid]
         else:
-            # XXX Although we use bisect to keep the list sorted,
+            # Although we use bisect to keep the list sorted,
             # we never expect the list to be very long.  So the
             # brute force approach should normally be fine.
             L = self.noncurrent[oid]
@@ -375,8 +437,8 @@
             self._trace(0x00)
         except IOError, msg:
             self.tracefile = None
-            self.log.warning("Could not write to trace file %s: %s",
-                             tfn, msg)
+            logger.warning("Could not write to trace file %s: %s",
+                           tfn, msg)
 
     def _notrace(self, *arg, **kwargs):
         pass
@@ -419,82 +481,135 @@
 # data and whether it is in a version.
 # <p>
 # The serialized format does not include the key, because it is stored
-# in the header used by the cache's storage format.
+# in the header used by the cache file's storage format.
+# <p>
+# Instances of Object are generally short-lived -- they're really a way to
+# package data on the way to or from the disk file.
 
 class Object(object):
-    __slots__ = (# pair, object id, txn id -- something usable as a dict key
-                 # the second part of the part is equal to start_tid below
+    __slots__ = (# pair (object id, txn id) -- something usable as a dict key;
+                 # the second part of the pair is equal to start_tid
                  "key",
 
-                 "start_tid", # string, id of txn that wrote the data
-                 "end_tid", # string, id of txn that wrote next revision
-                            # or None
-                 "version", # string, name of version
-                 "data", # string, the actual data record for the object
+                 # string, tid of txn that wrote the data
+                 "start_tid",
 
-                 "size", # total size of serialized object
+                 # string, tid of txn that wrote next revision, or None
+                 # if the data is current; if not None, end_tid is strictly
+                 # greater than start_tid
+                 "end_tid",
+
+                 # string, name of version
+                 "version",
+
+                 # string, the actual data record for the object
+                 "data",
+
+                 # total size of serialized object; this includes the
+                 # data, version, and all overhead (header) bytes.
+                 "size",
                 )
 
+    # A serialized Object on disk looks like:
+    #
+    #         offset             # bytes   value
+    #         ------             -------   -----
+    #              0                   8   end_tid; string
+    #              8                   2   len(version); 2-byte signed int
+    #             10                   4   len(data); 4-byte signed int
+    #             14        len(version)   version; string
+    # 14+len(version)          len(data)   the object pickle; string
+    # 14+len(version)+
+    #       len(data)                  8   oid; string
+
+    # The serialization format uses an end tid of "\0"*8 (z64), the least
+    # 8-byte string, to represent None.  It isn't possible for an end_tid
+    # to be 0, because it must always be strictly greater than the start_tid.
+
+    fmt = ">8shi"  # end_tid, len(self.version), len(self.data)
+    FIXED_HEADER_SIZE = struct.calcsize(fmt)
+    assert FIXED_HEADER_SIZE == 14
+    TOTAL_FIXED_SIZE = FIXED_HEADER_SIZE + 8  # +8 for the oid at the end
+
     def __init__(self, key, version, data, start_tid, end_tid):
         self.key = key
         self.version = version
         self.data = data
         self.start_tid = start_tid
         self.end_tid = end_tid
-        # The size of a the serialized object on disk, include the
-        # 14-byte header, the length of data and version, and a
+        # The size of the serialized object on disk, including the
+        # 14-byte header, the lengths of data and version, and a
         # copy of the 8-byte oid.
         if data is not None:
-            self.size = 22 + len(data) + len(version)
+            self.size = self.TOTAL_FIXED_SIZE + len(data) + len(version)
 
-    # The serialization format uses an end tid of "\0" * 8, the least
-    # 8-byte string, to represent None.  It isn't possible for an
-    # end_tid to be 0, because it must always be strictly greater
-    # than the start_tid.
+    ##
+    # Return the fixed-sized serialization header as a string:  pack end_tid,
+    # and the lengths of the .version and .data members.
+    def get_header(self):
+        return struct.pack(self.fmt,
+                           self.end_tid or z64,
+                           len(self.version),
+                           len(self.data))
 
-    fmt = ">8shi"
-
+    ##
+    # Write the serialized representation of self to file f, at its current
+    # position.
     def serialize(self, f):
-        # Write standard form of Object to file, f.
-        self.serialize_header(f)
-        f.write(self.data)
-        f.write(self.key[0])
+        f.writelines([self.get_header(),
+                      self.version,
+                      self.data,
+                      self.key[0]])
 
+    ##
+    # Write the fixed-size header for self, to file f at its current position.
+    # The only real use for this is when the current revision of an object
+    # in cache is invalidated.  Then the end_tid field gets set to the tid
+    # of the transaction that caused the invalidation.
     def serialize_header(self, f):
-        s = struct.pack(self.fmt, self.end_tid or "\0" * 8,
-                        len(self.version), len(self.data))
-        f.write(s)
-        f.write(self.version)
+        f.write(self.get_header())
 
-    def fromFile(cls, f, key, header_only=False):
-        s = f.read(struct.calcsize(cls.fmt))
+    ##
+    # fromFile is a class constructor, unserializing an Object from the
+    # current position in file f.  Exclusive access to f for the duration
+    # is assumed.  The key is a (oid, start_tid) pair, and the oid must
+    # match the serialized oid.  If `skip_data` is true, .data is left
+    # None in the Object returned, but all the other fields are populated.
+    # Else (`skip_data` is false, the default), all fields including .data
+    # are populated.  .data can be big, so it's prudent to skip it when it
+    # isn't needed.
+    def fromFile(cls, f, key, skip_data=False):
+        s = f.read(cls.FIXED_HEADER_SIZE)
         if not s:
             return None
         oid, start_tid = key
+
         end_tid, vlen, dlen = struct.unpack(cls.fmt, s)
         if end_tid == z64:
             end_tid = None
+
         version = f.read(vlen)
         if vlen != len(version):
             raise ValueError("corrupted record, version")
-        if header_only:
+
+        if skip_data:
             data = None
+            f.seek(dlen, 1)
         else:
             data = f.read(dlen)
             if dlen != len(data):
                 raise ValueError("corrupted record, data")
-            s = f.read(8)
-            if s != oid:
-                raise ValueError("corrupted record, oid")
+
+        s = f.read(8)
+        if s != oid:
+            raise ValueError("corrupted record, oid")
+
         return cls((oid, start_tid), version, data, start_tid, end_tid)
 
     fromFile = classmethod(fromFile)
 
-def sync(f):
-    f.flush()
-    if hasattr(os, 'fsync'):
-        os.fsync(f.fileno())
 
+# Entry just associates a key with a file offset.  It's used by FileCache.
 class Entry(object):
     __slots__ = (# object key -- something usable as a dict key.
                  'key',
@@ -513,22 +628,22 @@
         self.offset = offset
 
 
-magic = "ZEC3"
 
-OBJECT_HEADER_SIZE = 1 + 4 + 16
-
 ##
 # FileCache stores a cache in a single on-disk file.
 #
-# On-disk cache structure
+# On-disk cache structure.
 #
 # The file begins with a 12-byte header.  The first four bytes are the
 # file's magic number - ZEC3 - indicating zeo cache version 3.  The
 # next eight bytes are the last transaction id.
+
+magic = "ZEC3"
+ZEC3_HEADER_SIZE = 12
+
+# After the header, the file contains a contiguous sequence of blocks.  All
+# blocks begin with a one-byte status indicator:
 #
-# The file is a contiguous sequence of blocks.  All blocks begin with
-# a one-byte status indicator:
-#
 # 'a'
 #       Allocated.  The block holds an object; the next 4 bytes are >I
 #       format total block size.
@@ -540,22 +655,20 @@
 # '1', '2', '3', '4'
 #       The block is free, and consists of 1, 2, 3 or 4 bytes total.
 #
-# 'Z'
-#       File header.  The file starts with a magic number, currently
-#       'ZEC3' and an 8-byte transaction id.
-#
 # "Total" includes the status byte, and size bytes.  There are no
 # empty (size 0) blocks.
 
 
-# XXX This needs a lot more hair.
-# The structure of an allocated block is more complicated:
+# Allocated blocks have more structure:
 #
 #     1 byte allocation status ('a').
 #     4 bytes block size, >I format.
 #     16 bytes oid + tid, string.
-#     size-OBJECT_HEADER_SIZE bytes, the object pickle.
+#     size-OBJECT_HEADER_SIZE bytes, the serialization of an Object (see
+#         class Object for details).
 
+OBJECT_HEADER_SIZE = 1 + 4 + 16
+
 # The cache's currentofs goes around the file, circularly, forever.
 # It's always the starting offset of some block.
 #
@@ -564,47 +677,80 @@
 # blocks needed to make enough room for the new object are evicted,
 # starting at currentofs.  Exception:  if currentofs is close enough
 # to the end of the file that the new object can't fit in one
-# contiguous chunk, currentofs is reset to 0 first.
+# contiguous chunk, currentofs is reset to ZEC3_HEADER_SIZE first.
 
-# Do all possible to ensure that the bytes we wrote are really on
+# Do all possible to ensure that the bytes we wrote to file f are really on
 # disk.
+def sync(f):
+    f.flush()
+    if hasattr(os, 'fsync'):
+        os.fsync(f.fileno())
 
 class FileCache(object):
 
     def __init__(self, maxsize, fpath, parent, reuse=True):
-        # Maximum total of object sizes we keep in cache.
+        # - `maxsize`:  total size of the cache file, in bytes; this is
+        #   ignored if reuse is true and fpath names an existing file;
+        #   perhaps we should attempt to change the cache size in that
+        #   case
+        # - `fpath`:  filepath for the cache file, or None; see `reuse`
+        # - `parent`:  the ClientCache this FileCache is part of
+        # - `reuse`:  If true, and fpath is not None, and fpath names a
+        #    file that exists, that pre-existing file is used (persistent
+        #    cache).  In all other cases a new file is created:  a temp
+        #    file if fpath is None, else with path fpath.
         self.maxsize = maxsize
-        # Current total of object sizes in cache.
-        self.currentsize = 0
         self.parent = parent
+
+        # tid for the most recent transaction we know about.  This is also
+        # stored near the start of the file.
         self.tid = None
 
+        # There's one Entry instance, kept in memory, for each currently
+        # allocated block in the file, and there's one allocated block in the
+        # file per serialized Object.  filemap retrieves the Entry given the
+        # starting offset of a block, and key2entry retrieves the Entry given
+        # an object revision's key (an (oid, start_tid) pair).  From an
+        # Entry, we can get the Object's key and file offset.
+
         # Map offset in file to pair (data record size, Entry).
         # Entry is None iff the block starting at offset is free.
         # filemap always contains a complete account of what's in the
         # file -- study method _verify_filemap for executable checking
         # of the relevant invariants.  An offset is at the start of a
-        # block iff it's a key in filemap.
+        # block iff it's a key in filemap.  The data record size is
+        # stored in the file too, so we could just seek to the offset
+        # and read it up; keeping it in memory is an optimization.
         self.filemap = {}
 
-        # Map key to Entry.  There's one entry for each object in the
-        # cache file.  After
+        # Map key to Entry.  After
         #     obj = key2entry[key]
         # then
         #     obj.key == key
-        # is true.
+        # is true.  An object is currently stored on disk iff its key is in
+        # key2entry.
         self.key2entry = {}
 
         # Always the offset into the file of the start of a block.
         # New and relocated objects are always written starting at
         # currentofs.
-        self.currentofs = 12
+        self.currentofs = ZEC3_HEADER_SIZE
 
+        # self.f is the open file object.
+        # When we're not reusing an existing file, self.f is left None
+        # here -- the scan() method must be called then to open the file
+        # (and it sets self.f).
+
         self.fpath = fpath
-        if not reuse or not fpath or not os.path.exists(fpath):
-            self.new = True
+        if reuse and fpath and os.path.exists(fpath):
+            # Reuse an existing file.  scan() will open & read it.
+            self.f = None
+        else:
+            if reuse:
+                logger.warning("reuse=True but the given file path %r "
+                               "doesn't exist; ignoring reuse=True", fpath)
             if fpath:
-                self.f = file(fpath, 'wb+')
+                self.f = open(fpath, 'wb+')
             else:
                 self.f = tempfile.TemporaryFile()
             # Make sure the OS really saves enough bytes for the file.
@@ -616,34 +762,44 @@
             self.f.write(magic)
             self.f.write(z64)
             # and one free block.
-            self.f.write('f' + struct.pack(">I", self.maxsize - 12))
+            self.f.write('f' + struct.pack(">I", self.maxsize -
+                                                 ZEC3_HEADER_SIZE))
             self.sync()
-            self.filemap[12] = self.maxsize - 12, None
-        else:
-            self.new = False
-            self.f = None
+            self.filemap[ZEC3_HEADER_SIZE] = (self.maxsize - ZEC3_HEADER_SIZE,
+                                              None)
 
         # Statistics:  _n_adds, _n_added_bytes,
-        #              _n_evicts, _n_evicted_bytes
+        #              _n_evicts, _n_evicted_bytes,
+        #              _n_accesses
         self.clearStats()
 
-    # Scan the current contents of the cache file, calling install
+    ##
+    # Scan the current contents of the cache file, calling `install`
     # for each object found in the cache.  This method should only
     # be called once to initialize the cache from disk.
-
     def scan(self, install):
-        if self.new:
+        if self.f is not None:
             return
         fsize = os.path.getsize(self.fpath)
-        self.f = file(self.fpath, 'rb+')
+        if fsize != self.maxsize:
+            logger.warning("existing cache file %s has size %d; "
+                           "requested size %d ignored", self.fpath,
+                           fsize, self.maxsize)
+            self.maxsize = fsize
+        self.f = open(self.fpath, 'rb+')
         _magic = self.f.read(4)
         if _magic != magic:
             raise ValueError("unexpected magic number: %r" % _magic)
         self.tid = self.f.read(8)
-        # Remember the largest free block.  That seems a
-        # decent place to start currentofs.
+        if len(self.tid) != 8:
+            raise ValueError("cache file too small -- no tid at start")
+
+        # Populate .filemap and .key2entry to reflect what's currently in the
+        # file, and tell our parent about it too (via the `install` callback).
+        # Remember the location of the largest free block  That seems a decent
+        # place to start currentofs.
         max_free_size = max_free_offset = 0
-        ofs = 12
+        ofs = ZEC3_HEADER_SIZE
         while ofs < fsize:
             self.f.seek(ofs)
             ent = None
@@ -659,7 +815,8 @@
             elif status in '1234':
                 size = int(status)
             else:
-                assert 0, hex(ord(status))
+                raise ValueError("unknown status byte value %s in client "
+                                 "cache file" % 0, hex(ord(status)))
 
             self.filemap[ofs] = size, ent
             if ent is None and size > max_free_size:
@@ -667,7 +824,9 @@
 
             ofs += size
 
-        assert ofs == fsize
+        if ofs != fsize:
+            raise ValueError("final offset %s != file size %s in client "
+                             "cache file" % (ofs, fsize))
         if __debug__:
             self._verify_filemap()
         self.currentofs = max_free_offset
@@ -675,49 +834,59 @@
     def clearStats(self):
         self._n_adds = self._n_added_bytes = 0
         self._n_evicts = self._n_evicted_bytes = 0
-        self._n_removes = self._n_removed_bytes = 0
         self._n_accesses = 0
 
     def getStats(self):
         return (self._n_adds, self._n_added_bytes,
                 self._n_evicts, self._n_evicted_bytes,
-                self._n_removes, self._n_removed_bytes,
                 self._n_accesses
                )
 
+    ##
+    # The number of objects currently in the cache.
     def __len__(self):
         return len(self.key2entry)
 
+    ##
+    # Iterate over the objects in the cache, producing an Entry for each.
     def __iter__(self):
         return self.key2entry.itervalues()
 
+    ##
+    # Test whether an (oid, tid) pair is in the cache.
     def __contains__(self, key):
         return key in self.key2entry
 
+    ##
+    # Do all possible to ensure all bytes written to the file so far are
+    # actually on disk.
     def sync(self):
         sync(self.f)
 
+    ##
+    # Close the underlying file.  No methods accessing the cache should be
+    # used after this.
     def close(self):
         if self.f:
             self.sync()
             self.f.close()
             self.f = None
 
+    ##
     # Evict objects as necessary to free up at least nbytes bytes,
     # starting at currentofs.  If currentofs is closer than nbytes to
-    # the end of the file, currentofs is reset to 0.  The number of
-    # bytes actually freed may be (and probably will be) greater than
-    # nbytes, and is _makeroom's return value.  The file is not
+    # the end of the file, currentofs is reset to ZEC3_HEADER_SIZE first.
+    # The number of bytes actually freed may be (and probably will be)
+    # greater than nbytes, and is _makeroom's return value.  The file is not
     # altered by _makeroom.  filemap is updated to reflect the
     # evictions, and it's the caller's responsibilty both to fiddle
     # the file, and to update filemap, to account for all the space
     # freed (starting at currentofs when _makeroom returns, and
     # spanning the number of bytes retured by _makeroom).
-
     def _makeroom(self, nbytes):
-        assert 0 < nbytes <= self.maxsize
+        assert 0 < nbytes <= self.maxsize - ZEC3_HEADER_SIZE
         if self.currentofs + nbytes > self.maxsize:
-            self.currentofs = 12
+            self.currentofs = ZEC3_HEADER_SIZE
         ofs = self.currentofs
         while nbytes > 0:
             size, e = self.filemap.pop(ofs)
@@ -727,11 +896,11 @@
             nbytes -= size
         return ofs - self.currentofs
 
+    ##
     # Write Object obj, with data, to file starting at currentofs.
     # nfreebytes are already available for overwriting, and it's
     # guranteed that's enough.  obj.offset is changed to reflect the
     # new data record position, and filemap is updated to match.
-
     def _writeobj(self, obj, nfreebytes):
         size = OBJECT_HEADER_SIZE + obj.size
         assert size <= nfreebytes
@@ -763,11 +932,18 @@
             # written.
             self.filemap[self.currentofs] = excess, None
 
+    ##
+    # Add Object object to the cache.  This may evict existing objects, to
+    # make room (and almost certainly will, in steady state once the cache
+    # is first full).  The object must not already be in the cache.
     def add(self, object):
         size = OBJECT_HEADER_SIZE + object.size
-        if size > self.maxsize:
+        # A number of cache simulation experiments all concluded that the
+        # 2nd-level ZEO cache got a much higher hit rate if "very large"
+        # objects simply weren't cached.  For now, we ignore the request
+        # only if the entire cache file is too small to hold the object.
+        if size > self.maxsize - ZEC3_HEADER_SIZE:
             return
-        assert size <= self.maxsize
 
         assert object.key not in self.key2entry
         assert len(object.key[0]) == 8
@@ -779,43 +955,23 @@
         available = self._makeroom(size)
         self._writeobj(object, available)
 
-    def _verify_filemap(self, display=False):
-        a = 12
-        f = self.f
-        while a < self.maxsize:
-            f.seek(a)
-            status = f.read(1)
-            if status in 'af':
-                size, = struct.unpack(">I", f.read(4))
-            else:
-                size = int(status)
-            if display:
-                if a == self.currentofs:
-                    print '*****',
-                print "%c%d" % (status, size),
-            size2, obj = self.filemap[a]
-            assert size == size2
-            assert (obj is not None) == (status == 'a')
-            if obj is not None:
-                assert obj.offset == a
-                assert self.key2entry[obj.key] is obj
-            a += size
-        if display:
-            print
-        assert a == self.maxsize
-
+    ##
+    # Evict the object represented by Entry `e` from the cache, freeing
+    # `size` bytes in the file for reuse.  `size` is used only for summary
+    # statistics.  This does not alter the file, or self.filemap or
+    # self.key2entry (those are the caller's responsibilities).  It does
+    # invoke _evicted(Object) on our parent.
     def _evictobj(self, e, size):
         self._n_evicts += 1
         self._n_evicted_bytes += size
         # Load the object header into memory so we know how to
         # update the parent's in-memory data structures.
         self.f.seek(e.offset + OBJECT_HEADER_SIZE)
-        o = Object.fromFile(self.f, e.key, header_only=True)
+        o = Object.fromFile(self.f, e.key, skip_data=True)
         self.parent._evicted(o)
 
     ##
-    # Return object for key or None if not in cache.
-
+    # Return Object for key, or None if not in cache.
     def access(self, key):
         self._n_accesses += 1
         e = self.key2entry.get(key)
@@ -829,8 +985,7 @@
         return Object.fromFile(self.f, key)
 
     ##
-    # Remove object for key from cache, if present.
-
+    # Remove Object for key from cache, if present.
     def remove(self, key):
         # If an object is being explicitly removed, we need to load
         # its header into memory and write a free block marker to the
@@ -840,36 +995,76 @@
 
         # XXX Or we could just keep the header in memory at all times.
 
-        e = self.key2entry.get(key)
+        e = self.key2entry.pop(key, None)
         if e is None:
             return
         offset = e.offset
         size, e2 = self.filemap[offset]
+        assert e is e2
+        self.filemap[offset] = size, None
         self.f.seek(offset + OBJECT_HEADER_SIZE)
-        o = Object.fromFile(self.f, key, header_only=True)
-        self.f.seek(offset + OBJECT_HEADER_SIZE)
+        o = Object.fromFile(self.f, key, skip_data=True)
+        assert size >= 5  # only free blocks are tiny
+        # Because `size` >= 5, we can change an allocated block to a free
+        # block just by overwriting the 'a' status byte with 'f' -- the
+        # size field stays the same.
+        self.f.seek(offset)
         self.f.write('f')
         self.f.flush()
         self.parent._evicted(o)
-        self.filemap[offset] = size, None
 
     ##
-    # Update on-disk representation of obj.
+    # Update on-disk representation of Object obj.
     #
     # This method should be called when the object header is modified.
-
+    # obj must be in the cache.  The only real use for this is during
+    # invalidation, to set the end_tid field on a revision that was current
+    # (and so had an end_tid of None, but no longer does).
     def update(self, obj):
-
         e = self.key2entry[obj.key]
         self.f.seek(e.offset + OBJECT_HEADER_SIZE)
         obj.serialize_header(self.f)
 
+    ##
+    # Update our idea of the most recent tid.  This is stored in the
+    # instance, and also written out near the start of the cache file.  The
+    # new tid must be strictly greater than our current idea of the most
+    # recent tid.
     def settid(self, tid):
         if self.tid is not None and tid <= self.tid:
             raise ValueError("new last tid (%s) must be greater than "
                              "previous one (%s)" % (u64(tid),
                                                     u64(self.tid)))
+        assert isinstance(tid, str) and len(tid) == 8
         self.tid = tid
-        self.f.seek(4)
+        self.f.seek(len(magic))
         self.f.write(tid)
         self.f.flush()
+
+    ##
+    # This debug method marches over the entire cache file, verifying that
+    # the current contents match the info in self.filemap and self.key2entry.
+    def _verify_filemap(self, display=False):
+        a = ZEC3_HEADER_SIZE
+        f = self.f
+        while a < self.maxsize:
+            f.seek(a)
+            status = f.read(1)
+            if status in 'af':
+                size, = struct.unpack(">I", f.read(4))
+            else:
+                size = int(status)
+            if display:
+                if a == self.currentofs:
+                    print '*****',
+                print "%c%d" % (status, size),
+            size2, obj = self.filemap[a]
+            assert size == size2
+            assert (obj is not None) == (status == 'a')
+            if obj is not None:
+                assert obj.offset == a
+                assert self.key2entry[obj.key] is obj
+            a += size
+        if display:
+            print
+        assert a == self.maxsize

Modified: Zope3/branches/srichter-blow-services/src/ZEO/stats.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZEO/stats.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZEO/stats.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -374,6 +374,8 @@
     0x10: "invalidate (miss)",
     0x1A: "invalidate (hit, version)",
     0x1C: "invalidate (hit, saving non-current)",
+    # 0x1E can occur during startup verification.
+    0x1E: "invalidate (hit, discarding current or non-current)",
 
     0x20: "load (miss)",
     0x22: "load (hit)",

Modified: Zope3/branches/srichter-blow-services/src/ZEO/tests/ConnectionTests.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZEO/tests/ConnectionTests.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZEO/tests/ConnectionTests.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -386,7 +386,7 @@
         # objects that weren't in the _seriald, because the client was
         # interrupted waiting for tpc_vote() to return.  When the next
         # transaction committed, it tried to do something with the
-        # bogus _tbuf entries.  The exaplanation is wrong/incomplete,
+        # bogus _tbuf entries.  The explanation is wrong/incomplete,
         # because tpc_begin() should clear the _tbuf.
 
         # 2003-01-15T15:44:19 ERROR(200) ZODB A storage error occurred
@@ -418,26 +418,112 @@
         self.assertEqual(revid1, revid2)
         self._storage.close()
 
-    def checkRollover(self):
-        # Check that the cache works when the files are swapped.
+    def checkDisconnectedCacheWorks(self):
+        # Check that the cache works when the client is disconnected.
+        self._storage = self.openClientStorage('test')
+        oid1 = self._storage.new_oid()
+        obj1 = MinPO("1" * 500)
+        self._dostore(oid1, data=obj1)
+        oid2 = self._storage.new_oid()
+        obj2 = MinPO("2" * 500)
+        self._dostore(oid2, data=obj2)
+        expected1 = self._storage.load(oid1, '')
+        expected2 = self._storage.load(oid2, '')
 
-        # In this case, only one object fits in a cache file.  When the
-        # cache files swap, the first object is effectively uncached.
+        # Shut it all down, and try loading from the persistent cache file
+        # without a server present.
+        self._storage.close()
+        self.shutdownServer()
+        self._storage = self.openClientStorage('test', wait=False)
+        self.assertEqual(expected1, self._storage.load(oid1, ''))
+        self.assertEqual(expected2, self._storage.load(oid2, ''))
+        self._storage.close()
 
-        self._storage = self.openClientStorage('test', 1000)
+    def checkDisconnectedCacheFails(self):
+        # Like checkDisconnectedCacheWorks above, except the cache
+        # file is so small that only one object can be remembered.
+        self._storage = self.openClientStorage('test', cache_size=900)
         oid1 = self._storage.new_oid()
         obj1 = MinPO("1" * 500)
         self._dostore(oid1, data=obj1)
         oid2 = self._storage.new_oid()
         obj2 = MinPO("2" * 500)
+        # The cache file is so small that adding oid2 will evict oid1.
         self._dostore(oid2, data=obj2)
+        expected2 = self._storage.load(oid2, '')
+
+        # Shut it all down, and try loading from the persistent cache file
+        # without a server present.
         self._storage.close()
         self.shutdownServer()
-        self._storage = self.openClientStorage('test', 1000, wait=0)
-        self._storage.load(oid1, '')
-        self._storage.load(oid2, '')
+        self._storage = self.openClientStorage('test', cache_size=900,
+                                               wait=False)
+        # oid2 should still be in cache.
+        self.assertEqual(expected2, self._storage.load(oid2, ''))
+        # But oid1 should have been purged, so that trying to load it will
+        # try to fetch it from the (non-existent) ZEO server.
+        self.assertRaises(ClientDisconnected, self._storage.load, oid1, '')
         self._storage.close()
 
+    def checkVerificationInvalidationPersists(self):
+        # This tests a subtle invalidation bug from ZODB 3.3:
+        # invalidations processed as part of ZEO cache verification acted
+        # kinda OK wrt the in-memory cache structures, but had no effect
+        # on the cache file.  So opening the file cache again could
+        # incorrectly believe that a previously invalidated object was
+        # still current.  This takes some effort to set up.
+
+        # First, using a persistent cache ('test'), create an object
+        # MinPO(13).  We used to see this again at the end of this test,
+        # despite that we modify it, and despite that it gets invalidated
+        # in 'test', before the end.
+        self._storage = self.openClientStorage('test')
+        oid = self._storage.new_oid()
+        obj = MinPO(13)
+        self._dostore(oid, data=obj)
+        self._storage.close()
+
+        # Now modify obj via a temp connection.  `test` won't learn about
+        # this until we open a connection using `test` again.
+        self._storage = self.openClientStorage()
+        pickle, rev = self._storage.load(oid, '')
+        newobj = zodb_unpickle(pickle)
+        self.assertEqual(newobj, obj)
+        newobj.value = 42 # .value *should* be 42 forever after now, not 13
+        self._dostore(oid, data=newobj, revid=rev)
+        self._storage.close()
+
+        # Open 'test' again.  `oid` in this cache should be (and is)
+        # invalidated during cache verification.  The bug was that it
+        # got invalidated (kinda) in memory, but not in the cache file.
+        self._storage = self.openClientStorage('test')
+
+        # The invalidation happened already.  Now create and store a new
+        # object before closing this storage:  this is so `test` believes
+        # it's seen transactions beyond the one that invalidated `oid`, so
+        # that the *next* time we open `test` it doesn't process another
+        # invalidation for `oid`.  It's also important that we not try to
+        # load `oid` now:  because it's been (kinda) invalidated in the
+        # cache's memory structures, loading it now would fetch the
+        # current revision from the server, thus hiding the bug.
+        obj2 = MinPO(666)
+        oid2 = self._storage.new_oid()
+        self._dostore(oid2, data=obj2)
+        self._storage.close()
+
+        # Finally, open `test` again and load `oid`.  `test` believes
+        # it's beyond the transaction that modified `oid`, so its view
+        # of whether it has an up-to-date `oid` comes solely from the disk
+        # file, unaffected by cache verification.
+        self._storage = self.openClientStorage('test')
+        pickle, rev = self._storage.load(oid, '')
+        newobj_copy = zodb_unpickle(pickle)
+        # This used to fail, with
+        #     AssertionError: MinPO(13) != MinPO(42)
+        # That is, `test` retained a stale revision of the object on disk.
+        self.assertEqual(newobj_copy, newobj)
+        self._storage.close()
+
     def checkReconnection(self):
         # Check that the client reconnects when a server restarts.
 

Modified: Zope3/branches/srichter-blow-services/src/ZEO/tests/test_cache.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZEO/tests/test_cache.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZEO/tests/test_cache.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -114,13 +114,11 @@
             n = p64(i)
             self.cache.store(n, "", n, None, data[i])
             self.assertEquals(len(self.cache), i + 1)
-            self.assert_(self.cache.fc.currentsize < maxsize)
         # The cache now uses 1225 bytes.  The next insert
         # should delete some objects.
         n = p64(50)
         self.cache.store(n, "", n, None, data[51])
         self.assert_(len(self.cache) < 51)
-        self.assert_(self.cache.fc.currentsize <= maxsize)
 
         # XXX Need to make sure eviction of non-current data
         # and of version data are handled correctly.
@@ -145,7 +143,7 @@
         # Verify that internals of both objects are the same.
         # Could also test that external API produces the same results.
         eq = self.assertEqual
-        eq(copy.tid, self.cache.tid)
+        eq(copy.getLastTid(), self.cache.getLastTid())
         eq(len(copy), len(self.cache))
         eq(copy.version, self.cache.version)
         eq(copy.current, self.cache.current)

Modified: Zope3/branches/srichter-blow-services/src/ZEO/version.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZEO/version.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZEO/version.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1 +1 @@
-2.3
+2.4a0

Modified: Zope3/branches/srichter-blow-services/src/ZODB/BaseStorage.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/BaseStorage.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/BaseStorage.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -20,11 +20,10 @@
 import time
 import logging
 
-import UndoLogCompatible
-import POSException
 from persistent.TimeStamp import TimeStamp
 
-from ZODB import POSException, utils
+from ZODB import POSException
+from ZODB.utils import z64, oid_repr
 from ZODB.UndoLogCompatible import UndoLogCompatible
 
 log = logging.getLogger("ZODB.BaseStorage")
@@ -98,7 +97,7 @@
         t=self._ts=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))
         self._tid = `t`
         if base is None:
-            self._oid='\0\0\0\0\0\0\0\0'
+            self._oid=z64
         else:
             self._oid=base._oid
 
@@ -401,7 +400,7 @@
             for r in transaction:
                 oid=r.oid
                 if verbose:
-                    print utils.oid_repr(oid), r.version, len(r.data)
+                    print oid_repr(oid), r.version, len(r.data)
                 if restoring:
                     self.restore(oid, r.tid, r.data, r.version,
                                  r.data_txn, transaction)

Modified: Zope3/branches/srichter-blow-services/src/ZODB/ConflictResolution.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/ConflictResolution.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/ConflictResolution.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-import sys
+
 import logging
 from cStringIO import StringIO
 from cPickle import Unpickler, Pickler

Modified: Zope3/branches/srichter-blow-services/src/ZODB/Connection.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/Connection.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/Connection.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -20,10 +20,8 @@
 import threading
 import warnings
 from time import time
-from utils import u64
 
 from persistent import PickleCache
-from persistent.interfaces import IPersistent
 
 import transaction
 
@@ -33,11 +31,13 @@
      import ConflictError, ReadConflictError, InvalidObjectReference, \
             ConnectionStateError
 from ZODB.TmpStore import TmpStore
-from ZODB.utils import oid_repr, z64, positive_id
+from ZODB.utils import u64, oid_repr, z64, positive_id
 from ZODB.serialize import ObjectWriter, ConnectionObjectReader, myhasattr
 from ZODB.interfaces import IConnection
-from ZODB.interfaces import implements
+from ZODB.utils import DEPRECATED_ARGUMENT, deprecated36
 
+from zope.interface import implements
+
 global_reset_counter = 0
 
 def resetCaches():
@@ -264,9 +264,8 @@
         method.  You can pass a transaction manager (TM) to DB.open()
         to control which TM the Connection uses.
         """
-        warnings.warn("getTransaction() is deprecated. "
-                      "Use the txn_mgr argument to DB.open() instead.",
-                      DeprecationWarning)
+        deprecated36("getTransaction() is deprecated. "
+                     "Use the txn_mgr argument to DB.open() instead.")
         return self._txn_mgr.get()
 
     def setLocalTransaction(self):
@@ -278,9 +277,8 @@
         can pass a transaction manager (TM) to DB.open() to control
         which TM the Connection uses.
         """
-        warnings.warn("setLocalTransaction() is deprecated. "
-                      "Use the txn_mgr argument to DB.open() instead.",
-                      DeprecationWarning)
+        deprecated36("setLocalTransaction() is deprecated. "
+                     "Use the txn_mgr argument to DB.open() instead.")
         if self._txn_mgr is transaction.manager:
             if self._synch:
                 self._txn_mgr.unregisterSynch(self)
@@ -462,9 +460,10 @@
         self._cache = cache = PickleCache(self, cache_size)
 
     def abort(self, transaction):
-        """Abort the object in the transaction.
+        """Abort modifications to registered objects.
 
-        This just deactivates the thing.
+        This tells the cache to invalidate changed objects.  _p_jar
+        and _p_oid are deleted from new objects.
         """
 
         for obj in self._registered_objects:
@@ -487,14 +486,14 @@
 
     def cacheFullSweep(self, dt=None):
         # XXX needs doc string
-        warnings.warn("cacheFullSweep is deprecated. "
-                      "Use cacheMinimize instead.", DeprecationWarning)
+        deprecated36("cacheFullSweep is deprecated. "
+                     "Use cacheMinimize instead.")
         if dt is None:
             self._cache.full_sweep()
         else:
             self._cache.full_sweep(dt)
 
-    def cacheMinimize(self, dt=None):
+    def cacheMinimize(self, dt=DEPRECATED_ARGUMENT):
         """Deactivate all unmodified objects in the cache.
 
         Call _p_deactivate() on each cached object, attempting to turn
@@ -504,9 +503,8 @@
         :Parameters:
           - `dt`: ignored.  It is provided only for backwards compatibility.
         """
-        if dt is not None:
-            warnings.warn("The dt argument to cacheMinimize is ignored.",
-                          DeprecationWarning)
+        if dt is not DEPRECATED_ARGUMENT:
+            deprecated36("cacheMinimize() dt= is ignored.")
         self._cache.minimize()
 
     def cacheGC(self):
@@ -649,6 +647,7 @@
                 self._cache[oid] = obj
             except:
                 # Dang, I bet it's wrapped:
+                # XXX Deprecate, then remove, this.
                 if hasattr(obj, 'aq_base'):
                     self._cache[oid] = obj.aq_base
                 else:
@@ -781,8 +780,8 @@
             # an oid is being registered.  I can't think of any way to
             # achieve that without assignment to _p_jar.  If there is
             # a way, this will be a very confusing warning.
-            warnings.warn("Assigning to _p_jar is deprecated",
-                          DeprecationWarning)
+            deprecated36("Assigning to _p_jar is deprecated, and will be "
+                         "changed to raise an exception.")
         elif obj._p_oid in self._added:
             # It was registered before it was added to _added.
             return
@@ -798,7 +797,7 @@
     def root(self):
         """Return the database root object.
 
-        The root is a PersistentDict.
+        The root is a persistent.mapping.PersistentMapping.
         """
         return self.get(z64)
 

Modified: Zope3/branches/srichter-blow-services/src/ZODB/DB.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/DB.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/DB.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -16,19 +16,124 @@
 $Id$"""
 
 import cPickle, cStringIO, sys
-from thread import allocate_lock
+import threading
 from time import time, ctime
-import warnings
 import logging
 
 from ZODB.broken import find_global
+from ZODB.utils import z64
 from ZODB.Connection import Connection
 from ZODB.serialize import referencesf
+from ZODB.utils import WeakSet
+from ZODB.utils import DEPRECATED_ARGUMENT, deprecated36
 
 import transaction
 
 logger = logging.getLogger('ZODB.DB')
 
+class _ConnectionPool(object):
+    """Manage a pool of connections.
+
+    CAUTION:  Methods should be called under the protection of a lock.
+    This class does no locking of its own.
+
+    There's no limit on the number of connections this can keep track of,
+    but a warning is logged if there are more than pool_size active
+    connections, and a critical problem if more than twice pool_size.
+
+    New connections are registered via push().  This will log a message if
+    "too many" connections are active.
+
+    When a connection is explicitly closed, tell the pool via repush().
+    That adds the connection to a stack of connections available for
+    reuse, and throws away the oldest stack entries if the stack is too large.
+    pop() pops this stack.
+
+    When a connection is obtained via pop(), the pool holds only a weak
+    reference to it thereafter.  It's not necessary to inform the pool
+    if the connection goes away.  A connection handed out by pop() counts
+    against pool_size only so long as it exists, and provided it isn't
+    repush()'ed.  A weak reference is retained so that DB methods like
+    connectionDebugInfo() can still gather statistics.
+    """
+
+    def __init__(self, pool_size):
+        # The largest # of connections we expect to see alive simultaneously.
+        self.pool_size = pool_size
+
+        # A weak set of all connections we've seen.  A connection vanishes
+        # from this set if pop() hands it out, it's not reregistered via
+        # repush(), and it becomes unreachable.
+        self.all = WeakSet()
+
+        # A stack of connections available to hand out.  This is a subset
+        # of self.all.  push() and repush() add to this, and may remove
+        # the oldest available connections if the pool is too large.
+        # pop() pops this stack.  There are never more than pool_size entries
+        # in this stack.
+        # In Python 2.4, a collections.deque would make more sense than
+        # a list (we push only "on the right", but may pop from both ends).
+        self.available = []
+
+    # Change our belief about the expected maximum # of live connections.
+    # If the pool_size is smaller than the current value, this may discard
+    # the oldest available connections.
+    def set_pool_size(self, pool_size):
+        self.pool_size = pool_size
+        self._reduce_size()
+
+    # Register a new available connection.  We must not know about c already.
+    # c will be pushed onto the available stack even if we're over the
+    # pool size limit.
+    def push(self, c):
+        assert c not in self.all
+        assert c not in self.available
+        self._reduce_size(strictly_less=True)
+        self.all.add(c)
+        self.available.append(c)
+        n, limit = len(self.all), self.pool_size
+        if n > limit:
+            reporter = logger.warn
+            if n > 2 * limit:
+                reporter = logger.critical
+            reporter("DB.open() has %s open connections with a pool_size "
+                     "of %s", n, limit)
+
+    # Reregister an available connection formerly obtained via pop().  This
+    # pushes it on the stack of available connections, and may discard
+    # older available connections.
+    def repush(self, c):
+        assert c in self.all
+        assert c not in self.available
+        self._reduce_size(strictly_less=True)
+        self.available.append(c)
+
+    # Throw away the oldest available connections until we're under our
+    # target size (strictly_less=False) or no more than that (strictly_less=
+    # True, the default).
+    def _reduce_size(self, strictly_less=False):
+        target = self.pool_size - bool(strictly_less)
+        while len(self.available) > target:
+            c = self.available.pop(0)
+            self.all.remove(c)
+
+    # Pop an available connection and return it, or return None if none are
+    # available.  In the latter case, the caller should create a new
+    # connection, register it via push(), and call pop() again.  The
+    # caller is responsible for serializing this sequence.
+    def pop(self):
+        result = None
+        if self.available:
+            result = self.available.pop()
+            # Leave it in self.all, so we can still get at it for statistics
+            # while it's alive.
+            assert result in self.all
+        return result
+
+    # For every live connection c, invoke f(c).
+    def map(self, f):
+        self.all.map(f)
+
 class DB(object):
     """The Object Database
     -------------------
@@ -40,9 +145,9 @@
     The DB instance manages a pool of connections.  If a connection is
     closed, it is returned to the pool and its object cache is
     preserved.  A subsequent call to open() will reuse the connection.
-    There is a limit to the pool size; if all its connections are in
-    use, calls to open() will block until one of the open connections
-    is closed.
+    There is no hard limit on the pool size.  If more than `pool_size`
+    connections are opened, a warning is logged, and if more than twice
+    that many, a critical problem is logged.
 
     The class variable 'klass' is used by open() to create database
     connections.  It is set to Connection, but a subclass could override
@@ -80,41 +185,42 @@
     def __init__(self, storage,
                  pool_size=7,
                  cache_size=400,
-                 cache_deactivate_after=None,
+                 cache_deactivate_after=DEPRECATED_ARGUMENT,
                  version_pool_size=3,
                  version_cache_size=100,
-                 version_cache_deactivate_after=None,
+                 version_cache_deactivate_after=DEPRECATED_ARGUMENT,
                  ):
         """Create an object database.
 
         :Parameters:
           - `storage`: the storage used by the database, e.g. FileStorage
-          - `pool_size`: maximum number of open connections
+          - `pool_size`: expected maximum number of open connections
           - `cache_size`: target size of Connection object cache
-          - `cache_deactivate_after`: ignored
-          - `version_pool_size`: maximum number of connections (per version)
+          - `version_pool_size`: expected maximum number of connections (per
+            version)
           - `version_cache_size`: target size of Connection object cache for
             version connections
+          - `cache_deactivate_after`: ignored
           - `version_cache_deactivate_after`: ignored
         """
-        # Allocate locks:
-        l = allocate_lock()
-        self._a = l.acquire
-        self._r = l.release
+        # Allocate lock.
+        x = threading.RLock()
+        self._a = x.acquire
+        self._r = x.release
 
         # Setup connection pools and cache info
-        self._pools = {},[]
-        self._temps = []
+        # _pools maps a version string to a _ConnectionPool object.
+        self._pools = {}
         self._pool_size = pool_size
         self._cache_size = cache_size
         self._version_pool_size = version_pool_size
         self._version_cache_size = version_cache_size
 
         # warn about use of deprecated arguments
-        if (cache_deactivate_after is not None or
-            version_cache_deactivate_after is not None):
-            warnings.warn("cache_deactivate_after has no effect",
-                          DeprecationWarning)
+        if cache_deactivate_after is not DEPRECATED_ARGUMENT:
+            deprecated36("cache_deactivate_after has no effect")
+        if version_cache_deactivate_after is not DEPRECATED_ARGUMENT:
+            deprecated36("version_cache_deactivate_after has no effect")
 
         self._miv_cache = {}
 
@@ -124,7 +230,7 @@
         if not hasattr(storage,'tpc_vote'):
             storage.tpc_vote = lambda *args: None
         try:
-            storage.load('\0\0\0\0\0\0\0\0','')
+            storage.load(z64,'')
         except KeyError:
             # Create the database's root in the storage if it doesn't exist
             from persistent.mapping import PersistentMapping
@@ -138,7 +244,7 @@
             t = transaction.Transaction()
             t.description = 'initial database creation'
             storage.tpc_begin(t)
-            storage.store('\0\0\0\0\0\0\0\0', None, file.getvalue(), '', t)
+            storage.store(z64, None, file.getvalue(), '', t)
             storage.tpc_vote(t)
             storage.tpc_finish(t)
 
@@ -150,6 +256,7 @@
         if hasattr(storage, 'undoInfo'):
             self.undoInfo = storage.undoInfo
 
+    # This is called by Connection.close().
     def _closeConnection(self, connection):
         """Return a connection to the pool.
 
@@ -164,10 +271,10 @@
             am = self._activity_monitor
             if am is not None:
                 am.closedConnection(connection)
+
             version = connection._version
-            pools, pooll = self._pools
             try:
-                pool, allocated, pool_lock = pools[version]
+                pool = self._pools[version]
             except KeyError:
                 # No such version. We must have deleted the pool.
                 # Just let the connection go.
@@ -176,30 +283,17 @@
                 # XXX What objects are involved in the cycle?
                 connection.__dict__.clear()
                 return
+            pool.repush(connection)
 
-            pool.append(connection)
-            if len(pool) == 1:
-                # Pool now usable again, unlock it.
-                pool_lock.release()
         finally:
             self._r()
 
+    # Call f(c) for all connections c in all pools in all versions.
     def _connectionMap(self, f):
         self._a()
         try:
-            pools, pooll = self._pools
-            for pool, allocated in pooll:
-                for cc in allocated:
-                    f(cc)
-
-            temps = self._temps
-            if temps:
-                t = []
-                rc = sys.getrefcount
-                for cc in temps:
-                    if rc(cc) > 3:
-                        f(cc)
-                self._temps = t
+            for pool in self._pools.values():
+                pool.map(f)
         finally:
             self._r()
 
@@ -215,12 +309,12 @@
         """
 
         detail = {}
-        def f(con, detail=detail, have_detail=detail.has_key):
+        def f(con, detail=detail):
             for oid, ob in con._cache.items():
                 module = getattr(ob.__class__, '__module__', '')
                 module = module and '%s.' % module or ''
                 c = "%s%s" % (module, ob.__class__.__name__)
-                if have_detail(c):
+                if c in detail:
                     detail[c] += 1
                 else:
                     detail[c] = 1
@@ -275,7 +369,7 @@
         self._connectionMap(lambda c: c._cache.full_sweep())
 
     def cacheLastGCTime(self):
-        m=[0]
+        m = [0]
         def f(con, m=m):
             t = con._cache.cache_last_gc_time
             if t > m[0]:
@@ -288,7 +382,7 @@
         self._connectionMap(lambda c: c._cache.minimize())
 
     def cacheSize(self):
-        m=[0]
+        m = [0]
         def f(con, m=m):
             m[0] += con._cache.cache_non_ghost_count
 
@@ -298,9 +392,9 @@
     def cacheDetailSize(self):
         m = []
         def f(con, m=m):
-            m.append({'connection':repr(con),
-                      'ngsize':con._cache.cache_non_ghost_count,
-                      'size':len(con._cache)})
+            m.append({'connection': repr(con),
+                      'ngsize': con._cache.cache_non_ghost_count,
+                      'size': len(con._cache)})
         self._connectionMap(f)
         m.sort()
         return m
@@ -357,39 +451,24 @@
         if connection is not None:
             version = connection._version
         # Update modified in version cache
-        # XXX must make this work with list or dict to backport to 2.6
         for oid in oids.keys():
             h = hash(oid) % 131
             o = self._miv_cache.get(h, None)
             if o is not None and o[0]==oid:
                 del self._miv_cache[h]
 
-        # Notify connections
-        for pool, allocated in self._pools[1]:
-            for cc in allocated:
-                if (cc is not connection and
-                    (not version or cc._version==version)):
-                    if sys.getrefcount(cc) <= 3:
-                        cc.close()
-                    cc.invalidate(tid, oids)
+        # Notify connections.
+        def inval(c):
+            if (c is not connection and
+                  (not version or c._version == version)):
+                c.invalidate(tid, oids)
+        self._connectionMap(inval)
 
-        if self._temps:
-            t = []
-            for cc in self._temps:
-                if sys.getrefcount(cc) > 3:
-                    if (cc is not connection and
-                        (not version or cc._version == version)):
-                        cc.invalidate(tid, oids)
-                    t.append(cc)
-                else:
-                    cc.close()
-            self._temps = t
-
     def modifiedInVersion(self, oid):
         h = hash(oid) % 131
         cache = self._miv_cache
-        o=cache.get(h, None)
-        if o and o[0]==oid:
+        o = cache.get(h, None)
+        if o and o[0] == oid:
             return o[1]
         v = self._storage.modifiedInVersion(oid)
         cache[h] = oid, v
@@ -398,203 +477,111 @@
     def objectCount(self):
         return len(self._storage)
 
-    def open(self, version='', transaction=None, temporary=0, force=None,
-             waitflag=1, mvcc=True, txn_mgr=None, synch=True):
+    def open(self, version='',
+             transaction=DEPRECATED_ARGUMENT, temporary=DEPRECATED_ARGUMENT,
+             force=DEPRECATED_ARGUMENT, waitflag=DEPRECATED_ARGUMENT,
+             mvcc=True, txn_mgr=None, synch=True):
         """Return a database Connection for use by application code.
 
-        The optional version argument can be used to specify that a
+        The optional `version` argument can be used to specify that a
         version connection is desired.
 
-        The optional transaction argument can be provided to cause the
-        connection to be automatically closed when a transaction is
-        terminated.  In addition, connections per transaction are
-        reused, if possible.
-
         Note that the connection pool is managed as a stack, to
-        increate the likelihood that the connection's stack will
+        increase the likelihood that the connection's stack will
         include useful objects.
 
         :Parameters:
           - `version`: the "version" that all changes will be made
              in, defaults to no version.
-          - `transaction`: XXX
-          - `temporary`: XXX
-          - `force`: XXX
-          - `waitflag`: XXX
           - `mvcc`: boolean indicating whether MVCC is enabled
           - `txn_mgr`: transaction manager to use.  None means
              used the default transaction manager.
           - `synch`: boolean indicating whether Connection should
              register for afterCompletion() calls.
-
         """
-        self._a()
-        try:
 
-            if transaction is not None:
-                connections = transaction._connections
-                if connections:
-                    if connections.has_key(version) and not temporary:
-                        return connections[version]
-                else:
-                    transaction._connections = connections = {}
-                transaction = transaction._connections
+        if temporary is not DEPRECATED_ARGUMENT:
+            deprecated36("DB.open() temporary= ignored. "
+                         "open() no longer blocks.")
 
-            if temporary:
-                # This is a temporary connection.
-                # We won't bother with the pools.  This will be
-                # a one-use connection.
-                c = self.klass(version=version,
-                               cache_size=self._version_cache_size,
-                               mvcc=mvcc, txn_mgr=txn_mgr, synch=synch)
-                c._setDB(self)
-                self._temps.append(c)
-                if transaction is not None:
-                    transaction[id(c)] = c
-                return c
+        if force is not DEPRECATED_ARGUMENT:
+            deprecated36("DB.open() force= ignored. "
+                         "open() no longer blocks.")
 
-            pools, pooll = self._pools
+        if waitflag is not DEPRECATED_ARGUMENT:
+            deprecated36("DB.open() waitflag= ignored. "
+                         "open() no longer blocks.")
 
-            # pools is a mapping object:
-            #
-            #   {version -> (pool, allocated, lock)
-            #
-            # where:
-            #
-            #   pool is the connection pool for the version,
-            #   allocated is a list of all of the allocated
-            #     connections, and
-            #   lock is a lock that is used to block when a pool is
-            #     empty and no more connections can be allocated.
-            #
-            # pooll is a list of all of the pools and allocated for
-            # use in cases where we need to iterate over all
-            # connections or all inactive connections.
+        if transaction is not DEPRECATED_ARGUMENT:
+            deprecated36("DB.open() transaction= ignored.")
 
-            # Pool locks are tricky.  Basically, the lock needs to be
-            # set whenever the pool becomes empty so that threads are
-            # forced to wait until the pool gets a connection in it.
-            # The lock is acquired when the (empty) pool is
-            # created.  The lock is acquired just prior to removing
-            # the last connection from the pool and released just after
-            # adding a connection to an empty pool.
+        self._a()
+        try:
+            # pool <- the _ConnectionPool for this version
+            pool = self._pools.get(version)
+            if pool is None:
+                if version:
+                    size = self._version_pool_size
+                else:
+                    size = self._pool_size
+                self._pools[version] = pool = _ConnectionPool(size)
+            assert pool is not None
 
-
-            if pools.has_key(version):
-                pool, allocated, pool_lock = pools[version]
-            else:
-                pool, allocated, pool_lock = pools[version] = (
-                    [], [], allocate_lock())
-                pooll.append((pool, allocated))
-                pool_lock.acquire()
-
-
-            if not pool:
-                c = None
+            # result <- a connection
+            result = pool.pop()
+            if result is None:
                 if version:
-                    if self._version_pool_size > len(allocated) or force:
-                        c = self.klass(version=version,
-                                       cache_size=self._version_cache_size,
-                                       mvcc=mvcc, txn_mgr=txn_mgr)
-                        allocated.append(c)
-                        pool.append(c)
-                elif self._pool_size > len(allocated) or force:
-                    c = self.klass(version=version,
-                                   cache_size=self._cache_size,
-                                   mvcc=mvcc, txn_mgr=txn_mgr, synch=synch)
-                    allocated.append(c)
-                    pool.append(c)
+                    size = self._version_cache_size
+                else:
+                    size = self._cache_size
+                c = self.klass(version=version, cache_size=size,
+                               mvcc=mvcc, txn_mgr=txn_mgr)
+                pool.push(c)
+                result = pool.pop()
+            assert result is not None
 
-                if c is None:
-                    if waitflag:
-                        self._r()
-                        pool_lock.acquire()
-                        self._a()
-                        if len(pool) > 1:
-                            # Note that the pool size will normally be 1 here,
-                            # but it could be higher due to a race condition.
-                            pool_lock.release()
-                    else:
-                        return
+            # Tell the connection it belongs to self.
+            result._setDB(self, mvcc=mvcc, txn_mgr=txn_mgr, synch=synch)
 
-            elif len(pool)==1:
-                # Taking last one, lock the pool.
-                # Note that another thread might grab the lock
-                # before us, so we might actually block, however,
-                # when we get the lock back, there *will* be a
-                # connection in the pool.  OTOH, there's no limit on
-                # how long we may need to wait:  if the other thread
-                # grabbed the lock in this section too, we'll wait
-                # here until another connection is closed.
-                # checkConcurrentUpdates1Storage provoked this frequently
-                # on a hyperthreaded machine, with its second thread
-                # timing out after waiting 5 minutes for DB.open() to
-                # return.  So, if we can't get the pool lock immediately,
-                # now we make a recursive call.  This allows the current
-                # thread to allocate a new connection instead of waiting
-                # arbitrarily long for the single connection in the pool
-                # right now.
-                self._r()
-                if not pool_lock.acquire(0):
-                    result = DB.open(self, version, transaction, temporary,
-                                     force, waitflag)
-                    self._a()
-                    return result
-                self._a()
-                if len(pool) > 1:
-                    # Note that the pool size will normally be 1 here,
-                    # but it could be higher due to a race condition.
-                    pool_lock.release()
+            # A good time to do some cache cleanup.
+            self._connectionMap(lambda c: c.cacheGC())
 
-            c = pool.pop()
-            c._setDB(self, mvcc=mvcc, txn_mgr=txn_mgr, synch=synch)
-            for pool, allocated in pooll:
-                for cc in pool:
-                    cc.cacheGC()
+            return result
 
-            if transaction is not None:
-                transaction[version] = c
-            return c
-
         finally:
             self._r()
 
     def removeVersionPool(self, version):
-        pools, pooll = self._pools
-        info = pools.get(version)
-        if info:
-            del pools[version]
-            pool, allocated, pool_lock = info
-            pooll.remove((pool, allocated))
-            try:
-                pool_lock.release()
-            except: # XXX Do we actually expect this to fail?
-                pass
-            del pool[:]
-            del allocated[:]
+        try:
+            del self._pools[version]
+        except KeyError:
+            pass
 
     def connectionDebugInfo(self):
-        r = []
-        pools, pooll = self._pools
+        result = []
         t = time()
-        for version, (pool, allocated, lock) in pools.items():
-            for c in allocated:
-                o = c._opened
-                d = c._debug_info
-                if d:
-                    if len(d)==1:
-                        d = d[0]
-                else:
-                    d=''
-                d = "%s (%s)" % (d, len(c._cache))
 
-                r.append({
-                    'opened': o and ("%s (%.2fs)" % (ctime(o), t-o)),
-                    'info': d,
-                    'version': version,
-                    })
-        return r
+        def get_info(c):
+            # `result`, `time` and `version` are lexically inherited.
+            o = c._opened
+            d = c._debug_info
+            if d:
+                if len(d) == 1:
+                    d = d[0]
+            else:
+                d = ''
+            d = "%s (%s)" % (d, len(c._cache))
 
+            result.append({
+                'opened': o and ("%s (%.2fs)" % (ctime(o), t-o)),
+                'info': d,
+                'version': version,
+                })
+
+        for version, pool in self._pools.items():
+            pool.map(get_info)
+        return result
+
     def getActivityMonitor(self):
         return self._activity_monitor
 
@@ -622,34 +609,54 @@
             logger.error("packing", exc_info=True)
             raise
 
-    def setCacheSize(self, v):
-        self._cache_size = v
-        d = self._pools[0]
-        pool_info = d.get('')
-        if pool_info is not None:
-            for c in pool_info[1]:
-                c._cache.cache_size = v
+    def setActivityMonitor(self, am):
+        self._activity_monitor = am
 
     def classFactory(self, connection, modulename, globalname):
         # Zope will rebind this method to arbitrary user code at runtime.
         return find_global(modulename, globalname)
 
-    def setPoolSize(self, v):
-        self._pool_size = v
+    def setCacheSize(self, size):
+        self._a()
+        try:
+            self._cache_size = size
+            pool = self._pools.get('')
+            if pool is not None:
+                def setsize(c):
+                    c._cache.cache_size = size
+                pool.map(setsize)
+        finally:
+            self._r()
 
-    def setActivityMonitor(self, am):
-        self._activity_monitor = am
+    def setVersionCacheSize(self, size):
+        self._a()
+        try:
+            self._version_cache_size = size
+            def setsize(c):
+                c._cache.cache_size = size
+            for version, pool in self._pools.items():
+                if version:
+                    pool.map(setsize)
+        finally:
+            self._r()
 
-    def setVersionCacheSize(self, v):
-        self._version_cache_size = v
-        for ver in self._pools[0].keys():
-            if ver:
-                for c in self._pools[0][ver][1]:
-                    c._cache.cache_size = v
+    def setPoolSize(self, size):
+        self._pool_size = size
+        self._reset_pool_sizes(size, for_versions=False)
 
-    def setVersionPoolSize(self, v):
-        self._version_pool_size=v
+    def setVersionPoolSize(self, size):
+        self._version_pool_size = size
+        self._reset_pool_sizes(size, for_versions=True)
 
+    def _reset_pool_sizes(self, size, for_versions=False):
+        self._a()
+        try:
+            for version, pool in self._pools.items():
+                if (version != '') == for_versions:
+                    pool.set_pool_size(size)
+        finally:
+            self._r()
+
     def undo(self, id, txn=None):
         """Undo a transaction identified by id.
 
@@ -678,23 +685,19 @@
 
     def getCacheDeactivateAfter(self):
         """Deprecated"""
-        warnings.warn("cache_deactivate_after has no effect",
-                      DeprecationWarning)
+        deprecated36("getCacheDeactivateAfter has no effect")
 
     def getVersionCacheDeactivateAfter(self):
         """Deprecated"""
-        warnings.warn("cache_deactivate_after has no effect",
-                      DeprecationWarning)
+        deprecated36("getVersionCacheDeactivateAfter has no effect")
 
     def setCacheDeactivateAfter(self, v):
         """Deprecated"""
-        warnings.warn("cache_deactivate_after has no effect",
-                      DeprecationWarning)
+        deprecated36("setCacheDeactivateAfter has no effect")
 
     def setVersionCacheDeactivateAfter(self, v):
         """Deprecated"""
-        warnings.warn("cache_deactivate_after has no effect",
-                      DeprecationWarning)
+        deprecated36("setVersionCacheDeactivateAfter has no effect")
 
 class ResourceManager(object):
     """Transaction participation for a version or undo resource."""

Modified: Zope3/branches/srichter-blow-services/src/ZODB/DemoStorage.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/DemoStorage.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/DemoStorage.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -80,8 +80,9 @@
 
 """
 
-import base64, time, string
-from ZODB import POSException, BaseStorage, utils
+import base64, time
+from ZODB import POSException, BaseStorage
+from ZODB.utils import z64, oid_repr
 from persistent.TimeStamp import TimeStamp
 from cPickle import loads
 from BTrees import OOBTree
@@ -409,7 +410,7 @@
 
             # Now build an index of *only* those objects reachable
             # from the root.
-            rootl = ['\0\0\0\0\0\0\0\0']
+            rootl = [z64]
             pindex = {}
             while rootl:
                 oid = rootl.pop()
@@ -508,8 +509,8 @@
             o.append("  %s %s" % (TimeStamp(tid), p))
             for r in t:
                 oid, pre, vdata, p, tid = r
-                oid = utils.oid_repr(oid)
-                tid = utils.oid_repr(tid)
+                oid = oid_repr(oid)
+                tid = oid_repr(tid)
 ##                if serial is not None: serial=str(TimeStamp(serial))
                 pre=id(pre)
                 if vdata and vdata[1]: vdata=vdata[0], id(vdata[1])
@@ -522,7 +523,7 @@
         items.sort()
         for oid, r in items:
             if r: r=id(r)
-            o.append('  %s: %s' % (utils.oid_repr(oid), r))
+            o.append('  %s: %s' % (oid_repr(oid), r))
 
         o.append('\nVersion Index:')
         items=self._vindex.items()
@@ -533,6 +534,6 @@
             vitems.sort()
             for oid, r in vitems:
                 if r: r=id(r)
-                o.append('    %s: %s' % (utils.oid_repr(oid), r))
+                o.append('    %s: %s' % (oid_repr(oid), r))
 
-        return string.join(o,'\n')
+        return '\n'.join(o)

Modified: Zope3/branches/srichter-blow-services/src/ZODB/ExportImport.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/ExportImport.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/ExportImport.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -21,7 +21,6 @@
 from ZODB.POSException import ExportError
 from ZODB.utils import p64, u64
 from ZODB.serialize import referencesf
-import sys
 
 logger = logging.getLogger('ZODB.ExportImport')
 

Modified: Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/FileStorage.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/FileStorage.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/FileStorage.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -20,7 +20,6 @@
 from cPickle import Pickler, Unpickler, loads
 import errno
 import os
-import struct
 import sys
 import time
 import logging
@@ -454,7 +453,7 @@
         # middle holds bytes 16:34 of a data record:
         #    pos of transaction, len of version name, data length
         #    commit version never writes data, so data length is always 0
-        middle = struct.pack(">8sH8s", p64(self._pos), len(dest), z64)
+        middle = pack(">8sH8s", p64(self._pos), len(dest), z64)
 
         if dest:
             sd = p64(self._vindex_get(dest, 0))
@@ -697,7 +696,7 @@
         # XXX isn't the same, 0 is returned.  Why the discrepancy?
         self._file.seek(tpos)
         h = self._file.read(TRANS_HDR_LEN)
-        tid, tl, status, ul, dl, el = struct.unpack(TRANS_HDR, h)
+        tid, tl, status, ul, dl, el = unpack(TRANS_HDR, h)
         self._file.read(ul + dl + el)
         tend = tpos + tl + 8
         pos = self._file.tell()
@@ -1802,9 +1801,12 @@
     def _skip_to_start(self, start):
         # Scan through the transaction records doing almost no sanity
         # checks.
+        file = self._file
+        read = file.read
+        seek = file.seek
         while 1:
-            self._file.seek(self._pos)
-            h = self._file.read(16)
+            seek(self._pos)
+            h = read(16)
             if len(h) < 16:
                 return
             tid, stl = unpack(">8s8s", h)
@@ -1817,13 +1819,12 @@
                 self._pos = long(self._pos) + tl + 8
             if __debug__:
                 # Sanity check
-                self._file.seek(self._pos - 8, 0)
-                rtl = self._file.read(8)
+                seek(self._pos - 8, 0)
+                rtl = read(8)
                 if rtl != stl:
-                    pos = self._file.tell() - 8
+                    pos = file.tell() - 8
                     panic("%s has inconsistent transaction length at %s "
-                          "(%s != %s)",
-                          self._file.name, pos, u64(rtl), u64(stl))
+                          "(%s != %s)", file.name, pos, u64(rtl), u64(stl))
 
     def next(self, index=0):
         if self._file is None:
@@ -2008,7 +2009,7 @@
         self.pos -= u64(self.file.read(8)) + 8
         self.file.seek(self.pos)
         h = self.file.read(TRANS_HDR_LEN)
-        tid, tl, status, ul, dl, el = struct.unpack(TRANS_HDR, h)
+        tid, tl, status, ul, dl, el = unpack(TRANS_HDR, h)
         if status == 'p':
             self.stop = 1
             return None

Modified: Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fsdump.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fsdump.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fsdump.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,5 +1,16 @@
-from cPickle import Unpickler
-from cStringIO import StringIO
+##############################################################################
+#
+# 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.
+#
+##############################################################################
 import md5
 import struct
 
@@ -7,48 +18,9 @@
 from ZODB.FileStorage.format \
      import TRANS_HDR, TRANS_HDR_LEN, DATA_HDR, DATA_HDR_LEN
 from ZODB.TimeStamp import TimeStamp
-from ZODB.utils import u64
+from ZODB.utils import u64, get_pickle_metadata
 from ZODB.tests.StorageTestBase import zodb_unpickle
 
-def get_pickle_metadata(data):
-    # ZODB's data records contain two pickles.  The first is the class
-    # of the object, the second is the object.  We're only trying to
-    # pick apart the first here, to extract the module and class names.
-    if data.startswith('(c'):   # pickle MARK GLOBAL opcode sequence
-        global_prefix = 2
-    elif data.startswith('c'):  # pickle GLOBAL opcode
-        global_prefix = 1
-    else:
-        global_prefix = 0
-
-    if global_prefix:
-        # Don't actually unpickle a class, because it will attempt to
-        # load the class.  Just break open the pickle and get the
-        # module and class from it.  The module and the class names are
-        # given by newline-terminated strings following the GLOBAL opcode.
-        modname, classname, rest = data.split('\n', 2)
-        modname = modname[global_prefix:]   # strip GLOBAL opcode
-        return modname, classname
-
-    # Else there are a bunch of other possible formats.
-    f = StringIO(data)
-    u = Unpickler(f)
-    try:
-        class_info = u.load()
-    except Exception, err:
-        print "Error", err
-        return '', ''
-    if isinstance(class_info, tuple):
-        if isinstance(class_info[0], tuple):
-            modname, classname = class_info[0]
-        else:
-            modname, classname = class_info
-    else:
-        # XXX not sure what to do here
-        modname = repr(class_info)
-        classname = ''
-    return modname, classname
-
 def fsdump(path, file=None, with_offset=1):
     i = 0
     iter = FileIterator(path)

Copied: Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fsoids.py (from rev 28844, Zope3/trunk/src/ZODB/FileStorage/fsoids.py)


Property changes on: Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fsoids.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fspack.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fspack.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/FileStorage/fspack.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -264,7 +264,7 @@
             if tlen != th.tlen:
                 self.fail(pos, "redundant transaction length does not "
                           "match initial transaction length: %d != %d",
-                          u64(s), th.tlen)
+                          tlen, th.tlen)
             pos += 8
 
         self.packpos = pos
@@ -359,7 +359,7 @@
             if tlen != th.tlen:
                 self.fail(pos, "redundant transaction length does not "
                           "match initial transaction length: %d != %d",
-                          u64(s), th.tlen)
+                          tlen, th.tlen)
             pos += 8
 
         for pos in extra_roots:
@@ -553,7 +553,7 @@
             if tlen != th.tlen:
                 self.fail(pos, "redundant transaction length does not "
                           "match initial transaction length: %d != %d",
-                          u64(s), th.tlen)
+                          tlen, th.tlen)
             pos += 8
 
         return pos, new_pos

Modified: Zope3/branches/srichter-blow-services/src/ZODB/MappingStorage.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/MappingStorage.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/MappingStorage.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -21,16 +21,18 @@
 The Mapping storage uses a single data structure to map object ids to data.
 """
 
-from ZODB import utils
-from ZODB import BaseStorage
+from ZODB.utils import u64, z64
+from ZODB.BaseStorage import BaseStorage
 from ZODB import POSException
 from persistent.TimeStamp import TimeStamp
 
 
-class MappingStorage(BaseStorage.BaseStorage):
+class MappingStorage(BaseStorage):
+
     def __init__(self, name='Mapping Storage'):
-        BaseStorage.BaseStorage.__init__(self, name)
+        BaseStorage.__init__(self, name)
         self._index = {}
+        # FIXME: Why we don't use dict for _tindex?
         self._tindex = []
         self._ltid = None
         # Note: If you subclass this and use a persistent mapping facility
@@ -41,12 +43,15 @@
         return len(self._index)
 
     def getSize(self):
-        # These constants are for Python object memory overheads
-        s = 32
-        for oid in self._index.keys():
-            p = self._index[oid]
-            s += 56 + len(p)
-        return s
+        self._lock_acquire()
+        try:
+            # These constants are for Python object memory overheads
+            s = 32
+            for p in self._index.itervalues():
+                s += 56 + len(p)
+            return s
+        finally:
+            self._lock_release()
 
     def load(self, oid, version):
         self._lock_acquire()
@@ -70,8 +75,7 @@
         self._lock_acquire()
         try:
             # The tid is the first 8 bytes of the buffer.
-            s = self._index[oid]
-            return s[:8]
+            return self._index[oid][:8]
         finally:
             self._lock_release()
 
@@ -81,13 +85,12 @@
             raise POSException.StorageTransactionError(self, transaction)
 
         if version:
-            raise POSException.Unsupported, "Versions aren't supported"
+            raise POSException.Unsupported("Versions aren't supported")
 
         self._lock_acquire()
         try:
-            if self._index.has_key(oid):
-                old = self._index[oid]
-                oserial = old[:8]
+            if oid in self._index:
+                oserial = self._index[oid][:8]
                 if serial != oserial:
                     raise POSException.ConflictError(oid=oid,
                                                      serials=(oserial, serial),
@@ -102,8 +105,7 @@
         self._tindex = []
 
     def _finish(self, tid, user, desc, ext):
-        for oid, p in self._tindex:
-            self._index[oid] = p
+        self._index.update(dict(self._tindex))
         self._ltid = self._tid
 
     def lastTransaction(self):
@@ -115,21 +117,20 @@
             if not self._index:
                 return
             # Build an index of *only* those objects reachable from the root.
-            rootl = ['\0\0\0\0\0\0\0\0']
+            rootl = [z64]
             pindex = {}
             while rootl:
                 oid = rootl.pop()
-                if pindex.has_key(oid):
+                if oid in pindex:
                     continue
                 # Scan non-version pickle for references
                 r = self._index[oid]
                 pindex[oid] = r
-                p = r[8:]
-                referencesf(p, rootl)
+                referencesf(r[8:], rootl)
 
             # Now delete any unreferenced entries:
             for oid in self._index.keys():
-                if not pindex.has_key(oid):
+                if oid not in pindex:
                     del self._index[oid]
 
         finally:
@@ -137,13 +138,12 @@
 
     def _splat(self):
         """Spit out a string showing state."""
-        o = []
-        o.append('Index:')
+        o = ['Index:']
         keys = self._index.keys()
         keys.sort()
         for oid in keys:
             r = self._index[oid]
             o.append('  %s: %s, %s' %
-                     (utils.u64(oid),TimeStamp(r[:8]),`r[8:]`))
+                     (u64(oid), TimeStamp(r[:8]), repr(r[8:])))
 
         return '\n'.join(o)

Modified: Zope3/branches/srichter-blow-services/src/ZODB/Mount.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/Mount.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/Mount.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -15,9 +15,7 @@
 
 $Id$"""
 
-import string
 import time
-import sys
 import thread
 import logging
 import persistent
@@ -187,7 +185,7 @@
             if newMount:
                 try: id = data.getId()
                 except: id = '???'  # data has no getId() method.  Bad.
-                p = string.join(parent.getPhysicalPath() + (id,), '/')
+                p = '/'.join(parent.getPhysicalPath() + (id,))
                 logger.info('Mounted database %s at %s',
                             self._getMountParams(), p)
         else:

Modified: Zope3/branches/srichter-blow-services/src/ZODB/POSException.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/POSException.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/POSException.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -90,8 +90,8 @@
 
         if data is not None:
             # avoid circular import chain
-            from ZODB.serialize import SimpleObjectReader
-            self.class_name = SimpleObjectReader().getClassName(data)
+            from ZODB.utils import get_pickle_metadata
+            self.class_name = "%s.%s" % get_pickle_metadata(data)
 ##        else:
 ##            if message != "data read conflict error":
 ##                raise RuntimeError

Modified: Zope3/branches/srichter-blow-services/src/ZODB/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -13,7 +13,7 @@
 ##############################################################################
 
 # The next line must use double quotes, so release.py recognizes it.
-__version__ = "3.3"
+__version__ = "3.4a0"
 
 import sys
 import __builtin__

Copied: Zope3/branches/srichter-blow-services/src/ZODB/collaborations.txt (from rev 28844, Zope3/trunk/src/ZODB/collaborations.txt)


Property changes on: Zope3/branches/srichter-blow-services/src/ZODB/collaborations.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/srichter-blow-services/src/ZODB/dbmStorage.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/dbmStorage.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/dbmStorage.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -19,6 +19,8 @@
 in undo or versions.
 """
 
+from ZODB.utils import z64
+
 from MappingStorage import MappingStorage
 from BaseStorage import BaseStorage
 import anydbm, os
@@ -56,7 +58,7 @@
         self._index=index=gdbm.open(filename, flag[:1]+'f', mode)
         self._tindex=[]
 
-        m='\0\0\0\0\0\0\0\0'
+        m=z64
         oid=index.firstkey()
         while oid != None:
             m=max(m, oid)
@@ -76,7 +78,7 @@
             # Build an index of *only* those objects reachable
             # from the root.
             index=self._index
-            rootl=['\0\0\0\0\0\0\0\0']
+            rootl=[z64]
             pop=rootl.pop
             pindex={}
             referenced=pindex.has_key

Modified: Zope3/branches/srichter-blow-services/src/ZODB/interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -16,233 +16,9 @@
 $Id$
 """
 
-try:
-    from zope.interface import Interface, Attribute, implements
-    from zope.interface.verify import verifyObject
-except ImportError:
-    class Interface:
-        pass
+import zope.interface
 
-    class Attribute:
-        def __init__(self, __name__, __doc__):
-            self.__name__ = __name__
-            self.__doc__ = __doc__
-
-    def implements(*args):
-        pass
-
-    def verifyObject(*args):
-        pass
-
-class IDataManager(Interface):
-    """Objects that manage transactional storage.
-
-    These object's may manage data for other objects, or they may manage
-    non-object storages, such as relational databases.
-    """
-
-    def abort_sub(transaction):
-        """Discard all subtransaction data.
-
-        See subtransaction.txt
-
-        This is called when top-level transactions are aborted.
-
-        No further subtransactions can be started once abort_sub()
-        has been called; this is only used when the transaction is
-        being aborted.
-
-        abort_sub also implies the abort of a 2-phase commit.
-
-        This should never fail.
-        """
-
-    def commit_sub(transaction):
-        """Commit all changes made in subtransactions and begin 2-phase commit
-
-        Data are saved *as if* they are part of the current transaction.
-        That is, they will not be persistent unless the current transaction
-        is committed.
-
-        This is called when the current top-level transaction is committed.
-
-        No further subtransactions can be started once commit_sub()
-        has been called; this is only used when the transaction is
-        being committed.
-
-        This call also implied the beginning of 2-phase commit.
-        """
-
-    # Two-phase commit protocol.  These methods are called by the
-    # ITransaction object associated with the transaction being
-    # committed.
-
-    def tpc_begin(transaction, subtransaction=False):
-        """Begin commit of a transaction, starting the two-phase commit.
-
-        transaction is the ITransaction instance associated with the
-        transaction being committed.
-
-        subtransaction is a Boolean flag indicating whether the
-        two-phase commit is being invoked for a subtransaction.
-
-        Important note: Subtransactions are modelled in the sense that
-        when you commit a subtransaction, subsequent commits should be
-        for subtransactions as well.  That is, there must be a
-        commit_sub() call between a tpc_begin() call with the
-        subtransaction flag set to true and a tpc_begin() with the
-        flag set to false.
-
-        """
-
-
-    def tpc_abort(transaction):
-        """Abort a transaction.
-
-        This is always called after a tpc_begin call.
-
-        transaction is the ITransaction instance associated with the
-        transaction being committed.
-
-        This should never fail.
-        """
-
-    def tpc_finish(transaction):
-        """Indicate confirmation that the transaction is done.
-
-        transaction is the ITransaction instance associated with the
-        transaction being committed.
-
-        This should never fail. If this raises an exception, the
-        database is not expected to maintain consistency; it's a
-        serious error.
-
-        """
-
-    def tpc_vote(transaction):
-        """Verify that a data manager can commit the transaction
-
-        This is the last chance for a data manager to vote 'no'.  A
-        data manager votes 'no' by raising an exception.
-
-        transaction is the ITransaction instance associated with the
-        transaction being committed.
-        """
-
-    def commit(object, transaction):
-        """CCCommit changes to an object
-
-        Save the object as part of the data to be made persistent if
-        the transaction commits.
-        """
-
-    def abort(object, transaction):
-        """Abort changes to an object
-
-        Only changes made since the last transaction or
-        sub-transaction boundary are discarded.
-
-        This method may be called either:
-
-        o Outside of two-phase commit, or
-
-        o In the first phase of two-phase commit
-
-        """
-
-    def sortKey():
-        """
-        Return a key to use for ordering registered DataManagers
-
-        ZODB uses a global sort order to prevent deadlock when it commits
-        transactions involving multiple resource managers.  The resource
-        manager must define a sortKey() method that provides a global ordering
-        for resource managers.
-        """
-
-
-class ITransaction(Interface):
-    """Object representing a running transaction.
-
-    Objects with this interface may represent different transactions
-    during their lifetime (.begin() can be called to start a new
-    transaction using the same instance).
-    """
-
-    user = Attribute(
-        "user",
-        "The name of the user on whose behalf the transaction is being\n"
-        "performed.  The format of the user name is defined by the\n"
-        "application.")
-    # XXX required to be a string?
-
-    description = Attribute(
-        "description",
-        "Textual description of the transaction.")
-
-    def begin(info=None, subtransaction=None):
-        """Begin a new transaction.
-
-        If the transaction is in progress, it is aborted and a new
-        transaction is started using the same transaction object.
-        """
-
-    def commit(subtransaction=None):
-        """Finalize the transaction.
-
-        This executes the two-phase commit algorithm for all
-        IDataManager objects associated with the transaction.
-        """
-
-    def abort(subtransaction=0, freeme=1):
-        """Abort the transaction.
-
-        This is called from the application.  This can only be called
-        before the two-phase commit protocol has been started.
-        """
-
-    def join(datamanager):
-        """Add a datamanager to the transaction.
-
-        The datamanager must implement the
-        transactions.interfaces.IDataManager interface, and be
-        adaptable to ZODB.interfaces.IDataManager.
-        """
-
-    def register(object):
-        """Register the given object for transaction control."""
-
-    def note(text):
-        """Add text to the transaction description.
-
-        If a description has already been set, text is added to the
-        end of the description following two newline characters.
-        Surrounding whitespace is stripped from text.
-        """
-        # XXX does impl do the right thing with ''?  Not clear what
-        # the "right thing" is.
-
-    def setUser(user_name, path="/"):
-        """Set the user name.
-
-        path should be provided if needed to further qualify the
-        identified user.
-        """
-
-    def setExtendedInfo(name, value):
-        """Add extension data to the transaction.
-
-        name is the name of the extension property to set; value must
-        be a picklable value.
-
-        Storage implementations may limit the amount of extension data
-        which can be stored.
-        """
-        # XXX is this this allowed to cause an exception here, during
-        # the two-phase commit, or can it toss data silently?
-
-
-class IConnection(Interface):
+class IConnection(zope.interface.Interface):
     """ZODB connection.
 
     XXX: This interface is incomplete.

Modified: Zope3/branches/srichter-blow-services/src/ZODB/serialize.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/serialize.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/serialize.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -34,36 +34,44 @@
 provide backwards compatibility with earlier versions of Zope.  The
 two current formats for class description are:
 
-    - type(obj)
-    - type(obj), obj.__getnewargs__()
+    1. type(obj)
+    2. type(obj), obj.__getnewargs__()
 
-The second of these options is used if the object has a
-__getnewargs__() method.  It is intended to support objects like
-persistent classes that have custom C layouts that are determined by
-arguments to __new__().
+The second of these options is used if the object has a __getnewargs__()
+method.  It is intended to support objects like persistent classes that have
+custom C layouts that are determined by arguments to __new__().
 
-The type object is usually stored using the standard pickle mechanism,
-which uses a string containing the class's module and name.  The type
-may itself be a persistent object, in which case a persistent
-reference (see below) is used.
+The type object is usually stored using the standard pickle mechanism, which
+involves the pickle GLOBAL opcode (giving the type's module and name as
+strings).  The type may itself be a persistent object, in which case a
+persistent reference (see below) is used.
 
+It's unclear what "usually" means in the last paragraph.  There are two
+useful places to concentrate confusion about exactly which formats exist:
+
+- BaseObjectReader.getClassName() below returns a dotted "module.class"
+  string, via actually loading a pickle.  This requires that the
+  implementation of application objects be available.
+
+- ZODB/utils.py's get_pickle_metadata() tries to return the module and
+  class names (as strings) without importing any application modules or
+  classes, via analyzing the pickle.
+
 Earlier versions of Zope supported several other kinds of class
-descriptions.  The current serialization code reads these
-descriptions, but does not write them.
+descriptions.  The current serialization code reads these descriptions, but
+does not write them.  The four earlier formats are:
 
-The four formats are:
+    3. (module name, class name), None
+    4. (module name, class name), __getinitargs__()
+    5. class, None
+    6. class, __getinitargs__()
 
-    1. (module name, class name), None
-    2. (module name, class name), __getinitargs__()
-    3. class, None
-    4. class, __getinitargs__()
+Formats 4 and 6 are used only if the class defines a __getinitargs__()
+method.  Formats 5 and 6 are used if the class does not have a __module__
+attribute (I'm not sure when this applies, but I think it occurs for some
+but not all ZClasses).
 
-Formats 2 and 4 are used only if the class defines an
-__getinitargs__() method.  Formats 3 and 4 are used if the class does
-not have an __module__ attribute.  (I'm not sure when this applies,
-but I think it occurs for some but not all ZClasses.)
 
-
 Persistent references
 ---------------------
 
@@ -79,7 +87,6 @@
 changed the class of an object, a new record with new class metadata
 would be written but all the old references would still include the
 old class.
-
 """
 
 import cPickle

Copied: Zope3/branches/srichter-blow-services/src/ZODB/tests/dbopen.txt (from rev 28844, Zope3/trunk/src/ZODB/tests/dbopen.txt)


Property changes on: Zope3/branches/srichter-blow-services/src/ZODB/tests/dbopen.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Deleted: Zope3/branches/srichter-blow-services/src/ZODB/tests/loggingsupport.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/tests/loggingsupport.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/tests/loggingsupport.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,122 +0,0 @@
-##############################################################################
-#
-# 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 testing logging code
-
-If you want to test that your code generates proper log output, you
-can create and install a handler that collects output:
-
-  >>> handler = InstalledHandler('foo.bar')
-
-The handler is installed into loggers for all of the names passed. In
-addition, the logger level is set to 1, which means, log
-everything. If you want to log less than everything, you can provide a
-level keyword argument.  The level setting effects only the named
-loggers.
-
-Then, any log output is collected in the handler:
-
-  >>> logging.getLogger('foo.bar').exception('eek')
-  >>> logging.getLogger('foo.bar').info('blah blah')
-
-  >>> for record in handler.records:
-  ...     print record.name, record.levelname
-  ...     print ' ', record.getMessage()
-  foo.bar ERROR
-    eek
-  foo.bar INFO
-    blah blah
-
-A similar effect can be gotten by just printing the handler:
-
-  >>> print handler
-  foo.bar ERROR
-    eek
-  foo.bar INFO
-    blah blah
-
-After checking the log output, you need to uninstall the handler:
-
-  >>> handler.uninstall()
-
-At which point, the handler won't get any more log output.
-Let's clear the handler:
-
-  >>> handler.clear()
-  >>> handler.records
-  []
-
-And then log something:
-  
-  >>> logging.getLogger('foo.bar').info('blah')
-
-and, sure enough, we still have no output:
-  
-  >>> handler.records
-  []
-  
-$Id$
-"""
-
-import logging
-
-class Handler(logging.Handler):
-
-    def __init__(self, *names, **kw):
-        logging.Handler.__init__(self)
-        self.names = names
-        self.records = []
-        self.setLoggerLevel(**kw)
-
-    def setLoggerLevel(self, level=1):
-        self.level = level
-        self.oldlevels = {}
-
-    def emit(self, record):
-        self.records.append(record)
-
-    def clear(self):
-        del self.records[:]
-
-    def install(self):
-        for name in self.names:
-            logger = logging.getLogger(name)
-            self.oldlevels[name] = logger.level
-            logger.setLevel(self.level)
-            logger.addHandler(self)
-
-    def uninstall(self):
-        for name in self.names:
-            logger = logging.getLogger(name)
-            logger.setLevel(self.oldlevels[name])
-            logger.removeHandler(self)
-
-    def __str__(self):
-        return '\n'.join(
-            [("%s %s\n  %s" %
-              (record.name, record.levelname,
-               '\n'.join([line
-                          for line in record.getMessage().split('\n')
-                          if line.strip()])
-               )
-              )
-              for record in self.records]
-              )
-        
-
-class InstalledHandler(Handler):
-
-    def __init__(self, *names):
-        Handler.__init__(self, *names)
-        self.install()
-    

Modified: Zope3/branches/srichter-blow-services/src/ZODB/tests/testConnection.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/tests/testConnection.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/tests/testConnection.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -22,7 +22,7 @@
 from ZODB.config import databaseFromString
 from ZODB.utils import p64, u64
 from ZODB.tests.warnhook import WarningsHook
-from ZODB.interfaces import verifyObject
+from zope.interface.verify import verifyObject
 
 class ConnectionDotAdd(unittest.TestCase):
 
@@ -414,8 +414,9 @@
         >>> len(hook.warnings)
         1
         >>> message, category, filename, lineno = hook.warnings[0]
-        >>> message
-        'The dt argument to cacheMinimize is ignored.'
+        >>> print message
+        This will be removed in ZODB 3.6:
+        cacheMinimize() dt= is ignored.
         >>> category.__name__
         'DeprecationWarning'
         >>> hook.clear()
@@ -434,8 +435,9 @@
         >>> len(hook.warnings)
         2
         >>> message, category, filename, lineno = hook.warnings[0]
-        >>> message
-        'cacheFullSweep is deprecated. Use cacheMinimize instead.'
+        >>> print message
+        This will be removed in ZODB 3.6:
+        cacheFullSweep is deprecated. Use cacheMinimize instead.
         >>> category.__name__
         'DeprecationWarning'
         >>> message, category, filename, lineno = hook.warnings[1]
@@ -632,7 +634,7 @@
 
 class TestConnectionInterface(unittest.TestCase):
 
-    def test(self):
+    def test_connection_interface(self):
         from ZODB.interfaces import IConnection
         db = databaseFromString("<zodb>\n<mappingstorage/>\n</zodb>")
         cn = db.open()

Modified: Zope3/branches/srichter-blow-services/src/ZODB/tests/testDB.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/tests/testDB.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/tests/testDB.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -23,6 +23,10 @@
 
 from ZODB.tests.MinPO import MinPO
 
+# Return total number of connections across all pools in a db._pools.
+def nconn(pools):
+    return sum([len(pool.all) for pool in pools.values()])
+
 class DBTests(unittest.TestCase):
 
     def setUp(self):
@@ -75,22 +79,22 @@
         c12.close() # return to pool
         self.assert_(c1 is c12) # should be same
 
-        pools, pooll = self.db._pools
+        pools = self.db._pools
 
         self.assertEqual(len(pools), 3)
-        self.assertEqual(len(pooll), 3)
+        self.assertEqual(nconn(pools), 3)
 
         self.db.removeVersionPool('v1')
 
         self.assertEqual(len(pools), 2)
-        self.assertEqual(len(pooll), 2)
+        self.assertEqual(nconn(pools), 2)
 
         c12 = self.db.open('v1')
         c12.close() # return to pool
         self.assert_(c1 is not c12) # should be different
 
         self.assertEqual(len(pools), 3)
-        self.assertEqual(len(pooll), 3)
+        self.assertEqual(nconn(pools), 3)
 
     def _test_for_leak(self):
         self.dowork()
@@ -112,27 +116,27 @@
         c12 = self.db.open('v1')
         self.assert_(c1 is c12) # should be same
 
-        pools, pooll = self.db._pools
+        pools = self.db._pools
 
         self.assertEqual(len(pools), 3)
-        self.assertEqual(len(pooll), 3)
+        self.assertEqual(nconn(pools), 3)
 
         self.db.removeVersionPool('v1')
 
         self.assertEqual(len(pools), 2)
-        self.assertEqual(len(pooll), 2)
+        self.assertEqual(nconn(pools), 2)
 
         c12.close() # should leave pools alone
 
         self.assertEqual(len(pools), 2)
-        self.assertEqual(len(pooll), 2)
+        self.assertEqual(nconn(pools), 2)
 
         c12 = self.db.open('v1')
         c12.close() # return to pool
         self.assert_(c1 is not c12) # should be different
 
         self.assertEqual(len(pools), 3)
-        self.assertEqual(len(pooll), 3)
+        self.assertEqual(nconn(pools), 3)
 
 
 def test_suite():

Modified: Zope3/branches/srichter-blow-services/src/ZODB/tests/testFileStorage.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/tests/testFileStorage.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/tests/testFileStorage.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -167,7 +167,56 @@
 
         self.failUnless(self._storage._records_before_save > 20)
 
+    def checkCorruptionInPack(self):
+        # This sets up a corrupt .fs file, with a redundant transaction
+        # length mismatch.  The implementation of pack in many releases of
+        # ZODB blew up if the .fs file had such damage:  it detected the
+        # damage, but the code to raise CorruptedError referenced an undefined
+        # global.
+        import time
 
+        from ZODB.DB import DB
+        from ZODB.utils import U64, p64
+        from ZODB.FileStorage.format import CorruptedError
+
+        db = DB(self._storage)
+        conn = db.open()
+        conn.root()['xyz'] = 1
+        get_transaction().commit()
+
+        # Ensure it's all on disk.
+        db.close()
+        self._storage.close()
+
+        # Reopen before damaging.
+        self.open()
+
+        # Open .fs directly, and damage content.
+        f = open('FileStorageTests.fs', 'r+b')
+        f.seek(0, 2)
+        pos2 = f.tell() - 8
+        f.seek(pos2)
+        tlen2 = U64(f.read(8))  # length-8 of the last transaction
+        pos1 = pos2 - tlen2 + 8 # skip over the tid at the start
+        f.seek(pos1)
+        tlen1 = U64(f.read(8))  # should be redundant length-8
+        self.assertEqual(tlen1, tlen2)  # verify that it is redundant
+
+        # Now damage the second copy.
+        f.seek(pos2)
+        f.write(p64(tlen2 - 1))
+        f.close()
+
+        # Try to pack.  This used to yield
+        #     NameError: global name 's' is not defined
+        try:
+            self._storage.pack(time.time(), None)
+        except CorruptedError, detail:
+            self.assert_("redundant transaction length does not match "
+                         "initial transaction length" in str(detail))
+        else:
+            self.fail("expected CorruptedError")
+
 class FileStorageRecoveryTest(
     StorageTestBase.StorageTestBase,
     RecoveryStorage.RecoveryStorage,
@@ -227,7 +276,7 @@
     >>> from ZODB.FileStorage import FileStorage
     >>> from ZODB.DB import DB
     >>> import transaction
-    >>> from ZODB.tests.loggingsupport import InstalledHandler
+    >>> from zope.testing.loggingsupport import InstalledHandler
 
     Arrange to capture log messages -- they're an important part of
     this test!

Modified: Zope3/branches/srichter-blow-services/src/ZODB/tests/testUtils.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/tests/testUtils.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/tests/testUtils.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -53,6 +53,41 @@
         writer = BaseObjectWriter(None)
         self.assertEqual(writer.persistent_id(P), None)
 
+    # It's hard to know where to put this test.  We're checking that the
+    # ConflictError constructor uses utils.py's get_pickle_metadata() to
+    # deduce the class path from a pickle, instead of actually loading
+    # the pickle (and so also trying to import application module and
+    # class objects, which isn't a good idea on a ZEO server when avoidable).
+    def checkConflictErrorDoesntImport(self):
+        from ZODB.serialize import BaseObjectWriter
+        from ZODB.POSException import ConflictError
+        from ZODB.tests.MinPO import MinPO
+        import cPickle as pickle
+
+        obj = MinPO()
+        data = BaseObjectWriter().serialize(obj)
+
+        # The pickle contains a GLOBAL ('c') opcode resolving to MinPO's
+        # module and class.
+        self.assert_('cZODB.tests.MinPO\nMinPO\n' in data)
+
+        # Fiddle the pickle so it points to something "impossible" instead.
+        data = data.replace('cZODB.tests.MinPO\nMinPO\n',
+                            'cpath.that.does.not.exist\nlikewise.the.class\n')
+        # Pickle can't resolve that GLOBAL opcode -- gets ImportError.
+        self.assertRaises(ImportError, pickle.loads, data)
+
+        # Verify that building ConflictError doesn't get ImportError.
+        try:
+            raise ConflictError(object=obj, data=data)
+        except ConflictError, detail:
+            # And verify that the msg names the impossible path.
+            self.assert_('path.that.does.not.exist.likewise.the.class' in
+                         str(detail))
+        else:
+            self.fail("expected ConflictError, but no exception raised")
+
+
 def test_suite():
     return unittest.makeSuite(TestUtils, 'check')
 

Modified: Zope3/branches/srichter-blow-services/src/ZODB/tests/testZODB.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/tests/testZODB.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/tests/testZODB.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -243,9 +243,13 @@
             self.assertEqual(r1['item'], 2)
             self.assertEqual(r2['item'], 2)
             for msg, obj, filename, lineno in hook.warnings:
-                self.assert_(
-                    msg.startswith("setLocalTransaction() is deprecated.") or
-                    msg.startswith("getTransaction() is deprecated."))
+                self.assert_(msg in [
+                    "This will be removed in ZODB 3.6:\n"
+                        "setLocalTransaction() is deprecated. "
+                        "Use the txn_mgr argument to DB.open() instead.",
+                    "This will be removed in ZODB 3.6:\n"
+                        "getTransaction() is deprecated. "
+                        "Use the txn_mgr argument to DB.open() instead."])
         finally:
             conn1.close()
             conn2.close()

Copied: Zope3/branches/srichter-blow-services/src/ZODB/tests/test_doctest_files.py (from rev 28844, Zope3/trunk/src/ZODB/tests/test_doctest_files.py)


Property changes on: Zope3/branches/srichter-blow-services/src/ZODB/tests/test_doctest_files.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Copied: Zope3/branches/srichter-blow-services/src/ZODB/tests/testfsoids.py (from rev 28844, Zope3/trunk/src/ZODB/tests/testfsoids.py)


Property changes on: Zope3/branches/srichter-blow-services/src/ZODB/tests/testfsoids.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/srichter-blow-services/src/ZODB/utils.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/ZODB/utils.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/ZODB/utils.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -16,13 +16,61 @@
 import time
 from struct import pack, unpack
 from binascii import hexlify
+import cPickle as pickle
+from cStringIO import StringIO
+import weakref
+import warnings
 
 from persistent.TimeStamp import TimeStamp
 
+__all__ = ['z64',
+           't32',
+           'p64',
+           'u64',
+           'U64',
+           'cp',
+           'newTimeStamp',
+           'oid_repr',
+           'serial_repr',
+           'tid_repr',
+           'positive_id',
+           'get_refs',
+           'readable_tid_repr',
+           'WeakSet',
+           'DEPRECATED_ARGUMENT',
+           'deprecated36',
+           'get_pickle_metadata',
+          ]
+
+# A unique marker to give as the default value for a deprecated argument.
+# The method should then do a
+#
+#     if that_arg is not DEPRECATED_ARGUMENT:
+#         complain
+#
+# dance.
+DEPRECATED_ARGUMENT = object()
+
+# Raise DeprecationWarning, noting that the deprecated thing will go
+# away in ZODB 3.6.  Point to the caller of our caller (i.e., at the
+# code using the deprecated thing).
+def deprecated36(msg):
+    warnings.warn("This will be removed in ZODB 3.6:\n%s" % msg,
+                  DeprecationWarning, stacklevel=3)
+
 z64 = '\0'*8
+
+# TODO The purpose of t32 is unclear.  Code that uses it is usually
+# of the form:
+#
+#    if e < 0:
+#        e = t32 - e
+#
+# Doesn't make sense (since e is negative, it creates a number larger than
+# t32).  If users said "e += t32", *maybe* it would make sense.
 t32 = 1L << 32
 
-assert sys.hexversion >= 0x02020000
+assert sys.hexversion >= 0x02030000
 
 # The distinction between ints and longs is blurred in Python 2.2,
 # so u64() are U64() really the same.
@@ -109,3 +157,147 @@
             result += 1L << 64
             assert result >= 0 # else addresses are fatter than 64 bits
     return result
+
+# So full of undocumented magic it's hard to fathom.
+# The existence of cPickle.noload() isn't documented, and what it
+# does isn't documented either.  In general it unpickles, but doesn't
+# actually build any objects of user-defined classes.  Despite that
+# persistent_load is documented to be a callable, there's an
+# undocumented gimmick where if it's actually a list, for a PERSID or
+# BINPERSID opcode cPickle just appends "the persistent id" to that list.
+# Also despite that "a persistent id" is documented to be a string,
+# ZODB persistent ids are actually (often? always?) tuples, most often
+# of the form
+#     (oid, (module_name, class_name))
+# So the effect of the following is to dig into the object pickle, and
+# return a list of the persistent ids found (which are usually nested
+# tuples), without actually loading any modules or classes.
+# Note that pickle.py doesn't support any of this, it's undocumented code
+# only in cPickle.c.
+def get_refs(a_pickle):
+    # The pickle is in two parts.  First there's the class of the object,
+    # needed to build a ghost,  See get_pickle_metadata for how complicated
+    # this can get.  The second part is the state of the object.  We want
+    # to find all the persistent references within both parts (although I
+    # expect they can only appear in the second part).
+    f = StringIO(a_pickle)
+    u = pickle.Unpickler(f)
+    u.persistent_load = refs = []
+    u.noload() # class info
+    u.noload() # instance state info
+    return refs
+
+# Given a ZODB pickle, return pair of strings (module_name, class_name).
+# Do this without importing the module or class object.
+# See ZODB/serialize.py's module docstring for the only docs that exist about
+# ZODB pickle format.  If the code here gets smarter, please update those
+# docs to be at least as smart.  The code here doesn't appear to make sense
+# for what serialize.py calls formats 5 and 6.
+
+def get_pickle_metadata(data):
+    # ZODB's data records contain two pickles.  The first is the class
+    # of the object, the second is the object.  We're only trying to
+    # pick apart the first here, to extract the module and class names.
+    if data.startswith('(c'):   # pickle MARK GLOBAL opcode sequence
+        global_prefix = 2
+    elif data.startswith('c'):  # pickle GLOBAL opcode
+        global_prefix = 1
+    else:
+        global_prefix = 0
+
+    if global_prefix:
+        # Formats 1 and 2.
+        # Don't actually unpickle a class, because it will attempt to
+        # load the class.  Just break open the pickle and get the
+        # module and class from it.  The module and class names are given by
+        # newline-terminated strings following the GLOBAL opcode.
+        modname, classname, rest = data.split('\n', 2)
+        modname = modname[global_prefix:]   # strip GLOBAL opcode
+        return modname, classname
+
+    # Else there are a bunch of other possible formats.
+    f = StringIO(data)
+    u = pickle.Unpickler(f)
+    try:
+        class_info = u.load()
+    except Exception, err:
+        print "Error", err
+        return '', ''
+    if isinstance(class_info, tuple):
+        if isinstance(class_info[0], tuple):
+            # Formats 3 and 4.
+            modname, classname = class_info[0]
+        else:
+            # Formats 5 and 6 (probably) end up here.
+            modname, classname = class_info
+    else:
+        # This isn't a known format.
+        modname = repr(class_info)
+        classname = ''
+    return modname, classname
+
+# A simple implementation of weak sets, supplying just enough of Python's
+# sets.Set interface for our needs.
+
+class WeakSet(object):
+    """A set of objects that doesn't keep its elements alive.
+
+    The objects in the set must be weakly referencable.
+    The objects need not be hashable, and need not support comparison.
+    Two objects are considered to be the same iff their id()s are equal.
+
+    When the only references to an object are weak references (including
+    those from WeakSets), the object can be garbage-collected, and
+    will vanish from any WeakSets it may be a member of at that time.
+    """
+
+    def __init__(self):
+        # Map id(obj) to obj.  By using ids as keys, we avoid requiring
+        # that the elements be hashable or comparable.
+        self.data = weakref.WeakValueDictionary()
+
+    def __len__(self):
+        return len(self.data)
+
+    def __contains__(self, obj):
+        return id(obj) in self.data
+
+    # Same as a Set, add obj to the collection.
+    def add(self, obj):
+        self.data[id(obj)] = obj
+
+    # Same as a Set, remove obj from the collection, and raise
+    # KeyError if obj not in the collection.
+    def remove(self, obj):
+        del self.data[id(obj)]
+
+    # f is a one-argument function.  Execute f(elt) for each elt in the
+    # set.  f's return value is ignored.
+    def map(self, f):
+        for wr in self.as_weakref_list():
+            elt = wr()
+            if elt is not None:
+                f(elt)
+
+    # Return a list of weakrefs to all the objects in the collection.
+    # Because a weak dict is used internally, iteration is dicey (the
+    # underlying dict may change size during iteration, due to gc or
+    # activity from other threads).  as_weakef_list() is safe.
+    #
+    # Something like this should really be a method of Python's weak dicts.
+    # If we invoke self.data.values() instead, we get back a list of live
+    # objects instead of weakrefs.  If gc occurs while this list is alive,
+    # all the objects move to an older generation (because they're strongly
+    # referenced by the list!).  They can't get collected then, until a
+    # less frequent collection of the older generation.  Before then, if we
+    # invoke self.data.values() again, they're still alive, and if gc occurs
+    # while that list is alive they're all moved to yet an older generation.
+    # And so on.  Stress tests showed that it was easy to get into a state
+    # where a WeakSet grows without bounds, despite that almost all its
+    # elements are actually trash.  By returning a list of weakrefs instead,
+    # we avoid that, although the decision to use weakrefs is now# very
+    # visible to our clients.
+    def as_weakref_list(self):
+        # We're cheating by breaking into the internals of Python's
+        # WeakValueDictionary here (accessing its .data attribute).
+        return self.data.data.values()

Modified: Zope3/branches/srichter-blow-services/src/bugtracker/browser/bug.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/bugtracker/browser/bug.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/bugtracker/browser/bug.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -74,7 +74,8 @@
         if ttype is not None:
             source = zapi.createObject(None, self.context.description.ttype,
                                        self.context.description)
-            view = zapi.getView(removeAllProxies(source), '', self.request)
+            view = zapi.getMultiAdapter(
+                (removeAllProxies(source), self.request))
             html = view.render()
         else:
             html = self.context.description

Modified: Zope3/branches/srichter-blow-services/src/bugtracker/browser/bug_edit.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/bugtracker/browser/bug_edit.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/bugtracker/browser/bug_edit.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,7 +11,7 @@
 <body>
 <div metal:fill-slot="body">
 
-  <form action="." tal:attributes="action request/URL" method="POST"
+  <form action="." tal:attributes="action request/URL" method="post"
         enctype="multipart/form-data">
 
     <p tal:define="status view/update"

Modified: Zope3/branches/srichter-blow-services/src/bugtracker/browser/comment.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/bugtracker/browser/comment.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/bugtracker/browser/comment.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -55,7 +55,8 @@
         if ttype is not None:
             source = zapi.createObject(None, self.context.body.ttype,
                                        self.context.body)
-            view = zapi.getView(removeAllProxies(source), '', self.request)
+            view = zapi.getMultiAdapter(
+                (removeAllProxies(source), self.request))
             html = view.render()
         else:
             html = self.context.body

Modified: Zope3/branches/srichter-blow-services/src/bugtracker/tests/placelesssetup.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/bugtracker/tests/placelesssetup.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/bugtracker/tests/placelesssetup.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -22,7 +22,6 @@
 from datetime import datetime
 
 from zope.component.interfaces import IFactory
-from zope.component.service import defineService, serviceManager
 from zope.interface import classImplements, implements
 from zope.schema.vocabulary import getVocabularyRegistry
 
@@ -41,7 +40,7 @@
 from zope.app.renderer.plaintext import IPlainTextSource
 from zope.app.renderer.plaintext import PlainTextToHTMLRenderer
 from zope.app.renderer.plaintext import PlainTextSourceFactory
-from zope.app.security.interfaces import IAuthenticationService
+from zope.app.security.interfaces import IAuthentication
 from zope.app.size.interfaces import ISized
 from zope.app.traversing.interfaces import IContainmentRoot, ITraverser
 from zope.app.traversing.interfaces import ITraversable, IPhysicallyLocatable
@@ -105,10 +104,7 @@
         registry.register('Releases', ReleaseVocabulary)
         registry.register('Users', UserVocabulary)
 
-        defineService(zapi.servicenames.Authentication,
-                      IAuthenticationService)
-        serviceManager.provideService(zapi.servicenames.Authentication,
-                                      principalRegistry)
+        ztapi.provideUtility(IAuthentication, principalRegistry)
 
         principalRegistry.definePrincipal(u'zope.srichter',
                                           u'Stephan Richter', u'',

Modified: Zope3/branches/srichter-blow-services/src/bugtracker/tests/test_mail.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/bugtracker/tests/test_mail.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/bugtracker/tests/test_mail.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -17,7 +17,6 @@
 """
 import unittest
 
-from zope.component.service import defineService, serviceManager
 from zope.component.tests.placelesssetup import PlacelessSetup
 from zope.interface import classImplements, implements
 

Modified: Zope3/branches/srichter-blow-services/src/bugtracker/tests/test_vocabularies.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/bugtracker/tests/test_vocabularies.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/bugtracker/tests/test_vocabularies.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -17,7 +17,6 @@
 """
 import unittest
 
-from zope.component.service import defineService, serviceManager
 from zope.interface import classImplements, implements
 from zope.schema.interfaces import ITokenizedTerm
 from zope.schema.vocabulary import getVocabularyRegistry
@@ -28,7 +27,7 @@
 from zope.app.annotation.attribute import AttributeAnnotations
 from zope.app.annotation.interfaces import IAnnotations, IAttributeAnnotatable
 from zope.app.container.contained import contained, Contained
-from zope.app.security.interfaces import IAuthenticationService
+from zope.app.security.interfaces import IAuthentication
 from zope.app.security.principalregistry import principalRegistry, Principal
 
 from bugtracker.interfaces import IManagableVocabulary
@@ -195,9 +194,7 @@
 
     def setUp(self):
         PlacelessSetup.setUp(self)
-        defineService(zapi.servicenames.Authentication, IAuthenticationService)
-        serviceManager.provideService(zapi.servicenames.Authentication,
-                                      principalRegistry)
+        ztapi.provideUtility(IAuthentication, principalRegistry)
         principalRegistry.definePrincipal(
             '0', 'title0', 'desc0', 'zero', 'pass0')
         principalRegistry.definePrincipal(

Modified: Zope3/branches/srichter-blow-services/src/bugtracker/vocabulary.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/bugtracker/vocabulary.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/bugtracker/vocabulary.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -191,7 +191,7 @@
     implements(IVocabulary, IVocabularyTokenized)
 
     def __init__(self, context):
-        self.auth = zapi.getService(zapi.servicenames.Authentication)
+        self.auth = zapi.principals()
     
     def __contains__(self, value):
         ids = map(lambda p: p.id, self.auth.getPrincipals(''))

Modified: Zope3/branches/srichter-blow-services/src/persistent/cPersistence.c
===================================================================
--- Zope3/branches/srichter-blow-services/src/persistent/cPersistence.c	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/persistent/cPersistence.c	2005-01-17 15:30:10 UTC (rev 28853)
@@ -30,7 +30,6 @@
 static PyObject *py_keys, *py_setstate, *py___dict__, *py_timeTime;
 static PyObject *py__p_changed, *py__p_deactivate;
 static PyObject *py___getattr__, *py___setattr__, *py___delattr__;
-static PyObject *py___getstate__;
 static PyObject *py___slotnames__, *copy_reg_slotnames, *__newobj__;
 static PyObject *py___getnewargs__, *py___getstate__;
 
@@ -50,7 +49,6 @@
     INIT_STRING(__getattr__);
     INIT_STRING(__setattr__);
     INIT_STRING(__delattr__);
-    INIT_STRING(__getstate__);
     INIT_STRING(__slotnames__);
     INIT_STRING(__getnewargs__);
     INIT_STRING(__getstate__);
@@ -58,6 +56,24 @@
     return 0;
 }
 
+#ifdef Py_DEBUG
+static void
+fatal_1350(cPersistentObject *self, const char *caller, const char *detail)
+{
+	char buf[1000];
+
+	PyOS_snprintf(buf, sizeof(buf),
+	    "cPersistence.c %s(): object at %p with type %.200s\n"
+	    "%s.\n"
+	    "The only known cause is multiple threads trying to ghost and\n"
+	    "unghost the object simultaneously.\n"
+	    "That's not legal, but ZODB can't stop it.\n"
+	    "See Collector #1350.\n",
+	    caller, self, self->ob_type->tp_name, detail);
+	Py_FatalError(buf);
+}
+#endif
+
 static void ghostify(cPersistentObject*);
 
 /* Load the state of the object, unghostifying it.  Upon success, return 1.
@@ -88,6 +104,18 @@
         }
         self->state = cPersistent_UPTODATE_STATE;
         Py_DECREF(r);
+        if (self->cache && self->ring.r_next == NULL) {
+#ifdef Py_DEBUG
+        	fatal_1350(self, "unghostify",
+		    		 "is not in the cache despite that we just "
+		      		 "unghostified it");
+#else
+		PyErr_Format(PyExc_SystemError, "object at %p with type "
+			     "%.200s not in the cache despite that we just "
+			     "unghostified it", self, self->ob_type->tp_name);
+		return -1;
+#endif
+	}
     }
     return 1;
 }
@@ -134,13 +162,19 @@
         return;
     }
 
-    /* If the cache is still active, we must unlink the object. */
-    if (self->ring.r_next) {
-	/* if we're ghostifying an object, we better have some non-ghosts */
-	assert(self->cache->non_ghost_count > 0);
-	self->cache->non_ghost_count--;
-	ring_del(&self->ring);
+    if (self->ring.r_next == NULL) {
+	/* There's no way to raise an error in this routine. */
+#ifdef Py_DEBUG
+	fatal_1350(self, "ghostify", "claims to be in a cache but isn't");
+#else
+	return;
+#endif
     }
+
+    /* If we're ghostifying an object, we better have some non-ghosts. */
+    assert(self->cache->non_ghost_count > 0);
+    self->cache->non_ghost_count--;
+    ring_del(&self->ring);
     self->state = cPersistent_GHOST_STATE;
     dictptr = _PyObject_GetDictPtr((PyObject *)self);
     if (dictptr && *dictptr) {
@@ -249,7 +283,7 @@
 	return slotnames;
     }
 
-    slotnames = PyObject_CallFunctionObjArgs(copy_reg_slotnames, 
+    slotnames = PyObject_CallFunctionObjArgs(copy_reg_slotnames,
 					     (PyObject*)cls, NULL);
     if (slotnames && !(slotnames == Py_None || PyList_Check(slotnames))) {
 	PyErr_SetString(PyExc_TypeError,
@@ -257,7 +291,7 @@
 	Py_DECREF(slotnames);
 	return NULL;
     }
-  
+
     return slotnames;
 }
 
@@ -288,7 +322,7 @@
 	if (PyObject_SetItem(copy, key, value) < 0)
 	    goto err;
     }
-  
+
     return copy;
  err:
     Py_DECREF(copy);
@@ -366,13 +400,13 @@
         }
     }
 
-    if (n) 
+    if (n)
 	state = Py_BuildValue("(NO)", state, slots);
 
  end:
     Py_XDECREF(slotnames);
     Py_XDECREF(slots);
-  
+
     return state;
 }
 
@@ -381,12 +415,12 @@
 {
     PyObject *key, *value;
     int pos = 0;
-  
+
     if (!PyDict_Check(dict)) {
 	PyErr_SetString(PyExc_TypeError, "Expected dictionary");
 	return -1;
     }
-  
+
     while (PyDict_Next(dict, &pos, &key, &value)) {
 	if (PyObject_SetAttr(self, key, value) < 0)
 	    return -1;
@@ -451,7 +485,7 @@
     return Py_None;
 }
 
-static char pickle___reduce__doc[] = 
+static char pickle___reduce__doc[] =
 "Reduce an object to contituent parts for serialization\n"
 ;
 
@@ -475,18 +509,18 @@
 	PyErr_Clear();
 	l = 0;
     }
-  
+
     args = PyTuple_New(l+1);
     if (args == NULL)
 	goto end;
-  
+
     Py_INCREF(self->ob_type);
     PyTuple_SET_ITEM(args, 0, (PyObject*)(self->ob_type));
     for (i = 0; i < l; i++) {
 	Py_INCREF(PyTuple_GET_ITEM(bargs, i));
 	PyTuple_SET_ITEM(args, i+1, PyTuple_GET_ITEM(bargs, i));
     }
-  
+
     state = PyObject_CallMethodObjArgs(self, py___getstate__, NULL);
     if (!state)
 	goto end;
@@ -680,7 +714,7 @@
     }
     else
 	result = Py_True;
-      
+
     Py_INCREF(result);
 
   Done:
@@ -688,7 +722,7 @@
     return result;
 }
 
-/* 
+/*
    XXX we should probably not allow assignment of __class__ and __dict__.
 */
 
@@ -1012,7 +1046,7 @@
   {"__getstate__", (PyCFunction)Per__getstate__, METH_NOARGS,
    pickle___getstate__doc },
   {"__setstate__", (PyCFunction)pickle___setstate__, METH_O,
-   pickle___setstate__doc},                                          
+   pickle___setstate__doc},
   {"__reduce__", (PyCFunction)pickle___reduce__, METH_NOARGS,
    pickle___reduce__doc},
 

Modified: Zope3/branches/srichter-blow-services/src/persistent/interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/persistent/interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/persistent/interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -15,19 +15,10 @@
 
 $Id$
 """
-try:
-    from zope.interface import Interface
-    from zope.interface import Attribute
-except ImportError:
 
-    # just allow the module to compile if zope isn't available
+from zope.interface import Interface
+from zope.interface import Attribute
 
-    class Interface(object):
-        pass
-
-    def Attribute(s):
-        return s
-
 class IPersistent(Interface):
     """Python persistent interface
 
@@ -149,7 +140,8 @@
           - call _p_deactivate()
           - set _p_changed to None
 
-    There is one way to invalidate an object:  delete its _p_changed
+    There are two ways to invalidate an object:  call the
+    _p_invalidate() method (preferred) or delete its _p_changed
     attribute.  This cannot be ignored, and is used when semantics
     require invalidation.  Normally, an invalidated object transitions
     to the ghost state.  However, some objects cannot be ghosts.  When
@@ -235,6 +227,15 @@
         may choose to keep an object in the saved state.
         """
 
+    def _p_invalidate():
+        """Invalidate the object.
+
+        Invalidate the object.  This causes any data to be thrown
+        away, even if the object is in the changed state.  The object
+        is moved to the ghost state; further accesses will cause
+        object data to be reloaded.
+        """
+
 class IPersistentNoReadConflicts(IPersistent):
     def _p_independent():
         """Hook for subclasses to prevent read conflict errors.
@@ -277,84 +278,3 @@
         is returned.  If non-None, the return value is the kind of
         timestamp supplied by Python's time.time().
         """
-
-# XXX Should we keep the following?  Doesn't seem too useful, and
-# XXX we don't actually implement this interface (e.g., we have no
-# XXX .statistics() method).
-
-class ICache(Interface):
-    """In-memory object cache.
-
-    The cache serves two purposes.  It peforms pointer swizzling, and
-    it keeps a bounded set of recently used but otherwise unreferenced
-    in objects to avoid the cost of re-loading them.
-
-    Pointer swizzling is the process of converting between persistent
-    object ids and Python object ids.  When a persistent object is
-    serialized, its references to other persistent objects are
-    represented as persitent object ids (oids).  When the object is
-    unserialized, the oids are converted into references to Python
-    objects.  If several different serialized objects refer to the
-    same object, they must all refer to the same object when they are
-    unserialized.
-
-    A cache stores persistent objects, but it treats ghost objects and
-    non-ghost or active objects differently.  It has weak references
-    to ghost objects, because ghost objects are only stored in the
-    cache to satisfy the pointer swizzling requirement.  It has strong
-    references to active objects, because it caches some number of
-    them even if they are unreferenced.
-
-    The cache keeps some number of recently used but otherwise
-    unreferenced objects in memory.  We assume that there is a good
-    chance the object will be used again soon, so keeping it memory
-    avoids the cost of recreating the object.
-
-    An ICache implementation is intended for use by an
-    IPersistentDataManager.
-    """
-
-    def get(oid):
-        """Return the object from the cache or None."""
-
-    def set(oid, obj):
-        """Store obj in the cache under oid.
-
-        obj must implement IPersistent
-        """
-
-    def remove(oid):
-        """Remove oid from the cache if it exists."""
-
-    def invalidate(oids):
-        """Make all of the objects in oids ghosts.
-
-        `oids` is an iterable object that yields oids.
-
-        The cache must attempt to change each object to a ghost by
-        calling _p_deactivate().
-
-        If an oid is not in the cache, ignore it.
-        """
-
-    def clear():
-        """Invalidate all the active objects."""
-
-    def activate(oid):
-        """Notification that object oid is now active.
-
-        The caller is notifying the cache of a state change.
-
-        Raises LookupError if oid is not in cache.
-        """
-
-    def shrink():
-        """Remove excess active objects from the cache."""
-
-    def statistics():
-        """Return dictionary of statistics about cache size.
-
-        Contains at least the following keys:
-        active -- number of active objects
-        ghosts -- number of ghost objects
-        """

Modified: Zope3/branches/srichter-blow-services/src/persistent/tests/test_persistent.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/persistent/tests/test_persistent.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/persistent/tests/test_persistent.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,44 +11,16 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-import doctest
-import os
-import sys
-import unittest
 
-import persistent.tests
 from persistent import Persistent
 
+from zope.testing.doctestunit import DocFileSuite
+
 class P(Persistent):
     def __init__(self):
         self.x = 0
     def inc(self):
         self.x += 1
 
-try:
-    DocFileSuite = doctest.DocFileSuite # >= Python 2.4.0a2
-except AttributeError:
-    # <= Python 2.4.0a1
-    def DocFileSuite(path, globs=None):
-        # It's not entirely obvious how to connection this single string
-        # with unittest.  For now, re-use the _utest() function that comes
-        # standard with doctest in Python 2.3.  One problem is that the
-        # error indicator doesn't point to the line of the doctest file
-        # that failed.
-
-        path = os.path.join(persistent.tests.__path__[0], path)
-        
-        source = open(path).read()
-        if globs is None:
-            globs = sys._getframe(1).f_globals
-        t = doctest.Tester(globs=globs)
-        def runit():
-            doctest._utest(t, path, source, path, 0)
-        f = unittest.FunctionTestCase(runit,
-                                      description="doctest from %s" % path)
-        suite = unittest.TestSuite()
-        suite.addTest(f)
-        return suite
-
 def test_suite():
     return DocFileSuite("persistent.txt", globs={"P": P})

Modified: Zope3/branches/srichter-blow-services/src/transaction/_manager.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/transaction/_manager.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/transaction/_manager.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -18,7 +18,6 @@
 """
 
 import thread
-import weakref
 
 from transaction._transaction import Transaction
 
@@ -28,60 +27,28 @@
 # practice not to explicitly close Connection objects, and keeping
 # a Connection alive keeps a potentially huge number of other objects
 # alive (e.g., the cache, and everything reachable from it too).
+# Therefore we use "weak sets" internally.
 #
-# Therefore we use "weak sets" internally.  The implementation here
-# implements just enough of Python's sets.Set interface for our needs.
+# Obscure:  because of the __init__.py maze, we can't import WeakSet
+# at top level here.
 
-class WeakSet(object):
-    """A set of objects that doesn't keep its elements alive.
-
-    The objects in the set must be weakly referencable.
-    The objects need not be hashable, and need not support comparison.
-    Two objects are considered to be the same iff their id()s are equal.
-
-    When the only references to an object are weak references (including
-    those from WeakSets), the object can be garbage-collected, and
-    will vanish from any WeakSets it may be a member of at that time.
-    """
-
-    def __init__(self):
-        # Map id(obj) to obj.  By using ids as keys, we avoid requiring
-        # that the elements be hashable or comparable.
-        self.data = weakref.WeakValueDictionary()
-
-    # Same as a Set, add obj to the collection.
-    def add(self, obj):
-        self.data[id(obj)] = obj
-
-    # Same as a Set, remove obj from the collection, and raise
-    # KeyError if obj not in the collection.
-    def remove(self, obj):
-        del self.data[id(obj)]
-
-    # Return a list of all the objects in the collection.
-    # Because a weak dict is used internally, iteration
-    # is dicey (the underlying dict may change size during
-    # iteration, due to gc or activity from other threads).
-    # as_list() attempts to be safe.
-    def as_list(self):
-        return self.data.values()
-
-
 class TransactionManager(object):
 
     def __init__(self):
+        from ZODB.utils import WeakSet
+
         self._txn = None
         self._synchs = WeakSet()
 
     def begin(self):
         if self._txn is not None:
             self._txn.abort()
-        self._txn = Transaction(self._synchs.as_list(), self)
+        self._txn = Transaction(self._synchs, self)
         return self._txn
 
     def get(self):
         if self._txn is None:
-            self._txn = Transaction(self._synchs.as_list(), self)
+            self._txn = Transaction(self._synchs, self)
         return self._txn
 
     def free(self, txn):
@@ -104,8 +71,8 @@
         # _threads maps thread ids to transactions
         self._txns = {}
         # _synchs maps a thread id to a WeakSet of registered synchronizers.
-        # The set elements are passed to the Transaction constructor,
-        # because the latter needs to call the synchronizers when it commits.
+        # The WeakSet is passed to the Transaction constructor, because the
+        # latter needs to call the synchronizers when it commits.
         self._synchs = {}
 
     def begin(self):
@@ -114,8 +81,6 @@
         if txn is not None:
             txn.abort()
         synchs = self._synchs.get(tid)
-        if synchs is not None:
-            synchs = synchs.as_list()
         txn = self._txns[tid] = Transaction(synchs, self)
         return txn
 
@@ -124,8 +89,6 @@
         txn = self._txns.get(tid)
         if txn is None:
             synchs = self._synchs.get(tid)
-            if synchs is not None:
-                synchs = synchs.as_list()
             txn = self._txns[tid] = Transaction(synchs, self)
         return txn
 
@@ -135,6 +98,8 @@
         del self._txns[tid]
 
     def registerSynch(self, synch):
+        from ZODB.utils import WeakSet
+
         tid = thread.get_ident()
         ws = self._synchs.get(tid)
         if ws is None:

Modified: Zope3/branches/srichter-blow-services/src/transaction/_transaction.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/transaction/_transaction.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/transaction/_transaction.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -172,8 +172,15 @@
         self.status = Status.ACTIVE
         # List of resource managers, e.g. MultiObjectResourceAdapters.
         self._resources = []
-        self._synchronizers = synchronizers or []
+
+        # Weak set of synchronizer objects to call.
+        if synchronizers is None:
+            from ZODB.utils import WeakSet
+            synchronizers = WeakSet()
+        self._synchronizers = synchronizers
+
         self._manager = manager
+
         # _adapters: Connection/_p_jar -> MultiObjectResourceAdapter[Sub]
         self._adapters = {}
         self._voted = {} # id(Connection) -> boolean, True if voted
@@ -261,9 +268,10 @@
                 self._resources.append(adapter)
 
     def begin(self):
-        warnings.warn("Transaction.begin() should no longer be used; use "
-                      "the begin() method of a transaction manager.",
-                      DeprecationWarning, stacklevel=2)
+        from ZODB.utils import deprecated36
+
+        deprecated36("Transaction.begin() should no longer be used; use "
+                      "the begin() method of a transaction manager.")
         if (self._resources or
               self._sub or
               self._nonsub or
@@ -285,8 +293,7 @@
             self.commit(True)
 
         if not subtransaction:
-            for s in self._synchronizers:
-                s.beforeCompletion(self)
+            self._synchronizers.map(lambda s: s.beforeCompletion(self))
             self.status = Status.COMMITTING
 
         try:
@@ -310,8 +317,7 @@
             self.status = Status.COMMITTED
             if self._manager:
                 self._manager.free(self)
-            for s in self._synchronizers:
-                s.afterCompletion(self)
+            self._synchronizers.map(lambda s: s.afterCompletion(self))
             self.log.debug("commit")
 
     def _commitResources(self, subtransaction):
@@ -359,8 +365,7 @@
                 self._cleanup(L)
             finally:
                 if not subtransaction:
-                    for s in self._synchronizers:
-                        s.afterCompletion(self)
+                    self._synchronizers.map(lambda s: s.afterCompletion(self))
             raise t, v, tb
 
     def _cleanup(self, L):
@@ -426,8 +431,7 @@
 
     def abort(self, subtransaction=False):
         if not subtransaction:
-            for s in self._synchronizers:
-                s.beforeCompletion(self)
+            self._synchronizers.map(lambda s: s.beforeCompletion(self))
 
         if subtransaction and self._nonsub:
             from ZODB.POSException import TransactionError
@@ -457,8 +461,7 @@
         if not subtransaction:
             if self._manager:
                 self._manager.free(self)
-            for s in self._synchronizers:
-                s.afterCompletion(self)
+            self._synchronizers.map(lambda s: s.afterCompletion(self))
             self.log.debug("abort")
 
         if tb is not None:
@@ -630,4 +633,3 @@
 
     def sortKey(self):
         return self._datamanager.sortKey()
-

Modified: Zope3/branches/srichter-blow-services/src/transaction/interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/transaction/interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/transaction/interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -15,25 +15,246 @@
 
 $Id$
 """
-try:
-    from zope.interface import Interface
-except ImportError:
-    class Interface:
-        pass
 
+import zope.interface
 
-class IDataManager(Interface):
-    """Data management interface for storing objects transactionally
+class IResourceManager(zope.interface.Interface):
+    """Objects that manage resources transactionally.
 
-    This is currently implemented by ZODB database connections.
+    These objects may manage data for other objects, or they may manage
+    non-object storages, such as relational databases.
 
-    XXX This exists to document ZODB4 behavior, to help create some
-    backward-compatability support for Zope 3.  New classes shouldn't
-    implement this. They should implement ZODB.interfaces.IDataManager
-    for now. Our hope is that there will eventually be an interface
-    like this or that this interface will evolve and become the
-    standard interface. There are some issues to be resolved first, like:
+    IDataManagerOriginal is the interface currently provided by ZODB
+    database connections, but the intent is to move to the newer
+    IDataManager.
+    """
 
+    # Two-phase commit protocol.  These methods are called by the
+    # ITransaction object associated with the transaction being
+    # committed.
+
+    def tpc_begin(transaction):
+        """Begin two-phase commit, to save data changes.
+
+        An implementation should do as much work as possible without
+        making changes permanent.  Changes should be made permanent
+        when tpc_finish is called (or aborted if tpc_abort is called).
+        The work can be divided between tpc_begin() and tpc_vote(), and
+        the intent is that tpc_vote() be as fast as possible (to minimize
+        the period of uncertainty).
+
+        transaction is the ITransaction instance associated with the
+        transaction being committed.
+        """
+
+    def tpc_vote(transaction):
+        """Verify that a resource manager can commit the transaction.
+
+        This is the last chance for a resource manager to vote 'no'.  A
+        resource manager votes 'no' by raising an exception.
+
+        transaction is the ITransaction instance associated with the
+        transaction being committed.
+        """
+
+    def tpc_finish(transaction):
+        """Indicate confirmation that the transaction is done.
+
+        transaction is the ITransaction instance associated with the
+        transaction being committed.
+
+        This should never fail. If this raises an exception, the
+        database is not expected to maintain consistency; it's a
+        serious error.
+        """
+
+    def tpc_abort(transaction):
+        """Abort a transaction.
+
+        transaction is the ITransaction instance associated with the
+        transaction being committed.
+
+        All changes made by the current transaction are aborted.  Note
+        that this includes all changes stored in any savepoints that may
+        be associated with the current transaction.
+
+        tpc_abort() can be called at any time, either in or out of the
+        two-phase commit.
+
+        This should never fail.
+        """
+
+    # The savepoint/rollback API.
+
+    def savepoint(transaction):
+        """Save partial transaction changes.
+
+        There are two purposes:
+
+        1) To allow discarding partial changes without discarding all
+           dhanges.
+
+        2) To checkpoint changes to disk that would otherwise live in
+           memory for the duration of the transaction.
+
+        Returns an object implementing ISavePoint2 that can be used
+        to discard changes made since the savepoint was captured.
+
+        An implementation that doesn't support savepoints should implement
+        this method by returning a savepoint object that raises an
+        exception when its rollback method is called.  The savepoint method
+        shouldn't raise an error.  This way, transactions that create
+        savepoints can proceed as long as an attempt is never made to roll
+        back a savepoint.
+        """
+
+    def discard(transaction):
+        """Discard changes within the transaction since the last savepoint.
+
+        That means changes made since the last savepoint if one exists, or
+        since the start of the transaction.
+        """
+
+class IDataManagerOriginal(zope.interface.Interface):
+    """Objects that manage transactional storage.
+
+    These objects may manage data for other objects, or they may manage
+    non-object storages, such as relational databases.
+
+    IDataManagerOriginal is the interface currently provided by ZODB
+    database connections, but the intent is to move to the newer
+    IDataManager.
+    """
+
+    def abort_sub(transaction):
+        """Discard all subtransaction data.
+
+        See subtransaction.txt
+
+        This is called when top-level transactions are aborted.
+
+        No further subtransactions can be started once abort_sub()
+        has been called; this is only used when the transaction is
+        being aborted.
+
+        abort_sub also implies the abort of a 2-phase commit.
+
+        This should never fail.
+        """
+
+    def commit_sub(transaction):
+        """Commit all changes made in subtransactions and begin 2-phase commit
+
+        Data are saved *as if* they are part of the current transaction.
+        That is, they will not be persistent unless the current transaction
+        is committed.
+
+        This is called when the current top-level transaction is committed.
+
+        No further subtransactions can be started once commit_sub()
+        has been called; this is only used when the transaction is
+        being committed.
+
+        This call also implied the beginning of 2-phase commit.
+        """
+
+    # Two-phase commit protocol.  These methods are called by the
+    # ITransaction object associated with the transaction being
+    # committed.
+
+    def tpc_begin(transaction, subtransaction=False):
+        """Begin commit of a transaction, starting the two-phase commit.
+
+        transaction is the ITransaction instance associated with the
+        transaction being committed.
+
+        subtransaction is a Boolean flag indicating whether the
+        two-phase commit is being invoked for a subtransaction.
+
+        Important note: Subtransactions are modelled in the sense that
+        when you commit a subtransaction, subsequent commits should be
+        for subtransactions as well.  That is, there must be a
+        commit_sub() call between a tpc_begin() call with the
+        subtransaction flag set to true and a tpc_begin() with the
+        flag set to false.
+
+        """
+
+
+    def tpc_abort(transaction):
+        """Abort a transaction.
+
+        This is always called after a tpc_begin call.
+
+        transaction is the ITransaction instance associated with the
+        transaction being committed.
+
+        This should never fail.
+        """
+
+    def tpc_finish(transaction):
+        """Indicate confirmation that the transaction is done.
+
+        transaction is the ITransaction instance associated with the
+        transaction being committed.
+
+        This should never fail. If this raises an exception, the
+        database is not expected to maintain consistency; it's a
+        serious error.
+
+        """
+
+    def tpc_vote(transaction):
+        """Verify that a data manager can commit the transaction
+
+        This is the last chance for a data manager to vote 'no'.  A
+        data manager votes 'no' by raising an exception.
+
+        transaction is the ITransaction instance associated with the
+        transaction being committed.
+        """
+
+    def commit(object, transaction):
+        """CCCommit changes to an object
+
+        Save the object as part of the data to be made persistent if
+        the transaction commits.
+        """
+
+    def abort(object, transaction):
+        """Abort changes to an object
+
+        Only changes made since the last transaction or
+        sub-transaction boundary are discarded.
+
+        This method may be called either:
+
+        o Outside of two-phase commit, or
+
+        o In the first phase of two-phase commit
+
+        """
+
+    def sortKey():
+        """
+        Return a key to use for ordering registered DataManagers
+
+        ZODB uses a global sort order to prevent deadlock when it commits
+        transactions involving multiple resource managers.  The resource
+        manager must define a sortKey() method that provides a global ordering
+        for resource managers.
+        """
+
+class IDataManager(zope.interface.Interface):
+    """Data management interface for storing objects transactionally.
+
+    ZODB database connections currently provides the older
+    IDataManagerOriginal interface, but the intent is to move to this newer
+    IDataManager interface.
+
+    Our hope is that this interface will evolve and become the standard
+    interface.  There are some issues to be resolved first, like:
+
     - Probably want separate abort methods for use in and out of
       two-phase commit.
 
@@ -103,10 +324,128 @@
 
         """
 
+    def sortKey():
+        """
+        Return a key to use for ordering registered DataManagers
 
-class IRollback(Interface):
+        ZODB uses a global sort order to prevent deadlock when it commits
+        transactions involving multiple resource managers.  The resource
+        manager must define a sortKey() method that provides a global ordering
+        for resource managers.
+        """
 
+class ITransaction(zope.interface.Interface):
+    """Object representing a running transaction.
+
+    Objects with this interface may represent different transactions
+    during their lifetime (.begin() can be called to start a new
+    transaction using the same instance).
+    """
+
+    user = zope.interface.Attribute(
+        "user",
+        "The name of the user on whose behalf the transaction is being\n"
+        "performed.  The format of the user name is defined by the\n"
+        "application.")
+    # XXX required to be a string?
+
+    description = zope.interface.Attribute(
+        "description",
+        "Textual description of the transaction.")
+
+    def begin(info=None, subtransaction=None):
+        """Begin a new transaction.
+
+        If the transaction is in progress, it is aborted and a new
+        transaction is started using the same transaction object.
+        """
+
+    def commit(subtransaction=None):
+        """Finalize the transaction.
+
+        This executes the two-phase commit algorithm for all
+        IDataManager objects associated with the transaction.
+        """
+
+    def abort(subtransaction=0, freeme=1):
+        """Abort the transaction.
+
+        This is called from the application.  This can only be called
+        before the two-phase commit protocol has been started.
+        """
+
+    def join(datamanager):
+        """Add a datamanager to the transaction.
+
+        The datamanager must implement the
+        transactions.interfaces.IDataManager interface, and be
+        adaptable to ZODB.interfaces.IDataManager.
+        """
+
+    def register(object):
+        """Register the given object for transaction control."""
+
+    def note(text):
+        """Add text to the transaction description.
+
+        If a description has already been set, text is added to the
+        end of the description following two newline characters.
+        Surrounding whitespace is stripped from text.
+        """
+        # XXX does impl do the right thing with ''?  Not clear what
+        # the "right thing" is.
+
+    def setUser(user_name, path="/"):
+        """Set the user name.
+
+        path should be provided if needed to further qualify the
+        identified user.
+        """
+
+    def setExtendedInfo(name, value):
+        """Add extension data to the transaction.
+
+        name is the name of the extension property to set; value must
+        be a picklable value.
+
+        Storage implementations may limit the amount of extension data
+        which can be stored.
+        """
+        # XXX is this this allowed to cause an exception here, during
+        # the two-phase commit, or can it toss data silently?
+
+class ISavePoint(zope.interface.Interface):
+    """ISavePoint objects represent partial transaction changes.
+
+    Sequences of savepoint objects are associated with transactions,
+    and with IResourceManagers.
+    """
+
     def rollback():
+        """Discard changes made after this savepoint.
+
+        This includes discarding (call the discard method on) all
+        subsequent savepoints.
+        """
+
+    def discard():
+        """Discard changes saved by this savepoint.
+
+        That means changes made since the immediately preceding
+        savepoint if one exists, or since the start of the transaction,
+        until this savepoint.
+
+        Once a savepoint has been discarded, it's an error to attempt
+        to rollback or discard it again.
+        """
+
+    next_savepoint = zope.interface.Attribute(
+        """The next savepoint (later in time), or None if self is the
+           most recent savepoint.""")
+
+class IRollback(zope.interface.Interface):
+
+    def rollback():
         """Rollback changes since savepoint.
 
         IOW, rollback to the last savepoint.

Copied: Zope3/branches/srichter-blow-services/src/transaction/tests/test_SampleResourceManager.py (from rev 28844, Zope3/trunk/src/transaction/tests/test_SampleResourceManager.py)


Property changes on: Zope3/branches/srichter-blow-services/src/transaction/tests/test_SampleResourceManager.py
___________________________________________________________________
Name: cvs2svn:cvs-rev
   + 1.3
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Copied: Zope3/branches/srichter-blow-services/src/z3checkins/browser.py (from rev 28844, Zope3/trunk/src/z3checkins/browser.py)
===================================================================
--- Zope3/trunk/src/z3checkins/browser.py	2005-01-15 16:23:27 UTC (rev 28844)
+++ Zope3/branches/srichter-blow-services/src/z3checkins/browser.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -0,0 +1,456 @@
+"""
+Browser views for z3checkins.
+
+$Id$
+"""
+import re
+import mailbox
+from StringIO import StringIO
+
+from zope.interface import implements
+from zope.exceptions import DuplicationError
+from zope.proxy import removeAllProxies
+
+from zope.app import zapi
+from zope.app.datetimeutils import parseDatetimetz, DateTimeError
+from zope.app.publisher.browser import BrowserView
+from zope.app.form import CustomWidgetFactory
+from zope.app.form.browser import FileWidget
+from zope.app.dublincore.interfaces import IZopeDublinCore
+from zope.app.pagetemplate import ViewPageTemplateFile
+
+from z3checkins.message import Bookmark
+from z3checkins.interfaces import IMessage, ICheckinMessage, IMessageContained
+from z3checkins.interfaces import IMessageUpload, IMessageParser, IBookmark
+
+__metaclass__ = type
+
+
+class MessageUpload:
+    """Adding view mixin for uploading checkin messages."""
+
+    implements(IMessageUpload, IMessageContained)
+    data_widget = CustomWidgetFactory(FileWidget)
+
+    def createAndAdd(self, data):
+        if data.has_key('data'): # XXX should we bark if no data is given?
+            msg_raw = data['data']
+            parser = zapi.getUtility(IMessageParser)
+            if msg_raw.startswith("From "):
+                # detected an mbox file
+                mbox = StringIO(msg_raw)
+                messages = mailbox.PortableUnixMailbox(mbox,
+                        factory=parser.parse)
+                for message in messages:
+                    try:
+                        self.add(message)
+                        dc = IZopeDublinCore(message, None)
+                        if dc is not None:
+                            # TODO: should handle RFC-2047
+                            dc.title = unicode(message.subject)
+                            dc.created = message.date
+                    except DuplicationError:
+                        pass # leave the old mesage unchanged
+            else:
+                message = parser.parse(msg_raw)
+                self.add(message)
+
+
+class ContainerView:
+    """View mixin for locating checkin messages in a container."""
+
+    max_bookmarks = 5
+
+    def title(self):
+        """Returns the title of this archive.
+
+        Title is obtained from Dublin Core metadata of the folder.  If it is
+        empty, "Zope 3 Checkins" is used.
+        """
+        dc = IZopeDublinCore(self.context, None)
+        if dc is not None:
+            title = dc.title
+        else:
+            title = ''
+        return title or "Zope 3 Checkins"
+
+    def description(self):
+        """Return the description of this archive."""
+        return self.context.description
+
+    def archive_url(self):
+        """Return the URL for mailing list archives."""
+        return self.context.archive_url
+
+    def bookmarks(self):
+        """Return a list of bookmarks from a cookie.
+
+        Each bookmark is expressed as a datetime object.
+        """
+        bookmarks = []
+        cookie = self.request.get('bookmarks', '')
+        for item in cookie.split():
+            try:
+                bookmarks.append(parseDatetimetz(item))
+            except (DateTimeError, IndexError):
+                pass
+        return bookmarks
+
+    def placeBookmark(self):
+        """Place a new bookmark after the latest message in a cookie."""
+        if int(self.request.get('start', 0)) > 0:
+            return # The user can't see the newest checkins
+        if not self.context.messages:
+            return # No messages -- no bookmarks
+        bookmarks = self.bookmarks()
+        bookmarks.sort()
+        # Do not insert a bookmark if there were no checkins since the last
+        # bookmark
+        if (bookmarks and bookmarks[-1] >= self.context.messages[0].date):
+            return
+        bookmarks.append(self.context.messages[0].date)
+        if len(bookmarks) > self.max_bookmarks:
+            del bookmarks[:-self.max_bookmarks]
+        cookie = " ".join([dt.isoformat() for dt in bookmarks])
+        self.request.response.setCookie('bookmarks', cookie,
+                                        max_age=365*24*60*60) # 1 year
+
+    def checkins(self, start=None, size=None):
+        """Return a list of messages.
+
+        Returns the last 'size' checkin messages in self.context, newest
+        first, skipping the first 'start' messages.
+        """
+        if start is None:
+            start = int(self.request.get('start', 0))
+        if size is None:
+            size = int(self.request.get('size', 20))
+        last = start + size
+        items = self.context.messages[start:last]
+        items = removeAllProxies(items)
+
+        # insert bookmarks
+
+        def bookmarkBetween(msg1, msg2, bookmarks=self.bookmarks()):
+            for b in bookmarks:
+                if msg1.date > b >= msg2.date:
+                    return True
+            return False
+
+        n = 0
+        while n < len(items) - 1:
+            if bookmarkBetween(items[n], items[n+1]):
+                items.insert(n+1, Bookmark())
+                n += 1
+            n += 1
+        # insert bookmarks before the first / after the last batch item
+        if items:
+            before = self.context.messages[:1]
+            if before and bookmarkBetween(before[0], items[0]):
+                items.insert(0, Bookmark())
+            after = self.context.messages[-1:]
+            if after and bookmarkBetween(items[-1], after[0]):
+                items.append(Bookmark())
+        return items
+
+    def renderCheckins(self, start=None, size=None):
+        """Return a list of checkins rendered into HTML.
+
+        See `checkins` for description of parameters."""
+        html = []
+        previous_message = None
+        for item in self.checkins(start=start, size=size):
+            if ICheckinMessage.providedBy(item):
+                same_as_previous = item.log_message == previous_message
+                previous_message = item.log_message
+            else:
+                same_as_previous = None
+            view = zapi.getMultiAdapter((item, self.request), name='html')
+            output = view(same_as_previous=same_as_previous)
+            html.append(output)
+        return "".join(html)
+
+    def count(self):
+        """Return the number of checkin messages in the archive."""
+        return len(self.context.messages)
+
+
+class MessageRSSView(BrowserView):
+    """View for messages.
+
+    Makes sure the page template is treated as XML.
+    """
+
+    index = ViewPageTemplateFile('rss_message.pt', content_type='text/xml')
+
+
+class MessageView:
+    """View mixin for messages."""
+
+    _archive = None
+    _index = None
+
+    def _calc_index(self):
+
+        if not self.context.__parent__:
+            return
+
+        self._archive = self.context.__parent__.messages
+        try:
+            self._index = self._archive.index(self.context)
+        except ValueError:
+            pass
+
+    def previous(self):
+        """Return the previous message in archive."""
+        self._calc_index()
+        if self._index is not None and self._index < len(self._archive) - 1:
+            return self._archive[self._index + 1]
+        else:
+            return None
+
+    def next(self):
+        """Return the next message in archive."""
+        self._calc_index()
+        if self._index is not None and self._index > 0:
+            return self._archive[self._index - 1]
+        else:
+            return None
+
+    def last(self):
+        """Return the last message in archive."""
+        self._calc_index()
+        if self._archive:
+            return self._archive[0]
+        else:
+            return None
+
+    def first(self):
+        """Return the first message in archive."""
+        self._calc_index()
+        if self._archive:
+            return self._archive[-1]
+        else:
+            return None
+
+    def icon(self):
+        """Return a mapping describing an icon for this checkin."""
+        return {'src': '++resource++message.png',
+                'alt': 'Message',
+                'title': 'Email message'}
+
+    def body(self):
+        """Colorize the body of a message."""
+        text = self.context.body.replace('\r', '')\
+                                .replace('&', '&amp;') \
+                                .replace('<', '&lt;') \
+                                .replace('>', '&gt;') \
+                                .replace('"', '&quot;')
+        # It would be nice to highlight quoted text here
+        return '<pre>%s</pre>' % text
+
+    def body_plain(self):
+        """Return the full text of the message."""
+        return self.context.full_text
+
+
+class CheckinMessageView(MessageView):
+    """View mixin for checkin messages."""
+
+    _subtrees = None
+
+    def subtrees(self):
+        """Return a sequence of tuples (prefix, icon, alt, title).
+
+        (icon, alt, title) are the resource name, alt text and tooltip used
+        for any checkin messages that have directory starting with prefix.
+
+        This information is currently taken from Dublin Core metadata
+        description field, third paragraph.  Every line in that paragraph
+        defines a subtree, with all fields separated by spaces or tabs.
+        """
+        if self._subtrees is not None:
+            return self._subtrees
+        self._subtrees = []
+        container = self.context.__parent__
+        icons = container.icons
+        if not icons:
+            return self._subtrees
+        for line in icons.splitlines():
+            if line.startswith('#'):
+                continue
+            items = line.split(None, 3)
+            if len(items) < 4:
+                continue
+            if items[0] == '*':  # catch-all
+                items[0] = ''
+            self._subtrees.append(items)
+        return self._subtrees
+
+    def icon(self):
+        """Return a mapping describing an icon for this checkin."""
+        for prefix, icon, alt, title in self.subtrees():
+            if self.context.directory.startswith(prefix):
+                return {'src': '++resource++%s' % icon,
+                        'alt': alt,
+                        'title': title}
+        return {'src': '++resource++product.png',
+                'alt': 'Checkin',
+                'title': 'Checkin'}
+
+    def body(self):
+        """Colorize checkin message body."""
+        # XXX This method is rather bloated and hard to understand.
+        text = self.context.body.replace('\r', '') \
+                                .replace('&', '&amp;') \
+                                .replace('<', '&lt;') \
+                                .replace('>', '&gt;') \
+                                .replace('"', '&quot;')
+
+        text = re.sub(r'(https?://.+?)'
+                      r'($|[ \t\r\n)]|&gt;|&quot;|[.,](?:$|[ \t\r\n]))',
+                      r'<a href="\1">\1</a>\2', text)
+
+        log_idx = text.find('\nLog message:\n')
+        if log_idx == -1:
+            log_idx = text.find('\nLog Message:\n')
+        if log_idx != -1:
+            log_idx += len('\nLog message:\n')
+        if log_idx == -1:
+            log_idx = text.find('\nLog:\n')
+            if log_idx != -1:
+                log_idx += len('\nLog:\n')
+            else:
+                log_idx = text.find('Log message')
+                # XXX: Specific to Zope3 checkins
+                if log_idx != -1:
+                    log_idx = text.find('\n', log_idx) + 1
+        if log_idx == -1:
+            return '<pre>%s</pre>' % text
+
+        sig_idx = text.rfind(
+                        '\n_______________________________________________')
+        if sig_idx == -1:
+            sig_idx = len(text)
+
+        diff_idx = text.find('\n===')
+        if diff_idx == -1:
+            diff_idx = sig_idx
+
+        status_idx = text.find('\nStatus:\n')
+        if status_idx == -1:
+            status_idx = text.find('\nChanged:\n')
+        if status_idx == -1:
+            if text[diff_idx:diff_idx+5] == "\n====":
+                # Subversion
+                status_idx = text.rfind('\n', 0, diff_idx)
+            else:
+                status_idx = diff_idx
+
+            propchange_idx = text.find('\nProperty changes on:')
+            if propchange_idx != -1 and propchange_idx < status_idx:
+                status_idx = propchange_idx
+
+        assert log_idx <= status_idx <= diff_idx <= sig_idx
+
+        intro = text[:log_idx]
+        log = text[log_idx:status_idx].strip()
+        import_status = text[status_idx:diff_idx]
+        diff = text[diff_idx:sig_idx]
+        sig = text[sig_idx:]
+
+        def empty2nbsp(s):
+            if not s:
+                return '&nbsp;'
+            n = 0
+            while n < len(s) and s[n] == ' ':
+                n += 1
+            if n:
+                return '&nbsp;' * n + s[n:]
+            else:
+                return s
+        log = '<p>%s</p>' % '</p>\n<p>'.join(map(empty2nbsp, log.splitlines()))
+
+        if import_status is None:
+            import_status = ''
+
+        if diff is None:
+            diff = ''
+
+        diff = "\n".join(map(self.mark_whitespace, diff.splitlines()))
+
+        def colorize(style):
+            return r'<div class="%s">\1</div>' % style
+
+        # Unified diff
+        diff = re.sub(r'(?m)^(===.*)$', colorize("file"), diff)
+        diff = re.sub(r'(?m)^(---.*)$', colorize("oldfile"), diff)
+        diff = re.sub(r'(?m)^(\+\+\+.*)$', colorize("newfile"), diff)
+        diff = re.sub(r'(?m)^(@@.*)$', colorize("chunk"), diff)
+        diff = re.sub(r'(?m)^(-.*)$', colorize("old"), diff)
+        diff = re.sub(r'(?m)^(\+.*)$', colorize("new"), diff)
+
+        # Postprocess for Mozilla
+        diff = re.sub('</div>\n', '\n</div>', diff)
+
+        if sig:
+            sig = '<div class="signature">%s</div>' % sig
+
+        text = '<pre>%s</pre><div class="log">%s</div><pre>%s%s%s</pre>' \
+               % (intro, log, import_status, diff, sig)
+        # TODO: find out the actual encoding instead of assuming UTF-8
+        return unicode(text, 'UTF-8', 'replace')
+
+    def mark_whitespace(self, line, tab=('>', '-'), trail='.'):
+        """Mark whitespace in diff lines.
+
+        Suggested values for tab: ('>', '-'), ('&#187;', ' '),
+                                  ('&#187;', '&#x2010;')
+
+        Suggested values for trail: '.', '&#x2423;'
+        """
+        if line == ' ' or (not line.endswith(' ') and '\t' not in line):
+            return line
+        m = re.search(r'\s+\Z', line)
+        if m:
+            n = m.start()
+            if n == 0:
+                n = 1 # don't highlight the first space in a diff
+            line = '%s<span class="trail">%s</span>' % (line[:n],
+                            line[n:].replace(' ', trail))
+        if '\t' in line:
+            NORMAL, TAG, ENTITY = 0, 1, 2
+            idx = col = 0
+            mode = NORMAL
+            tabs = []
+            for c in line[1:]: # ignore first space in a diff
+                idx += 1
+                if mode == TAG:
+                    if c == '>':
+                        mode = NORMAL
+                elif mode == ENTITY:
+                    if c == ';':
+                        col += 1
+                        mode = NORMAL
+                else:
+                    if c == '<':
+                        mode = TAG
+                    elif c == '&':
+                        mode = ENTITY
+                    elif c == '\t':
+                        width = 8 - (col % 8)
+                        tabs.append((idx, width))
+                        col += width
+                    else:
+                        col += 1
+            if tabs:
+                parts = []
+                last = 0
+                for idx, width in tabs:
+                    parts.append(line[last:idx])
+                    parts.append('<span class="tab">%s%s</span>'
+                                 % (tab[0], tab[1] * (width - 1)))
+                    last = idx + 1
+                parts.append(line[last:])
+                line = "".join(parts)
+        return line


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/browser.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/srichter-blow-services/src/z3checkins/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/z3checkins/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/z3checkins/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,8 +1,7 @@
 <configure
     xmlns="http://namespaces.zope.org/zope"
     xmlns:browser="http://namespaces.zope.org/browser"
-    i18n_domain='z3checkins'
-    >
+    i18n_domain='z3checkins'>
 
 <!-- CheckinMessage content object -->
 
@@ -40,6 +39,9 @@
      <require permission="zope.View"
               interface=".interfaces.ICheckinFolderSchema" />
 
+     <require permission="zope.View"
+              attributes="messages" />
+
      <factory id="z3checkins.CheckinFolder"
               title="Checkin Folder"
               description="A checkin folder" />
@@ -51,11 +53,6 @@
 
 <!-- Utilities -->
 
-  <adapter for="zope.app.container.interfaces.IReadContainer"
-           factory=".message.MessageContainerAdapter"
-           permission="zope.View"
-           provides=".interfaces.IMessageArchive" />
-
   <adapter for=".interfaces.ICheckinFolder"
            factory=".folder.MessageNameChooser"
            permission="zope.View"
@@ -73,12 +70,12 @@
 
 <!-- Generic views for date/time formatting -->
 
-  <!-- XXX: there should be an interface that datetime.datetime implements -->
+  <!-- TODO: there should be an interface that datetime.datetime implements -->
 
   <view
     for="*"
     name="rfc822"
-    factory=".message.RFCDateTimeFormatter"
+    factory=".timeutils.RFCDateTimeFormatter"
     type="zope.publisher.interfaces.http.IHTTPRequest"
     permission="zope.Public"
     />
@@ -86,7 +83,7 @@
   <view
     for="*"
     name="isodatetime"
-    factory=".message.ISODateTimeFormatter"
+    factory=".timeutils.ISODateTimeFormatter"
     type="zope.publisher.interfaces.http.IHTTPRequest"
     permission="zope.Public"
     />
@@ -99,11 +96,11 @@
     fields="data"
     label="Upload a checkin message"
     permission="zope.ManageContent"
-    class=".message.MessageUpload" />
+    class=".browser.MessageUpload" />
 
   <browser:addMenuItem
     title="Checkin message"
-    class=".message.MessageUpload"
+    class=".browser.MessageUpload"
     permission="zope.ManageContent"
     view="CheckinMessage" />
 
@@ -135,9 +132,9 @@
 
   <browser:editform
     name="EditFolder"
-    schema=".interfaces.ICheckinFolder"
+    schema=".interfaces.ICheckinFolderSchema"
     label="Change properties of a checkin message folder"
-    menu="zmi_views" title="Edit"
+    menu="zmi_views" title="Edit properties"
     permission="zope.ManageContent" />
 
 <!-- Browser views: email message -->
@@ -145,7 +142,7 @@
   <browser:page
     for=".interfaces.IMessage"
     name="rss"
-    class=".message.MessageRSSView"
+    class=".browser.MessageRSSView"
     attribute="index"
     permission="zope.View" />
 
@@ -153,28 +150,28 @@
     for=".interfaces.IMessage"
     name="html"
     template="message_part.pt"
-    class=".message.MessageView"
+    class=".browser.MessageView"
     permission="zope.View" />
 
   <browser:page
     for=".interfaces.IMessage"
     name="html-sidebar"
     template="message_sidebar.pt"
-    class=".message.MessageView"
+    class=".browser.MessageView"
     permission="zope.View" />
 
   <browser:page
     for=".interfaces.IMessage"
     name="index.html"
     template="message.pt"
-    class=".message.MessageView"
+    class=".browser.MessageView"
     permission="zope.View" />
 
   <browser:page
     for=".interfaces.IMessage"
     name="index.txt"
-    template="message_plain.pt"
-    class=".message.MessageView"
+    class=".browser.MessageView"
+    attribute="body_plain"
     permission="zope.View" />
 
 <!-- Browser views: checkin message -->
@@ -182,7 +179,7 @@
   <browser:page
     for=".interfaces.ICheckinMessage"
     name="rss"
-    class=".message.MessageRSSView"
+    class=".browser.MessageRSSView"
     attribute="index"
     permission="zope.View" />
 
@@ -190,31 +187,24 @@
     for=".interfaces.ICheckinMessage"
     name="html"
     template="message_part.pt"
-    class=".message.CheckinMessageView"
+    class=".browser.CheckinMessageView"
     permission="zope.View" />
 
   <browser:page
     for=".interfaces.ICheckinMessage"
     name="html-sidebar"
     template="message_sidebar.pt"
-    class=".message.CheckinMessageView"
+    class=".browser.CheckinMessageView"
     permission="zope.View" />
 
   <browser:page
     for=".interfaces.ICheckinMessage"
     name="index.html"
     template="message.pt"
-    class=".message.CheckinMessageView"
+    class=".browser.CheckinMessageView"
     menu="zmi_views" title="Preview"
     permission="zope.View" />
 
-  <browser:page
-    for=".interfaces.ICheckinMessage"
-    name="index.txt"
-    template="message_plain.pt"
-    class=".message.CheckinMessageView"
-    permission="zope.View" />
-
 <!-- Browser views: bookmark -->
 
   <browser:page
@@ -225,25 +215,30 @@
 
 <!-- Browser views: containers -->
 
+  <browser:containerViews
+      for=".interfaces.ICheckinFolder"
+      contents="zope.ManageContent" />
+
   <browser:page
     for=".interfaces.ICheckinFolder"
     name="checkins.rss"
     template="rss_container.pt"
-    class=".message.ContainerView"
+    class=".browser.ContainerView"
     permission="zope.View" />
 
   <browser:page
     for=".interfaces.ICheckinFolder"
     name="index.html"
     template="container.pt"
-    class=".message.ContainerView"
-    permission="zope.View" />
+    class=".browser.ContainerView"
+    permission="zope.View"
+    menu="zmi_views" title="View" />
 
   <browser:page
     for=".interfaces.ICheckinFolder"
     name="checkins-sidebar.html"
     template="container_sidebar.pt"
-    class=".message.ContainerView"
+    class=".browser.ContainerView"
     permission="zope.View" />
 
 <!-- Resources -->

Modified: Zope3/branches/srichter-blow-services/src/z3checkins/folder.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/z3checkins/folder.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/z3checkins/folder.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -3,22 +3,71 @@
 
 Checkin message folder handling.
 
-$Id: folder.py,v 1.5 2004/03/14 10:56:48 gintautasm Exp $
+$Id$
 """
 
+from persistent.list import PersistentList
 from zope.interface import implements
 from zope.app.container.btree import BTreeContainer
 from zope.app.container.interfaces import INameChooser
 from zope.app.container.interfaces import IContainerNamesContainer
 from zope.app.size.interfaces import ISized
-from interfaces import ICheckinFolder
+from z3checkins.interfaces import ICheckinFolder, IMessage
 
+
 class CheckinFolder(BTreeContainer):
-    """A message folder."""
+    """A message folder.
 
+    The attribute `messages` is a list of Messages sorted by date in
+    reverse order (that is, latest messages first).  It was introduced for
+    performance reasons.
+    """
+
     implements(ICheckinFolder, IContainerNamesContainer)
 
+    description = None
+    archive_url = None
+    icons = None
 
+    def __init__(self):
+        BTreeContainer.__init__(self)
+        self.messages = PersistentList()
+
+    def __setitem__(self, key, message):
+        BTreeContainer.__setitem__(self, key, message)
+        if IMessage.providedBy(message):
+            for ind, oldmsg in enumerate(self.messages):
+                if message.date > oldmsg.date:
+                    self.messages.insert(ind, message)
+                    break
+            else:
+                self.messages.append(message)
+
+    def __delitem__(self, key):
+        message = self.get(key)
+        if IMessage.providedBy(message):
+            for ind, oldmsg in enumerate(self.messages):
+                if oldmsg is message:
+                    self.messages.pop(ind)
+                    break
+        BTreeContainer.__delitem__(self, key)
+
+    def __setstate__(self, state):
+        """Rebuild the internal message date index.
+
+        This method is for backwards compatibility, so that data does
+        not need to be reimported into existing z3checkins instances.
+        """
+        # BBB 2005-01-05
+        BTreeContainer.__setstate__(self, state)
+        if not hasattr(self, 'messages'):
+            self.messages = [msg for msg in self.values()
+                             if IMessage.providedBy(msg)]
+            self.messages.sort(lambda msg1, msg2: cmp(msg2.date, msg1.date))
+        if not isinstance(self.messages, PersistentList):
+            self.messages = PersistentList(self.messages)
+
+
 class MessageNameChooser:
     """An adapter to choose names for messages."""
 
@@ -36,6 +85,7 @@
 
 class MessageSized:
     """An adapter to calculate size of a message."""
+
     implements(ISized)
 
     def __init__(self, message):


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/folder.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: Zope3/branches/srichter-blow-services/src/z3checkins/interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/z3checkins/interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/z3checkins/interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,15 +1,13 @@
 """
 Interfaces for the z3checkins product.
 
-$Id: interfaces.py,v 1.14 2004/05/15 13:23:57 gintautasm Exp $
+$Id$
 """
 
 from zope.interface import Interface, Attribute
-from zope.app.folder.interfaces import IFolder
 from zope.app.container.interfaces import IContainer, IContained
-from zope.app.container.constraints import ContainerTypesConstraint
-from zope.app.container.constraints import ItemTypePrecondition
-from zope.schema import Field, Text, TextLine
+from zope.app.container.constraints import contains, containers
+from zope.schema import Field, Text, TextLine, List, Object, Datetime
 
 
 class IMessageUpload(Interface):
@@ -19,13 +17,13 @@
 class IMessage(IMessageUpload):
     """Mail message."""
 
-    message_id = Attribute("Unique message ID")
-    author_name = Attribute("Author's real name")
-    author_email = Attribute("Author's email address")
-    subject = Attribute("Subject line of the message")
-    date = Attribute("Date and time of the message")
-    body = Attribute("Body of the message")
-    full_text = Attribute("Full message text (headers and body)")
+    message_id = TextLine(title=u"Unique message ID")
+    author_name = TextLine(title=u"Author's real name")
+    author_email = TextLine(title=u"Author's email address")
+    subject = TextLine(title=u"Subject line of the message")
+    date = Datetime(title=u"Date and time of the message")
+    body = Text(title=u"Body of the message")
+    full_text = Text(title=u"Full message text (headers and body)")
 
 
 class ICheckinMessage(IMessage):
@@ -69,42 +67,20 @@
     icons = Text(title=u"Icon definitions", required=False)
 
 
-class ICheckinFolder(IFolder, ICheckinFolderSchema):
+class ICheckinFolder(IContainer, ICheckinFolderSchema):
     """A marker interface for the checkins folder."""
 
-    def __setitem__(name, object):
-        """Add a message"""
+    contains(IMessageUpload)
 
-    __setitem__.precondition = ItemTypePrecondition(IMessageUpload)
+    messages = List(title=u"Messages",
+                    description=u"""
+                    Messages in this container, sorted by date in
+                    descending order.
+                    """,
+                    value_type=Object(title=u"A message", schema=IMessage))
 
 
 class IMessageContained(IContained):
     """A contained message."""
 
-    __parent__ = Field(constraint=ContainerTypesConstraint(ICheckinFolder))
-
-
-class IMessageArchive(Interface):
-    """A chronologically ordered sequence of messages.
-
-    Implements the Python sequence procotol.
-    """
-
-    def __len__():
-        """Returns the number of messages in the archive."""
-
-    def __getitem__(index):
-        """Returns a given message."""
-
-    def __getslice__(start, stop):
-        """Returns a range of messages."""
-
-    def __iter__():
-        """Returns an iterator."""
-
-    def index(message):
-        """Returns the index of a given message.
-
-        Raises ValueError if message is not in the archive.
-        """
-
+    containers(ICheckinFolder)


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/interfaces.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: Zope3/branches/srichter-blow-services/src/z3checkins/message.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/z3checkins/message.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/z3checkins/message.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,128 +1,55 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
 """
-Python code for z3checkins product.
+The z3checkins product.
 
-# This module could be split into three: timeutils.py, message.py and views.py
-# but it is small enough IMHO.
-
-$Id: message.py,v 1.40 2004/05/14 19:56:05 gintautasm Exp $
+$Id$
 """
-
-import re
 import email
 import email.Utils
 import mailbox
 import time
-from StringIO import StringIO
 from datetime import datetime, tzinfo, timedelta
-
 from persistent import Persistent
-from zope.app.form import CustomWidgetFactory
-from zope.app.form.browser import FileWidget
+from StringIO import StringIO
+
+from zope.exceptions import DuplicationError
+from zope.interface import implements
+from zope.proxy import removeAllProxies
+
+from zope.app import zapi
+from zope.app.container.contained import Contained
 from zope.app.container.interfaces import IReadContainer
 from zope.app.datetimeutils import parseDatetimetz, DateTimeError
 from zope.app.dublincore.interfaces import IZopeDublinCore
+from zope.app.form import CustomWidgetFactory
+from zope.app.form.browser import FileWidget
 from zope.app.pagetemplate import ViewPageTemplateFile
-from zope.component import getUtility
-from zope.component import getView
-from zope.exceptions import DuplicationError
-from zope.interface import implements
-from zope.proxy import removeAllProxies
 from zope.app.publisher.browser import BrowserView
 
-from interfaces import IMessage, ICheckinMessage, IMessageContained
-from interfaces import IMessageUpload, IBookmark
-from interfaces import IMessageParser, IMessageArchive
-from interfaces import FormatError
+from z3checkins.interfaces import IMessage, ICheckinMessage, IMessageContained
+from z3checkins.interfaces import IBookmark, IMessageParser, FormatError
+from z3checkins.timeutils import FixedTimezone
 
 __metaclass__ = type
 
-#
-# Date/time utils
-#
 
-class FixedTimezone(tzinfo):
-    """Timezone with a fixed UTC offset"""
-
-    def __init__(self, offset=None):
-        """Creates a timezone with a given UTC offset (minutes east of UTC)."""
-        self._offset = offset
-
-    def tzname(self, dt):
-        if self._offset >= 0:
-            sign = '+'
-            h, m = divmod(self._offset, 60)
-        else:
-            sign = '-'
-            h, m = divmod(-self._offset, 60)
-        return '%c%02d%02d' % (sign, h, m)
-
-    def utcoffset(self, dt):
-        return timedelta(minutes=self._offset)
-
-    def dst(self, dt):
-        return timedelta(0)
-
-
-class RFCDateTimeFormatter:
-    """RFC822 view for datetime objects."""
-
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-
-    def __str__(self):
-        """Renders datetime objects in RFC822 format."""
-        return self.context.strftime("%a, %d %b %Y %H:%M:%S %z")
-
-    __call__ = __str__
-
-
-class ISODateTimeFormatter:
-    """ISO 8601 view for datetime objects."""
-
-    if time.localtime()[-1]:
-        userstz = FixedTimezone(-time.altzone / 60)
-    else:
-        userstz = FixedTimezone(-time.timezone / 60)
-
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-
-    def __str__(self):
-        """Renders datetime objects as "YYYY-MM-DD hh:mm" in the local time
-        zone."""
-        return self.context.astimezone(self.userstz).strftime("%Y-%m-%d %H:%M")
-
-    __call__ = __str__
-
-
-#
-# Checkin message content object
-#
-
-def find_body_start(full_text):
-    """Find the body of an RFC-822 message and return its index in full_text."""
-    pos1 = full_text.find('\n\n')
-    pos2 = full_text.find('\r\n\r\n')
-    if pos1 == -1:
-        pos1 = len(full_text)
-    else:
-        pos1 += 2
-    if pos2 == -1:
-        pos2 = len(full_text)
-    else:
-        pos2 += 4
-    return min(pos1, pos2)
-
-
-class Message(Persistent):
+class Message(Persistent, Contained):
     """Persistent email message."""
 
     implements(IMessage, IMessageContained)
 
-    __parent__ = __name__ = None
-
     def __init__(self, message_id=None, author_name=None,
                  author_email=None, subject=None, date=None,
                  full_text=None):
@@ -174,7 +101,6 @@
 
     def parse(self, input):
         """See IMessageParser."""
-
         if not hasattr(input, 'readline'):
             full_text = str(input)
         elif hasattr(input, 'seek') and hasattr(input, 'tell'):
@@ -189,7 +115,7 @@
         message_id = m.get('Message-Id', None)
         if message_id is None:
             raise FormatError("Message does not have a message id")
-        if message_id[0] == "<" and message_id[-1] == ">":
+        if message_id.startswith("<") and message_id.endswith(">"):
             message_id = message_id[1:-1] # strip angle brackets
         author = m.get('From', '')
         author_name, author_email = email.Utils.parseaddr(author)
@@ -202,7 +128,7 @@
         (year, month, day, hours, minutes, seconds,
          weekday, yearday, dst, tzoffset) = email.Utils.parsedate_tz(date)
 
-        # TODO: a workaround to deal with messages that don't specify a timezone
+        # A workaround to deal with messages that don't specify a timezone.
         if tzoffset is None:
             tzoffset = 0
 
@@ -230,7 +156,6 @@
         Returns a tuple (directory, log_message, branch) for checkin
         messages, and None if the message is not a checkin message.
         """
-
         if subject.startswith("Re:"):
             return None
 
@@ -242,7 +167,7 @@
             subject = parts[1]
             directory = subject.split(' - ')[0]
         elif "SVN:" in subject:
-            # TODO: Format specific to the Zope3 mailing list
+            # XXX: Format specific to the Zope3 mailing list
             # [foo-bar] SVN: foobaz/boo/bar.py log message
             parts = subject.split("SVN: ", 1)
             if len(parts) < 2:
@@ -276,14 +201,14 @@
         for line in lines:
             if in_log_msg:
                 if (line.startswith('=== ')
-                    or line.startswith('-=-') # TODO: Zope3 ML specific
+                    or line.startswith("Changed:")
                     or line.startswith("Added:")
                     or line.startswith("Modified:")
                     or line.startswith("Removed:")
                     or line.startswith("Deleted:")
+                    or line.startswith("Copied:")
                     or line.startswith("Property changes on:")
-                    or line == "Status:"
-                    ):
+                    or line == "Status:"):
                     break
                 else:
                     log_message.append(line)
@@ -298,471 +223,25 @@
         return "\n".join(log_message).strip(), branch
 
 
-class MessageContainerAdapter:
-    """Adapts a container to a message archive."""
-
-    implements(IMessageArchive)
-    __used_for__ = IReadContainer
-
-    def __init__(self, context):
-        self.context = context
-        items = []
-        for key, item in self.context.items():
-            if IMessage.providedBy(item):
-                items.append((item.date, key, item))
-        items.sort()
-        self.messages = []
-        for date, key, item in items:
-            # TODO: is this nice?
-            #item.__parent__ = self.context
-            #item.__name__ = key
-            self.messages.append(item)
-
-    def __len__(self):
-        return len(self.messages)
-
-    def __getitem__(self, index):
-        return self.messages[index]
-
-    def __getslice__(self, start, stop):
-        return self.messages[start:stop]
-
-    def __iter__(self):
-        return iter(self.messages)
-
-    def index(self, message):
-        return self.messages.index(message)
-
-
 class Bookmark:
+    """A bookmark placed between messages."""
 
     implements(IBookmark)
 
 
-#
-# Browser views
-#
+def find_body_start(full_text):
+    """Find the body of an RFC-822 message and return its index in full_text."""
+    pos1 = full_text.find('\n\n')
+    pos2 = full_text.find('\r\n\r\n')
+    if pos1 == -1:
+        pos1 = len(full_text)
+    else:
+        pos1 += 2
+    if pos2 == -1:
+        pos2 = len(full_text)
+    else:
+        pos2 += 4
+    return min(pos1, pos2)
 
-class MessageUpload:
-    """Adding view mixin for uploading checkin messages."""
-
     implements(IMessageUpload, IMessageContained)
     data_widget = CustomWidgetFactory(FileWidget)
-
-    def createAndAdd(self, data):
-        if data.has_key('data'): # XXX should we bark if no data is given?
-            msg_raw = data['data']
-            parser = getUtility(IMessageParser)
-            if msg_raw.startswith("From "):
-                # detected an mbox file
-                mbox = StringIO(msg_raw)
-                messages = mailbox.PortableUnixMailbox(mbox,
-                        factory=parser.parse)
-                for message in messages:
-                    try:
-                        self.add(message)
-                        dc = IZopeDublinCore(message, None)
-                        if dc is not None:
-                            # TODO: should handle RFC-2047
-                            dc.title = unicode(message.subject)
-                            dc.created = message.date
-                    except DuplicationError:
-                        pass # leave the old mesage unchanged
-            else:
-                message = parser.parse(msg_raw)
-                self.add(message)
-
-
-class ContainerView:
-    """View mixin for locating checkin messages in a container."""
-
-    max_bookmarks = 5
-
-    def title(self):
-        """Returns the title of this archive.
-
-        Title is obtained from Dublin Core metadata of the folder.  If it is
-        empty, "Zope 3 Checkins" is used.
-        """
-        dc = IZopeDublinCore(self.context, None)
-        if dc is not None:
-            title = dc.title
-        else:
-            title = ''
-        return title or "Zope 3 Checkins"
-
-    def description(self):
-        """Returns the description of this archive.
-        """
-        return self.context.description
-
-    def archive_url(self):
-        """Returns the URL for mailing list archives.
-        """
-        return self.context.archive_url
-
-    def bookmarks(self):
-        """Returns a list of bookmarks from a cookie.  Each bookmark is
-        expressed as a datetime object.
-        """
-        bookmarks = []
-        cookie = self.request.get('bookmarks', '')
-        for item in cookie.split():
-            try:
-                bookmarks.append(parseDatetimetz(item))
-            except (DateTimeError, IndexError):
-                pass
-        return bookmarks
-
-    def placeBookmark(self):
-        """Place a new bookmark after the latest checkin message in a
-        cookie."""
-        if int(self.request.get('start', 0)) > 0:
-            return # The user can't see the newest checkins
-        if not hasattr(self, '_archive'):
-            self._archive = IMessageArchive(self.context)
-        if not self._archive:
-            return # No messages -- no bookmarks
-        bookmarks = self.bookmarks()
-        bookmarks.sort()
-        # Do not insert a bookmark if there were no checkins since the last
-        # bookmark
-        if (bookmarks and bookmarks[-1] >= self._archive[-1].date):
-            return
-        bookmarks.append(self._archive[-1].date)
-        if len(bookmarks) > self.max_bookmarks:
-            del bookmarks[:-self.max_bookmarks]
-        cookie = " ".join([dt.isoformat() for dt in bookmarks])
-        self.request.response.setCookie('bookmarks', cookie,
-                                        max_age=365*24*60*60) # 1 year
-
-    def checkins(self, start=None, size=None):
-        """Returns a list of the last 'size' checkin messages in
-        self.context, newest first, skipping the first 'start' messages.
-        """
-        if start is None: start = int(self.request.get('start', 0))
-        if size is None: size = int(self.request.get('size', 20))
-        if not hasattr(self, '_archive'):
-            self._archive = IMessageArchive(self.context)
-        idx = len(self._archive) - start
-        items = self._archive[max(0, idx-size):idx]
-        items = removeAllProxies(items)
-        # insert bookmarks
-        def bookmarkBetween(msg1, msg2, bookmarks=self.bookmarks()):
-            for b in bookmarks:
-                if msg1.date <= b < msg2.date:
-                    return True
-            return False
-        n = 1
-        while n < len(items):
-            if bookmarkBetween(items[n-1], items[n]):
-                items.insert(n, Bookmark())
-                n += 2
-            else:
-                n += 1
-        # insert bookmarks before the first/after the last batch item
-        if items:
-            before = self._archive[max(0, idx-size-1):max(0, idx-size)]
-            if before and bookmarkBetween(before[0], items[0]):
-                items.insert(0, Bookmark())
-            after = self._archive[idx:idx+1]
-            if after and bookmarkBetween(items[-1], after[0]):
-                items.insert(len(items), Bookmark())
-        # reverse order to present newest checkins first
-        items.reverse()
-        return items
-
-    def renderCheckins(self, start=None, size=None):
-        """Returns a list of checkins rendered into HTML.  See `checkins` for
-        description of parameters."""
-        html = []
-        previous_message = None
-        for item in self.checkins(start=start, size=size):
-            if ICheckinMessage.providedBy(item):
-                same_as_previous = item.log_message == previous_message
-                previous_message = item.log_message
-            else:
-                same_as_previous = None
-            view = getView(item, 'html', self.request)
-            output = view(same_as_previous=same_as_previous)
-            html.append(output)
-        return "".join(html)
-
-    def count(self):
-        """Returns the number of checkin messages in the archive."""
-        if not hasattr(self, '_archive'):
-            self._archive = IMessageArchive(self.context)
-        return len(self._archive)
-
-
-class MessageRSSView(BrowserView):
-    """View for messages.
-
-    Makes sure the page template is treated as XML.
-    """
-
-    index = ViewPageTemplateFile('rss_message.pt', content_type='text/xml')
-
-
-class MessageView:
-    """View mixin for messages."""
-
-    def _calc_index(self):
-        if not hasattr(self, '_archive'):
-            container = self.context.__parent__
-            self._archive = container and IMessageArchive(container, None)
-        if not self._archive:
-            self._index = None
-        elif not hasattr(self, '_index'):
-            self._index = self._archive.index(self.context)
-
-    def next(self):
-        """Returns the next message in archive."""
-        self._calc_index()
-        if self._index is not None and self._index < len(self._archive) - 1:
-            return self._archive[self._index + 1]
-        else:
-            return None
-
-    def previous(self):
-        """Returns the previous message in archive."""
-        self._calc_index()
-        if self._index is not None and self._index > 0:
-            return self._archive[self._index - 1]
-        else:
-            return None
-
-    def first(self):
-        """Returns the first message in archive."""
-        self._calc_index()
-        if self._archive:
-            return self._archive[0]
-        else:
-            return None
-
-    def last(self):
-        """Returns the last message in archive."""
-        self._calc_index()
-        if self._archive:
-            return self._archive[-1]
-        else:
-            return None
-
-    def icon(self):
-        """Returns a mapping describing an icon for this checkin.  The mapping
-        contains 'src', 'alt' and 'title' attributes."""
-        return {'src': '++resource++message.png',
-                'alt': 'Message',
-                'title': 'Email message'}
-
-    def body(self):
-        """Colorizes the body of a checkin message."""
-
-        text = self.context.body.replace('\r', '')\
-                                .replace('&', '&amp;') \
-                                .replace('<', '&lt;') \
-                                .replace('>', '&gt;') \
-                                .replace('"', '&quot;')
-        # It would be nice to highlight quoted text here
-        return '<pre>%s</pre>' % text
-
-
-class CheckinMessageView(MessageView):
-    """View mixin for checkin messages."""
-
-    _subtrees = None
-    def subtrees(self):
-        """Returns a sequence of tuples (prefix, icon, alt, title).
-
-        (icon, alt, title) are the resource name, alt text and tooltip used
-        for any checkin messages that have directory starting with prefix.
-
-        This information is currently taken from Dublin Core metadata
-        description field, third paragraph.  Every line in that paragraph
-        defines a subtree, with all fields separated by spaces or tabs.
-        """
-        if self._subtrees is not None:
-            return self._subtrees
-        self._subtrees = []
-        container = self.context.__parent__
-        icons = container.icons
-        if not icons:
-            return self._subtrees
-        for line in icons.splitlines():
-            if line.startswith('#'):
-                continue
-            items = line.split(None, 3)
-            if len(items) < 4:
-                continue
-            if items[0] == '*':  # catch-all
-                items[0] = ''
-            self._subtrees.append(items)
-        return self._subtrees
-
-    def icon(self):
-        """Returns a mapping describing an icon for this checkin.  The mapping
-        contains 'src', 'alt' and 'title' attributes."""
-        for prefix, icon, alt, title in self.subtrees():
-            if self.context.directory.startswith(prefix):
-                return {'src': '++resource++%s' % icon,
-                        'alt': alt,
-                        'title': title}
-        return {'src': '++resource++product.png',
-                'alt': 'Checkin',
-                'title': 'Checkin'}
-
-    def body(self):
-        """Colorizes checkin message body."""
-
-        text = self.context.body.replace('\r', '')\
-                                .replace('&', '&amp;') \
-                                .replace('<', '&lt;') \
-                                .replace('>', '&gt;') \
-                                .replace('"', '&quot;')
-
-        text = re.sub(r'(https?://.+?)'
-                      r'($|[ \t\r\n)]|&gt;|&quot;|[.,](?:$|[ \t\r\n]))',
-                      r'<a href="\1">\1</a>\2', text)
-
-        log_idx = text.find('\nLog message:\n')
-        if log_idx == -1:
-            log_idx = text.find('\nLog Message:\n')
-        if log_idx != -1:
-            log_idx += len('\nLog message:\n')
-        if log_idx == -1:
-            log_idx = text.find('\nLog:\n')
-            if log_idx != -1:
-                log_idx += len('\nLog:\n')
-            else:
-                log_idx = text.find('Log message') #TODO: Zope3 checkin-specific
-                if log_idx != -1:
-                    log_idx = text.find('\n', log_idx) + 1
-                    # TODO: This is yucky...
-                    text = text.replace('\n-=-\n', '')
-        if log_idx == -1:
-            return '<pre>%s</pre>' % text
-
-        sig_idx = text.rfind(
-                        '\n_______________________________________________')
-        if sig_idx == -1:
-            sig_idx = len(text)
-
-        diff_idx = text.find('\n===')
-        if diff_idx == -1:
-            diff_idx = sig_idx
-
-        status_idx = text.find('\nStatus:\n')
-        if status_idx == -1:
-            if text[diff_idx:diff_idx+5] == "\n====":
-                # Subversion
-                status_idx = text.rfind('\n', 0, diff_idx)
-            else:
-                status_idx = diff_idx
-
-            propchange_idx = text.find('\nProperty changes on:')
-            if propchange_idx != -1 and propchange_idx < status_idx:
-                status_idx = propchange_idx
-
-        assert log_idx <= status_idx <= diff_idx <= sig_idx
-
-        intro = text[:log_idx]
-        log = text[log_idx:status_idx].strip()
-        import_status = text[status_idx:diff_idx]
-        diff = text[diff_idx:sig_idx]
-        sig = text[sig_idx:]
-
-        def empty2nbsp(s):
-            if not s:
-                return '&nbsp;'
-            n = 0
-            while n < len(s) and s[n] == ' ':
-                n += 1
-            if n:
-                return '&nbsp;' * n + s[n:]
-            else:
-                return s
-        log = '<p>%s</p>' % '</p>\n<p>'.join(map(empty2nbsp, log.splitlines()))
-
-        if import_status is None:
-            import_status = ''
-
-        if diff is None:
-            diff = ''
-
-        diff = "\n".join(map(self.mark_whitespace, diff.splitlines()))
-
-        def colorize(style):
-            return r'<div class="%s">\1</div>' % style
-
-        # Unified diff
-        diff = re.sub(r'(?m)^(===.*)$', colorize("file"), diff)
-        diff = re.sub(r'(?m)^(---.*)$', colorize("oldfile"), diff)
-        diff = re.sub(r'(?m)^(\+\+\+.*)$', colorize("newfile"), diff)
-        diff = re.sub(r'(?m)^(@@.*)$', colorize("chunk"), diff)
-        diff = re.sub(r'(?m)^(-.*)$', colorize("old"), diff)
-        diff = re.sub(r'(?m)^(\+.*)$', colorize("new"), diff)
-
-        # Postprocess for Mozilla
-        diff = re.sub('</div>\n', '\n</div>', diff)
-
-        if sig:
-            sig = '<div class="signature">%s</div>' % sig
-
-        text = '<pre>%s</pre><div class="log">%s</div><pre>%s%s%s</pre>' \
-               % (intro, log, import_status, diff, sig)
-        # TODO: find out the actual encoding instead of assuming UTF-8
-        return unicode(text, 'UTF-8', 'replace')
-
-    def mark_whitespace(self, line, tab=('>', '-'), trail='.'):
-        """Mark whitespace in diff lines.
-
-        Suggested values for tab: ('>', '-'), ('&#187;', ' '),
-                                  ('&#187;', '&#x2010;')
-
-        Suggested values for trail: '.', '&#x2423;'
-        """
-        if line == ' ' or (not line.endswith(' ') and '\t' not in line):
-            return line
-        m = re.search('\s+\Z', line)
-        if m:
-            n = m.start()
-            if n == 0:
-                n = 1 # don't highlight the first space in a diff
-            line = '%s<span class="trail">%s</span>' % (line[:n],
-                            line[n:].replace(' ', trail))
-        if '\t' in line:
-            NORMAL, TAG, ENTITY = 0, 1, 2
-            idx = col = 0
-            mode = NORMAL
-            tabs = []
-            for c in line[1:]: # ignore first space in a diff
-                idx += 1
-                if mode == TAG:
-                    if c == '>':
-                        mode = NORMAL
-                elif mode == ENTITY:
-                    if c == ';':
-                        col += 1
-                        mode = NORMAL
-                else:
-                    if c == '<':
-                        mode = TAG
-                    elif c == '&':
-                        mode = ENTITY
-                    elif c == '\t':
-                        width = 8 - (col % 8)
-                        tabs.append((idx, width))
-                        col += width
-                    else:
-                        col += 1
-            if tabs:
-                parts = []
-                last = 0
-                for idx, width in tabs:
-                    parts.append(line[last:idx])
-                    parts.append('<span class="tab">%s%s</span>'
-                                 % (tab[0], tab[1] * (width - 1)))
-                    last = idx + 1
-                parts.append(line[last:])
-                line = "".join(parts)
-        return line


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/message.py
___________________________________________________________________
Name: svn:keywords
   + Id

Deleted: Zope3/branches/srichter-blow-services/src/z3checkins/message_plain.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/z3checkins/message_plain.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/z3checkins/message_plain.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1 +0,0 @@
-<tal:block tal:replace="structure context/full_text" />

Modified: Zope3/branches/srichter-blow-services/src/z3checkins/tests/svn_msg4.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/z3checkins/tests/svn_msg4.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/z3checkins/tests/svn_msg4.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -9,6 +9,9 @@
 
 Changed a comment.
 
+Changed:
+  foo bar baz
+
 -=-
 Modified: Zope3/trunk/src/zope/app/traversing/interfaces.py
 ===================================================================

Copied: Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_browser.py (from rev 28844, Zope3/trunk/src/z3checkins/tests/test_browser.py)


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_browser.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Copied: Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_folder.py (from rev 28844, Zope3/trunk/src/z3checkins/tests/test_folder.py)


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_folder.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_message.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_message.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_message.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -9,66 +9,16 @@
 import os
 import sys
 import time
-from difflib import SequenceMatcher
-from datetime import datetime, timedelta
+from datetime import datetime
 
-from zope.app.tests import ztapi
-from zope.app.tests.placelesssetup import PlacelessSetup
-from zope.publisher.browser import TestRequest
 from zope.interface import Interface, implements
 from zope.interface.verify import verifyObject
-from zope.exceptions import DuplicationError
+from zope.app.container.contained import Contained
 
-from z3checkins.interfaces import IMessage, IMessageContained, \
-        ICheckinMessage, IBookmark, IMessageParser, IMessageArchive
+from z3checkins.interfaces import IMessage, IMessageContained
+from z3checkins.interfaces import ICheckinMessage, IBookmark, IMessageParser
 
 
-class TestFixedTimezone(unittest.TestCase):
-
-    def test_timezone(self):
-        from z3checkins.message import FixedTimezone
-        for tzoff, name in ((30, "+0030"), (-300, "-0500")):
-            tz = FixedTimezone(tzoff)
-            self.assertEquals(tz.tzname(None), name)
-            self.assertEquals(tz.utcoffset(None), timedelta(minutes=tzoff))
-            self.assertEquals(tz.dst(None), timedelta(0))
-
-
-class TestRFCDateTimeFormatter(unittest.TestCase):
-
-    times = ((2003, 4, 2, 12, 33, 41, 3*60, "Wed, 02 Apr 2003 12:33:41 +0300"),
-             (2000, 1, 2, 17, 41, 33, -5*60, "Sun, 02 Jan 2000 17:41:33 -0500"))
-
-    def test_rfctime(self):
-        from z3checkins.message import FixedTimezone, RFCDateTimeFormatter
-        for Y, M, D, h, m, s, tz, res in self.times:
-            dt = datetime(Y, M, D, h, m, s, tzinfo=FixedTimezone(tz))
-            view = RFCDateTimeFormatter(dt, None)
-            self.assertEquals(str(view), res)
-            self.assertEquals(view(), res)
-
-
-class TestISODateTimeFormatter(unittest.TestCase):
-
-    times = ((2003, 4, 2, 12, 33, 41, 3*60, "2003-04-02 09:33"),
-             (2000, 1, 2, 17, 41, 33, -5*60, "2000-01-02 22:41"))
-
-    def test_usertz(self):
-        from z3checkins.message import ISODateTimeFormatter
-        t = time.time()
-        delta = ISODateTimeFormatter.userstz._offset * 60
-        self.assertEquals(time.gmtime(t)[:8], time.localtime(t - delta)[:8])
-
-    def test_isotime(self):
-        from z3checkins.message import FixedTimezone, ISODateTimeFormatter
-        for Y, M, D, h, m, s, tz, res in self.times:
-            dt = datetime(Y, M, D, h, m, s, tzinfo=FixedTimezone(tz))
-            dt -= ISODateTimeFormatter.userstz.utcoffset(None)
-            view = ISODateTimeFormatter(dt, None)
-            self.assertEquals(str(view), res)
-            self.assertEquals(view(), res)
-
-
 class TestCheckinMessage(unittest.TestCase):
 
     def test_find_body_start(self):
@@ -113,7 +63,7 @@
 
     def test_parser1(self):
         from z3checkins.message import CheckinMessageParser
-        from z3checkins.message import FixedTimezone
+        from z3checkins.timeutils import FixedTimezone
         sample_msg1 = open_test_data("sample_msg1.txt")
         sample_msg1_text = sample_msg1.read()
         sample_msg1.seek(0)
@@ -135,7 +85,7 @@
 
     def test_parser2(self):
         from z3checkins.message import CheckinMessageParser
-        from z3checkins.message import FixedTimezone
+        from z3checkins.timeutils import FixedTimezone
         sample_msg2 = open_test_data("sample_msg2.txt")
         sample_msg2_text = sample_msg2.read()
         sample_msg2.seek(0)
@@ -163,7 +113,7 @@
 
     def test_parser_importmsg(self):
         from z3checkins.message import CheckinMessageParser
-        from z3checkins.message import FixedTimezone
+        from z3checkins.timeutils import FixedTimezone
         sample_import_msg = open_test_data("sample_import_msg.txt")
         sample_import_msg_text = sample_import_msg.read()
         sample_import_msg.seek(0)
@@ -185,7 +135,7 @@
 
     def test_parser_simplemsg(self):
         from z3checkins.message import CheckinMessageParser
-        from z3checkins.message import FixedTimezone
+        from z3checkins.timeutils import FixedTimezone
         simple_msg = open_test_data("simple_msg.txt")
         simple_msg_text = simple_msg.read()
         simple_msg.seek(0)
@@ -202,7 +152,7 @@
 
     def test_parser_svnmsg(self):
         from z3checkins.message import CheckinMessageParser
-        from z3checkins.message import FixedTimezone
+        from z3checkins.timeutils import FixedTimezone
         svn_msg = open_test_data("svn_msg.txt")
         svn_msg_text = svn_msg.read()
         svn_msg.seek(0)
@@ -222,7 +172,7 @@
 
     def test_parser_svnmsg_with_split_subject(self):
         from z3checkins.message import CheckinMessageParser
-        from z3checkins.message import FixedTimezone
+        from z3checkins.timeutils import FixedTimezone
         svn_msg2 = open_test_data("svn_msg2.txt")
         svn_msg2_text = svn_msg2.read()
         svn_msg2.seek(0)
@@ -242,7 +192,7 @@
 
     def test_parser_svnmsg_with_rev(self):
         from z3checkins.message import CheckinMessageParser
-        from z3checkins.message import FixedTimezone
+        from z3checkins.timeutils import FixedTimezone
         svn_msg3 = open_test_data("svn_msg3.txt")
         svn_msg3_text = svn_msg3.read()
         svn_msg3.seek(0)
@@ -258,7 +208,7 @@
 
     def test_parser_svnmsg_zope(self):
         from z3checkins.message import CheckinMessageParser
-        from z3checkins.message import FixedTimezone
+        from z3checkins.timeutils import FixedTimezone
         # TODO: The Zope 3 checkin mailing list uses a non-standard format.
         # This test checks the compatibility hacks planted in the code.
         # I hope that they will be removed in the future.
@@ -281,11 +231,11 @@
         self.assertEquals(msg.body, svn_msg4_text.split("\n\n", 1)[1])
 
 
-class MessageStub:
+class MessageStub(Contained):
 
     implements(ICheckinMessage, IMessageContained)
 
-    __name__ = __parent__ = None
+    full_text = "full text of this message"
 
     def __init__(self, data=None, date=None, body=None, log_message='',
                        subject='', message_id="message at id"):
@@ -297,775 +247,6 @@
         self.message_id = message_id
 
 
-class TestMessageContainerAdapter(unittest.TestCase):
-
-    def test_interface(self):
-        from z3checkins.message import MessageContainerAdapter
-        verifyObject(IMessageArchive, MessageContainerAdapter({}))
-
-    def test_len(self):
-        from z3checkins.message import MessageContainerAdapter
-        a = MessageContainerAdapter({})
-        self.assertEquals(len(a), 0)
-        a = MessageContainerAdapter({'1': 2, '3': 'abc'})
-        self.assertEquals(len(a), 0)
-        a = MessageContainerAdapter({'1': 2, '3': 'abc', 4: MessageStub()})
-        self.assertEquals(len(a), 1)
-
-    def test_getitem(self):
-        from z3checkins.message import MessageContainerAdapter
-        a = MessageContainerAdapter({'1': 2, '3': 'abc',
-                                     '4': MessageStub(date=1, message_id='1'),
-                                     '5': MessageStub(date=4, message_id='2'),
-                                     '6': MessageStub(date=3, message_id='3'),
-                                     '7': MessageStub(date=2, message_id='4')})
-        self.assertEquals(a[0].message_id, '1')
-        self.assertEquals(a[1].message_id, '4')
-        self.assertEquals(a[2].message_id, '3')
-        self.assertEquals(a[3].message_id, '2')
-        self.assertEquals(a[-1].message_id, '2')
-        self.assertRaises(IndexError, a.__getitem__, 4)
-        self.assertRaises(IndexError, a.__getitem__, -5)
-        self.assertRaises(TypeError, a.__getitem__, 'xyzzy')
-        self.assertRaises(TypeError, a.__getitem__, None)
-        self.assertEquals(len(a[1:3]), 2)
-        self.assertEquals(len(a[1:-1]), 2)
-        self.assertEquals(len(a[3:1]), 0)
-
-    def test_iter(self):
-        from z3checkins.message import MessageContainerAdapter
-        a = MessageContainerAdapter({'1': 2, '3': 'abc',
-                                     '4': MessageStub(date=1, message_id='1'),
-                                     '5': MessageStub(date=4, message_id='2'),
-                                     '6': MessageStub(date=3, message_id='3'),
-                                     '7': MessageStub(date=2, message_id='4')})
-        b = [x.message_id for x in a]
-        self.assertEquals(b, ['1', '4', '3', '2'])
-        self.assert_(MessageStub(message_id='5') not in a)
-        self.assert_(a.context['6'] in a)
-
-    def test_index(self):
-        from z3checkins.message import MessageContainerAdapter
-        m1 = MessageStub(date=1, message_id='1')
-        m2 = MessageStub(date=4, message_id='2')
-        m3 = MessageStub(date=3, message_id='3')
-        m4 = MessageStub(date=2, message_id='4')
-        a = MessageContainerAdapter({'1': 2, '3': 'abc',
-                                     '4': m1, '5': m2, '6': m3, '7': m4})
-        self.assertEquals(a.index(m1), 0)
-        self.assertEquals(a.index(m4), 1)
-        self.assertEquals(a.index(m3), 2)
-        self.assertEquals(a.index(m2), 3)
-        self.assertRaises(ValueError, a.index, MessageStub)
-
-
-class ParserStub:
-
-    implements(IMessageParser)
-
-    def parse(self, data):
-        if hasattr(data, 'read'):
-            full_text = data.read()
-        else:
-            full_text = data
-
-        message_id = "message at id"
-        id_lines = filter(lambda s: s.lower().startswith("message-id: "),
-                full_text.splitlines())
-        if len(id_lines) == 1:
-            message_id = id_lines[0][len("message-id: "):]
-        return MessageStub(data=full_text, message_id=message_id)
-
-class AddingStub:
-
-    def __init__(self):
-        self.added = []
-
-    def add(self, obj):
-        # ignore duplicates happening with default messages
-        if obj.message_id != "message at id":
-            for message in self.added:
-                if message.message_id == obj.message_id:
-                    raise DuplicationError()
-        self.added.append(obj)
-
-class TestMessageUpload(PlacelessSetup, unittest.TestCase):
-
-    def setUp(self):
-        PlacelessSetup.setUp(self)
-        ztapi.provideUtility(IMessageParser, ParserStub())
-
-    def test_createAndAdd(self):
-        from z3checkins.message import MessageUpload
-        view = MessageUpload()
-        view.context = AddingStub()
-        view.add = view.context.add
-        added = view.context.added
-        self.assertEquals(len(added), 0)
-        view.createAndAdd({})
-        self.assertEquals(len(added), 0)
-        view.createAndAdd({'data': 'Ipsum suum'})
-        self.assertEquals(len(added), 1)
-        self.assertEquals(added[0].__class__, MessageStub)
-        self.assertEquals(added[0].message_id, "message at id")
-        self.assertEquals(added[0].data, "Ipsum suum")
-
-    def test_createAndAdd_mbox(self):
-        from z3checkins.message import MessageUpload
-        view = MessageUpload()
-        view.context = AddingStub()
-        view.add = view.context.add
-        added = view.context.added
-        data = open_test_data('mbox.txt').read()
-        self.assertEquals(len(added), 0)
-        view.createAndAdd({'data': data})
-        self.assertEquals(len(added), 4)
-        for message in added:
-            self.assertEquals(message.__class__, MessageStub)
-        self.assertEquals(added[0].data.count("Steve Alexander"), 1)
-        self.assertEquals(added[1].data.count("Steve Alexander"), 1)
-        self.assertEquals(added[2].data.count("Tim Peters"), 1)
-        self.assertEquals(added[3].data.count("Tim Peters"), 1)
-
-    def test_createAndAdd_mbox_with_dupes(self):
-        from z3checkins.message import MessageUpload
-        view = MessageUpload()
-        view.context = AddingStub()
-        view.add = view.context.add
-        added = view.context.added
-        data = open_test_data('mbox_with_dupes.txt').read()
-        self.assertEquals(len(added), 0)
-        view.createAndAdd({'data': data})
-        self.assertEquals(len(added), 2)
-        for message in added:
-            self.assertEquals(message.__class__, MessageStub)
-        self.assertEquals(added[0].data.count("Steve Alexander"), 1)
-        self.assertEquals(added[1].data.count("Tim Peters"), 1)
-
-
-class IUnitTestPresentation(Interface):
-    pass
-
-class MessageTestView:
-    def __init__(self, context, request):
-        self.context = context
-    def __call__(self, same_as_previous=False):
-        result = 'msg%d' % self.context.date
-        if same_as_previous:
-            result += '*'
-        return result + '\n'
-
-class BookmarkTestView:
-    def __init__(self, context, request):
-        self.context = context
-    def __call__(self, same_as_previous=False):
-        return '-\n'
-
-class RequestStub(dict):
-
-    _cookies = ()
-
-    def __init__(self, **kw):
-        super(RequestStub, self).__init__()
-        self.update(kw)
-        self.response = self
-
-    def setCookie(self, name, value, **kw):
-        self._cookies += (name, value, kw)
-
-    def getPresentationType(self):
-        return IUnitTestPresentation
-
-
-class TestContainerView(PlacelessSetup, unittest.TestCase):
-
-    def setUp(self):
-        from z3checkins.message import MessageContainerAdapter
-        PlacelessSetup.setUp(self)
-        ztapi.provideAdapter(None, IMessageArchive, MessageContainerAdapter)
-
-    def test_checkins(self):
-        from z3checkins.message import ContainerView
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(), 'z': MessageStub(date=1),
-                        'a': MessageStub(date=2), 'c': MessageStub(date=3)}
-        view.request = {}
-        res = view.checkins()
-        self.assertEquals(len(res), 3)
-        self.assertEquals(view.count(), 3)
-        self.assertEquals(res[0].date, 3)
-        self.assertEquals(res[1].date, 2)
-        self.assertEquals(res[2].date, 1)
-
-    def test_checkins_limited(self):
-        from z3checkins.message import ContainerView
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(), 'z': MessageStub(date=1),
-                        'a': MessageStub(date=2), 'c': MessageStub(date=3)}
-        view.request = {}
-        res = view.checkins(size=2)
-        self.assertEquals(len(res), 2)
-        self.assertEquals(res[0].date, 3)
-        self.assertEquals(res[1].date, 2)
-
-        res = view.checkins(start=1, size=3)
-        self.assertEquals(len(res), 2)
-        self.assertEquals(res[0].date, 2)
-        self.assertEquals(res[1].date, 1)
-
-    def test_checkins_bookmarks(self):
-        from z3checkins.message import ContainerView
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(), 'z': MessageStub(date=1),
-                        'a': MessageStub(date=2), 'c': MessageStub(date=4)}
-        view.request = {}
-        view.bookmarks = lambda: [3]
-        res = view.checkins()
-        self.assertEquals(len(res), 4)
-        self.assertEquals(res[0].date, 4)
-        self.assert_(IBookmark.providedBy(res[1]))
-        self.assertEquals(res[2].date, 2)
-        self.assertEquals(res[3].date, 1)
-
-        view.bookmarks = lambda: [2]
-        res = view.checkins()
-        self.assertEquals(len(res), 4)
-        self.assertEquals(res[0].date, 4)
-        self.assert_(IBookmark.providedBy(res[1]))
-        self.assertEquals(res[2].date, 2)
-        self.assertEquals(res[3].date, 1)
-
-        view.bookmarks = lambda: [0, 1, 2, 3, 4, 5, 6, 2, 3, 1]
-        res = view.checkins()
-        self.assertEquals(len(res), 5)
-        self.assertEquals(res[0].date, 4)
-        self.assert_(IBookmark.providedBy(res[1]))
-        self.assertEquals(res[2].date, 2)
-        self.assert_(IBookmark.providedBy(res[3]))
-        self.assertEquals(res[4].date, 1)
-
-        res = view.checkins(start=1, size=1)
-        self.assertEquals(len(res), 3)
-        self.assert_(IBookmark.providedBy(res[0]))
-        self.assertEquals(res[1].date, 2)
-        self.assert_(IBookmark.providedBy(res[2]))
-
-    def test_bookmarks(self):
-        from z3checkins.message import ContainerView
-        from z3checkins.message import FixedTimezone
-        view = ContainerView()
-        view.request = {}
-        self.assertEquals(view.bookmarks(), [])
-        view.request = {'bookmarks': '2003-01-04T21:33:04-05:00'}
-        self.assertEquals(view.bookmarks(),
-                          [datetime(2003, 01, 04, 21, 33, 04,
-                                    tzinfo=FixedTimezone(-5*60))])
-        view.request = {'bookmarks': '2003-01-04T21:33:04-05:00 '
-                                     'errors are ignored '
-                                     '2004-05-06T07:08:09+10:00 '
-                                     '2002-02-02T02:02:02+02:00 '
-                                     '2005-02-29T07:08:09+10:00'}
-        self.assertEquals(view.bookmarks(),
-                          [datetime(2003, 1, 4, 21, 33, 4,
-                                    tzinfo=FixedTimezone(-5*60)),
-                           datetime(2004, 5, 6, 7, 8, 9,
-                                    tzinfo=FixedTimezone(10*60)),
-                           datetime(2002, 2, 2, 2, 2, 2,
-                                    tzinfo=FixedTimezone(2*60))])
-
-    def test_placeBookmark_empty_archive(self):
-        from z3checkins.message import ContainerView
-        from z3checkins.message import FixedTimezone
-        view = ContainerView()
-        view.context = {}
-        view.request = RequestStub()
-        view.placeBookmark()
-        self.assertEquals(view.request._cookies, ())
-
-    def test_placeBookmark_empty_bookmarks(self):
-        from z3checkins.message import ContainerView
-        from z3checkins.message import FixedTimezone
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(),
-                        'z': MessageStub(date=datetime(2003, 1, 4, 21, 33, 4,
-                                                tzinfo=FixedTimezone(-5*60)))}
-        view.request = RequestStub()
-        view.placeBookmark()
-        self.assertEquals(view.request._cookies,
-                          ('bookmarks', '2003-01-04T21:33:04-05:00',
-                           {'max_age': 31536000}))
-
-    def test_placeBookmark_not_at_start(self):
-        from z3checkins.message import ContainerView
-        from z3checkins.message import FixedTimezone
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(),
-                        'z': MessageStub(date=datetime(2003, 1, 4, 21, 33, 4,
-                                                tzinfo=FixedTimezone(-5*60)))}
-        view.request = RequestStub(start=1)
-        view.placeBookmark()
-        self.assertEquals(view.request._cookies, ())
-
-    def test_placeBookmark_no_new_checkins(self):
-        from z3checkins.message import ContainerView
-        from z3checkins.message import FixedTimezone
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(),
-                        'z': MessageStub(date=datetime(2003, 1, 4, 21, 33, 4,
-                                                tzinfo=FixedTimezone(-5*60)))}
-        view.request = RequestStub(bookmarks='2003-01-04T21:33:04-05:00 '
-                                             'errors are ignored '
-                                             '2002-02-02T02:02:02+02:00')
-        view.placeBookmark()
-        self.assertEquals(view.request._cookies, ())
-
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(),
-                        'z': MessageStub(date=datetime(2003, 1, 4, 21, 33, 4,
-                                                tzinfo=FixedTimezone(-5*60)))}
-        view.request = RequestStub(bookmarks='2004-01-04T21:33:04-05:00 '
-                                             'errors are ignored '
-                                             '2002-02-02T02:02:02+02:00')
-        view.placeBookmark()
-        self.assertEquals(view.request._cookies, ())
-
-    def test_placeBookmark_new_checkins(self):
-        from z3checkins.message import ContainerView
-        from z3checkins.message import FixedTimezone
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(),
-                        'z': MessageStub(date=datetime(2003, 1, 4, 21, 33, 4,
-                                                tzinfo=FixedTimezone(-5*60))),
-                        'w': MessageStub(date=datetime(2003, 1, 6, 22, 33, 44,
-                                                tzinfo=FixedTimezone(+3*60)))}
-        view.request = RequestStub(bookmarks='2003-01-04T21:33:04-05:00 '
-                                             'errors are ignored '
-                                             '2002-02-02T02:02:02+02:00')
-        view.placeBookmark()
-        self.assertEquals(view.request._cookies,
-                          ('bookmarks', '2002-02-02T02:02:02+02:00 '
-                                        '2003-01-04T21:33:04-05:00 '
-                                        '2003-01-06T22:33:44+03:00',
-                           {'max_age': 31536000}))
-
-    def test_placeBookmark_new_checkins_overflow(self):
-        from z3checkins.message import ContainerView
-        from z3checkins.message import FixedTimezone
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(),
-                        'z': MessageStub(date=datetime(2003, 1, 4, 21, 33, 4,
-                                                tzinfo=FixedTimezone(-5*60))),
-                        'w': MessageStub(date=datetime(2003, 1, 6, 22, 33, 44,
-                                                tzinfo=FixedTimezone(+3*60)))}
-        view.request = RequestStub(bookmarks='2003-01-04T21:33:04-05:00 '
-                                             'errors are ignored '
-                                             '2002-01-01T02:02:02+02:00 '
-                                             '2002-01-02T02:02:02+02:00 '
-                                             '2002-01-03T02:02:02+02:00 '
-                                             '2002-01-04T02:02:02+02:00 '
-                                             '2002-02-02T02:02:02+02:00 ')
-        view.placeBookmark()
-        self.assertEquals(view.request._cookies,
-                          ('bookmarks', '2002-01-03T02:02:02+02:00 '
-                                        '2002-01-04T02:02:02+02:00 '
-                                        '2002-02-02T02:02:02+02:00 '
-                                        '2003-01-04T21:33:04-05:00 '
-                                        '2003-01-06T22:33:44+03:00',
-                           {'max_age': 31536000}))
-
-    def test_renderCheckins(self):
-        from z3checkins.message import ContainerView
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(),
-                        'z': MessageStub(date=1, log_message='xxx'),
-                        'a': MessageStub(date=2, log_message='xxx'),
-                        'c': MessageStub(date=3, log_message='yyy')}
-        view.request = TestRequest()
-        view.index = view
-        ztapi.browserView(ICheckinMessage, 'html', MessageTestView)
-
-        res = view.renderCheckins()
-        self.assertEquals(res, 'msg3\nmsg2\nmsg1*\n')
-        res = view.renderCheckins(start=1, size=1)
-        self.assertEquals(res, 'msg2\n')
-
-    def test_renderCheckins_with_bookmarks(self):
-        from z3checkins.message import ContainerView
-        view = ContainerView()
-        view.context = {'x': 123, 'y': object(),
-                        'z': MessageStub(date=1, log_message='xxx'),
-                        'a': MessageStub(date=2, log_message='xxx'),
-                        'c': MessageStub(date=3, log_message='yyy')}
-        view.request = TestRequest()
-        view.index = view
-        view.bookmarks = lambda: [1]
-        ztapi.browserView(ICheckinMessage, 'html', MessageTestView)
-        ztapi.browserView(IBookmark, 'html', BookmarkTestView)
-
-        res = view.renderCheckins()
-        self.assertEquals(res, 'msg3\nmsg2\n-\nmsg1*\n')
-
-
-def diff(a, b):
-    "Compare the differences of two sequences of strings"
-
-    if isinstance(a, (str, unicode)): a = a.splitlines()
-    if isinstance(b, (str, unicode)): b = b.splitlines()
-
-    diff = []
-    def dump(tag, x, lo, hi, diff=diff):
-        for i in xrange(lo, hi):
-            diff.append(tag + x[i])
-
-    differ = SequenceMatcher(a=a, b=b)
-    for tag, alo, ahi, blo, bhi in differ.get_opcodes():
-        if tag == 'replace':
-            dump('-', a, alo, ahi)
-            dump('+', 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 "\n".join(diff)
-
-class TestCheckinMessageView(PlacelessSetup, unittest.TestCase):
-
-    def setUp(self):
-        PlacelessSetup.setUp(self)
-        from z3checkins.message import MessageContainerAdapter
-        ztapi.provideAdapter(None, IMessageArchive, MessageContainerAdapter)
-
-    def test_body_strange(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        view.context = MessageStub(body="Something & strange")
-        self.assertEquals(view.body(), "<pre>Something &amp; strange</pre>")
-
-    def test_body(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        view.context = MessageStub(body="Blah blah\n"
-                          "blah\n"
-                          "Log message:\n"
-                          "Blurb blurb\n"
-                          "blurb.\n"
-                          "\n"
-                          "=== foo.py: 1.2 -> 1.3 ===\n"
-                          "--- foo.py:1.2\tdatetime\n"
-                          "+++ foo.py\tdatetime\n"
-                          "@@@ -123,4 +567,8 @@@\n"
-                          " fwoosh <>&\"\n"
-                          "-fouoww\n"
-                          "+fruuuh\n"
-                          " fargle\n"
-                          "_______________________________________________\n"
-                          "signature\n")
-        result = view.body()
-        expected = ('<pre>Blah blah\n'
-                    'blah\n'
-                    'Log message:\n'
-                    '</pre>'
-                    '<div class="log">'
-                    '<p>Blurb blurb</p>\n'
-                    '<p>blurb.</p>'
-                    '</div>'
-                    '<pre>\n'
-                    '<div class="file">=== foo.py: 1.2 -&gt; 1.3 ===\n</div>'
-                    '<div class="oldfile">--- foo.py:1.2<span class="tab">>--</span>datetime\n</div>'
-                    '<div class="newfile">+++ foo.py<span class="tab">>------</span>datetime\n</div>'
-                    '<div class="chunk">@@@ -123,4 +567,8 @@@\n</div>'
-                    ' fwoosh &lt;&gt;&amp;&quot;\n'
-                    '<div class="old">-fouoww\n</div>'
-                    '<div class="new">+fruuuh\n</div>'
-                    ' fargle'
-                    '<div class="signature">\n'
-                    '_______________________________________________\n'
-                    'signature\n'
-                    '</div>'
-                    '</pre>')
-        self.assertEquals(result, expected, diff(expected, result))
-
-    def test_body_svn(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        view.context = MessageStub(body="""\
-Author: mg
-Date: 2003-09-09 21:21:09 +0300 (Tue, 09 Sep 2003)
-New Revision: 15
-
-Added:
-   trunk/schooltool/schooltool/ftests/
-   trunk/schooltool/schooltool/ftests/__init__.py
-   trunk/schooltool/schooltool/ftests/test_rest.py
-   trunk/schooltool/schooltool/main.py
-   trunk/schooltool/schooltool/tests/test_main.py
-Removed:
-   trunk/schooltool/schooltool/tests/test_rest.py
-Modified:
-   trunk/schooltool/schooltool/tests/__init__.py
-Log:
-First prototype of SchoolTool HTTP server that serves RESTful pages, complete
-with unit and functional tests.
-
-
-
-Added: trunk/schooltool/schooltool/ftests/__init__.py
-===================================================================
---- trunk/schooltool/schooltool/ftests/__init__.py	2003-09-09 18:03:43 UTC (rev 14)
-+++ trunk/schooltool/schooltool/ftests/__init__.py	2003-09-09 18:21:09 UTC (rev 15)
-@@ -0,0 +1,21 @@
-+#
-+# SchoolTool - common information systems platform for school administration
-"""
-)
-        result = view.body()
-        expected = ("""\
-<pre>Author: mg
-Date: 2003-09-09 21:21:09 +0300 (Tue, 09 Sep 2003)
-New Revision: 15
-
-Added:
-   trunk/schooltool/schooltool/ftests/
-   trunk/schooltool/schooltool/ftests/__init__.py
-   trunk/schooltool/schooltool/ftests/test_rest.py
-   trunk/schooltool/schooltool/main.py
-   trunk/schooltool/schooltool/tests/test_main.py
-Removed:
-   trunk/schooltool/schooltool/tests/test_rest.py
-Modified:
-   trunk/schooltool/schooltool/tests/__init__.py
-Log:
-</pre><div class="log"><p>First prototype of SchoolTool HTTP server that serves RESTful pages, complete</p>
-<p>with unit and functional tests.</p></div><pre>
-Added: trunk/schooltool/schooltool/ftests/__init__.py
-<div class="file">===================================================================
-</div><div class="oldfile">--- trunk/schooltool/schooltool/ftests/__init__.py<span class="tab">>------</span>2003-09-09 18:03:43 UTC (rev 14)
-</div><div class="newfile">+++ trunk/schooltool/schooltool/ftests/__init__.py<span class="tab">>------</span>2003-09-09 18:21:09 UTC (rev 15)
-</div><div class="chunk">@@ -0,0 +1,21 @@
-</div><div class="new">+#
-</div><div class="new">+# SchoolTool - common information systems platform for school administration</div></pre>"""
-                    )
-        self.assertEquals(result, expected, diff(expected, result))
-
-    def test_body_crlf(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        view.context = MessageStub(body="Blah blah\r\n"
-                          "blah\r\n"
-                          "Log message:\r\n"
-                          "Blurb blurb\r\n"
-                          "blurb.\r\n"
-                          "\r\n"
-                          "=== foo.py: 1.2 -> 1.3 ===\r\n"
-                          "--- foo.py:1.2\tdatetime\r\n"
-                          "+++ foo.py\tdatetime\r\n"
-                          "@@@ -123,4 +567,8 @@@\r\n"
-                          " fwoosh <>&\"\r\n"
-                          "-fouoww  \r\n"
-                          "+fruuuh\r\n"
-                          " fargle\r\n"
-                          "   \r\n"
-                          " \r\n"
-                          "_______________________________________________\r\n"
-                          "signature\r\n")
-        result = view.body()
-        expected = ('<pre>Blah blah\n'
-                    'blah\n'
-                    'Log message:\n'
-                    '</pre>'
-                    '<div class="log">'
-                    '<p>Blurb blurb</p>\n'
-                    '<p>blurb.</p>'
-                    '</div>'
-                    '<pre>\n'
-                    '<div class="file">=== foo.py: 1.2 -&gt; 1.3 ===\n</div>'
-                    '<div class="oldfile">--- foo.py:1.2<span class="tab">>--</span>datetime\n</div>'
-                    '<div class="newfile">+++ foo.py<span class="tab">>------</span>datetime\n</div>'
-                    '<div class="chunk">@@@ -123,4 +567,8 @@@\n</div>'
-                    ' fwoosh &lt;&gt;&amp;&quot;\n'
-                    '<div class="old">-fouoww<span class="trail">..</span>\n</div>'
-                    '<div class="new">+fruuuh\n</div>'
-                    ' fargle\n'
-                    ' <span class="trail">..</span>\n'
-                    ' '
-                    '<div class="signature">\n'
-                    '_______________________________________________\n'
-                    'signature\n'
-                    '</div>'
-                    '</pre>')
-        self.assertEquals(result, expected, diff(expected, result))
-
-    def test_body_nosig(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        view.context = MessageStub(body="Blah blah\n"
-                          "blah\n"
-                          "Log message:\n"
-                          "Blurb blurb\n"
-                          "blurb.\n"
-                          "\n"
-                          "=== foo.py: 1.2 -> 1.3 ===\n"
-                          "--- foo.py:1.2\tdatetime\n"
-                          "+++ foo.py\tdatetime\n"
-                          "@@@ -123,4 +567,8 @@@\n"
-                          " fwoosh <>&\"\n"
-                          "-fouoww\n"
-                          "+fruuuh\n"
-                          " fargle")
-        result = view.body()
-        expected = ('<pre>Blah blah\n'
-                    'blah\n'
-                    'Log message:\n'
-                    '</pre>'
-                    '<div class="log">'
-                    '<p>Blurb blurb</p>\n'
-                    '<p>blurb.</p>'
-                    '</div>'
-                    '<pre>\n'
-                    '<div class="file">=== foo.py: 1.2 -&gt; 1.3 ===\n</div>'
-                    '<div class="oldfile">--- foo.py:1.2<span class="tab">>--</span>datetime\n</div>'
-                    '<div class="newfile">+++ foo.py<span class="tab">>------</span>datetime\n</div>'
-                    '<div class="chunk">@@@ -123,4 +567,8 @@@\n</div>'
-                    ' fwoosh &lt;&gt;&amp;&quot;\n'
-                    '<div class="old">-fouoww\n</div>'
-                    '<div class="new">+fruuuh\n</div>'
-                    ' fargle'
-                    '</pre>')
-        self.assertEquals(result, expected, diff(expected, result))
-
-    def test_body_importmsg(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        view.context = MessageStub(body="Blah blah\n"
-                          "blah\n"
-                          "Log message:\n"
-                          "Blurb blurb\n"
-                          "blurb.\n"
-                          "\n"
-                          "Status:\n"
-                          "\n"
-                          "Vendor Tag:\tnovendor\n"
-                          "Release Tags:\tstart\n"
-                          "\n"
-                          "N foo/bar.py\n"
-                          "N foo/baz.pt\n"
-                          "\n"
-                          "No conflicts created by this import\n")
-        result = view.body()
-        expected = ('<pre>Blah blah\n'
-                    'blah\n'
-                    'Log message:\n'
-                    '</pre>'
-                    '<div class="log">'
-                    '<p>Blurb blurb</p>\n'
-                    '<p>blurb.</p>'
-                    '</div>'
-                    '<pre>\n'
-                    'Status:\n'
-                    '\n'
-                    'Vendor Tag:\tnovendor\n'
-                    'Release Tags:\tstart\n'
-                    '\n'
-                    'N foo/bar.py\n'
-                    'N foo/baz.pt\n'
-                    '\n'
-                    'No conflicts created by this import\n'
-                    '</pre>')
-        self.assertEquals(result, expected, diff(expected, result))
-
-    def test_markwitespace(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        m = view.mark_whitespace
-        self.assertEquals(m(''), '')
-        self.assertEquals(m('xyzzy'), 'xyzzy')
-        self.assertEquals(m('  '), ' <span class="trail">.</span>')
-        self.assertEquals(m('  xy z  '), '  xy z<span class="trail">..</span>')
-        self.assertEquals(m('  xy z \t '), '  xy z<span class="trail">.<span class="tab">>-</span>.</span>')
-        self.assertEquals(m(' \t|'), ' <span class="tab">>-------</span>|')
-        self.assertEquals(m(' |\t|'), ' |<span class="tab">>------</span>|')
-        self.assertEquals(m(' xxxxxx|\t|'), ' xxxxxx|<span class="tab">></span>|')
-        self.assertEquals(m(' x<tag\t>xxxxx|\t|'), ' x<tag\t>xxxxx|<span class="tab">></span>|')
-        self.assertEquals(m(' x&ent;xxxx|\t|'), ' x&ent;xxxx|<span class="tab">></span>|')
-
-    def test_urls(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        prefixes = ['', 'A link: ', '(', '<']
-        suffixes = ['', ' etc.', ')', '>', '.', ',', '\n']
-        urls = ['http://www.example.com', 'https://www.example.com', 'http://localhost:8080/foo?q=a&w=b']
-
-        def quote(text):
-            return (text.replace('&', '&amp;')
-                        .replace('<', '&lt;')
-                        .replace('>', '&gt;')
-                        .replace('"', '&quot;'))
-
-        for prefix in prefixes:
-            for link in urls:
-                for suffix in suffixes:
-                    view.context = MessageStub(body="%s%s%s" % (prefix, link, suffix))
-                    prefix = quote(prefix)
-                    link = quote(link)
-                    suffix = quote(suffix)
-                    self.assertEquals(view.body(), '<pre>%s<a href="%s">%s</a>%s</pre>' % (prefix, link, link, suffix))
-
-    def test_navigation_no_archive(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        view.context = MessageStub()
-        self.assertEquals(view.first(), None)
-        self.assertEquals(view.last(), None)
-        self.assertEquals(view.next(), None)
-        self.assertEquals(view.previous(), None)
-
-    def test_navigation_empty_archive(self):
-        from z3checkins.message import CheckinMessageView
-        view = CheckinMessageView()
-        view.context = MessageStub()
-        view.context.__parent__ = {'1': 2}
-        self.assertEquals(view.first(), None)
-        self.assertEquals(view.last(), None)
-        self.assertEquals(view.next(), None)
-        self.assertEquals(view.previous(), None)
-
-    def test_navigation(self):
-        from z3checkins.message import CheckinMessageView
-        m1 = MessageStub(date=1, message_id='1')
-        m2 = MessageStub(date=2, message_id='2')
-        m3 = MessageStub(date=3, message_id='3')
-        m4 = MessageStub(date=4, message_id='4')
-
-        folder = {'1': 2, '3': 'abc', '4': m1, '5': m2, '6': m3, '7': m4}
-        view = CheckinMessageView()
-        view.context = m3
-        m3.__parent__ = folder
-        self.assertEquals(view.first(), m1)
-        self.assertEquals(view.last(), m4)
-        self.assertEquals(view.next(), m4)
-        self.assertEquals(view.previous(), m2)
-
-        view = CheckinMessageView()
-        view.context = m1
-        m1.__parent__ = folder
-        self.assertEquals(view.first(), m1)
-        self.assertEquals(view.last(), m4)
-        self.assertEquals(view.next(), m2)
-        self.assertEquals(view.previous(), None)
-
-        view = CheckinMessageView()
-        view.context = m4
-        m4.__parent__ = folder
-        self.assertEquals(view.first(), m1)
-        self.assertEquals(view.last(), m4)
-        self.assertEquals(view.next(), None)
-        self.assertEquals(view.previous(), m3)
-
-
 class TestMessageNameChooser(unittest.TestCase):
 
     def test_chooseName(self):
@@ -1119,15 +300,8 @@
 
 def test_suite():
     suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(TestFixedTimezone))
-    suite.addTest(unittest.makeSuite(TestRFCDateTimeFormatter))
-    suite.addTest(unittest.makeSuite(TestISODateTimeFormatter))
     suite.addTest(unittest.makeSuite(TestCheckinMessage))
     suite.addTest(unittest.makeSuite(TestCheckinMessageParser))
-    suite.addTest(unittest.makeSuite(TestMessageContainerAdapter))
-    suite.addTest(unittest.makeSuite(TestMessageUpload))
-    suite.addTest(unittest.makeSuite(TestContainerView))
-    suite.addTest(unittest.makeSuite(TestCheckinMessageView))
     suite.addTest(unittest.makeSuite(TestMessageNameChooser))
     suite.addTest(unittest.makeSuite(TestMessageSized))
     return suite

Copied: Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_timeutils.py (from rev 28844, Zope3/trunk/src/z3checkins/tests/test_timeutils.py)


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/tests/test_timeutils.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Copied: Zope3/branches/srichter-blow-services/src/z3checkins/timeutils.py (from rev 28844, Zope3/trunk/src/z3checkins/timeutils.py)


Property changes on: Zope3/branches/srichter-blow-services/src/z3checkins/timeutils.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/srichter-blow-services/src/zope/app/apidoc/classmodule/menu.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/apidoc/classmodule/menu.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/apidoc/classmodule/menu.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -17,7 +17,7 @@
     <div>
       <span i18n:translate="">Class Finder:</span> <br/>
       <i i18n:translate="">(Enter partial Python path)</i></div>
-    <form action="menu.html" method="POST">
+    <form action="menu.html" method="post">
       <input type="text" name="path" 
              style="font-size: 80%; width=95%" />
       <input type="submit" name="SUBMIT" value="Find" 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/applicationcontrol/browser/zodbcontrol.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/applicationcontrol/browser/zodbcontrol.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/applicationcontrol/browser/zodbcontrol.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -19,7 +19,7 @@
          tal:condition="status"
          tal:content="status" />
 
-      <form action="." method="POST" tal:attributes="action request/URL">
+      <form action="." method="post" tal:attributes="action request/URL">
         <div class="row">
           <div class="label" i18n:translate="">Keep up to:</div>
           <div class="view">

Modified: Zope3/branches/srichter-blow-services/src/zope/app/catalog/browser/advanced.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/catalog/browser/advanced.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/catalog/browser/advanced.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -17,7 +17,7 @@
     </tr>
 </table>
 
-<form method="POST" action="reindex.html">
+<form method="post" action="reindex.html">
     <input type="submit" value="Reindex"
            i18n:attributes="value reindex-button"/>
 </form>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -19,6 +19,14 @@
 import zope.component
 from zope.app import zapi
 
+##############################################################################
+# BBB: Goes away in 3.3
+import bbb
+bbb.install()
+del bbb
+##############################################################################
+
+
 _marker = object()
 
 def getNextSiteManager(context):

Added: Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -0,0 +1,8 @@
+
+
+def install():
+
+    import sys
+    import localservice
+
+    sys.modules['zope.app.component.localservice'] = localservice

Added: Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/localservice.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/localservice.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/bbb/localservice.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -0,0 +1,16 @@
+# BBB: Goes away in 3.3
+from zope.component.exceptions import ComponentLookupError
+
+
+def queryNextService(context, name, default=None):
+    try:
+        return getNextService(context, name)
+    except ComponentLookupError:
+        return default
+
+def getNextService(context, name):
+    """Returns the service with the given name from the next service manager.
+    """
+    from zope.component.bbb.service import IService
+    from zope.app.component import getNextSiteManager
+    return getNextSiteManager(context).queryUtility(IService, name)

Modified: Zope3/branches/srichter-blow-services/src/zope/app/component/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/component/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/component/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -4,8 +4,8 @@
 
 <!-- Registration Managemenet -->
 
-  <!-- BBB: Backward-compatibility: 12/07/2004 -->
-  <!-- XXX: temp. deactivated
+  <!-- BBB: Gone with 3.3 -->
+  <!-- XXX
   <zope:view
       for="zope.app.component.interfaces.registration.IComponentPath"
       type="zope.publisher.interfaces.browser.IBrowserRequest"
@@ -21,7 +21,7 @@
       factory=".registration.ComponentPathWidget"
       permission="zope.Public"
       />
-   -->
+  -->
   <!-- BBB: End of backward-compatibility block -->
 
   <zope:view

Modified: Zope3/branches/srichter-blow-services/src/zope/app/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -48,7 +48,7 @@
   <include package=".zopeappgenerations" />
 
   <!-- Services -->
-  <!-- XXX: include package="zope.app.pluggableauth" /-->
+  <include package="zope.app.pluggableauth" />
   <include package="zope.app.principalannotation" />
 
   <!-- Utilities -->

Modified: Zope3/branches/srichter-blow-services/src/zope/app/container/browser/contents.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/container/browser/contents.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/container/browser/contents.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -175,7 +175,6 @@
     </form>
 
     <script type="text/javascript"><!--
-        prettydump('focus', LG_INFO);
         if (document.containerContentsForm.new_value)
 	        document.containerContentsForm.new_value.focus();
         //-->

Modified: Zope3/branches/srichter-blow-services/src/zope/app/container/browser/metaconfigure.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/container/browser/metaconfigure.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/container/browser/metaconfigure.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -15,6 +15,7 @@
 
 $Id$
 """
+
 __docformat__ = 'restructuredtext'
 
 from zope.interface import Interface
@@ -25,35 +26,33 @@
 from zope.app.i18n import ZopeMessageIDFactory as _
 from zope.app.security.fields import Permission
 
+
 class IContainerViews(Interface):
     """Define several container views for an `IContainer` implementation."""
 
     for_ = GlobalInterface(
         title=u"The interface this containerViews are for.",
         description=u"""
-        The containerViews will be for all objects that implement this
-        interface.""",
-        required=True
-        )
-     
+        The containerViews will be available for all objects that
+        implement this interface.
+        """,
+        required=True)
+
     contents = Permission(
         title=u"The permission needed for content page.",
-        required=False,
-        )
+        required=False)
 
     index = Permission(
         title=u"The permission needed for index page.",
-        required=False,
-        )
+        required=False)
 
     add = Permission(
         title=u"The permission needed for add page.",
-        required=False,
-        )
+        required=False)
 
 
 def containerViews(_context, for_, contents=None, add=None, index=None):
-    """Create an container view for a given content type"""
+    """Set up container views for a given content type."""
 
     if for_ is None:
             raise ValueError("A for interface must be specified.")
@@ -63,9 +62,9 @@
         page(_context, name='contents.html', permission=contents,
              for_=for_, class_=Contents, attribute='contents',
              menu=zmi_views, title=_('Contents'))
-            
+
     if index is not None:
-        page(_context, name='index.html', permission=index, for_=for_, 
+        page(_context, name='index.html', permission=index, for_=for_,
              class_=Contents, attribute='index')
 
     if add is not None:

Modified: Zope3/branches/srichter-blow-services/src/zope/app/dav/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/dav/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/dav/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -108,7 +108,7 @@
       permission="zope.Public"
       factory=".adapter.DAVSchemaAdapter" />
 
-  <!-- XXX: This interface needs to be split up so we can apply seperate 
+  <!-- TODO: This interface needs to be split up so we can apply seperate 
        permissions for reading and writing -->
   <adapter
       factory=".opaquenamespaces.DAVOpaqueNamespacesAdapter"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/dublincore/browser/edit.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/dublincore/browser/edit.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/dublincore/browser/edit.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -5,7 +5,7 @@
   <form action="request/URL"
      tal:attributes="action request/URL"
      tal:define="data view/edit"
-     method="POST"
+     method="post"
      >
 
     <p tal:condition="data/message"
@@ -29,15 +29,6 @@
     </div>
 
     <div class="row">
-      <div class="controls">
-        <input type="submit" value="Refresh" 
-            i18n:attributes="value refresh-button" />
-        <input type="submit" name="save" value="Save" 
-            i18n:attributes="value save-changes-button"/>
-      </div>
-    </div>
-
-    <div class="row">
       <div class="label" i18n:translate="">Created</div>
       <div class="field" tal:content="data/created">2000-01-01 01:01:01</div>
     </div>
@@ -53,6 +44,15 @@
       </div>
     </div>
 
+    <div class="row">
+      <div class="controls">
+        <input type="submit" value="Refresh" 
+            i18n:attributes="value refresh-button" />
+        <input type="submit" name="save" value="Save" 
+            i18n:attributes="value save-changes-button"/>
+      </div>
+    </div>
+
   </form>
 
 </div>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/event/dispatching.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/event/dispatching.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/event/dispatching.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -13,7 +13,7 @@
 ##############################################################################
 """Implement zope-specific event dispatching, based on subscription adapters
 
-This package instals an event dispatcher that calls event handlers,
+This package installs an event dispatcher that calls event handlers,
 registered as subscription adapters providing ``None``.
 
 So, to subscribe to an event, use a subscription adapter to ``None``:

Modified: Zope3/branches/srichter-blow-services/src/zope/app/exception/browser/tests/test_unauthorized.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/exception/browser/tests/test_unauthorized.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/exception/browser/tests/test_unauthorized.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -19,7 +19,7 @@
 from zope.interface import implements
 from zope.publisher.browser import TestRequest
 from zope.app.testing import ztapi
-from zope.app.security.interfaces import IAuthenticationUtility, IPrincipal
+from zope.app.security.interfaces import IAuthentication, IPrincipal
 from zope.app.exception.browser.unauthorized import Unauthorized
 from zope.app.testing.placelesssetup import PlacelessSetup
 
@@ -41,7 +41,7 @@
         return self.id
 
 class DummyAuthUtility(object):
-    implements(IAuthenticationUtility)  # this is a lie
+    implements(IAuthentication)  # this is a lie
 
     def unauthorized(self, principal_id, request):
         self.principal_id = principal_id
@@ -56,7 +56,7 @@
     def setUp(self):
         super(Test, self).setUp()
         self.auth = DummyAuthUtility()
-        ztapi.provideUtility(IAuthenticationUtility, self.auth)
+        ztapi.provideUtility(IAuthentication, self.auth)
 
     def tearDown(self):
         super(Test, self).tearDown()

Modified: Zope3/branches/srichter-blow-services/src/zope/app/file/browser/file_add.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/file/browser/file_add.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/file/browser/file_add.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -3,7 +3,7 @@
 <div metal:fill-slot="body">
 
   <form action="." tal:attributes="action request/URL"
-        method="POST" enctype="multipart/form-data">
+        method="post" enctype="multipart/form-data">
 
     <h3>Add a File</h3>
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/file/browser/file_upload.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/file/browser/file_upload.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/file/browser/file_upload.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -3,7 +3,7 @@
 <div metal:fill-slot="body">
 
   <form action="." tal:attributes="action request/URL"
-        method="POST" enctype="multipart/form-data">
+        method="post" enctype="multipart/form-data">
 
     <h3>Upload a file</h3>
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/form/browser/edit.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/form/browser/edit.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/form/browser/edit.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -5,7 +5,7 @@
 
   <div metal:define-macro="body">
 
-    <form action="." tal:attributes="action request/URL" method="POST"
+    <form action="." tal:attributes="action request/URL" method="post"
           enctype="multipart/form-data">
 
       <div metal:define-macro="formbody">

Modified: Zope3/branches/srichter-blow-services/src/zope/app/form/browser/editwizard.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/form/browser/editwizard.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/form/browser/editwizard.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -4,7 +4,7 @@
 
       <div metal:define-macro="body">
 
-        <form action="." tal:attributes="action request/URL" method="POST"
+        <form action="." tal:attributes="action request/URL" method="post"
               enctype="multipart/form-data" >
 
           <div metal:define-macro="formbody">

Modified: Zope3/branches/srichter-blow-services/src/zope/app/homefolder/homefolder.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/homefolder/homefolder.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/homefolder/homefolder.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -7,7 +7,7 @@
      tal:condition="status"
      tal:content="status" />
 
-  <form action="" method="POST">
+  <form action="" method="post">
     <h3 i18n:translate="">Assign a Principal</h3>
 
     <div class="row" tal:define="widget nocall:view/principal_widget">
@@ -41,4 +41,4 @@
  
 </div>
 </body> 
-</html>
\ No newline at end of file
+</html>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/introspector/introspector.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/introspector/introspector.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/introspector/introspector.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -184,7 +184,7 @@
             </div>
             <form tal:attributes=" 
                       action string:${request/URL/-1}/@@objectMarker.html"
-                  method="POST">
+                  method="post">
               <input type="submit" value="Modify" 
                      i18n:attributes="value modify-button"/>
             </form>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/introspector/marker.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/introspector/marker.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/introspector/marker.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -19,7 +19,7 @@
       tal:define ="global introspector view/getIntrospector;
                    global status view/update">
 
-  <form action="" tal:attributes="action request/URL" method="POST">
+  <form action="" tal:attributes="action request/URL" method="post">
 
     <table>
       <tr>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/meta.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/meta.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/meta.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -13,4 +13,8 @@
 <include package="zope.app.schema" file="meta.zcml" />
 <include package="zope.app.container.browser" file="meta.zcml" />
 
+<!-- BBB: Goes away in 3.3 -->
+<include package="zope.app.site" file="meta.zcml" />
+
+
 </configure>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/observable/README.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/observable/README.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/observable/README.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,10 +1,9 @@
-Instance Based Event Subscription Support
+Instance-based Event Subscription Support
 =========================================
 
 This package implements support for subscribing to events for a
-particular instance of an object.  This package
-implements the proposal found at
-http://dev.zope.org/Zope3/InstanceAndTypeBasedSubscriptions .
+particular instance of an object.  This package implements the proposal
+found at http://dev.zope.org/Zope3/InstanceAndTypeBasedSubscriptions .
 
 The package provides an event channel for dispatching events to the
 appropriate instance as well as an adapter from `IAnnotatable` to

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pau/README.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pau/README.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pau/README.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -98,7 +98,7 @@
   ...         notify(interfaces.FoundPrincipalCreated(principal, info))
   ...         return principal
 
-  >>> provideUtility(interfaces.IPrincipalFactoryPlugin, PrincipalFactory(), 
+  >>> provideUtility(interfaces.IPrincipalFactoryPlugin, PrincipalFactory(),
   ...                name='pf')
 
 Finally, we create a PAU instance:
@@ -207,7 +207,7 @@
 
   >>> provideUtility(interfaces.IExtractionPlugin, OddExtractor(), name='eodd')
   >>> auth.extractors = 'eodd', 'emy'
- 
+
   >>> request = TestRequest(credentials=41)
   >>> auth.authenticate(request)
   Principal('1', "{'int': 1}")
@@ -245,15 +245,15 @@
   ...                     principal, info))
   ...         return principal
 
-  >>> provideUtility(interfaces.IPrincipalFactoryPlugin, OddFactory(), 
+  >>> provideUtility(interfaces.IPrincipalFactoryPlugin, OddFactory(),
   ...                name='oddf')
 
   >>> auth.factories = 'oddf', 'pf'
- 
+
   >>> request = TestRequest(credentials=41)
   >>> auth.authenticate(request)
   OddPrincipal('1', "{'int': 1}")
- 
+
   >>> request = TestRequest(credentials=42)
   >>> auth.authenticate(request)
   Principal('42', "{'domain': 42}")
@@ -269,7 +269,7 @@
 Get a principal given an id
 ===========================
 
-We can ask the PAU for a principal, given an id. 
+We can ask the PAU for a principal, given an id.
 
 To do this, the PAU uses principal search plugins:
 
@@ -281,7 +281,7 @@
   ...         if principal_id == '42':
   ...             return {'domain': 42}
 
-  >>> provideUtility(interfaces.IPrincipalSearchPlugin, Search42(), 
+  >>> provideUtility(interfaces.IPrincipalSearchPlugin, Search42(),
   ...                name='s42')
 
   >>> class IntSearch:
@@ -296,9 +296,9 @@
   ...         if (i >= 0 and i < 100):
   ...             return {'int': i}
 
-  >>> provideUtility(interfaces.IPrincipalSearchPlugin, IntSearch(), 
+  >>> provideUtility(interfaces.IPrincipalSearchPlugin, IntSearch(),
   ...                name='sint')
- 
+
   >>> auth.searchers = 's42', 'sint'
 
   >>> auth.getPrincipal('41')
@@ -322,11 +322,11 @@
 a fake utility.
 
   >>> from zope.app.component.testing import testingNextUtility
-  >>> from zope.app.security.interfaces import IAuthenticationUtility
+  >>> from zope.app.security.interfaces import IAuthentication
 
   >>> class FakeAuthUtility:
   ...
-  ...     zope.interface.implements(IAuthenticationUtility)
+  ...     zope.interface.implements(IAuthentication)
   ...
   ...     lastGetPrincipalCall = lastUnauthorizedCall = None
   ...
@@ -337,11 +337,11 @@
   ...         self.lastUnauthorizedCall = id
 
   >>> nextauth = FakeAuthUtility()
-  >>> testingNextUtility(auth, nextauth, IAuthenticationUtility)
+  >>> testingNextUtility(auth, nextauth, IAuthentication)
 
   >>> auth.getPrincipal('123')
-  >>> '123' == nextauth.lastGetPrincipalCall
-  True
+  >>> nextauth.lastGetPrincipalCall
+  '123'
 
 Issuing a challenge
 ===================
@@ -364,9 +364,9 @@
 create a plugin that sets a response header:
 
   >>> class Challenge:
-  ...     
+  ...
   ...     zope.interface.implements(interfaces.IChallengePlugin)
-  ...     
+  ...
   ...     def challenge(self, requests, response):
   ...         response.setHeader('X-Unauthorized', 'True')
   ...         return True
@@ -387,8 +387,8 @@
 -----------------------------
 
 To understand how the challenge plugins work, it's helpful to
-understand how the unauthorized method of authenticaton services 
-get called.
+understand how the unauthorized method of authentication utilities
+gets called.
 
 If an 'Unauthorized' exception is raised and not caught by application
 code, then the following things happen:
@@ -420,12 +420,12 @@
 
   >>> class ColorChallenge:
   ...     zope.interface.implements(interfaces.IChallengePlugin)
-  ...     
+  ...
   ...     protocol = 'bridge'
-  ...     
+  ...
   ...     def challenge(self, requests, response):
   ...         challenge = response.getHeader('X-Challenge', '')
-  ...         response.setHeader('X-Challenge', 
+  ...         response.setHeader('X-Challenge',
   ...                            challenge + 'favorite color? ')
   ...         return True
 
@@ -434,12 +434,12 @@
 
   >>> class BirdChallenge:
   ...     zope.interface.implements(interfaces.IChallengePlugin)
-  ...     
+  ...
   ...     protocol = 'bridge'
-  ...     
+  ...
   ...     def challenge(self, requests, response):
   ...         challenge = response.getHeader('X-Challenge', '')
-  ...         response.setHeader('X-Challenge', 
+  ...         response.setHeader('X-Challenge',
   ...                            challenge + 'swallow air speed? ')
   ...         return True
 
@@ -523,10 +523,10 @@
 
 Different search plugins are likely to use very different search
 criteria.  There are two approaches a plugin can use to support
-searching: 
+searching:
 
 - A plugin can provide IQuerySchemaSearch, in addition to
-  `IPrincipalSearchPlugin`.  In this case, the plugin provises a search
+  `IPrincipalSearchPlugin`.  In this case, the plugin provides a search
   method and a schema that describes the input to be provided to the
   search method.
 
@@ -539,7 +539,7 @@
 
   >>> [id for (id, queriable) in auth.getQueriables()]
   ['s42', 'sint']
-  >>> [queriable.__class__.__name__ 
+  >>> [queriable.__class__.__name__
   ...  for (id, queriable) in auth.getQueriables()]
   ['Search42', 'IntSearch']
 
@@ -550,4 +550,4 @@
   search or extraction and challenge. See
   `ISearchableAuthenticationPlugin` and
   `IExtractionAndChallengePlugin`.
- 
+

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pau/browser/groupfolder.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pau/browser/groupfolder.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pau/browser/groupfolder.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -443,7 +443,7 @@
   ... ------------01mLTAiXW04sKSCkjnjf13EYQIvFsu2svmDMwutSuvLCNXx7JekIhxq
   ... Content-Disposition: form-data; name="field.provided"
   ... 
-  ... zope.app.security.interfaces.IAuthenticationUtility
+  ... zope.app.security.interfaces.IAuthentication
   ... ------------01mLTAiXW04sKSCkjnjf13EYQIvFsu2svmDMwutSuvLCNXx7JekIhxq
   ... Content-Disposition: form-data; name="field.provided-empty-marker"
   ... 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pau/browser/principalfolder.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pau/browser/principalfolder.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pau/browser/principalfolder.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -171,7 +171,7 @@
   ... ------------01mLTAiXW04sKSCkjnjf13EYQIvFsu2svmDMwutSuvLCNXx7JekIhxq
   ... Content-Disposition: form-data; name="field.provided"
   ... 
-  ... zope.app.security.interfaces.IAuthenticationUtility
+  ... zope.app.security.interfaces.IAuthentication
   ... ------------01mLTAiXW04sKSCkjnjf13EYQIvFsu2svmDMwutSuvLCNXx7JekIhxq
   ... Content-Disposition: form-data; name="field.provided-empty-marker"
   ... 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pau/groupfolder.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pau/groupfolder.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pau/groupfolder.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -63,7 +63,7 @@
 
     prefix = zope.schema.TextLine(
         title=u"Group ID prefix",
-        description=u"Prefix added to is of groups in this folder",
+        description=u"Prefix added to IDs of groups in this folder",
         readonly=True,
         )
        

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pau/groupfolder.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pau/groupfolder.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pau/groupfolder.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -16,14 +16,14 @@
   >>> groups['g1'] = g1
 
 Groups are defined with respect to an authentication service.  Groups
-must be accessable via an authentication service and can contain
-principals accessable via an authentication service.
+must be accessible via an authentication service and can contain
+principals accessible via an authentication service.
 
 To illustrate the group interaction with the authentication service,
 we'll create a sample authentication service:
 
   >>> import zope.interface
-  >>> from zope.app.security.interfaces import IAuthenticationUtility
+  >>> from zope.app.security.interfaces import IAuthentication
   >>> from zope.app.pau.groupfolder import setGroupsForPrincipal
 
   >>> class Principal:
@@ -37,7 +37,7 @@
 
   >>> class Principals:
   ...
-  ...     zope.interface.implements(IAuthenticationUtility)
+  ...     zope.interface.implements(IAuthentication)
   ...
   ...     def __init__(self, groups):
   ...         self.principals = {
@@ -56,7 +56,7 @@
   ...         setGroupsForPrincipal(PrincipalCreatedEvent(principal))
   ...         return principal
 
-This class doesn't really implement the fill `IAuthenticationService`
+This class doesn't really implement the full `IAuthenticationService`
 interface, but it implements the `getPrincipal` method used by groups.
 It works very much like PAU.  It creates principals on demand.  It
 calls `setGroupsForPrincipal`, which is normally called as an event
@@ -72,7 +72,7 @@
 `principals` dictionary and a groups folder.
 
   >>> principals = Principals(groups)
-  >>> ztapi.provideUtility(IAuthenticationUtility, principals)
+  >>> ztapi.provideUtility(IAuthentication, principals)
 
 Now we can set the principals on the group:
 
@@ -85,7 +85,7 @@
   >>> groups.getGroupsForPrincipal('p1')
   (u'group.g1',)
 
-Note that the group id is a concatination of the group-folder prefix
+Note that the group id is a concatenation of the group-folder prefix
 and the name of the group-information object within the folder.
 
 If we delete a group:

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pau/pau.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pau/pau.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pau/pau.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -24,7 +24,7 @@
 
 from zope.app import zapi
 
-from zope.app.security.interfaces import IAuthenticationUtility
+from zope.app.security.interfaces import IAuthentication
 from zope.app.component import queryNextUtility
 from zope.app.container.contained import Contained
 from zope.app.component.interfaces import ILocalUtility
@@ -74,7 +74,7 @@
 
 class PAU(object):
 
-    zope.interface.implements(IPAU, IAuthenticationUtility, ISourceQueriables)
+    zope.interface.implements(IPAU, IAuthentication, ISourceQueriables)
 
     authenticators = extractors = challengers = factories = searchers = ()
 
@@ -164,11 +164,20 @@
 
     def _delegate(self, meth, *args):
         # delegate to next AU
-        next = queryNextUtility(self, IAuthenticationUtility)
+        next = queryNextUtility(self, IAuthentication)
         if next is None:
             return None
         return getattr(next, meth)(*args)
 
+    # BBB
+    def getPrincipals(self, name):
+        import warnings
+        warnings.warn(
+            "The getPrincipals method has been deprecicated. "
+            "It will be removed in Zope X3.3. "
+            "You'll find no principals here.",
+            DeprecationWarning, stacklevel=2)
+        return ()
 
 class LocalPAU(PAU, Persistent, Contained):
     zope.interface.implements(IPAU, ILocation, ILocalUtility)

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pau/principalfolder.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pau/principalfolder.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pau/principalfolder.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -29,9 +29,9 @@
     'login': 'login1',
     'title': 'Principal 1'})
 
-we get back a principal id and supplimentary information, including the
+we get back a principal id and supplementary information, including the
 principal title and description.  Note that the principal id is a
-concatination of the principal-folder prefix and the name of the
+concatenation of the principal-folder prefix and the name of the
 principal-information object within the folder.
 
 None is returned if the credentials are invalid:
@@ -121,7 +121,7 @@
 Changing credentials
 --------------------
 
-Creedentials can be changed by modifying principal-information
+Credentials can be changed by modifying principal-information
 objects:
 
   >>> p1.login = 'bob'

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,9 +11,9 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Pluggable Authentication service implementation.
+"""Pluggable Authentication utility implementation.
 
-BBB: ENTIRE PACKAGE IS DEPRECATED!!! 12/05/2004
+BBB: ENTIRE PACKAGE IS DEPRECATED!!! 12/05/2004 Gone in 3.3
 
 $Id$
 """
@@ -24,7 +24,6 @@
 import time
 import random
 import zope.schema
-
 from warnings import warn
 from persistent import Persistent
 from BTrees.IOBTree import IOBTree
@@ -32,12 +31,9 @@
 
 from zope.interface import implements
 from zope.component.interfaces import IViewFactory
-from zope.app.security.interfaces import PrincipalLookupError
 
 from zope.app import zapi
-from zope.app.location import locate
-from zope.app.traversing.api import getPath
-
+from zope.app.component import queryNextUtility
 from zope.app.container.interfaces import IOrderedContainer
 from zope.app.container.interfaces import IContainerNamesContainer, INameChooser
 from zope.app.container.interfaces import IContained
@@ -45,12 +41,12 @@
 from zope.app.container.constraints import ContainerTypesConstraint
 from zope.app.container.contained import Contained, setitem, uncontained
 from zope.app.container.ordered import OrderedContainer
+from zope.app.location import locate
+from zope.app.security.interfaces import ILoginPassword, IAuthentication
+from zope.app.security.interfaces import PrincipalLookupError
+from zope.app.traversing.api import getPath
 
-from zope.app.servicenames import Authentication
-from zope.app.security.interfaces import ILoginPassword
-from zope.app.component.localservice import queryNextService
-
-from interfaces import IUserSchemafied, IPluggableAuthenticationService
+from interfaces import IUserSchemafied, IPluggableAuthentication
 from interfaces import IPrincipalSource, ILoginPasswordPrincipalSource
 from interfaces import IContainedPrincipalSource, IContainerPrincipalSource
 
@@ -59,9 +55,9 @@
 
     return random.randint(0, sys.maxint-1)
 
-class PluggableAuthenticationService(OrderedContainer):
+class PluggableAuthentication(OrderedContainer):
 
-    implements(IPluggableAuthenticationService, IOrderedContainer)
+    implements(IPluggableAuthentication, IOrderedContainer)
 
     def __init__(self, earmark=None, hide_deprecation_warning=False):
         if not hide_deprecation_warning:
@@ -70,18 +66,18 @@
                  DeprecationWarning, 2)        
         self.earmark = earmark
         # The earmark is used as a token which can uniquely identify
-        # this authentication service instance even if the service moves
+        # this authentication utility instance even if the utility moves
         # from place to place within the same context chain or is renamed.
         # It is included in principal ids of principals which are obtained
-        # from this auth service, so code which dereferences a principal
-        # (like getPrincipal of this auth service) needs to take the earmark
+        # from this auth utility, so code which dereferences a principal
+        # (like getPrincipal of this auth utility) needs to take the earmark
         # into account. The earmark cannot change once it is assigned.  If it
         # does change, the system will not be able to dereference principal
         # references which embed the old earmark.
         OrderedContainer.__init__(self)
 
     def authenticate(self, request):
-        """ See `IAuthenticationService`. """
+        """ See `IAuthentication`. """
         for ps_key, ps in self.items():
             loginView = zapi.queryView(ps, "login", request)
             if loginView is not None:
@@ -89,36 +85,36 @@
                 if principal is not None:
                     return principal
 
-        next = queryNextService(self, Authentication, None)
+        next = queryNextUtility(self, IAuthentication, None)
         if next is not None:
             return next.authenticate(request)
 
         return None
 
     def unauthenticatedPrincipal(self):
-        # It's safe to assume that the global auth service will
+        # It's safe to assume that the global auth utility will
         # provide an unauthenticated principal, so we won't bother.
         return None
 
     def unauthorized(self, id, request):
-        """ See `IAuthenticationService`. """
+        """ See `IAuthentication`. """
 
-        next = queryNextService(self, Authentication, None)
+        next = queryNextUtility(self, IAuthentication)
         if next is not None:
             return next.unauthorized(id, request)
 
         return None
 
     def getPrincipal(self, id):
-        """ See `IAuthenticationService`.
+        """ See `IAuthentication`.
 
         For this implementation, an `id` is a string which can be
         split into a 3-tuple by splitting on tab characters.  The
-        three tuple consists of (`auth_service_earmark`,
+        three tuple consists of (`auth_utility_earmark`,
         `principal_source_id`, `principal_id`).
 
         In the current strategy, the principal sources that are members
-        of this authentication service cannot be renamed; if they are,
+        of this authentication utility cannot be renamed; if they are,
         principal references that embed the old name will not be
         dereferenceable.
 
@@ -130,11 +126,11 @@
             auth_svc_earmark, principal_src_id, principal_id = id.split('\t',2)
         except (TypeError, ValueError, AttributeError):
             auth_svc_earmark, principal_src_id, principal_id = None, None, None
-            next = queryNextService(self, Authentication, None)
+            next = queryNextUtility(self, IAuthentication)
 
         if auth_svc_earmark != self.earmark:
             # this is not our reference because its earmark doesnt match ours
-            next = queryNextService(self, Authentication, None)
+            next = queryNextUtility(self, IAuthentication)
 
         if next is not None:
             return next.getPrincipal(id)
@@ -145,21 +141,21 @@
         return source.getPrincipal(id)
 
     def getPrincipals(self, name):
-        """ See `IAuthenticationService`. """
+        """ See `IAuthentication`. """
 
         for ps_key, ps in self.items():
             for p in ps.getPrincipals(name):
                 yield p
 
-        next = queryNextService(self, Authentication, None)
+        next = queryNextUtility(self, IAuthentication)
         if next is not None:
             for p in next.getPrincipals(name):
                 yield p
 
     def addPrincipalSource(self, id, principal_source):
-        """ See `IPluggableAuthenticationService`.
+        """ See `IPluggableAuthentication`.
 
-        >>> pas = PluggableAuthenticationService(None, True)
+        >>> pas = PluggableAuthentication(None, True)
         >>> sps = BTreePrincipalSource()
         >>> pas.addPrincipalSource('simple', sps)
         >>> sps2 = BTreePrincipalSource()
@@ -174,9 +170,9 @@
         self[id] = principal_source        
 
     def removePrincipalSource(self, id):
-        """ See `IPluggableAuthenticationService`.
+        """ See `IPluggableAuthentication`.
 
-        >>> pas = PluggableAuthenticationService(None, True)
+        >>> pas = PluggableAuthentication(None, True)
         >>> sps = BTreePrincipalSource()
         >>> pas.addPrincipalSource('simple', sps)
         >>> sps2 = BTreePrincipalSource()
@@ -193,10 +189,10 @@
         del self[id]
 
 
-def PluggableAuthenticationServiceAddSubscriber(self, event):
+def PluggableAuthenticationAddSubscriber(self, event):
     r"""Generates an earmark if one is not provided.
 
-    Define a stub for `PluggableAuthenticationService`
+    Define a stub for `PluggableAuthentication`
 
     >>> from zope.app.traversing.interfaces import IPhysicallyLocatable
     >>> class PluggableAuthStub(object):
@@ -206,22 +202,22 @@
     ...     def getName(self):
     ...         return 'PluggableAuthName'
 
-    The subscriber generates an earmark for the auth service if one is not
+    The subscriber generates an earmark for the auth utility if one is not
     set in the init.
 
     >>> stub = PluggableAuthStub()
     >>> event = ''
-    >>> PluggableAuthenticationServiceAddSubscriber(stub, event)
+    >>> PluggableAuthenticationAddSubscriber(stub, event)
     >>> stub.earmark is not None
     True
 
-    The subscriber does not modify an earmark for the auth service if one
+    The subscriber does not modify an earmark for the auth utility if one
     exists already.
 
     >>> earmark = 'my sample earmark'
     >>> stub = PluggableAuthStub(earmark=earmark)
     >>> event = ''
-    >>> PluggableAuthenticationServiceAddSubscriber(stub, event)
+    >>> PluggableAuthenticationAddSubscriber(stub, event)
     >>> stub.earmark == earmark
     True
     """

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -2,20 +2,20 @@
    xmlns="http://namespaces.zope.org/browser"
    xmlns:zope="http://namespaces.zope.org/zope">
 
-<!-- Pluggable Authentication Service -->
+<!-- Pluggable Authentication -->
 
   <!-- BBB: Deactivated as a deprecation measure. -->
   <!--addMenuItem
-       class="zope.app.pluggableauth.PluggableAuthenticationService"
-       title="Authentication Service"
+       class="zope.app.pluggableauth.PluggableAuthentication"
+       title="Authentication"
        description="A Pluggable Authentication uses plug-in principal sources."
-       permission="zope.ManageServices"
+       permission="zope.ManageSite"
        /-->
 
   <containerViews
-       for="zope.app.pluggableauth.interfaces.IPluggableAuthenticationService"
-       contents="zope.ManageServices"
-       add="zope.ManageServices"
+       for="zope.app.pluggableauth.interfaces.IPluggableAuthentication"
+       contents="zope.ManageSite"
+       add="zope.ManageSite"
        />
 
 <!-- Principal Source -->
@@ -23,13 +23,13 @@
   <addMenuItem
       title="Add Principal Source" 
       class="zope.app.pluggableauth.BTreePrincipalSource"	
-      permission="zope.ManageServices"
+      permission="zope.ManageSite"
       />
 
   <containerViews
        for="zope.app.pluggableauth.interfaces.IContainerPrincipalSource"
-       contents="zope.ManageServices"
-       add="zope.ManageServices"
+       contents="zope.ManageSite"
+       add="zope.ManageSite"
        />
 
 <!-- Principal -->
@@ -48,7 +48,7 @@
   <addMenuItem
       title="Principal" 
       class="zope.app.pluggableauth.SimplePrincipal"
-      permission="zope.ManageServices"
+      permission="zope.ManageSite"
       view="AddPrincipalForm.html"
       />
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -2,13 +2,13 @@
     xmlns="http://namespaces.zope.org/zope"
     xmlns:browser="http://namespaces.zope.org/browser">
 
-  <localService class=".PluggableAuthenticationService">
+  <localService class=".PluggableAuthentication">
     <factory
-        id="zope.app.services.PluggableAuthenticationService"
+        id="zope.app.services.PluggableAuthentication"
         />
     <require
-        permission="zope.ManageServices"
-        interface=".interfaces.IPluggableAuthenticationService"
+        permission="zope.ManageSite"
+        interface=".interfaces.IPluggableAuthentication"
         />
 <!--
     <allow
@@ -16,20 +16,20 @@
         />
 
     <require
-        permission="zope.ManageServices"
+        permission="zope.ManageSite"
         interface="zope.app.container.interfaces.IWriteContainer"
         />
 -->
     <require
-        permission="zope.ManageServices"
+        permission="zope.ManageSite"
         interface="zope.app.site.interfaces.ISimpleService"
         />
   </localService>
 
   <subscriber
-        for=".interfaces.IPluggableAuthenticationService
+        for=".interfaces.IPluggableAuthentication
              zope.app.container.interfaces.IObjectAddedEvent"
-        factory=".PluggableAuthenticationServiceAddSubscriber"
+        factory=".PluggableAuthenticationAddSubscriber"
         />
 
   <content class=".BTreePrincipalSource">
@@ -40,7 +40,7 @@
         interface="zope.app.container.interfaces.IReadContainer"
         />
     <require
-        permission="zope.ManageServices"
+        permission="zope.ManageSite"
         interface="zope.app.container.interfaces.IWriteContainer
                    zope.app.container.interfaces.INameChooser"
         />
@@ -57,7 +57,7 @@
         interface=".interfaces.IUserSchemafied"
         />
     <require
-        permission="zope.ManageServices"
+        permission="zope.ManageSite"
         set_schema=".interfaces.IUserSchemafied"
         />
   </content>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Pluggable Authentication service.
+"""Pluggable Authentication Utility.
 
 $Id$
 """
@@ -21,7 +21,7 @@
 from zope.app.container.interfaces import IContainer, IContained
 from zope.app.container.constraints import ItemTypePrecondition
 from zope.app.container.constraints import ContainerTypesConstraint
-from zope.app.security.interfaces import IAuthenticationService, IPrincipal
+from zope.app.security.interfaces import IAuthentication, IPrincipal
 from zope.interface import Interface
 from zope.schema import Text, TextLine, Password, Field
 
@@ -55,14 +55,14 @@
         found.
 
         Note that the id has three parts, separated by tabs.  The
-        first two part are an authentication service id and a
+        first two part are an authentication utility id and a
         principal source id.  The pricipal source will typically need
         to remove the two leading parts from the id when doing it's
         own internal lookup.
 
-        Note that the authentication service nearest to the requested
-        resource is called. It is up to authentication service
-        implementations to collaborate with services higher in the
+        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.
         """
 
@@ -74,8 +74,8 @@
         """
 
 
-class IPluggableAuthenticationService(IAuthenticationService, IContainer):
-    """An `AuthenticationService` that can contain multiple pricipal sources.
+class IPluggableAuthentication(IAuthentication, IContainer):
+    """An `Authentication` utility that can contain multiple pricipal sources.
     """
 
     def __setitem__(id, principal_source):
@@ -113,8 +113,8 @@
 
 class IContainedPrincipalSource(IPrincipalSource, IContained):
     """This is a marker interface for principal sources that can be directly
-    added to an authentication service. It ensures that principal source can
-    **only** be added to pluggable authentication services."""
+    added to an authentication utility. It ensures that principal source can
+    **only** be added to pluggable authentication utilities."""
 
     __parent__= Field(
-        constraint = ContainerTypesConstraint(IPluggableAuthenticationService))
+        constraint = ContainerTypesConstraint(IPluggableAuthentication))

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/tests/authsetup.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/tests/authsetup.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/tests/authsetup.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Setup local Pluggable Authentication Service for tests
+"""Setup local Pluggable Authentication for tests
 
 This setup class can be used, if a set of local principals are required for a
 test.
@@ -24,10 +24,10 @@
 from zope.app.testing import ztapi, setup
 from zope.app.component.testing import PlacefulSetup
 from zope.publisher.interfaces.http import IHTTPCredentials
-from zope.app.security.interfaces import ILoginPassword
+from zope.app.security.interfaces import ILoginPassword, IAuthentication
 from zope.app.security.basicauthadapter import BasicAuthAdapter
 from zope.app.pluggableauth import \
-     PrincipalAuthenticationView, PluggableAuthenticationService, \
+     PrincipalAuthenticationView, PluggableAuthentication, \
      BTreePrincipalSource, SimplePrincipal
 from zope.app.pluggableauth.interfaces import IPrincipalSource
 
@@ -40,8 +40,8 @@
         ztapi.browserView(IPrincipalSource, "login",
                           PrincipalAuthenticationView)
 
-        auth = setup.addService(sm, "PluggableAuthService",
-                                PluggableAuthenticationService(None, True))
+        auth = setup.addUtility(sm, '', IAuthentication,
+                                PluggableAuthentication(None, True))
 
         one = BTreePrincipalSource()
         two = BTreePrincipalSource()

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/tests/test_pluggableauth.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/tests/test_pluggableauth.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pluggableauth/tests/test_pluggableauth.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -27,12 +27,13 @@
 from zope.publisher.interfaces.http import IHTTPCredentials
 
 from zope.app.pluggableauth import BTreePrincipalSource, \
-     SimplePrincipal, PluggableAuthenticationService, \
+     SimplePrincipal, PluggableAuthentication, \
      PrincipalAuthenticationView
 from zope.app.pluggableauth.interfaces import IPrincipalSource
 
 from zope.app.pluggableauth.interfaces import IUserSchemafied
 from zope.app.security.interfaces import IPrincipal, ILoginPassword
+from zope.app.security.interfaces import IAuthentication
 from zope.app.security.basicauthadapter import BasicAuthAdapter
 
 from zope.publisher.browser import TestRequest as Request
@@ -51,8 +52,8 @@
         ztapi.browserView(IPrincipalSource, "login",
                           PrincipalAuthenticationView)
 
-        auth = setup.addService(sm, "TestPluggableAuthenticationService",
-                                PluggableAuthenticationService(None, True))
+        auth = setup.addUtility(sm, "", IAuthentication,
+                                PluggableAuthentication(None, True))
 
         one = BTreePrincipalSource()
         two = BTreePrincipalSource()
@@ -87,9 +88,9 @@
         return Request(**dict)
 
 
-class AuthServiceTest(Setup):
+class AuthUtilityTest(Setup):
 
-    def testAuthServiceAuthenticate(self):
+    def testAuthUtilityAuthenticate(self):
         auth = self._auth
         req = self.getRequest('slinkp', '123')
         pid = auth.authenticate(req).getLogin()
@@ -119,7 +120,7 @@
     def _fail_BadIdLength(self):
         self._auth.getPrincipal((self._auth.earmark, None, None))
 
-    def testAuthServiceGetPrincipal(self):
+    def testAuthUtilityGetPrincipal(self):
         auth = self._auth
         id = self._slinkp.id
         self.assertEqual(self._slinkp, auth.getPrincipal(id))
@@ -158,7 +159,7 @@
 
 
 def test_suite():
-    t1 = makeSuite(AuthServiceTest)
+    t1 = makeSuite(AuthUtilityTest)
     t2 = DocTestSuite('zope.app.pluggableauth',
                       setUp=setUp, tearDown=tearDown)
     t3 = makeSuite(BTreePrincipalSourceTest)

Modified: Zope3/branches/srichter-blow-services/src/zope/app/presentation/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/presentation/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/presentation/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -17,5 +17,5 @@
 """
 __docformat__ = 'restructuredtext'
 
-from zope.app.presentation.presentation import IPageRegistration
+from zope.app.presentation.interfaces import IPageRegistration
 from zope.app.presentation.presentation import PageRegistration

Modified: Zope3/branches/srichter-blow-services/src/zope/app/presentation/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/presentation/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/presentation/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -1,7 +1,7 @@
 <configure xmlns="http://namespaces.zope.org/browser">
 
   <editform
-    schema="...presentation.IPageRegistration"
+    schema="..interfaces.IPageRegistration"
     name="index.html"
     class=".PageRegistrationView"
     menu="zmi_views"
@@ -9,7 +9,7 @@
     permission="zope.ManageServices" />
 
   <addform
-      schema="..presentation.IPageRegistration"
+      schema="..interfaces.IPageRegistration"
       name="PageRegistration"
       content_factory="..presentation.PageRegistration"
       keyword_arguments="required factoryName name permission 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/presentation/browser/zpt.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/presentation/browser/zpt.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/presentation/browser/zpt.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -23,7 +23,7 @@
 
  <addform
       for="zope.app.presentation.zpt.IZPTTemplate"
-      schema="zope.app.presentation.IPageRegistration"
+      schema="zope.app.presentation.interfaces.IPageRegistration"
       name="addRegistration.html"
       class="zope.app.registration.browser.AddComponentRegistration"
       content_factory="zope.app.presentation.PageRegistration"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/presentation/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/presentation/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/presentation/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -7,7 +7,7 @@
 
 <content class=".presentation.ViewRegistration">
   <require
-      permission="zope.ManageServices"
+      permission="zope.ManageSite"
       interface=".presentation.IViewRegistration"
       set_schema="zope.app.registration.interfaces.IRegistration"
       />
@@ -15,21 +15,21 @@
 
 <content class=".presentation.PageRegistration">
   <require
-      permission="zope.ManageServices"
-      interface=".presentation.IPageRegistration"
-      set_schema=".presentation.IPageRegistration"
+      permission="zope.ManageSite"
+      interface=".interfaces.IPageRegistration"
+      set_schema=".interfaces.IPageRegistration"
       />
 </content>
 
 <subscriber
     factory=".presentation.PageRegistrationAddSubscriber"
-    for=".presentation.IPageRegistration 
+    for=".interfaces.IPageRegistration 
          zope.app.container.interfaces.IObjectAddedEvent"
     />
 
 <subscriber
     factory=".presentation.PageRegistrationRemoveSubscriber"
-    for=".presentation.IPageRegistration 
+    for=".interfaces.IPageRegistration 
          zope.app.container.interfaces.IObjectRemovedEvent"
     />
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/presentation/presentation.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/presentation/presentation.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/presentation/presentation.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -22,20 +22,21 @@
 from zope.interface import implements, providedBy, Interface, Attribute
 from zope.security.checker import NamesChecker, ProxyFactory
 
-import zope.app.container.contained
-import zope.app.component.interfaces.registration
-import zope.app.component.adapter
-import zope.app.interface.interfaces
 import zope.component.interfaces
 import zope.configuration.exceptions
 import zope.proxy
 import zope.publisher.interfaces.browser
-import zope.schema
 
+import zope.app.component.interfaces.registration
+import zope.app.component.adapter
+import zope.app.container.contained
+import zope.app.interface.interfaces
 from zope.app import zapi
 from zope.app.i18n import ZopeMessageIDFactory as _
 from zope.app.dependable.interfaces import IDependable, DependencyError
 
+import interfaces
+
 class GlobalViewRegistration(object):
     """Registrations representing global view thingies."""
 
@@ -83,35 +84,8 @@
         self.__parent__ = parent
         self.__name__ = name
 
-class IViewRegistration(zope.app.component.interfaces.IAdapterRegistration):
-
-    required = zope.schema.Choice(
-        title = _(u"For interface"),
-        description = _(u"The interface of the objects being viewed"),
-        vocabulary="Interfaces",
-        readonly = True,
-        required = True,
-        )
-
-    requestType = zope.schema.Choice(
-        title = _(u"Request type"),
-        description = _(u"The type of requests the view works with"),
-        vocabulary="Interfaces",
-        readonly = True,
-        required = True,
-        )
-
-    layer = zope.schema.BytesLine(
-        title = _(u"Layer"),
-        description = _(u"The skin layer the view is registered for"),
-        required = False,
-        readonly = True,
-        min_length = 1,
-        default = "default",
-        )
-
 class ViewRegistration(zope.app.component.registration.SimpleRegistration):
-    implements(IViewRegistration)
+    implements(interfaces.IViewRegistration)
 
     provided = Interface
 
@@ -157,36 +131,8 @@
         return folder.resolve(self.factoryName)
     factory = property(factory)
 
-
-class IPageRegistration(IViewRegistration):
-
-    factoryName = zope.schema.BytesLine(
-        title=_(u"Page class"),
-        required = False,
-        )
-
-    template = zope.app.component.interfaces.registration.Component(
-        title = _(u"Page template"),
-        required = False,
-        )
-
-    attribute = zope.schema.TextLine(
-        title = _(u"Class attribute"),
-        required = False,
-        )
-
-    factory = Attribute(
-        _("Factory to be called to construct an adapter")
-        )
-
-    def validate(self):
-        """Verifies that the registration is valid.
-
-        Raises a ConfigurationError if the validation is failed.
-        """
-
 class PageRegistration(ViewRegistration):
-    implements(IPageRegistration)
+    implements(interfaces.IPageRegistration)
 
     # We only care about browser pages
     requestType = zope.publisher.interfaces.browser.IBrowserRequest
@@ -210,21 +156,6 @@
         self.template = template
         self.attribute = attribute
 
-    def implementationSummary(self):
-        L = []
-        if self.template:
-            prefix = "/++etc++site/"
-            t = self.template
-            i = t.rfind(prefix)
-            if i >= 0:
-                t = t[i + len(prefix):]
-            L.append("template=%s" % t)
-        if self.factoryName:
-            L.append("class=%s" % self.factoryName)
-        if self.attribute:
-            L.append("attribute=%s" % self.attribute)
-        return ", ".join(L)
-
     def validate(self):
         if self.template and self.attribute:
             raise zope.configuration.exceptions.ConfigurationError(
@@ -253,7 +184,7 @@
             folder = self.__parent__.__parent__
             class_ = folder.resolve(self.factoryName)
         else:
-            class_  = DefaultClass
+            class_  = BrowserView
 
         if self.attribute:
             return AttrViewFactory(class_, self.attribute)
@@ -270,19 +201,21 @@
 
     factory = property(factory)
 
-def PageRegistrationAddSubscriber(self, event):
-    if self.template:
-        template = zapi.traverse(self.__parent__.__parent__,self.template)
+def PageRegistrationAddSubscriber(registration, event):
+    if registration.template:
+        template = zapi.traverse(registration.__parent__.__parent__,
+                                 registration.template)
         dependents = IDependable(template)
-        objectpath = zapi.getPath(self)
+        objectpath = zapi.getPath(registration)
         dependents.addDependent(objectpath)
 
 
-def PageRegistrationRemoveSubscriber(self, event):
-    if self.template:
-        template = zapi.traverse(self.__parent__.__parent__,self.template)
+def PageRegistrationRemoveSubscriber(registration, event):
+    if registration.template:
+        template = zapi.traverse(registration.__parent__.__parent__,
+                                 registration.template)
         dependents = IDependable(template)
-        objectpath = zapi.getPath(self)
+        objectpath = zapi.getPath(registration)
         dependents.removeDependent(objectpath)
 
 class TemplateViewFactory(object):
@@ -312,12 +245,6 @@
         attr = getattr(self.cls(object, request), self.attr)
         return ProxyFactory(attr)
 
-class DefaultClass(object):
-
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-
 class BoundTemplate(object):
 
     def __init__(self, template, view):

Modified: Zope3/branches/srichter-blow-services/src/zope/app/presentation/xxx_Tests/test_presentation.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/presentation/xxx_Tests/test_presentation.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/presentation/xxx_Tests/test_presentation.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -30,10 +30,10 @@
 from zope.app.presentation.zpt import IZPTTemplate
 from zope.app.registration.tests.iregistry import TestingIRegistry
 from zope.app.site.tests.placefulsetup import PlacefulSetup
+from zope.app.presentation.interfaces import IPageRegistration
 from zope.app.presentation.presentation import ViewRegistration
 from zope.app.presentation.presentation import PageRegistration
 from zope.app.presentation.presentation import BoundTemplate
-from zope.app.presentation.presentation import IPageRegistration
 from zope.app.presentation.presentation import PageRegistrationAddSubscriber
 from zope.app.presentation.presentation import PageRegistrationRemoveSubscriber
 from zope.app.testing import setup

Modified: Zope3/branches/srichter-blow-services/src/zope/app/publication/httpfactory.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/publication/httpfactory.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/publication/httpfactory.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -30,6 +30,8 @@
 from zope.app.publication.http import HTTPPublication
 from zope.app.publication.browser import BrowserPublication
 from zope.app.publication.xmlrpc import XMLRPCPublication
+from zope.app.publication.soap import SOAPPublication
+from zope.app.publication.interfaces import ISOAPRequestFactory
 
 _browser_methods = 'GET', 'POST', 'HEAD'
 
@@ -41,15 +43,23 @@
         self._http = HTTPPublication(db)
         self._brower = BrowserPublication(db)
         self._xmlrpc = XMLRPCPublication(db)
+        self._soappub = SOAPPublication(db)
+        self._soapreq = zapi.queryUtility(ISOAPRequestFactory)
 
     def __call__(self, input_stream, output_steam, env):
         """See `zope.app.publication.interfaces.IPublicationRequestFactory`"""
         method = env.get('REQUEST_METHOD', 'GET').upper()
 
         if method in _browser_methods:
-            if (method == 'POST' and
-                env.get('CONTENT_TYPE', '').startswith('text/xml')
-                ):
+            content_type = env.get('CONTENT_TYPE', '')
+            is_xml = content_type.startswith('text/xml')
+
+            if (method == 'POST' and is_xml and
+                env.get('HTTP_SOAPACTION', None)
+                and self._soapreq is not None):
+                request = self._soapreq(input_stream, output_steam, env)
+                request.setPublication(self._soappub)
+            elif (method == 'POST' and is_xml):
                 request = XMLRPCRequest(input_stream, output_steam, env)
                 request.setPublication(self._xmlrpc)
             else:

Modified: Zope3/branches/srichter-blow-services/src/zope/app/publication/interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/publication/interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/publication/interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -50,3 +50,12 @@
     def __init__(self, ob, request):
         self.object = ob
         self.request = request
+
+
+class ISOAPRequestFactory(Interface):
+    """SOAP request factory"""
+
+    def __call__(input_stream, output_steam, env):
+        """Create a request object to handle SOAP input."""
+
+

Copied: Zope3/branches/srichter-blow-services/src/zope/app/publication/soap.py (from rev 28844, Zope3/trunk/src/zope/app/publication/soap.py)


Property changes on: Zope3/branches/srichter-blow-services/src/zope/app/publication/soap.py
___________________________________________________________________
Name: svn:executable
   + *

Modified: Zope3/branches/srichter-blow-services/src/zope/app/publication/zopepublication.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/publication/zopepublication.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/publication/zopepublication.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -48,7 +48,7 @@
 from zope.app.publication.publicationtraverse import PublicationTraverse
 from zope.app.security.principalregistry import principalRegistry as prin_reg
 from zope.app.security.interfaces import IUnauthenticatedPrincipal
-from zope.app.security.interfaces import IAuthenticationUtility
+from zope.app.security.interfaces import IAuthentication
 from zope.app.component.interfaces import ISite
 from zope.app.traversing.interfaces import IPhysicallyLocatable
 
@@ -95,7 +95,7 @@
 
         sm = removeSecurityProxy(ob).getSiteManager()
 
-        auth = sm.queryUtility(IAuthenticationUtility)
+        auth = sm.queryUtility(IAuthentication)
         if auth is None:
             # No auth utility here
             return

Modified: Zope3/branches/srichter-blow-services/src/zope/app/pythonpage/edit.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/pythonpage/edit.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/pythonpage/edit.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -5,7 +5,7 @@
 
   <div metal:define-macro="body">
 
-    <form action="." tal:attributes="action request/URL" method="POST"
+    <form action="." tal:attributes="action request/URL" method="post"
           enctype="multipart/form-data">
 
       <div metal:define-macro="formbody">

Modified: Zope3/branches/srichter-blow-services/src/zope/app/rdb/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/rdb/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/rdb/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,7 +11,7 @@
 
 <!-- ZopeDatabaseAdapter default views -->
 
-  <!-- XXX need an index.html that gives the source and is the def view -->
+  <!-- TODO: need an index.html that gives the source and is the def view -->
 
   <view
       name="+"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/schema/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/schema/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/schema/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -25,26 +25,12 @@
 
   </view>
 
-<!-- XXX: Activate again
-  <addform
-      label="New Mutable Schema Registration"
-      for="zope.app.schema.interfaces.ISchemaUtility"
-      name="addRegistration.html"
-      schema="zope.app.component.interfaces.IUtilityRegistration"
-      class="zope.app.component.browser.AddRegistration"
-      permission="zope.ManageServices"
-      content_factory="zope.app.schema.schema.SchemaRegistration"
-      arguments="name interface componentPath"
-      set_after_add="status"
-      fields="name interface componentPath permission status" />
-
   <addMenuItem
       title="Mutable Schema"
       description="A Persistent Schema that can be edited through the web"
       class="zope.app.schema.schema.SchemaUtility"
       permission="zope.ManageServices"
     />
--->
 
   <defaultView
       for="zope.app.schema.interfaces.IMutableSchema"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/schema/browser/schema_edit.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/schema/browser/schema_edit.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/schema/browser/schema_edit.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -9,7 +9,7 @@
       <span tal:content="view/name" i18n:name="schema_name"/>
     </h3>
 
-    <form action="." tal:attributes="action request/URL" method="POST"
+    <form action="." tal:attributes="action request/URL" method="post"
           enctype="multipart/form-data">
 
     <p tal:define="status view/update"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/schema/fields.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/schema/fields.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/schema/fields.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -23,7 +23,7 @@
 
     <allow attributes="__name__" />
 
-    <!-- XXX put the whole interface under one permission for now -->
+    <!-- TODO: put the whole interface under one permission for now -->
 
     <require
 	permission="zope.ManageContent"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/schema/tests/test_fieldfactory.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/schema/tests/test_fieldfactory.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/schema/tests/test_fieldfactory.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -25,7 +25,7 @@
 	/>
     -->
 
-    <!-- XXX put the whole interface under one permission for now -->
+    <!-- TODO: put the whole interface under one permission for now -->
     <require
 	permission="zope.ManageContent"
 	interface="zope.schema.interfaces.IField"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/schemacontent/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/schemacontent/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/schemacontent/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,20 +11,6 @@
 
 <!-- Content Component Definition -->
 
-  <!-- XXX: addform
-      label="Content Component Definition Registration"
-      for= "zope.app.schemacontent.interfaces.IContentComponentDefinition"
-      name="addRegistration.html"
-      schema="zope.app.component.interfaces.IUtilityRegistration"
-      class="zope.app.component.browser.registration.AddRegistration"
-      permission="zope.ManageServices"
-      content_factory="
-          zope.app.schemacontent.content.ContentComponentDefinitionRegistration"
-      arguments="name interface componentPath"
-      set_after_add="status"
-      fields="name interface componentPath permission status" /-->
-
-
   <!-- Menu entry for "add component" menu -->
   <menuItem
       for="zope.app.container.interfaces.IAdding"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/schemacontent/browser/permission_edit.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/schemacontent/browser/permission_edit.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/schemacontent/browser/permission_edit.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -7,7 +7,7 @@
      tal:content="status" />
 
   <form action="./@@edit.html"
-        tal:attributes="action request/URL" method="POST">
+        tal:attributes="action request/URL" method="post">
 
     <tal:block define="widgets view/getPermissionWidgets"
                condition="widgets">
@@ -42,4 +42,4 @@
 
 </div>
 </body>
-</html>
\ No newline at end of file
+</html>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/browser/auth.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/browser/auth.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/browser/auth.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -18,7 +18,7 @@
 from zope.interface import implements
 from zope.i18n import translate
 from zope.app.publisher.interfaces.http import ILogin, ILogout
-from zope.app.security.interfaces import IAuthenticationUtility
+from zope.app.security.interfaces import IAuthentication
 from zope.app.security.principalregistry import UnauthenticatedPrincipal
 from zope.app.pagetemplate import ViewPageTemplateFile
 from zope.proxy import removeAllProxies
@@ -28,7 +28,7 @@
 search_label = _('search-button', 'Search')
 
 class AuthUtilitySearchView(object):
-    __used_for__ = IAuthenticationUtility
+    __used_for__ = IAuthentication
 
     def __init__(self, context, request):
         self.context = context

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -4,7 +4,7 @@
 
 
   <adapter
-      for="zope.app.security.interfaces.IAuthenticationUtility
+      for="zope.app.security.interfaces.IAuthentication
            zope.publisher.interfaces.browser.IBrowserRequest"
       provides="zope.app.form.browser.interfaces.ISourceQueryView"
       factory="zope.app.security.browser.auth.AuthUtilitySearchView" 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/browser/principalterms.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/browser/principalterms.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/browser/principalterms.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -11,10 +11,10 @@
   ...         self.id, self.title = id, title
 
   >>> from zope.interface import implements
-  >>> from zope.app.security.interfaces import IAuthenticationUtility
+  >>> from zope.app.security.interfaces import IAuthentication
   >>> from zope.app.security.interfaces import PrincipalLookupError
   >>> class AuthUtility:
-  ...     implements(IAuthenticationUtility)
+  ...     implements(IAuthentication)
   ...     data = {'jim': 'Jim Fulton', 'stephan': 'Stephan Richter'}
   ...
   ...     def getPrincipal(self, id):
@@ -26,14 +26,14 @@
 Now we need to install the authentication utility:
 
   >>> from zope.app.testing import ztapi
-  >>> ztapi.provideUtility(IAuthenticationUtility, AuthUtility())
+  >>> ztapi.provideUtility(IAuthentication, AuthUtility())
 
 We need a principal source so that we can create a view from it.
 
   >>> from zope.app import zapi
   >>> class PrincipalSource:
   ...     def __contains__(self, id):
-  ...          auth = zapi.getUtility(IAuthenticationUtility)
+  ...          auth = zapi.getUtility(IAuthentication)
   ...          try:
   ...              auth.getPrincipal(id)
   ...          except PrincipalLookupError:

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -23,7 +23,7 @@
   <include file="_protections.zcml" />
 
   <utility
-      provides=".interfaces.IAuthenticationUtility" 
+      provides=".interfaces.IAuthentication" 
       component=".principalregistry.principalRegistry" />
 
   <localUtility class=".permission.LocalPermission">

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -33,13 +33,13 @@
     Authenticated principals are preferable to UnauthenticatedPrincipals.
     """
 
-class IAuthenticationUtility(Interface):
+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.
 
-    `IAuthenticationUtility` objects are used to implement authentication
+    `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
@@ -119,11 +119,12 @@
         object hierarchy.
         """
 
+class IAuthenticationUtility(IAuthentication):
+    """This interface is deprecated
+    """
+    
     def getPrincipals(name):
-        """Get principals with matching names.
-
-        Get an iterable object with the principals with names that are
-        similar to (e.g. contain) the given name.
+        """This interface is deprecated
         """
 
 ############################################################################
@@ -134,7 +135,7 @@
 class ILoginPassword(Interface):
     """A password based login.
 
-    An `IAuthenticationUtility` would use this (adapting a request),
+    An `IAuthentication` would use this (adapting a request),
     to discover the login/password passed from the user, or to
     indicate that a login is required.
     """

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/principal.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/principal.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/principal.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -17,7 +17,7 @@
 """
 from zope.app import zapi
 from zope.app.security.interfaces import PrincipalLookupError
-from zope.app.security.interfaces import IAuthenticationUtility
+from zope.app.security.interfaces import IAuthentication
 
 # BBB Backward Compatibility
 from zope.exceptions import NotFoundError
@@ -25,7 +25,7 @@
 
 def checkPrincipal(context, principal_id):
 
-    auth = zapi.getUtility(IAuthenticationUtility, context=context)
+    auth = zapi.getUtility(IAuthentication, context=context)
     try:
         if auth.getPrincipal(principal_id):
             return

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/principalregistry.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/principalregistry.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/principalregistry.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -20,7 +20,7 @@
 from zope.app.security.interfaces import PrincipalLookupError
 from zope.app import zapi
 from zope.app.security.interfaces import ILoginPassword
-from zope.app.security.interfaces import IAuthenticationUtility, IPrincipal
+from zope.app.security.interfaces import IAuthentication, IPrincipal
 from zope.app.security.interfaces import IUnauthenticatedPrincipal
 from zope.app.container.contained import Contained, contained
 from warnings import warn
@@ -30,9 +30,9 @@
 
 class PrincipalRegistry(object):
 
-    implements(IAuthenticationUtility)
+    implements(IAuthentication)
 
-    # Methods implementing IAuthenticationUtility
+    # Methods implementing IAuthentication
 
     def authenticate(self, request):
         a = ILoginPassword(request, None)

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/tests/test_securitydirectives.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/tests/test_securitydirectives.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/tests/test_securitydirectives.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -24,7 +24,7 @@
 from zope.app.testing.placelesssetup import PlacelessSetup
 
 from zope.app.servicenames import Authentication
-from zope.app.security.interfaces import IAuthenticationUtility, IPermission
+from zope.app.security.interfaces import IAuthentication, IPermission
 from zope.app.security.principalregistry import principalRegistry
 from zope.app.security.settings import Allow
 import zope.app.security.tests
@@ -34,7 +34,7 @@
 
     def setUp(self):
         super(TestBase, self).setUp()
-        ztapi.provideUtility(IAuthenticationUtility, principalRegistry)
+        ztapi.provideUtility(IAuthentication, principalRegistry)
 
 
 class TestPrincipalDirective(TestBase, unittest.TestCase):

Modified: Zope3/branches/srichter-blow-services/src/zope/app/security/vocabulary.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/security/vocabulary.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/security/vocabulary.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -22,7 +22,7 @@
 from zope.interface import implements
 from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 from zope.schema.interfaces import ISourceQueriables
-from zope.app.security.interfaces import IPermission, IAuthenticationUtility
+from zope.app.security.interfaces import IPermission, IAuthentication
 from zope.app.security.interfaces import PrincipalLookupError
 from zope.app.component import queryNextUtility
 
@@ -147,7 +147,7 @@
 
         >>> zapi.getUtility = temp
         """
-        auth = zapi.getUtility(IAuthenticationUtility)
+        auth = zapi.getUtility(IAuthentication)
         try:
             auth.getPrincipal(id)
         except PrincipalLookupError:
@@ -174,27 +174,27 @@
         authentication utilities to look for queriables.
 
         >>> class DummyUtility1:
-        ...     implements(IAuthenticationUtility)
+        ...     implements(IAuthentication)
         ...     __parent__ = None
         ...     def __repr__(self): return 'dummy1'
         >>> dummy1 = DummyUtility1()
         
         >>> class DummyUtility2:
-        ...     implements(ISourceQueriables, IAuthenticationUtility)
+        ...     implements(ISourceQueriables, IAuthentication)
         ...     __parent__ = None
         ...     def getQueriables(self):
         ...         return ('1', 1), ('2', 2), ('3', 3)
         >>> dummy2 = DummyUtility2()
 
         >>> class DummyUtility3(DummyUtility2):
-        ...     implements(IAuthenticationUtility)
+        ...     implements(IAuthentication)
         ...     def getQueriables(self):
         ...         return ('4', 4),
         >>> dummy3 = DummyUtility3()
 
         >>> from zope.app.component.testing import testingNextUtility
-        >>> testingNextUtility(dummy1, dummy2, IAuthenticationUtility)
-        >>> testingNextUtility(dummy2, dummy3, IAuthenticationUtility)
+        >>> testingNextUtility(dummy1, dummy2, IAuthentication)
+        >>> testingNextUtility(dummy2, dummy3, IAuthentication)
         
         >>> temp = zapi.getUtility
         >>> zapi.getUtility = lambda iface: dummy1
@@ -206,7 +206,7 @@
         >>> zapi.getUtility = temp
         """
         i = 0
-        auth = zapi.getUtility(IAuthenticationUtility)
+        auth = zapi.getUtility(IAuthentication)
         while True:
             queriables = ISourceQueriables(auth, None)
             if queriables is None:
@@ -214,7 +214,7 @@
             else:
                 for qid, queriable in queriables.getQueriables():
                     yield unicode(i)+'.'+unicode(qid), queriable
-            auth = queryNextUtility(auth, IAuthenticationUtility)
+            auth = queryNextUtility(auth, IAuthentication)
             if auth is None:
                 break
             i += 1

Modified: Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/browser/granting.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/browser/granting.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/browser/granting.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -8,7 +8,7 @@
    tal:condition="status"
    tal:content="status" />
 
-<form action="" method="POST">
+<form action="" method="post">
 <p i18n:translate="">Select a principal:</p>
 <div tal:content="structure view/principal_widget">...</div>
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/browser/granting.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/browser/granting.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/browser/granting.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -29,11 +29,11 @@
     >>> class Principal:
     ...     def __init__(self, id, title): self.id, self.title = id, title
 
-    >>> from zope.app.security.interfaces import IAuthenticationUtility
+    >>> from zope.app.security.interfaces import IAuthentication
     >>> from zope.app.security.interfaces import PrincipalLookupError
     >>> from zope.interface import implements
     >>> class AuthUtility:
-    ...     implements(IAuthenticationUtility)
+    ...     implements(IAuthentication)
     ...     data = {'jim': Principal('jim', 'Jim Fulton'),
     ...             'stephan': Principal('stephan', 'Stephan Richter')}
     ...
@@ -48,7 +48,7 @@
     ...                 for principal in self.data.values()
     ...                 if search in principal.title]
 
-    >>> ztapi.provideUtility(IAuthenticationUtility, AuthUtility())
+    >>> ztapi.provideUtility(IAuthentication, AuthUtility())
 
   - Security-related Adapters
 
@@ -91,7 +91,7 @@
 
     >>> from zope.app.security.browser.auth import AuthUtilitySearchView
     >>> from zope.app.form.browser.interfaces import ISourceQueryView
-    >>> ztapi.browserViewProviding(IAuthenticationUtility, 
+    >>> ztapi.browserViewProviding(IAuthentication, 
     ...                            AuthUtilitySearchView,
     ...                            ISourceQueryView)
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_principalpermissionmanager.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_principalpermissionmanager.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_principalpermissionmanager.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -22,7 +22,7 @@
 from zope.app.testing.placelesssetup import PlacelessSetup
 
 from zope.app.security.interfaces import IPermission
-from zope.app.security.interfaces import IAuthenticationUtility
+from zope.app.security.interfaces import IAuthentication
 from zope.app.security.permission import Permission
 
 from zope.app.security.settings import Allow, Deny, Unset
@@ -41,7 +41,7 @@
 
     def setUp(self):
         super(Test, self).setUp()
-        ztapi.provideUtility(IAuthenticationUtility, principalRegistry)
+        ztapi.provideUtility(IAuthentication, principalRegistry)
 
 
     def _make_principal(self, id=None, title=None):

Modified: Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_principalrolemanager.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_principalrolemanager.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_principalrolemanager.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -21,7 +21,7 @@
 from zope.app.testing import ztapi
 from zope.app.testing.placelesssetup import PlacelessSetup
 
-from zope.app.security.interfaces import IAuthenticationUtility
+from zope.app.security.interfaces import IAuthentication
 from zope.app.security.settings import Allow, Deny
 from zope.app.security.principalregistry import principalRegistry
 
@@ -38,7 +38,7 @@
 
     def setUp(self):
         super(Test, self).setUp()
-        ztapi.provideUtility(IAuthenticationUtility, principalRegistry)
+        ztapi.provideUtility(IAuthentication, principalRegistry)
 
     def _make_principal(self, id=None, title=None):
         p = principalRegistry.definePrincipal(

Modified: Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_securitydirectives.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_securitydirectives.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/tests/test_securitydirectives.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -26,7 +26,7 @@
 from zope.app.testing.placelesssetup import PlacelessSetup
 
 from zope.app.security.interfaces import IPermission
-from zope.app.security.interfaces import IAuthenticationUtility
+from zope.app.security.interfaces import IAuthentication
 from zope.app.security.permission import Permission
 from zope.app.security.settings import Allow
 from zope.app.security.principalregistry import principalRegistry
@@ -52,7 +52,7 @@
 
     def setUp(self):
         super(TestBase, self).setUp()
-        ztapi.provideUtility(IAuthenticationUtility, principalRegistry)
+        ztapi.provideUtility(IAuthentication, principalRegistry)
 
 
 class TestRoleDirective(TestBase, unittest.TestCase):

Modified: Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/zopepolicy.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/zopepolicy.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/securitypolicy/zopepolicy.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -75,7 +75,7 @@
   >>> interaction.checkPermission('P1', ob)
   False
 
-But CheckerPublic permission id always have a permission:
+Note, however, that we always have the CheckerPublic permission:
 
   >>> from zope.security.checker import CheckerPublic
   >>> interaction.checkPermission(CheckerPublic, ob)
@@ -496,16 +496,16 @@
 For our examples here, we'll create and register a stub principal
 authentication service:
 
-  >>> from zope.app.security.interfaces import IAuthenticationUtility
+  >>> from zope.app.security.interfaces import IAuthentication
   >>> class FauxPrincipals(dict):
-  ...     zope.interface.implements(IAuthenticationUtility)
+  ...     zope.interface.implements(IAuthentication)
   ...     def getPrincipal(self, id):
   ...         return self[id]
 
   >>> auth = FauxPrincipals()
 
   >>> from zope.app.testing import ztapi
-  >>> ztapi.provideUtility(IAuthenticationUtility, auth)
+  >>> ztapi.provideUtility(IAuthentication, auth)
   >>> from zope.app import zapi
 
 Let's define a group:

Added: Zope3/branches/srichter-blow-services/src/zope/app/site/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/site/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/site/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -0,0 +1 @@
+# Import this

Added: Zope3/branches/srichter-blow-services/src/zope/app/site/interfaces.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/site/interfaces.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/site/interfaces.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -0,0 +1,16 @@
+from zope.app.component.interfaces import registration
+
+class ILocalService(registration.IRegisterable):
+    """A local service isn't a local service if it doesn't implement this.
+
+    The contract of a local service includes collaboration with
+    services above it.  A local service should also implement
+    IRegisterable (which implies that it is adaptable to
+    IRegistered).  Implementing ILocalService implies this.
+    """
+class ISimpleService(ILocalService):
+    """Most local services should implement this instead of ILocalService.
+
+    It implies a specific way of implementing IRegisterable,
+    by subclassing IAttributeRegisterable.
+    """

Added: Zope3/branches/srichter-blow-services/src/zope/app/site/meta.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/site/meta.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/site/meta.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -0,0 +1,36 @@
+<configure
+    xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.zope.org/zope">
+
+    <meta:complexDirective
+        name="localService"
+        schema="zope.app.component.metadirectives.IClassDirective"
+        handler=".metaconfigure.LocalServiceDirective"
+        >
+
+      <meta:subdirective
+          name="implements"
+          schema="zope.app.component.metadirectives.IImplementsSubdirective"
+          />
+
+      <meta:subdirective
+          name="require"
+          schema="zope.app.component.metadirectives.IRequireSubdirective"
+          />
+
+      <meta:subdirective
+          name="allow"
+          schema="zope.app.component.metadirectives.IAllowSubdirective"
+          />
+
+      <meta:subdirective
+          name="factory"
+          schema="zope.app.component.metadirectives.IFactorySubdirective"
+          />
+
+    </meta:complexDirective>
+
+  </meta:directives>
+
+</configure>

Added: Zope3/branches/srichter-blow-services/src/zope/app/site/metaconfigure.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/site/metaconfigure.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/site/metaconfigure.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -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.
+#
+##############################################################################
+"""Local Service Directive
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.interface import classImplements
+from zope.app.component.contentdirective import ContentDirective
+
+from interfaces import ISimpleService
+
+
+class LocalServiceDirective(ContentDirective):
+
+    def __init__(self, _context, class_):
+        if not ISimpleService.implementedBy(class_):
+            classImplements(class_, ISimpleService)
+        super(LocalServiceDirective, self).__init__(_context, class_)

Added: Zope3/branches/srichter-blow-services/src/zope/app/site/tests/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/site/tests/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/site/tests/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -0,0 +1,6 @@
+# BBB: Goes away in 3.3
+
+from zope.app.component.testing import PlacefulSetup
+
+def test_suite():
+    return None

Added: Zope3/branches/srichter-blow-services/src/zope/app/site/tests/placefulsetup.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/site/tests/placefulsetup.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/site/tests/placefulsetup.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -0,0 +1 @@
+from zope.app.component.testing import *

Copied: Zope3/branches/srichter-blow-services/src/zope/app/table (from rev 28844, Zope3/trunk/src/zope/app/table)

Modified: Zope3/branches/srichter-blow-services/src/zope/app/workflow/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/workflow/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/workflow/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -45,7 +45,7 @@
       name="contents.html" />  
 
   <!-- ProcessInstanceContainerAdaptable 
-       XXX Commented Out .. is just a demo
+       Note: Commented Out ... it is just a demo
   <pages
       for="zope.app.workflow.interfaces.IProcessInstanceContainerAdaptable"
       permission="zope.workflow.UseProcessInstances"

Modified: Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/configure.zcml
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/configure.zcml	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/configure.zcml	2005-01-17 15:30:10 UTC (rev 28853)
@@ -160,7 +160,7 @@
       factory=".filteradapter.FilterAdapter"
       provides=".interfaces.IContentFilterAdapter"
       for="zope.app.annotation.interfaces.IAttributeAnnotatable"
-      permission="zope.View" /> <!-- XXX is this permission right? -->
+      permission="zope.View" /> <!-- TODO: is this permission right? -->
 
   <!--include file="testobject.zcml"/-->
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/contentworkflow_registry.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/contentworkflow_registry.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/contentworkflow_registry.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -16,7 +16,7 @@
     This screen let's you specify which content types (by interface) can
     receive which workflows (process definitions).</p>
 
-  <form action="." method="POST">
+  <form action="." method="post">
     <h3 i18n:translate="">Available Mappings</h3>
      
     <ul tal:condition="view/process_based">
@@ -72,4 +72,4 @@
  
 </div>
 </body> 
-</html>
\ No newline at end of file
+</html>

Modified: Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/definition_edit.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/definition_edit.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/definition_edit.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -12,7 +12,7 @@
      tal:condition="status"
      tal:content="status" />
 
-  <form action="./@@edit.html" method="POST">
+  <form action="./@@edit.html" method="post">
     <h3 i18n:translate="">Set Workflow-Relevant Data Schema</h3>
 
     <div class="row" tal:define="widget nocall:view/relevantDataSchema_widget">

Modified: Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/instance_manage.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/instance_manage.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/workflow/stateful/browser/instance_manage.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -65,7 +65,7 @@
      tal:condition="status"
      tal:content="status" />
 
-  <form name="." method="POST"> 
+  <form name="." method="post"> 
     
     <div metal:use-macro="context/@@form_macros/widget_rows" />
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/zapi/README.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/zapi/README.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/zapi/README.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -18,19 +18,19 @@
   Traceback (most recent call last):
   ...
   ComponentLookupError:
-  (<InterfaceClass zope.app.security.interfaces.IAuthenticationUtility>, '')
+  (<InterfaceClass zope.app.security.interfaces.IAuthentication>, '')
 
 
 But if we provide an authentication service:
 
   >>> import zope.interface
-  >>> from zope.app.security.interfaces import IAuthenticationUtility
+  >>> from zope.app.security.interfaces import IAuthentication
   >>> class FakeAuthenticationUtility:
-  ...     zope.interface.implements(IAuthenticationUtility)
+  ...     zope.interface.implements(IAuthentication)
   >>> fake = FakeAuthenticationUtility()
   
   >>> from zope.app.testing import ztapi
-  >>> ztapi.provideUtility(IAuthenticationUtility, fake)
+  >>> ztapi.provideUtility(IAuthentication, fake)
 
 Then we should be able to get the service back when we ask for the
 principals: 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/zapi/__init__.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/zapi/__init__.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/zapi/__init__.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -41,6 +41,6 @@
 name = getName
 
 def principals():
-    from zope.app.security.interfaces import IAuthenticationUtility
-    return getUtility(IAuthenticationUtility)
+    from zope.app.security.interfaces import IAuthentication
+    return getUtility(IAuthentication)
 

Modified: Zope3/branches/srichter-blow-services/src/zope/app/zptpage/browser/inlinecode.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/app/zptpage/browser/inlinecode.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/app/zptpage/browser/inlinecode.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -5,7 +5,7 @@
 
   <div metal:define-macro="body">
 
-    <form action="." tal:attributes="action request/URL" method="POST"
+    <form action="." tal:attributes="action request/URL" method="post"
           enctype="multipart/form-data">
 
       <div metal:define-macro="formbody">

Modified: Zope3/branches/srichter-blow-services/src/zope/component/README.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/component/README.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/component/README.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -3,7 +3,7 @@
 
 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.  
+kinds of components: adapters and utilities.
 
 Utilities
 ---------
@@ -93,7 +93,7 @@
     ...         print "Hello", self.person.name
 
 The class defines a constructor that takes an argument for every
-object adapted. 
+object adapted.
 
 We use `zope.component.adapts` to declare what we adapt.  If we
 declare the interfaces adapted and if we provide only one interface,
@@ -107,7 +107,7 @@
 
     >>> class Person:
     ...     zope.interface.implements(IPerson)
-    ... 
+    ...
     ...     def __init__(self, name):
     ...         self.name = name
 
@@ -131,7 +131,7 @@
     ...     name = "Ted"
 
     >>> zope.component.provideAdapter(
-    ...     factory=TedPersonGreeter, adapts=[IPerson], 
+    ...     factory=TedPersonGreeter, adapts=[IPerson],
     ...     provides=IGreeter, name='ted')
 
 For named adapters, use `queryAdapter`, or `getAdapter`:
@@ -141,7 +141,7 @@
 
     >>> zope.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:
 
@@ -181,6 +181,6 @@
 
 
 .. [1] CAUTION: This API should only be used from test or
-       application-setup code. This api shouldn't be used by regular
+       application-setup code. This API shouldn't be used by regular
        library modules, as component registration is a configuration
-       activity. 
+       activity.

Modified: Zope3/branches/srichter-blow-services/src/zope/component/bbb/service.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/component/bbb/service.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/component/bbb/service.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -165,5 +165,8 @@
     return GlobalServiceManager('serviceManager', __name__, sitemanager)
 
 def defineService(name, interface, sitemanager=None):
-    __getSM().defineService(name, interface)
+    if sitemanager is None:
+        from zope.component.site import globalSiteManager
+        sitemanager = globalSiteManager
+    __getSM(sitemanager).defineService(name, interface)
 

Modified: Zope3/branches/srichter-blow-services/src/zope/event/README.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/event/README.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/event/README.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -4,12 +4,12 @@
 This package provides a simple event system on which
 application-specific event systems can be built.
 
-Application code can generate events without be concerned about the
+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. 
+points.
 
 The package has a list of subscribers.  Application code can manage
 subscriptions by manipulating this list.  For the examples here, we'll

Modified: Zope3/branches/srichter-blow-services/src/zope/interface/README.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/interface/README.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/interface/README.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -6,7 +6,7 @@
 
 Interfaces are objects that specify (document) the external behavior
 of objects that "provide" them.  An interface specifies behavior
-through: 
+through:
 
 - Informal documentation in a doc string
 
@@ -42,18 +42,18 @@
 
   >>> type(IFoo)
   <class 'zope.interface.interface.InterfaceClass'>
-  
-We can ask for the interfaces documentation::
 
+We can ask for the interface's documentation::
+
   >>> IFoo.__doc__
   'Foo blah blah'
 
-and it's name::
+and its name::
 
   >>> IFoo.__name__
   'IFoo'
 
-and even it's module::
+and even its module::
 
   >>> IFoo.__module__
   '__main__'
@@ -158,7 +158,7 @@
   ...
   ...     def __init__(self, x=None):
   ...         self.x = x
-  ...     
+  ...
   ...     def bar(self, q, r=None):
   ...         return q, r, self.x
   ...
@@ -173,7 +173,7 @@
 
   >>> IFoo.implementedBy(Foo)
   True
-  
+
 And we can ask whether an interface is provided by an object::
 
   >>> foo = Foo()
@@ -220,7 +220,7 @@
   ...
   ...     def __call__(x=None):
   ...         """Create a foo
-  ...         
+  ...
   ...         The argument provides the initial value for x ...
   ...         """
 
@@ -246,7 +246,7 @@
   ...
   ...     def __init__(self, x=None):
   ...         self.x = x
-  ...     
+  ...
   ...     def bar(self, q, r=None):
   ...         return q, r, self.x
   ...
@@ -380,7 +380,7 @@
   >>> class Special2(Foo):
   ...     zope.interface.implementsOnly(
   ...          zope.interface.implementedBy(Foo),
-  ...          ISpecial, 
+  ...          ISpecial,
   ...          )
   ...     reason = 'I just am'
   ...     def brag(self):
@@ -402,7 +402,7 @@
 
   >>> class IBlat(zope.interface.Interface):
   ...     """Blat blah blah"""
-  ...  
+  ...
   ...     y = zope.interface.Attribute("y blah blah")
   ...     def eek():
   ...         """eek blah blah"""
@@ -414,11 +414,11 @@
   ...     """Baz blah"""
   ...     def eek(a=1):
   ...         """eek in baz blah"""
-  ... 
+  ...
 
   >>> IBaz.__bases__
   (<InterfaceClass __main__.IFoo>, <InterfaceClass __main__.IBlat>)
-  
+
   >>> names = list(IBaz)
   >>> names.sort()
   >>> names
@@ -472,7 +472,7 @@
 inherited from the most specific interface. For example, with:
 
   >>> class IBase(zope.interface.Interface):
-  ...    
+  ...
   ...     def foo():
   ...         "base foo doc"
 
@@ -480,7 +480,7 @@
   ...     pass
 
   >>> class IBase2(IBase):
-  ...    
+  ...
   ...     def foo():
   ...         "base2 foo doc"
 
@@ -531,10 +531,10 @@
 that lists the specification and all of it's ancestors:
 
   >>> baz_implements.__sro__
-  (<implementedBy __main__.Baz>, 
-   <InterfaceClass __main__.IBaz>, 
-   <InterfaceClass __main__.IFoo>, 
-   <InterfaceClass __main__.IBlat>, 
+  (<implementedBy __main__.Baz>,
+   <InterfaceClass __main__.IBaz>,
+   <InterfaceClass __main__.IFoo>,
+   <InterfaceClass __main__.IBlat>,
    <InterfaceClass zope.interface.Interface>)
 
 
@@ -625,7 +625,7 @@
   Invalid: [RangeError(Range(2, 1))]
 
 And the list will be filled with the individual exceptions::
-  
+
   >>> errors
   [RangeError(Range(2, 1))]
 

Modified: Zope3/branches/srichter-blow-services/src/zope/interface/adapter.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/interface/adapter.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/interface/adapter.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -625,15 +625,15 @@
 
     # This dictionary is used to catch situations specific adapters
     # override less specific adapters.
-    # Because subscriptions are cummulative, registered doesn't apply.
+    # Because subscriptions are cumulative, registered doesn't apply.
     registered = {}
 
     # Add adapters and interfaces directly implied by same:
 
     for key, value in adapters.iteritems():
 
-        # TODO: Backward compatability
-        # Don't need to handle 3-tuples some day
+        # TODO: Backward compatibility
+        # BBB ? Don't need to handle 3-tuples some day
         try:
             (subscription, with, name, target) = key
         except ValueError:

Modified: Zope3/branches/srichter-blow-services/src/zope/schema/README.txt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/schema/README.txt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/schema/README.txt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -17,7 +17,7 @@
 Zope 3 schemas were born when Jim Fulton and Martijn Faassen thought
 about Formulator for Zope 3 and PropertySets while at the `Zope 3
 sprint`_ at the `Zope BBQ`_ in Berlin.  They realized that if you strip
-all view logic from forms than you have something to interfaces.  And
+all view logic from forms then you have something similar to interfaces.  And
 thus schemas were born.
 
 .. _Zope 3 sprint: http://dev.zope.org/Zope3/ZopeBBQ2002Sprint

Copied: Zope3/branches/srichter-blow-services/src/zope/testing/formparser.py (from rev 28844, Zope3/trunk/src/zope/testing/formparser.py)


Property changes on: Zope3/branches/srichter-blow-services/src/zope/testing/formparser.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native

Copied: Zope3/branches/srichter-blow-services/src/zope/testing/formparser.txt (from rev 28844, Zope3/trunk/src/zope/testing/formparser.txt)


Property changes on: Zope3/branches/srichter-blow-services/src/zope/testing/formparser.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:eol-style
   + native

Modified: Zope3/branches/srichter-blow-services/src/zope/testing/tests.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zope/testing/tests.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zope/testing/tests.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -16,11 +16,12 @@
 $Id$
 """
 import unittest
-from zope.testing.doctestunit import DocTestSuite
+from zope.testing.doctestunit import DocTestSuite, DocFileSuite
 
 
 def test_suite():
     return unittest.TestSuite((
+        DocFileSuite('formparser.txt'),
         DocTestSuite('zope.testing.loggingsupport'),
         ))
 

Modified: Zope3/branches/srichter-blow-services/src/zwiki/browser/add.pt
===================================================================
--- Zope3/branches/srichter-blow-services/src/zwiki/browser/add.pt	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zwiki/browser/add.pt	2005-01-17 15:30:10 UTC (rev 28853)
@@ -2,7 +2,7 @@
 <body>
 
 <div metal:fill-slot="body">
-<form action="action.html" method="POST">
+<form action="action.html" method="post">
 <table class="TypeListing" cellpadding="3">
 
   <caption>Add Content</caption>

Modified: Zope3/branches/srichter-blow-services/src/zwiki/browser/wikipage.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zwiki/browser/wikipage.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zwiki/browser/wikipage.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -126,7 +126,7 @@
     def render(self):
         """Render the wiki page source."""
         source = zapi.createObject(None, self.context.type, self.context.source)
-        view = zapi.getView(removeAllProxies(source), '', self.request)
+        view = zapi.getMultiAdapter((removeAllProxies(source), self.request))
         html = view.render()
         html = self.renderWikiLinks(html)
         return html
@@ -136,7 +136,8 @@
         for name, comment in self.context.items():
             dc = DublinCoreViews(comment, self.request)
             source = zapi.createObject(None, comment.type, comment.source)
-            view = zapi.getView(removeAllProxies(source), '', self.request)
+            view = zapi.getMultiAdapter(
+                (removeAllProxies(source), self.request))
             result.append({
                 'name': name,
                 'title': comment.title,

Modified: Zope3/branches/srichter-blow-services/src/zwiki/traversal.py
===================================================================
--- Zope3/branches/srichter-blow-services/src/zwiki/traversal.py	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/src/zwiki/traversal.py	2005-01-17 15:30:10 UTC (rev 28853)
@@ -15,7 +15,6 @@
 """
 from zope.interface import implements
 from zope.proxy import removeAllProxies
-from zope.component import getDefaultViewName, queryView
 from zope.publisher.interfaces import IPublishTraverse
 from zope.publisher.interfaces import NotFound
 from zope.app.traversing.interfaces import TraversalError
@@ -43,7 +42,7 @@
         if page is None or \
            not zapi.getName(self.context) in IWikiPageHierarchy(page).parents:
 
-            view = queryView(self.context, name, request)
+            view = zapi.queryMultiAdapter((self.context, request), name=name)
             if view is not None:
                 return view
 
@@ -54,7 +53,7 @@
 
     def browserDefault(self, request):
         c = self.context
-        view_name = getDefaultViewName(c, request)
+        view_name = zapi.getDefaultViewName(c, request)
         view_uri = "@@%s" % view_name
         return c, (view_uri,)
 

Modified: Zope3/branches/srichter-blow-services/zopeskel/bin/debugzope.in
===================================================================
--- Zope3/branches/srichter-blow-services/zopeskel/bin/debugzope.in	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/zopeskel/bin/debugzope.in	2005-01-17 15:30:10 UTC (rev 28853)
@@ -20,7 +20,7 @@
 import sys
 
 
-SOFTWARE_HOME = "<<SOFTWARE_HOME>>"
+SOFTWARE_HOME = r"<<SOFTWARE_HOME>>"
 INSTANCE_HOME = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
 CONFIG_FILE = os.path.join(INSTANCE_HOME, "etc", "zope.conf")
 

Modified: Zope3/branches/srichter-blow-services/zopeskel/bin/i18nextract.in
===================================================================
--- Zope3/branches/srichter-blow-services/zopeskel/bin/i18nextract.in	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/zopeskel/bin/i18nextract.in	2005-01-17 15:30:10 UTC (rev 28853)
@@ -45,8 +45,8 @@
 """
 import os, sys, getopt
 
-SOFTWARE_HOME = "<<SOFTWARE_HOME>>"
-INSTANCE_HOME = "<<INSTANCE_HOME>>"
+SOFTWARE_HOME = r"<<SOFTWARE_HOME>>"
+INSTANCE_HOME = r"<<INSTANCE_HOME>>"
 
 def usage(code, msg=''):
     # Python 2.1 required

Modified: Zope3/branches/srichter-blow-services/zopeskel/bin/pyskel.in
===================================================================
--- Zope3/branches/srichter-blow-services/zopeskel/bin/pyskel.in	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/zopeskel/bin/pyskel.in	2005-01-17 15:30:10 UTC (rev 28853)
@@ -29,8 +29,8 @@
 import sys, os, re
 from types import ModuleType
 
-SOFTWARE_HOME = "<<SOFTWARE_HOME>>"
-INSTANCE_HOME = "<<INSTANCE_HOME>>"
+SOFTWARE_HOME = r"<<SOFTWARE_HOME>>"
+INSTANCE_HOME = r"<<INSTANCE_HOME>>"
 
 sys.path.insert(0, os.getcwd())
 

Modified: Zope3/branches/srichter-blow-services/zopeskel/bin/test.in
===================================================================
--- Zope3/branches/srichter-blow-services/zopeskel/bin/test.in	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/zopeskel/bin/test.in	2005-01-17 15:30:10 UTC (rev 28853)
@@ -21,7 +21,7 @@
 here = os.path.dirname(os.path.realpath(__file__))
 here = os.path.dirname(here)
 
-SOFTWARE_HOME = "<<SOFTWARE_HOME>>"
+SOFTWARE_HOME = r"<<SOFTWARE_HOME>>"
 
 instance_lib = os.path.join(here, "lib", "python")
 

Modified: Zope3/branches/srichter-blow-services/zopeskel/bin/zopectl.in
===================================================================
--- Zope3/branches/srichter-blow-services/zopeskel/bin/zopectl.in	2005-01-17 00:21:45 UTC (rev 28852)
+++ Zope3/branches/srichter-blow-services/zopeskel/bin/zopectl.in	2005-01-17 15:30:10 UTC (rev 28853)
@@ -20,7 +20,7 @@
 import sys
 
 
-SOFTWARE_HOME = "<<SOFTWARE_HOME>>"
+SOFTWARE_HOME = r"<<SOFTWARE_HOME>>"
 INSTANCE_HOME = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
 CONFIG_FILE = os.path.join(INSTANCE_HOME, "etc", "zdaemon.conf")
 



More information about the Zope3-Checkins mailing list