[Checkins] SVN: z3c.extfile/trunk/ added static namespace, see CHANGES.txt

Bernd Dorn bernd.dorn at lovelysystems.com
Wed May 23 05:03:13 EDT 2007


Log message for revision 75902:
  added static namespace, see CHANGES.txt

Changed:
  U   z3c.extfile/trunk/CHANGES.txt
  U   z3c.extfile/trunk/setup.py
  U   z3c.extfile/trunk/src/z3c/extfile/browser/configure.zcml
  A   z3c.extfile/trunk/src/z3c/extfile/browser/views.py
  U   z3c.extfile/trunk/src/z3c/extfile/configure.zcml
  A   z3c.extfile/trunk/src/z3c/extfile/datamanager.py
  U   z3c.extfile/trunk/src/z3c/extfile/hashdir.py
  U   z3c.extfile/trunk/src/z3c/extfile/hashdir.txt
  U   z3c.extfile/trunk/src/z3c/extfile/interfaces.py
  A   z3c.extfile/trunk/src/z3c/extfile/namespace.py
  A   z3c.extfile/trunk/src/z3c/extfile/namespace.txt
  A   z3c.extfile/trunk/src/z3c/extfile/namespace.zcml
  U   z3c.extfile/trunk/src/z3c/extfile/property.py
  U   z3c.extfile/trunk/src/z3c/extfile/tests.py

-=-
Modified: z3c.extfile/trunk/CHANGES.txt
===================================================================
--- z3c.extfile/trunk/CHANGES.txt	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/CHANGES.txt	2007-05-23 09:03:12 UTC (rev 75902)
@@ -8,3 +8,7 @@
 - Use product config z3c.extfile:storagedir to set the storage directory
 
 - Registration of utility is now logged on INFO
+
+- New ++static++ namespace and index.html view for IReadFile, see
+  namespace.txt. This adds a dependency to z3c.filetype.
+

Modified: z3c.extfile/trunk/setup.py
===================================================================
--- z3c.extfile/trunk/setup.py	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/setup.py	2007-05-23 09:03:12 UTC (rev 75902)
@@ -19,17 +19,23 @@
     namespace_packages = ['z3c'],
     zip_safe = False,
     extras_require={"test":['zope.testing',
+                            'zope.app.testing',
                             ]},
     install_requires = ['setuptools',
+                        'z3c.filetype',
+                        'zope.app.component',
                         'zope.app.appsetup',
                         'zope.app.file',
                         'zope.app.form',
                         'zope.app.wsgi',
+                        'zope.cachedescriptors',
                         'zope.contenttype',
+                        'zope.datetime',
                         'zope.formlib',
                         'zope.i18nmessageid',
                         'zope.publisher',
                         'zope.schema',
-                        'zope.security'
+                        'zope.security',
+                        'zope.traversing',
                         ],
     )

Modified: z3c.extfile/trunk/src/z3c/extfile/browser/configure.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/browser/configure.zcml	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/src/z3c/extfile/browser/configure.zcml	2007-05-23 09:03:12 UTC (rev 75902)
@@ -4,13 +4,38 @@
     i18n_domain="zope"
     >
 
- <view
-  type="zope.publisher.interfaces.browser.IBrowserRequest"
-  for="..interfaces.IExtBytesField"
-  provides="zope.app.form.interfaces.IInputWidget"
-  factory=".widget.ExtBytesWidget"
-  permission="zope.Public"
-  />
- 
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="..interfaces.IExtBytesField"
+      provides="zope.app.form.interfaces.IInputWidget"
+      factory=".widget.ExtBytesWidget"
+      permission="zope.Public"
+      />
+
+  <browser:page
+      for="..interfaces.IReadFile"
+      name="index.html"
+      permission="zope.View"
+      class=".views.ReadFileView"/>
+
+  <view
+      for="..interfaces.IReadFile"
+      name="absolute_url"
+      factory=".views.ReadFileAbsoluteURL"
+      type="zope.publisher.interfaces.http.IHTTPRequest"
+      permission="zope.Public"
+      allowed_interface="zope.traversing.browser.interfaces.IAbsoluteURL"
+      />
+
+  <view
+      for="..interfaces.IReadFile"
+      factory=".views.ReadFileAbsoluteURL"
+      type="zope.publisher.interfaces.http.IHTTPRequest"
+      permission="zope.Public"
+      provides="zope.traversing.browser.interfaces.IAbsoluteURL"
+      />
+
 </configure>
 
+
+

Added: z3c.extfile/trunk/src/z3c/extfile/browser/views.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/browser/views.py	                        (rev 0)
+++ z3c.extfile/trunk/src/z3c/extfile/browser/views.py	2007-05-23 09:03:12 UTC (rev 75902)
@@ -0,0 +1,51 @@
+from z3c.filetype import api
+from z3c.filetype.interfaces import filetypes
+from zope import interface
+from zope.cachedescriptors.property import Lazy
+import zope.datetime
+from zope.publisher.browser import BrowserView
+from zope.traversing.browser import absoluteurl
+from zope.app.component import hooks
+
+class ReadFileAbsoluteURL(absoluteurl.SiteAbsoluteURL):
+
+    def __str__(self):
+        siteURL = absoluteurl.absoluteURL(hooks.getSite(), self.request)
+        return '%s/++static++%s' % (siteURL, self.context.digest)
+
+    __call__ = __str__
+
+class ReadFileView(BrowserView):
+
+    @Lazy
+    def contentType(self):
+        ifaces = api.getInterfacesFor(self.context)
+        decl = interface.Declaration(ifaces)
+        for iface in decl.flattened():
+            mt = iface.queryTaggedValue(filetypes.MT)
+            if mt is not None:
+                return mt
+        raise ValueError, "Unable to detect content type %r" % self.context
+
+    def __call__(self):
+        """Sets various headers if the request for IFLVFile."""
+
+        self.request.response.setHeader('Content-Type',
+                                        self.contentType)
+        self.request.response.setHeader('Content-Length',
+                                        len(self.context))
+        ms= self.request.getHeader('If-Modified-Since', None)
+        if ms is not None:
+            # we cannot be modified, so we can return a 304 anytime
+            self.request.response.setStatus(304)
+            return ''
+        # set the modified header to creation time
+        self.request.response.setHeader(
+            'Last-Modified',
+            zope.datetime.rfc1123_date(self.context.ctime))
+        # let it cache forever (360 days)
+        self.request.response.setHeader(
+            'Cache-Control',
+            's-max-age=31104000; max-age=31104000')
+        return self.context
+


Property changes on: z3c.extfile/trunk/src/z3c/extfile/browser/views.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: z3c.extfile/trunk/src/z3c/extfile/configure.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/configure.zcml	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/src/z3c/extfile/configure.zcml	2007-05-23 09:03:12 UTC (rev 75902)
@@ -1,35 +1,36 @@
 <configure
- xmlns="http://namespaces.zope.org/zope"
- i18n_domain='zope'
- >
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain='zope'
+    >
 
- <adapter factory=".adapter.ReadFileResult" />
- 
- <class class=".hashdir.ReadFile">
-  <allow interface=".interfaces.IReadFile"/>
- </class>
- 
- <class class=".hashdir.HashDir">
+  <adapter factory=".adapter.ReadFileResult" />
 
-  <implements
-   interface="zope.annotation.interfaces.IAttributeAnnotatable" />
+  <class class=".hashdir.ReadFile">
+    <allow interface=".interfaces.IReadFile"/>
+  </class>
 
-  <require
-   permission="zope.ManageServices"
-   interface=".interfaces.IHashDir"
-   />
+  <class class=".hashdir.HashDir">
 
-  <require
-   permission="zope.ManageServices"
-   set_schema=".interfaces.IHashDir"
-   />
+    <implements
+        interface="zope.annotation.interfaces.IAttributeAnnotatable" />
 
+    <require
+        permission="zope.ManageServices"
+        interface=".interfaces.IHashDir"
+        />
+
+    <require
+        permission="zope.ManageServices"
+        set_schema=".interfaces.IHashDir"
+        />
+
   </class>
 
   <subscriber
-   for="zope.app.appsetup.IDatabaseOpenedEvent"
-   handler=".utility.bootStrapSubscriber"
-   />
-  
- <include package=".browser"/>
+      for="zope.app.appsetup.IDatabaseOpenedEvent"
+      handler=".utility.bootStrapSubscriber"
+      />
+
+  <include package=".browser"/>
+
 </configure>
\ No newline at end of file

Added: z3c.extfile/trunk/src/z3c/extfile/datamanager.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/datamanager.py	                        (rev 0)
+++ z3c.extfile/trunk/src/z3c/extfile/datamanager.py	2007-05-23 09:03:12 UTC (rev 75902)
@@ -0,0 +1,61 @@
+from zope import thread
+from zope import interface
+from zope import component
+from transaction.interfaces import IDataManager
+import interfaces
+import transaction
+
+_storage = thread.local()
+
+def getFile(digest):
+    if not hasattr(_storage, 'dataManager'):
+        _storage.dataManager = ReadFileDataManager()
+        txn = transaction.manager.get()
+        if txn is not None:
+            txn.join(_storage.dataManager)
+    return _storage.dataManager.getFile(digest)
+
+
+class ReadFileDataManager(object):
+
+    """Takes care of closing open files"""
+
+    interface.implements(IDataManager)
+
+    def __init__(self):
+        self.files = {}
+
+    @property
+    def hd(self):
+        return component.getUtility(interfaces.IHashDir)
+
+    def getFile(self, digest):
+        if digest in self.files:
+            return self.files[digest]
+        self.files[digest] = self.hd.open(digest)
+        return self.files[digest]
+
+    def _close(self):
+        for f in self.files.values():
+            f.close()
+
+    def abort(self, trans):
+        self._close()
+
+    def tpc_begin(self, trans):
+        pass
+
+    def commit(self, trans):
+        self._close()
+
+    def tpc_vote(self, trans):
+        pass
+
+    def tpc_finish(self, trans):
+        self._close()
+
+    def tpc_abort(self, trans):
+        self._close()
+
+    def sortKey(self):
+        return str(id(self))


Property changes on: z3c.extfile/trunk/src/z3c/extfile/datamanager.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: z3c.extfile/trunk/src/z3c/extfile/hashdir.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/hashdir.py	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/src/z3c/extfile/hashdir.py	2007-05-23 09:03:12 UTC (rev 75902)
@@ -7,8 +7,8 @@
 import interfaces
 from zope import interface
 from persistent import Persistent
+from zope.cachedescriptors.property import Lazy
 
-
 class HashDir(Persistent):
 
     """a directory holding files named after their sha1 hash"""
@@ -26,7 +26,7 @@
         self.tmp = os.path.join(self.path, 'tmp')
         self.var = os.path.join(self.path, 'var')
         self._initPaths()
-        
+
     def _getPath(self):
         return self._path
 
@@ -36,7 +36,7 @@
         for path in [self.path,self.var,self.tmp]:
             if not os.path.exists(path):
                 os.mkdir(path)
-            
+
     def new(self):
         """returns a new filehandle"""
         handle, path = tempfile.mkstemp(prefix='dirty.',
@@ -53,7 +53,7 @@
         else:
             shutil.move(f.path, target)
         return digest
-        
+
     def digests(self):
         """returns all digests stored"""
         return os.listdir(self.var)
@@ -71,8 +71,8 @@
 
     def open(self, digest):
         return ReadFile(self.getPath(digest))
-        
 
+
 class ReadFile(object):
 
     """A lazy read file implementation"""
@@ -92,7 +92,15 @@
             return self._v_file
         self._v_file = file(self.name, 'rb', self.bufsize)
         return self._v_file
-    
+
+    @Lazy
+    def ctime(self):
+        return int(os.stat(self.name)[stat.ST_CTIME])
+
+    @Lazy
+    def atime(self):
+        return int(os.stat(self.name)[stat.ST_ATIME])
+
     def __len__(self):
         if self._v_len is None:
             self._v_len = int(os.stat(self.name)[stat.ST_SIZE])
@@ -130,7 +138,7 @@
         if not self.closed:
             self._v_file.close()
         self._v_file = None
-        
+
     def fileno(self):
         return self._file.fileno()
 

Modified: z3c.extfile/trunk/src/z3c/extfile/hashdir.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/hashdir.txt	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/src/z3c/extfile/hashdir.txt	2007-05-23 09:03:12 UTC (rev 75902)
@@ -68,6 +68,15 @@
   >>> f.digest
   '0db0e5fa1ecf3e7659504f2e4048434cd9f20d2d'
 
+We can get the creation- and access time of the file as timestamp. For
+testing we just look if it is smaller than now.
+
+  >>> import time
+  >>> f.ctime < time.time()
+  True
+  >>> f.atime >= f.ctime
+  True
+
 The seek and tell operations are supported.
 
   >>> f.seek(1)

Modified: z3c.extfile/trunk/src/z3c/extfile/interfaces.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/interfaces.py	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/src/z3c/extfile/interfaces.py	2007-05-23 09:03:12 UTC (rev 75902)
@@ -21,6 +21,8 @@
 
     digest = schema.ASCII(title=u'Digest', readonly=True)
     closed = schema.Bool(title=u'Closed', readonly=True)
+    ctime = schema.Float(title=u'Creation Time', readonly=True)
+    atime = schema.Float(title=u'Access Time', readonly=True)
 
     def __len__():
         """returns the length/size of file"""
@@ -40,6 +42,8 @@
     def __iter__():
         """see file.__iter__"""
 
+
+
 class IWriteFile(IFile):
 
     def write(s):

Added: z3c.extfile/trunk/src/z3c/extfile/namespace.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/namespace.py	                        (rev 0)
+++ z3c.extfile/trunk/src/z3c/extfile/namespace.py	2007-05-23 09:03:12 UTC (rev 75902)
@@ -0,0 +1,15 @@
+from zope.traversing.namespace import view
+from zope.traversing.interfaces import TraversalError
+import datamanager
+
+class Static(view):
+
+    def traverse(self, name, ignored):
+        if len(name)==40:
+            try:
+                return datamanager.getFile(str(name))
+            except KeyError:
+                pass
+        raise TraversalError(self.context, name)
+
+


Property changes on: z3c.extfile/trunk/src/z3c/extfile/namespace.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: z3c.extfile/trunk/src/z3c/extfile/namespace.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/namespace.txt	                        (rev 0)
+++ z3c.extfile/trunk/src/z3c/extfile/namespace.txt	2007-05-23 09:03:12 UTC (rev 75902)
@@ -0,0 +1,112 @@
+=========================
+Static Readfile Namespace
+=========================
+
+The static namespace returns an IReadFiel object like the
+ExtBytesProperty does. The name is the digest of the file.
+
+Security Warning! Note that this namespace reveals content, that might
+be protected in other cases. It is not enabled by default. If you want
+to enable it include the 'namespace.zcml' file.
+
+At first we need some setup.
+
+  >>> from z3c.extfile import hashdir
+  >>> import tempfile, os
+  >>> tmp = tempfile.mkdtemp()
+  >>> hdPath = os.path.join(tmp, 'testhashdir')
+  >>> hd = hashdir.HashDir(hdPath)
+  >>> from zope import component
+  >>> from z3c.extfile.interfaces import IHashDir
+  >>> component.provideUtility(hd, provides=IHashDir)
+
+Let us make a test object whith an ExtBytesProperty in order to get
+some content into our hash directory.
+
+  >>> from z3c.extfile.property import ExtBytesProperty
+  >>> from cStringIO import StringIO
+  >>> class Foo(object):
+  ...     data1 = ExtBytesProperty('data1')
+  ...     data2 = ExtBytesProperty('data2')
+  >>> foo = Foo()
+  >>> foo.data1 = StringIO('contents of foo.data1')
+  >>> foo.data2 = StringIO('contents of foo.data2')
+
+Now our namespace should return a readfile.
+
+  >>> from z3c.extfile.namespace import Static
+  >>> ns = Static(None, None)
+
+A bad hash does not work:
+
+  >>> ns.traverse(u'not a hash', None)
+  Traceback (most recent call last):
+  ...
+  TraversalError: (None, u'not a hash')
+
+Existing hashes return a ReadFile.
+
+  >>> ns.traverse(foo.data1.digest, None)
+  <ReadFile named 'b34d7a2ebbef51196b8db24f6233e750d6a30e18'>
+  >>> ns.traverse(foo.data2.digest, None)
+  <ReadFile named '8923e22aa8772e3525dc91f008fc10ce6812b39d'>
+
+A non existing hash also raises a traversal error.
+
+  >>> ns.traverse('b34d7a2ebbef51196b8db24f6233e750d6a30e10', None)
+  Traceback (most recent call last):
+  ...
+  TraversalError: (None, 'b34d7a2ebbef51196b8db24f6233e750d6a30e10')
+
+Default Browserview
+===================
+
+When we use the namespace with a browser, we will get the default view
+implementation of Readfile.
+
+  >>> from z3c.extfile.browser import views
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+  >>> view = views.ReadFileView(foo.data1, request)
+
+The view returns the readfile directly, which is actually a file and
+will be directly and efficiently published by the publisher.
+
+  >>> view()
+  <ReadFile named 'b34d7a2ebbef51196b8db24f6233e750d6a30e18'>
+
+The view sets various headers. Note that the we can cache this view
+forever, so the cache time is set to 360 days. Also the
+"Last-Modified" header is set to the creation date of the file. The
+content type is guessed by using the z3c.filetype package, which uses
+magic numbers to get the type. For our sample content it can't find a
+type so it uses the fallback 'application/octet-stream'.
+
+  >>> request.response.getHeaders()
+  [('X-Powered-By', 'Zope (www.zope.org), Python (www.python.org)'),
+   ('Last-Modified', '...'), ('Content-Length', '21'),
+   ('Content-Type', 'application/octet-stream'),
+   ('Cache-Control', 's-max-age=31104000; max-age=31104000')]
+
+Absolute URLs
+=============
+
+With this namespace we now have an absolute url for each
+readfile. There is an absolute url view implemented for this.
+
+  >>> request = TestRequest()
+  >>> view = views.ReadFileAbsoluteURL(foo.data1, request)
+  >>> view()
+  'http://127.0.0.1/++static++b34d7a2ebbef51196b8db24f6233e750d6a30e18'
+  >>> str(view)
+  'http://127.0.0.1/++static++b34d7a2ebbef51196b8db24f6233e750d6a30e18'
+  >>> unicode(view)
+  u'http://127.0.0.1/++static++b34d7a2ebbef51196b8db24f6233e750d6a30e18'
+
+Cleanup
+
+  >>> import z3c.extfile.datamanager
+  >>> z3c.extfile.datamanager._storage.dataManager._close()
+  >>> import shutil
+  >>> shutil.rmtree(tmp)
+


Property changes on: z3c.extfile/trunk/src/z3c/extfile/namespace.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.extfile/trunk/src/z3c/extfile/namespace.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/namespace.zcml	                        (rev 0)
+++ z3c.extfile/trunk/src/z3c/extfile/namespace.zcml	2007-05-23 09:03:12 UTC (rev 75902)
@@ -0,0 +1,21 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain='zope'
+    >
+
+  <view
+      name="static"
+      type="zope.interface.Interface"
+      provides="zope.traversing.interfaces.ITraversable"
+      for="zope.app.component.interfaces.ISite"
+      factory=".namespace.Static"
+      />
+
+  <adapter
+      name="static"
+      provides="zope.traversing.interfaces.ITraversable"
+      for="zope.app.component.interfaces.ISite"
+      factory=".namespace.Static"
+      />
+
+</configure>
\ No newline at end of file


Property changes on: z3c.extfile/trunk/src/z3c/extfile/namespace.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: z3c.extfile/trunk/src/z3c/extfile/property.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/property.py	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/src/z3c/extfile/property.py	2007-05-23 09:03:12 UTC (rev 75902)
@@ -1,27 +1,24 @@
-from zope import component, interface
+from zope import component
 import interfaces
 from cStringIO import StringIO
-from transaction.interfaces import IDataManager
-from zope import thread
-import transaction
 
+from datamanager import getFile, _storage
+
 _marker = object()
+
 BLOCK_SIZE = 1024*128
 
-_storage = thread.local()
-
 class ExtBytesProperty(object):
 
     """a property which's values are stored as external files"""
 
     def __init__(self, name):
         self.__name = name
-        
 
     @property
     def hd(self):
         return component.getUtility(interfaces.IHashDir)
-    
+
     def __get__(self, inst, klass):
 
         if inst is None:
@@ -29,12 +26,7 @@
         digest = inst.__dict__.get(self.__name, _marker)
         if digest is _marker:
             return None
-        if not hasattr(_storage, 'dataManager'):
-            _storage.dataManager = ReadFileDataManager()
-            txn = transaction.manager.get()
-            if txn is not None:
-                txn.join(_storage.dataManager)
-        return _storage.dataManager.getFile(digest)
+        return getFile(digest)
 
     def __set__(self, inst, value):
         # ignore if value is None
@@ -66,46 +58,3 @@
             f.write(chunk)
 
 
-class ReadFileDataManager(object):
-
-    """Takes care of closing open files"""
-    
-    interface.implements(IDataManager)
-
-    def __init__(self):
-        self.files = {}
-        
-    @property
-    def hd(self):
-        return component.getUtility(interfaces.IHashDir)
-
-    def getFile(self, digest):
-        if digest in self.files:
-            return self.files[digest]
-        self.files[digest] = self.hd.open(digest)
-        return self.files[digest]
-
-    def _close(self):
-        for f in self.files.values():
-            f.close()
-        
-    def abort(self, trans):
-        self._close()
-
-    def tpc_begin(self, trans):
-        pass
-
-    def commit(self, trans):
-        self._close()
-
-    def tpc_vote(self, trans):
-        pass
-
-    def tpc_finish(self, trans):
-        self._close()
-
-    def tpc_abort(self, trans):
-        self._close()
-
-    def sortKey(self):
-        return str(id(self))

Modified: z3c.extfile/trunk/src/z3c/extfile/tests.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/tests.py	2007-05-23 02:58:48 UTC (rev 75901)
+++ z3c.extfile/trunk/src/z3c/extfile/tests.py	2007-05-23 09:03:12 UTC (rev 75902)
@@ -1,6 +1,8 @@
 import doctest
 import unittest
 from zope.testing.doctestunit import DocFileSuite, DocTestSuite
+from zope.app.testing import setup
+
 def test_suite():
     
     return unittest.TestSuite(
@@ -8,6 +10,11 @@
         DocFileSuite('hashdir.txt',
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                      ),
+        DocFileSuite('namespace.txt',
+                     setUp=setup.placefulSetUp,
+                     tearDown=lambda x: setup.placefulTearDown(),
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
         DocFileSuite('processor.txt',
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                      ),



More information about the Checkins mailing list