[Checkins] SVN: z3c.extfile/ initial import of the z3c.extfile package

Bernd Dorn bernd.dorn at fhv.at
Thu Aug 10 12:28:45 EDT 2006


Log message for revision 69391:
  initial import of the z3c.extfile package
  
  

Changed:
  A   z3c.extfile/
  A   z3c.extfile/trunk/
  A   z3c.extfile/trunk/src/
  A   z3c.extfile/trunk/src/z3c/
  A   z3c.extfile/trunk/src/z3c/__init__.py
  A   z3c.extfile/trunk/src/z3c/extfile/
  A   z3c.extfile/trunk/src/z3c/extfile/README.txt
  A   z3c.extfile/trunk/src/z3c/extfile/__init__.py
  A   z3c.extfile/trunk/src/z3c/extfile/browser/
  A   z3c.extfile/trunk/src/z3c/extfile/browser/__init__.py
  A   z3c.extfile/trunk/src/z3c/extfile/browser/configure.zcml
  A   z3c.extfile/trunk/src/z3c/extfile/browser/widget.py
  A   z3c.extfile/trunk/src/z3c/extfile/browser/widget.txt
  A   z3c.extfile/trunk/src/z3c/extfile/configure.zcml
  A   z3c.extfile/trunk/src/z3c/extfile/file/
  A   z3c.extfile/trunk/src/z3c/extfile/file/__init__.py
  A   z3c.extfile/trunk/src/z3c/extfile/file/browser/
  A   z3c.extfile/trunk/src/z3c/extfile/file/browser/__init__.py
  A   z3c.extfile/trunk/src/z3c/extfile/file/browser/configure.zcml
  A   z3c.extfile/trunk/src/z3c/extfile/file/browser/views.py
  A   z3c.extfile/trunk/src/z3c/extfile/file/configure.zcml
  A   z3c.extfile/trunk/src/z3c/extfile/file/file.py
  A   z3c.extfile/trunk/src/z3c/extfile/file/file.txt
  A   z3c.extfile/trunk/src/z3c/extfile/file/interfaces.py
  A   z3c.extfile/trunk/src/z3c/extfile/file/tests.py
  A   z3c.extfile/trunk/src/z3c/extfile/file/z3c.extfile.file-configure.zcml
  A   z3c.extfile/trunk/src/z3c/extfile/filter.py
  A   z3c.extfile/trunk/src/z3c/extfile/hashdir.py
  A   z3c.extfile/trunk/src/z3c/extfile/hashdir.txt
  A   z3c.extfile/trunk/src/z3c/extfile/interfaces.py
  A   z3c.extfile/trunk/src/z3c/extfile/processor.py
  A   z3c.extfile/trunk/src/z3c/extfile/processor.txt
  A   z3c.extfile/trunk/src/z3c/extfile/property.py
  A   z3c.extfile/trunk/src/z3c/extfile/property.txt
  A   z3c.extfile/trunk/src/z3c/extfile/schema.py
  A   z3c.extfile/trunk/src/z3c/extfile/testdata/
  A   z3c.extfile/trunk/src/z3c/extfile/testdata/input1.inp
  A   z3c.extfile/trunk/src/z3c/extfile/tests.py
  A   z3c.extfile/trunk/src/z3c/extfile/todo.txt
  A   z3c.extfile/trunk/src/z3c/extfile/utility.py
  A   z3c.extfile/trunk/src/z3c/extfile/z3c.extfile-configure.zcml

-=-
Added: z3c.extfile/trunk/src/z3c/__init__.py
===================================================================
--- z3c.extfile/trunk/src/z3c/__init__.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/__init__.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,23 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+#
+# This file is necessary to make this directory a package.
+
+try:
+    # Declare this a namespace package if pkg_resources is available.
+    import pkg_resources
+    pkg_resources.declare_namespace('z3c')
+except ImportError:
+    pass
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/README.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/README.txt	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/README.txt	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,62 @@
+===================
+Filestorage Package
+===================
+
+This package offers large file handling solutions for zope3. The first
+implementation is based on properties, the second on wsgi. The
+property implementation runs on plain zope 3.
+
+This is work in progress: see todo.txt
+
+Property
+========
+
+Installation
+------------
+
+Define an evnironment variable in your runzope like this::
+
+ os.environ['EXTFILE_STORAGEDIR'] = '/tmp/filestorage'
+
+This causes a IHashDir utilitiy to be registered upon zope startup.
+
+see hashdir.txt, property.txt
+
+WSGI (optional)
+===============
+
+This package provides a wsgi filter that upon upload replaces the
+content of the upload with the sha digest of the content and stores
+the file on the filesystem. Upon download it looks if it has a digest
+and returns the according file directly from the filesystem.
+
+See also: hashdir.txt, processort.txt
+
+This package is currently only tested with zope3 twisted server.
+
+Requirements
+============
+
+Zope 3 with twisted server, this package does not work with zserver.
+
+:zope.paste: follow the instructions in
+http://svn.zope.org/zope.paste/trunk/README.txt
+
+Installation
+============
+
+Add the following to instance_home/etc/paste.ini. The directory
+parameter defines the storage directory for files.
+
+Example paste.ini::
+
+ [pipeline:Paste.Main]
+ pipeline = fs main
+ 
+ [app:main]
+ paste.app_factory = zope.paste.application:zope_publisher_app_factory
+ 
+ [filter:fs]
+ paste.filter_factory = z3c.extfile.filter:filter_factory
+ directory = /path/to/hashdir/storage
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/__init__.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/__init__.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/__init__.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1 @@
+#module


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

Added: z3c.extfile/trunk/src/z3c/extfile/browser/__init__.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/browser/__init__.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/browser/__init__.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1 @@
+# package


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

Added: z3c.extfile/trunk/src/z3c/extfile/browser/configure.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/browser/configure.zcml	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/browser/configure.zcml	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,16 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    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"
+  />
+ 
+</configure>
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/browser/widget.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/browser/widget.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/browser/widget.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,8 @@
+from zope.app.form.browser.textwidgets import FileWidget
+from z3c.extfile import hashdir
+
+class ExtBytesWidget(FileWidget):
+
+    def _toFieldValue(self, si):
+        # we pass the file object to the field
+        return si


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

Added: z3c.extfile/trunk/src/z3c/extfile/browser/widget.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/browser/widget.txt	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/browser/widget.txt	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,37 @@
+=============
+ File widget
+=============
+
+At first we need a hashdir as a utility.
+
+  >>> 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)
+
+
+  >>> from z3c.extfile.browser.widget import ExtBytesWidget
+  >>> from zope.publisher.browser import TestRequest
+  >>> from z3c.extfile.schema import ExtBytesField
+  >>> from cStringIO import StringIO
+  >>> si = StringIO('file contents')
+  >>> field = ExtBytesField(__name__='foo', title=u'Foo')
+  >>> request = TestRequest(form={'field.foo': si})
+  >>> widget = ExtBytesWidget(field, request)
+  >>> widget.hasInput()
+  True
+  >>> widget.getInputValue()
+  <cStringIO.StringI object at ...>
+
+
+  
+Cleanup
+
+  >>> import shutil
+  >>> shutil.rmtree(tmp) 
+
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/configure.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/configure.zcml	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/configure.zcml	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,30 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain='zope'
+ >
+
+ <class class=".hashdir.HashDir">
+
+  <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"/>
+</configure>
\ No newline at end of file


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/__init__.py
===================================================================


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/browser/__init__.py
===================================================================


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/browser/configure.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/file/browser/configure.zcml	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/file/browser/configure.zcml	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,21 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ i18n_domain='zope'
+ >
+
+ <browser:addMenuItem
+  class="..file.ExtFile"
+  title="Large File"
+  description="A Large File with content stored externally"
+  permission="zope.ManageContent"
+  />
+
+ <browser:page
+  name="upload.html"
+  class=".views.EditForm"
+  for="..interfaces.IExtFile"
+  permission="zope.ManageContent"
+  />
+ 
+</configure>
\ No newline at end of file


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/browser/views.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/file/browser/views.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/file/browser/views.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,24 @@
+from zope.formlib import form
+from z3c.extfile.file.interfaces import IExtFile
+from zope.i18nmessageid import MessageFactory
+_ = MessageFactory("zope")
+from zope.contenttype import guess_content_type
+
+class EditForm(form.EditForm):
+    form_fields = form.Fields(IExtFile)
+
+    @form.action(_("Apply"), condition=form.haveInputWidgets)
+    def handle_edit_action(self, action, data):
+        #use the filename to guess the content type
+        if not data.get('contentType'):
+            try:
+                filename = data.get('data').filename
+            except:
+                filename = None
+            if filename:
+                contentType = guess_content_type(filename)[0]
+                if contentType:
+                    data['contentType'] = contentType
+        return super(EditForm, self).handle_edit_action.success(data)
+        
+        


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/configure.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/file/configure.zcml	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/file/configure.zcml	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,24 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain='zope'
+ >
+ 
+ <class class=".file.ExtFile">
+  <require
+   permission="zope.View"
+   interface=".interfaces.IExtFile"
+   />
+
+  <require
+   permission="zope.ManageContent"
+   set_schema=".interfaces.IExtFile"
+   />
+  
+  <implements
+   interface="zope.annotation.interfaces.IAttributeAnnotatable"
+   />
+ </class>
+
+
+ <include package=".browser"/>
+</configure>
\ No newline at end of file


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/file.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/file/file.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/file/file.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,18 @@
+from persistent import  Persistent
+from z3c.extfile.property import ExtBytesProperty
+from interfaces import IExtFile
+from zope import interface
+
+class ExtFile(Persistent):
+
+    """A zope file implementation based on z3c.extfile"""
+
+    interface.implements(IExtFile)
+    data = ExtBytesProperty('data')
+    
+    def __init__(self, data='', contentType=''):
+        self.data = data
+        self.contentType = contentType
+        
+    def getSize(self):
+        return len(self.data)


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/file.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/file/file.txt	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/file/file.txt	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,54 @@
+=======================
+Persistent File Objects
+=======================
+
+This file implementation holds its data in a hashdir.
+
+At first we need a hashdir as a utility.
+
+  >>> from z3c.extfile import hashdir
+  >>> from z3c.extfile.file  import file
+  >>> 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)
+
+So we can create a file with a file like object
+
+  >>> from cStringIO import StringIO
+  >>> si = StringIO('file contents')
+  >>> f = file.ExtFile(si)
+  >>> f.data
+  <ReadFile named '034fa2ed8e211e4d20f20e792d777f4a30af1a93'>
+  >>> len(f.data)
+  13
+  >>> ''.join(iter(f.data))
+  'file contents'
+
+The data always returns a ReadFile instance.
+
+  >>> f.data
+  <ReadFile named '034fa2ed8e211e4d20f20e792d777f4a30af1a93'>
+
+So we cannot write directly to it
+
+  >>> f.data.write("hello")
+  Traceback (most recent call last):
+  ...
+  IOError: [Errno 9] Bad file descriptor
+
+But we can of course set a new value on data
+
+  >>> f.data = "Hello new World"
+  >>> ''.join(iter(f.data))
+  'Hello new World'
+
+Cleanup
+
+  >>> import shutil
+  >>> shutil.rmtree(tmp) 
+
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/interfaces.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/file/interfaces.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/file/interfaces.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,6 @@
+from z3c.extfile.schema import ExtBytesField
+from zope.app.file.interfaces import IFile
+
+class IExtFile(IFile):
+
+    data = ExtBytesField(title=u'Data')


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/tests.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/file/tests.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/file/tests.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,15 @@
+import doctest
+import unittest
+from zope.testing.doctestunit import DocFileSuite, DocTestSuite
+def test_suite():
+    
+    return unittest.TestSuite(
+        (
+        DocFileSuite('file.txt',
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/file/z3c.extfile.file-configure.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/file/z3c.extfile.file-configure.zcml	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/file/z3c.extfile.file-configure.zcml	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1 @@
+<include package="z3c.extfile.file"/>


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

Added: z3c.extfile/trunk/src/z3c/extfile/filter.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/filter.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/filter.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,114 @@
+import os.path
+import stat
+import cgi
+from z3c.extfile import processor, hashdir
+from cStringIO import StringIO
+import mimetypes
+
+BLOCK_SIZE = 1024*128
+
+class FSFilter(object):
+
+    def __init__(self, app, directory=None):
+        self.app = app
+        if directory is not None:
+            # use provided directory
+            self.dir = os.path.abspath(directory)
+        else:
+            #use environment variable
+            if not os.environ.has_key('EXTFILE_STORAGEDIR'):
+                raise RuntimeError, "EXTFILE_STORAGEDIR not defined"
+            self.dir = os.environ.get('EXTFILE_STORAGEDIR')
+        self.hd = hashdir.HashDir(self.dir)
+
+    def __call__(self, env, start_response):
+        if env.get('REQUEST_METHOD')=='POST' and \
+           env.get('CONTENT_TYPE','').startswith('multipart/form-data;'):
+            fp = env['wsgi.input']
+            out = StringIO()
+            proc = processor.Processor(self.hd)
+            proc.pushInput(fp, out)
+            out.seek(0)
+            env['wsgi.input'] = out
+        elif env.get('REQUEST_METHOD') in ('GET',):
+            resp = FileResponse(self.app, self.hd)
+            return resp(env, start_response)
+        return self.app(env, start_response)
+
+class FileResponse(object):
+
+    def __init__(self, app, hd):
+        self.hd = hd
+        self.app = app
+        
+    def start_response(self, status, headers_out, exc_info=None):
+        """Intercept the response start from the filtered app."""
+        self.status      = status
+        self.headers_out = headers_out
+        self.exc_info    = exc_info
+        
+    def __call__(self, env, start_response):
+        """Facilitate WSGI API by providing a callable hook."""
+        self.env        = env
+        self.real_start = start_response
+        return self.__iter__()
+
+    def __iter__(self):
+        
+        result = self.app(self.env, self.start_response)
+        result_iter  = result.__iter__()
+        doHandle = False
+        for n,v in self.headers_out:
+            # the length is digest(40) + len(z3c.extfile.digest)
+            if n.lower()=='content-length' and v=='59':
+                doHandle = True
+                break
+        if not doHandle:
+            # this is not for us
+            headers_out = self.headers_out
+            iter_out = result_iter
+        else:
+            headers_out = dict(
+                [(k.lower(),v) for k,v in self.headers_out]
+                )
+            
+            body = "".join(result_iter)
+            if body.startswith('z3c.extfile.digest:'):
+                digest = body[19:]
+            else:
+                digest = None
+            # do we have to handle the content. type?
+            # zope sniffs if it has no extension, so we get an unknown
+            # text content-type for our digest
+            filename = self.env['PATH_INFO']
+            content_type, content_encoding =mimetypes.guess_type(filename)
+            if content_type and not 'unknown' in \
+                   headers_out.get('content-type','unknown'):
+                headers_out['content-type'] = content_type
+            if content_encoding and not 'content-encoding' in headers_out:
+                headers_out['content-encoding'] = content_encoding
+            if digest is not None:
+                try:
+                    size = self.hd.getSize(digest)
+                    headers_out['content-length'] = size 
+                    f = self.hd.open(digest)
+                    fw = self.env.get('wsgi.file_wrapper')
+                    iter_out = fw(f, BLOCK_SIZE)
+                except KeyError:
+                    # no such digest available, just return the body
+                    iter_out = body.__iter__()
+            else:
+                # we have no digest so return body
+                iter_out = body.__iter__()
+            headers_out = headers_out.items()
+        self.real_start(self.status, headers_out, exc_info=self.exc_info)
+        return iter_out
+    
+        
+def filter_factory(global_conf, **local_conf):
+    def filter(app):
+        return FSFilter(app, **local_conf)
+    return filter
+
+
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/hashdir.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/hashdir.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/hashdir.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,111 @@
+import sha
+import os
+import stat
+import tempfile
+import shutil
+from types import StringType
+import interfaces
+from zope import interface
+from persistent import Persistent
+
+class HashDir(Persistent):
+
+    """a directory holding files named after their sha1 hash"""
+
+    interface.implements(interfaces.IHashDir)
+    _path = None
+
+    def __init__(self, path=None):
+        self.path = path
+
+    def _setPath(self, path):
+        if path is None:
+            return
+        self._path = os.path.abspath(path)
+        self.tmp = os.path.join(self.path, 'tmp')
+        self.var = os.path.join(self.path, 'var')
+        self._initPaths()
+        
+    def _getPath(self):
+        return self._path
+
+    path = property(_getPath,_setPath)
+
+    def _initPaths(self):
+        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.',
+                                        dir=self.tmp)
+        return WriteFile(self, handle, path)
+
+    def commit(self, f):
+        """commit a file, this is called by the file"""
+        digest = f.sha.hexdigest()
+        target = os.path.join(self.var, digest)
+        if os.path.exists(target):
+            # we have that content so just delete the tmp file
+            os.remove(f.path)
+        else:
+            shutil.move(f.path, target)
+        return digest
+        
+    def digests(self):
+        """returns all digests stored"""
+        return os.listdir(self.var)
+
+    def getPath(self, digest):
+        if  type(digest) != StringType or len(digest) != 40:
+            raise ValueError, digest
+        path = os.path.join(self.var, digest)
+        if not os.path.isfile(path):
+            raise KeyError, digest
+        return path
+
+    def getSize(self, digest):
+        return os.path.getsize(self.getPath(digest))
+
+    def open(self, digest):
+        return ReadFile(self.getPath(digest))
+        
+
+class ReadFile(file):
+
+    interface.implements(interfaces.IReadFile)
+
+    def __init__(self, filename, bufsize=-1):
+        """we always use read binary as mode"""
+        super(ReadFile, self).__init__(filename, 'rb', bufsize)
+    
+    def __len__(self):
+        return int(os.fstat(self.fileno())[stat.ST_SIZE])
+
+    def __repr__(self):
+        return "<ReadFile named %s>" % repr(self.digest)
+
+    @property
+    def digest(self):
+        return os.path.split(self.name)[1]
+
+class WriteFile(object):
+
+    interface.implements(interfaces.IWriteFile)
+
+    def __init__(self, hd, handle, path):
+        self.hd = hd
+        self.handle = handle
+        self.path = path
+        self.sha = sha.new()
+
+    def write(self, s):
+        self.sha.update(s)
+        os.write(self.handle, s)
+
+    def commit(self):
+        """returns the sha digest and saves the file"""
+        os.close(self.handle)
+        return self.hd.commit(self)
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/hashdir.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/hashdir.txt	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/hashdir.txt	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,113 @@
+======================
+Hashdir implementation
+======================
+
+The hashdir module provides a way to store files based on the sha
+digest of their contents.
+
+  >>> from z3c.extfile import hashdir, interfaces
+  >>> import tempfile, os
+  >>> tmp = tempfile.mkdtemp()
+  >>> hdPath = os.path.join(tmp, 'testhashdir')
+  >>> hd = hashdir.HashDir(hdPath)
+
+We can get a new file from the hashdir instance.
+
+  >>> f1 = hd.new()
+  >>> f1
+  <z3c.extfile.hashdir.WriteFile object at ...>
+
+  >>> f1.write("Content 1")
+
+After writing we get a sha digest back, when we commit it.
+
+  >>> d1 = f1.commit()
+  >>> d1
+  'ab8366d7206c431e6e15a625a04d0fbe5510984d'
+
+We cannot commit twice!
+
+  >>> f1.commit()
+  Traceback (most recent call last):
+  ...
+  OSError: [Errno 9] Bad file descriptor
+
+We can get all digests stored by using the digests method.
+
+  >>> hd.digests()
+  ['ab8366d7206c431e6e15a625a04d0fbe5510984d']
+
+If we store the same content twice we only have one digest, so each
+unique content is only stored once.
+
+  >>> f1 = hd.new()
+  >>> f1.write("Content 1")
+  >>> d1 = f1.commit()
+  >>> hd.digests()
+  ['ab8366d7206c431e6e15a625a04d0fbe5510984d']
+
+  >>> f2 = hd.new()
+  >>> f2.write("Content 2")
+  >>> d2 = f2.commit()
+  >>> print '\n'.join(sorted(hd.digests()))
+  0db0e5fa1ecf3e7659504f2e4048434cd9f20d2d
+  ab8366d7206c431e6e15a625a04d0fbe5510984d
+
+We can get an open file for reading by issuing the open method.
+
+  >>> f = hd.open('0db0e5fa1ecf3e7659504f2e4048434cd9f20d2d')
+  >>> f.read()
+  'Content 2'
+
+This object implemnts the IReadFile interface
+  >>> interfaces.IReadFile.providedBy(f)
+  True
+
+We can get the digest of the object
+
+  >>> f.digest
+  '0db0e5fa1ecf3e7659504f2e4048434cd9f20d2d'
+  
+We can also get the path of the file.
+
+  >>> hd.getPath('0db0e5fa1ecf3e7659504f2e4048434cd9f20d2d')
+  '/.../testhashdir/var/0db0e5fa1ecf3e7659504f2e4048434cd9f20d2d'
+
+We can get the size of the file.
+
+  >>> f = hd.new()
+  >>> for s in ['abc']*1024*500:
+  ...     f.write(s)
+  >>> d = f.commit()
+  >>> hd.getSize(d)
+  1536000L
+
+
+Empty files are also allowed.
+
+  >>> f = hd.new()
+  >>> f.commit()
+  'da39a3ee5e6b4b0d3255bfef95601890afd80709'
+  
+If no file is in progress the tmp dir of the hd should be empty.
+
+  >>> os.listdir(hd.tmp)
+  []
+
+If we provide a wrong digest a Value error is raised.
+
+  >>> hd.getPath('abc')
+  Traceback (most recent call last):
+  ...
+  ValueError: abc
+
+If we have a valid digest but it is not there a KeyError is raised.
+  >>> hd.getPath('da39a3ee5e6b4b0d3255bfef95601890afd80700')
+  Traceback (most recent call last):
+  ...
+  KeyError: 'da39a3ee5e6b4b0d3255bfef95601890afd80700'
+
+Cleanup
+
+  >>> import shutil
+  >>> shutil.rmtree(tmp) 


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

Added: z3c.extfile/trunk/src/z3c/extfile/interfaces.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/interfaces.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/interfaces.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,39 @@
+from zope import interface, schema
+from zope.schema.interfaces import IBytes
+
+class IHashDir(interface.Interface):
+
+    """a hashdir utility"""
+
+    path = schema.TextLine(title=u"Path")
+    
+class IExtBytesField(IBytes):
+
+    """A field holding Bytes in external files"""
+
+class IFile(interface.Interface):
+
+    """marker for file objects"""
+
+class IReadFile(IFile):
+
+    """a readonly file"""
+
+    digest = schema.ASCII(title=u'Digest', readonly=True)
+
+    def __len__():
+        """returns the length/size of file"""
+
+
+class IWriteFile(IFile):
+
+    def write(s):
+
+        """writes s to file"""
+
+    def commit():
+
+        """commits the file to be stored with the digest"""
+
+    
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/processor.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/processor.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/processor.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,136 @@
+import time
+
+def parse_header(s):
+    l = [e.strip() for e in s.split(';')]
+    result_value = l.pop(0).lower()
+    result_d = {}
+    for e in l:
+        try:
+            key, value = e.split('=', 1)
+        except ValueError:
+            continue
+        key = key.strip().lower()
+        value = value.strip()
+        if len(value) >= 2 and value.startswith('"') and value.endswith('"'):
+            value = value[1:-1]
+        result_d[key] = value
+    return result_value, result_d
+
+class Processor:
+
+    def __init__(self, hd):
+        self.hd = hd
+        self._incoming = []
+        # we use a state pattern where the handle method gets
+        # replaced by the current handle method for this state.
+        self.handle = self.handle_first_boundary
+
+    def pushInput(self, fp, out):
+        while True:
+            s = fp.read(1024*128)
+            if not s:
+                return
+            lines = s.splitlines(True)
+            for line in lines:
+                self.pushInputLine(line, out)
+
+    def pushInputLine(self, data, out):
+        # collect data
+        self._incoming.append(data)
+        # if we're not at the end of the line, input was broken
+        # somewhere. We return to collect more first.
+        if data[-1] != '\n':
+            return
+        # now use the line in whatever handle method is current
+        if len(self._incoming) == 1:
+            line = data
+        else:
+            line = ''.join(self._incoming)
+        self._incoming = []
+
+        self.handle(line, out)
+
+    def handle_first_boundary(self, line, out):
+        #print "handle_first_boundary", line
+        self._boundary = line
+        self._last_boundary = self._boundary.rstrip() + '--\r\n'
+        self.init_headers()
+        self.handle = self.handle_headers
+        out.write(line)
+        
+    def init_headers(self):
+        self._disposition = None
+        self._disposition_options = {}
+        self._content_type = 'text/plain'
+        self._content_type_options = {}
+
+    def handle_headers(self, line, out):
+        #print "handle_headers_boundary", line
+
+        if line in ['\n', '\r\n']:
+            out.write(line)
+            self.init_data(out)
+            return
+        key, value = line.split(':', 1)
+        key = key.lower()
+        if key == "content-disposition":
+            self._disposition, self._disposition_options = parse_header(
+                value)
+        elif key == "content-type":
+            self._content_type, self._content_type_options = parse_header(
+                value)
+            line = line.replace(self._content_type,
+                                'application/x-z3c.extfile-info')
+        out.write(line)
+
+    def init_data(self, out):
+        self._dt_start = time.time()
+        filename = self._disposition_options.get('filename')
+        # if filename is empty, assume no file is submitted and submit
+        # empty file -- don't handle it
+        if filename is None or not filename:
+            self.handle = self.handle_data
+            return
+        self._f = self.hd.new()
+        self._previous_line = None
+        self.handle = self.handle_file_data
+        
+    def handle_data(self, line, out):
+        out.write(line)
+        if line == self._boundary:
+            self.init_headers()
+            self.handle = self.handle_headers
+        elif line == self._last_boundary:
+            # we should be done
+            self.handle = None # shouldn't be called again
+
+    def handle_file_data(self, line, out):
+        #print "handle_file_data", len(line)
+        def _end():
+            # write last line, but without \r\n
+            self._f.write(self._previous_line[:-2])
+            digest = self._f.commit()
+            out.write('z3c.extfile.digest:%s' % digest)
+            out.write('\r\n')
+            out.write(line)
+            self._f = None
+            self._dt_start = time.time()
+            #print "done file: %s in %s seconds" % (
+            #    self._disposition_options.get('filename'),
+            #    (time.time()-self._dt_start))
+
+        if line == self._boundary:
+            #print "end of handle_file_boundary", line
+            # write last line, but without \r\n
+            _end()
+            self.handle = self.handle_headers
+        elif line == self._last_boundary:
+            #print "last boundary handle_file_data", len(line)
+            _end()
+            self._f = None
+            self.handle = None # shouldn't be called again
+        else:
+            #print "normal handle_file_data", len(line)
+            if self._previous_line is not None:
+                self._f.write(self._previous_line)
+            self._previous_line = line


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

Added: z3c.extfile/trunk/src/z3c/extfile/processor.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/processor.txt	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/processor.txt	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,66 @@
+==================
+ Upload Processor
+==================
+
+The upload processor takes a hashdir for initialization and creates
+files with the filedata fields in the input stream. It replaces the
+actual file content with the digest of the contents.
+
+  >>> from z3c.extfile import hashdir, processor
+  >>> import tempfile, os
+  >>> testDir = os.path.join(os.path.dirname(hashdir.__file__),'testdata')
+  >>> tmp = tempfile.mkdtemp()
+  >>> hdPath = os.path.join(tmp, 'testhashdir')
+  >>> hd = hashdir.HashDir(hdPath)
+
+  >>> from cStringIO import StringIO
+  >>> out = StringIO()
+  >>> proc = processor.Processor(hd)
+  >>> fp = open(os.path.join(testDir,'input1.inp'))
+  >>> proc.pushInput(fp, out)
+
+  >>> print '\n'.join(hd.digests())
+  28a33adad3dbce5d72ec8012c9e0563b8ef1eb17
+  3ac60068645f651bf2e528e15402a32daecb6873
+
+So now we should have two files.
+
+  >>> d2, d1 = sorted(hd.digests())
+  
+  >>> f = hd.open(d1)
+  >>> print f.read()
+  first line of 1
+  second line of 1
+
+  >>> f.close()
+  >>> f = hd.open(d2)
+  >>> print f.read()
+  first line of 2
+  second line of 2
+  >>> f.close()
+
+And the output only contains the digests as content.
+
+  >>> out.seek(0)
+  >>> print out.read()
+  -----------------------------100323068321119442571506749230
+  Content-Disposition: form-data; filename="test1.txt"; name="test1"
+  Content-Type: application/x-z3c.extfile-info
+  <BLANKLINE>
+  z3c.extfile.digest:3ac60068645f651bf2e528e15402a32daecb6873
+  -----------------------------100323068321119442571506749230
+  Content-Disposition: form-data; filename="test2.txt"; name="test2"
+  Content-Type: application/x-z3c.extfile-info
+  <BLANKLINE>
+  z3c.extfile.digest:28a33adad3dbce5d72ec8012c9e0563b8ef1eb17
+  -----------------------------100323068321119442571506749230
+  Content-Disposition: form-data; name="submit"
+  <BLANKLINE>
+  submit data
+  -----------------------------100323068321119442571506749230--
+  <BLANKLINE>
+  
+Cleanup
+
+  >>> import shutil
+  >>> shutil.rmtree(tmp) 


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

Added: z3c.extfile/trunk/src/z3c/extfile/property.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/property.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/property.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,43 @@
+from zope import component, interface
+import interfaces
+from cStringIO import StringIO
+
+_marker = object()
+BLOCK_SIZE = 1024*128
+
+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:
+            return self
+        digest = inst.__dict__.get(self.__name, _marker)
+        if digest is _marker:
+            return None
+        return self.hd.open(digest)
+
+    def __set__(self, inst, value):
+        # Handle case when value is a string
+        if isinstance(value, unicode):
+            value = value.encode('UTF-8')
+        if isinstance(value, str):
+            value = StringIO(value)
+        value.seek(0)
+        f = self.hd.new()
+        while True:
+            chunk = value.read(BLOCK_SIZE)
+            if not chunk:
+                digest = f.commit()
+                inst.__dict__[self.__name] = digest
+                break
+            f.write(chunk)
+
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/property.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/property.txt	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/property.txt	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,66 @@
+===============
+ExtFileProperty
+===============
+
+This property implementation is able to hold large amounts of data
+(e.g. Videos) by storing data on the filesystem.
+
+The storage of this implementation depends on an IHashDir utility to
+be present.
+
+  >>> 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.
+
+  >>> from z3c.extfile.property import ExtBytesProperty
+  >>> from cStringIO import StringIO
+  >>> class Foo(object):
+  ...     data = ExtBytesProperty('data')
+  ...     data2 = ExtBytesProperty('data2')
+  >>> foo = Foo()
+
+The normal usecase is to assign a file-like object to the property,
+which at least implements at least the ``seek`` and ``read``
+methods. So for this test we take a normal StringIO object.
+
+  >>> si = StringIO('hello world')
+
+Upon assignement, the whole file is stored onto the filesystem. And
+when accessed the property returns an open read-only file.
+
+  >>> foo.data=si
+  >>> foo.data
+  <ReadFile named '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'>
+  >>> ''.join(iter(foo.data))
+  'hello world'
+
+The actual data stored on the object itself is only the sha digest of
+the file stored on the filesystem.
+
+  >>> foo.__dict__
+  {'data': '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'}
+
+We can also assign unicode and strings as values. This method is not
+suitable for handling large amounts of data, because the whole data
+has to be loaded into memory.
+
+  >>> foo.data = "abc"
+  >>> ''.join(iter(foo.data))
+  'abc'
+
+  >>> foo.data = u"abc"
+  >>> ''.join(iter(foo.data))
+  'abc'
+
+Cleanup
+
+  >>> import shutil
+  >>> shutil.rmtree(tmp) 
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/schema.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/schema.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/schema.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,12 @@
+from zope import schema, interface
+from z3c.extfile import interfaces
+from zope.schema.interfaces import InvalidValue
+
+class ExtBytesField(schema.Bytes):
+
+    interface.implements(interfaces.IExtBytesField)
+
+    def validate(self, value):
+        """test if we have a file"""
+        return hasattr(value,'seek') and hasattr(value,'read')
+    


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

Added: z3c.extfile/trunk/src/z3c/extfile/testdata/input1.inp
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/testdata/input1.inp	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/testdata/input1.inp	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,19 @@
+-----------------------------100323068321119442571506749230
+Content-Disposition: form-data; filename="test1.txt"; name="test1"
+Content-Type: application/octet-stream
+
+first line of 1
+second line of 1
+
+-----------------------------100323068321119442571506749230
+Content-Disposition: form-data; filename="test2.txt"; name="test2"
+Content-Type: application/octet-stream
+
+first line of 2
+second line of 2
+
+-----------------------------100323068321119442571506749230
+Content-Disposition: form-data; name="submit"
+
+submit data
+-----------------------------100323068321119442571506749230--

Added: z3c.extfile/trunk/src/z3c/extfile/tests.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/tests.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/tests.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,24 @@
+import doctest
+import unittest
+from zope.testing.doctestunit import DocFileSuite, DocTestSuite
+def test_suite():
+    
+    return unittest.TestSuite(
+        (
+        DocFileSuite('hashdir.txt',
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('processor.txt',
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('property.txt',
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        DocFileSuite('browser/widget.txt',
+                     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                     ),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/todo.txt
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/todo.txt	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/todo.txt	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,21 @@
+=======
+ TODOs
+=======
+
+Functional Tests
+
+Further Improvements
+====================
+
+zope.publisher.browser.BrowserRequest uses FieldStorage directly which
+creates a tempflile for each multipart boundary upon upload. We have
+to make the FieldStorage implementation pluggable by creating some
+IFieldStorageFactory utilities.
+
+If we are able to replace the FieldStorage implementation we can
+provide an implemntation which stores the files as hashdir files, so
+upon upload the file only has to be moved instead of iterated over in
+python. This results in much faster handling of huge files upon
+upload.
+
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/utility.py
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/utility.py	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/utility.py	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1,17 @@
+from zope import component
+import os
+import hashdir
+import interfaces
+
+def bootStrapSubscriber(event):
+    """create an IHashDir util if the EXTFILE_STORAGEDIR environment
+    variable is present"""
+
+    if os.environ.has_key('EXTFILE_STORAGEDIR'):
+        path = os.environ.get('EXTFILE_STORAGEDIR')
+        if not os.path.exists(path):
+            raise ValueError, path
+        hd = hashdir.HashDir(path)
+        component.provideUtility(hd, provides=interfaces.IHashDir)
+
+


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

Added: z3c.extfile/trunk/src/z3c/extfile/z3c.extfile-configure.zcml
===================================================================
--- z3c.extfile/trunk/src/z3c/extfile/z3c.extfile-configure.zcml	2006-08-10 12:55:00 UTC (rev 69390)
+++ z3c.extfile/trunk/src/z3c/extfile/z3c.extfile-configure.zcml	2006-08-10 16:28:44 UTC (rev 69391)
@@ -0,0 +1 @@
+<include package="z3c.extfile"/>


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



More information about the Checkins mailing list