[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