[Zope3-checkins] CVS: Zope3/src/zope/app/file - __init__.py:1.1.2.1
configure.zcml:1.1.2.1 file.py:1.1.2.1 fssync.py:1.1.2.1
image.py:1.1.2.1 interfaces.py:1.1.2.1
Philipp von Weitershausen
philikon at philikon.de
Fri Feb 20 14:39:48 EST 2004
Update of /cvs-repository/Zope3/src/zope/app/file
In directory cvs.zope.org:/tmp/cvs-serv21299
Added Files:
Tag: philikon-movecontent-branch
__init__.py configure.zcml file.py fssync.py image.py
interfaces.py
Log Message:
File and Image content types live in zope.app.file now.
=== Added File Zope3/src/zope/app/file/__init__.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: __init__.py,v 1.1.2.1 2004/02/20 19:39:47 philikon Exp $
"""
from file import File
from image import Image
=== Added File Zope3/src/zope/app/file/configure.zcml ===
<configure
xmlns='http://namespaces.zope.org/zope'
xmlns:fssync='http://namespaces.zope.org/fssync'
i18n_domain='zope'
>
<!-- Module alias for backward compat -->
<modulealias
module=".file"
alias="zope.app.content.file"
/>
<modulealias
module=".interfaces"
alias="zope.app.interfaces.content.file"
/>
<modulealias
module=".image"
alias="zope.app.content.image"
/>
<modulealias
module=".interfaces"
alias="zope.app.interfaces.content.image"
/>
<!-- setting up content types -->
<interface
interface=".interfaces.IFile"
type="zope.app.interfaces.content.IContentType"
/>
<interface
interface=".interfaces.IImage"
type="zope.app.interfaces.content.IContentType"
/>
<permission
id="zope.AddImages"
title="[add-images-permission] Add Images"
/>
<!-- content classes -->
<content class=".file.File">
<factory
id="File"
permission="zope.ManageContent"
title="File"
description="A File"
/>
<require
permission="zope.View"
interface=".interfaces.IReadFile"
/>
<require
permission="zope.ManageContent"
interface=".interfaces.IWriteFile"
set_schema=".interfaces.IReadFile"
/>
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
/>
</content>
<content class=".image.Image">
<factory
id="Image"
permission="zope.ManageContent"
title="Image"
description="An Image"
/>
<require
permission="zope.View"
interface="zope.app.file.interfaces.IReadFile"
attributes="getImageSize"
/>
<require
permission="zope.ManageContent"
interface="zope.app.file.interfaces.IWriteFile"
set_schema="zope.app.file.interfaces.IReadFile"
/>
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
/>
</content>
<adapter
factory=".image.ImageSized"
provides="zope.app.interfaces.size.ISized"
for=".interfaces.IImage"
/>
<!-- fssync adapters -->
<adapter
for=".interfaces.IFile"
provides="zope.app.interfaces.file.IReadFile"
factory=".file.FileReadFile"
permission="zope.View"
/>
<adapter
for=".interfaces.IFile"
provides="zope.app.interfaces.file.IWriteFile"
factory=".file.FileWriteFile"
permission="zope.ManageContent"
/>
<adapter
for=".interfaces.IReadFile"
provides="zope.app.interfaces.index.text.ISearchableText"
factory=".file.SearchableText"
/>
<fssync:adapter
class=".file.File"
factory=".fssync.FileAdapter"
/>
<adapter
for="zope.app.folder.interfaces.IFolder"
provides="zope.app.interfaces.file.IFileFactory"
factory=".image.FileFactory"
permission="zope.ManageContent"
/>
<fssync:adapter
class=".image.Image"
factory=".fssync.FileAdapter"
/>
<!-- include browser package -->
<include package=".browser" />
</configure>
=== Added File Zope3/src/zope/app/file/file.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""File content component
$Id: file.py,v 1.1.2.1 2004/02/20 19:39:47 philikon Exp $
"""
from persistence import Persistent
from transaction import get_transaction
from zope.interface import implements
from zope.publisher.browser import FileUpload
from interfaces import IFile, IReadFile, IFileContent
__metaclass__ = type
# set the size of the chunks
MAXCHUNKSIZE = 1 << 16
class File(Persistent):
"""A persistent content component storing binary file data
Let's test the constructor:
>>> file = File()
>>> file.getContentType()
''
>>> file.getData()
''
>>> file = File('Foobar')
>>> file.getContentType()
''
>>> file.getData()
'Foobar'
>>> file = File('Foobar', 'text/plain')
>>> file.getContentType()
'text/plain'
>>> file.getData()
'Foobar'
>>> file = File(data='Foobar', contentType='text/plain')
>>> file.getContentType()
'text/plain'
>>> file.getData()
'Foobar'
Let's test the mutators:
>>> file = File()
>>> file.setContentType('text/plain')
>>> file.getContentType()
'text/plain'
>>> file.setData('Foobar')
>>> file.getData()
'Foobar'
>>> file.edit('Blah', 'text/html')
>>> file.getContentType()
'text/html'
>>> file.getData()
'Blah'
>>> file.setData(None)
Traceback (most recent call last):
...
TypeError: Cannot set None data on a file.
Let's test large data input:
>>> file = File()
Insert as string:
>>> file.setData('Foobar'*60000)
>>> file.getSize()
360000
>>> file.getData() == 'Foobar'*60000
True
Insert data as FileChunk:
>>> fc = FileChunk('Foobar'*4000)
>>> file.setData(fc)
>>> file.getSize()
24000
>>> file.getData() == 'Foobar'*4000
True
Insert data from file object:
>>> import cStringIO
>>> sio = cStringIO.StringIO()
>>> sio.write('Foobar'*100000)
>>> sio.seek(0)
>>> file.setData(sio)
>>> file.getSize()
600000
>>> file.getData() == 'Foobar'*100000
True
Last, but not least, verify the interface:
>>> from zope.interface.verify import verifyClass
>>> IFile.isImplementedByInstancesOf(File)
True
>>> verifyClass(IFile, File)
True
"""
implements(IFileContent, IFile)
def __init__(self, data='', contentType=''):
self.data = data
self.contentType = contentType
def __len__(self):
return self.size
def setContentType(self, contentType):
'''See interface IFile'''
self._contentType = contentType
def getContentType(self):
'''See interface IFile'''
return self._contentType
def edit(self, data, contentType=None):
'''See interface IFile'''
# XXX This seems broken to me, as setData can override the
# content type explicitly passed in.
if contentType is not None:
self._contentType = contentType
if isinstance(data, FileUpload) and not data.filename:
data = None # Ignore empty files
if data is not None:
self.data = data
def getData(self):
'''See interface IFile'''
if isinstance(self._data, FileChunk):
return str(self._data)
else:
return self._data
def setData(self, data):
'''See interface IFile'''
# Handle case when data is a string
if isinstance(data, unicode):
data = data.encode('UTF-8')
if isinstance(data, str):
self._data, self._size = FileChunk(data), len(data)
return
# Handle case when data is None
if data is None:
raise TypeError('Cannot set None data on a file.')
# Handle case when data is already a FileChunk
if isinstance(data, FileChunk):
size = len(data)
self._data, self._size = data, size
return
# Handle case when data is a file object
seek = data.seek
read = data.read
seek(0, 2)
size = end = data.tell()
if size <= 2*MAXCHUNKSIZE:
seek(0)
if size < MAXCHUNKSIZE:
self._data, self._size = read(size), size
return
self._data, self._size = FileChunk(read(size)), size
return
# Make sure we have an _p_jar, even if we are a new object, by
# doing a sub-transaction commit.
get_transaction().savepoint()
jar = self._p_jar
if jar is None:
# Ugh
seek(0)
self._data, self._size = FileChunk(read(size)), size
return
# Now we're going to build a linked list from back
# to front to minimize the number of database updates
# and to allow us to get things out of memory as soon as
# possible.
next = None
while end > 0:
pos = end - MAXCHUNKSIZE
if pos < MAXCHUNKSIZE:
pos = 0 # we always want at least MAXCHUNKSIZE bytes
seek(pos)
data = FileChunk(read(end - pos))
# Woooop Woooop Woooop! This is a trick.
# We stuff the data directly into our jar to reduce the
# number of updates necessary.
data._p_jar = jar
# This is needed and has side benefit of getting
# the thing registered:
data.next = next
# Now make it get saved in a sub-transaction!
get_transaction().savepoint()
# Now make it a ghost to free the memory. We
# don't need it anymore!
data._p_changed = None
next = data
end = pos
self._data, self._size = next, size
return
def getSize(self):
'''See interface IFile'''
return self._size
data = property(getData, setData, None,
"""Contains the data of the file.""")
contentType = property(getContentType, setContentType, None,
"""Specifies the content type of the data.""")
size = property(getSize, None, None,
"""Specifies the size of the file in bytes. Read only.""")
# Adapter for ISearchableText
from zope.app.interfaces.index.text import ISearchableText
class SearchableText:
implements(ISearchableText)
__used_for__ = IReadFile
def __init__(self, file):
self.file = file
def getSearchableText(self):
if self.file.contentType == "text/plain":
return [unicode(self.file.data)]
else:
return None
class FileChunk(Persistent):
# Wrapper for possibly large data
next = None
def __init__(self, data):
self._data = data
def __getslice__(self, i, j):
return self._data[i:j]
def __len__(self):
data = str(self)
return len(data)
def __str__(self):
next = self.next
if next is None:
return self._data
result = [self._data]
while next is not None:
self = next
result.append(self._data)
next = self.next
return ''.join(result)
class FileReadFile:
"""Adapter for file-system style read access.
>>> file = File()
>>> content = "This is some file\\ncontent."
>>> file.edit(content, 'text/plain')
>>> FileReadFile(file).read() == content
True
>>> FileReadFile(file).size() == len(content)
True
"""
def __init__(self, context):
self.context = context
def read(self):
return self.context.getData()
def size(self):
return len(self.context.getData())
class FileWriteFile:
"""Adapter for file-system style write access.
>>> file = File()
>>> content = "This is some file\\ncontent."
>>> FileWriteFile(file).write(content)
>>> file.getData() == content
True
"""
def __init__(self, context):
self.context = context
def write(self, data):
self.context.setData(data)
=== Added File Zope3/src/zope/app/file/fssync.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Filesystem synchronization support.
$Id: fssync.py,v 1.1.2.1 2004/02/20 19:39:47 philikon Exp $
"""
from zope.interface import implements
from zope.fssync.server.entryadapter import ObjectEntryAdapter
from zope.fssync.server.interfaces import IObjectFile
class FileAdapter(ObjectEntryAdapter):
"""ObjectFile adapter for file objects.
"""
implements(IObjectFile)
def getBody(self):
return self.context.getData()
def setBody(self, data):
self.context.setData(data)
def extra(self):
return AttrMapping(self.context, ('contentType',))
=== Added File Zope3/src/zope/app/file/image.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: image.py,v 1.1.2.1 2004/02/20 19:39:47 philikon Exp $
"""
import struct
from cStringIO import StringIO
from zope.interface import implements
from zope.app.interfaces.size import ISized
from zope.app.size import byteDisplay
from zope.app.content_types import guess_content_type
from zope.app.i18n import ZopeMessageIDFactory as _
from file import File
from interfaces import IImage
__metaclass__ = type
class Image(File):
implements(IImage)
def __init__(self, data=''):
'''See interface IFile'''
self.contentType, self._width, self._height = getImageInfo(data)
self.data = data
def setData(self, data):
super(Image, self).setData(data)
contentType, self._width, self._height = getImageInfo(self.data)
if contentType:
self.contentType = contentType
def getImageSize(self):
'''See interface IImage'''
return (self._width, self._height)
data = property(File.getData, setData, None,
"""Contains the data of the file.""")
class ImageSized:
implements(ISized)
def __init__(self, image):
self._image = image
def sizeForSorting(self):
'See ISized'
return ('byte', self._image.getSize())
def sizeForDisplay(self):
'See ISized'
w, h = self._image.getImageSize()
if w < 0:
w = '?'
if h < 0:
h = '?'
bytes = self._image.getSize()
byte_size = byteDisplay(bytes)
mapping = byte_size.mapping
size = _(byte_size + ' ${width}x${height}')
mapping.update({'width': str(w), 'height': str(h)})
size.mapping = mapping
return size
def getImageInfo(data):
data = str(data)
size = len(data)
height = -1
width = -1
content_type = ''
# handle GIFs
if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
# Check to see if content_type is correct
content_type = 'image/gif'
w, h = struct.unpack("<HH", data[6:10])
width = int(w)
height = int(h)
# See PNG v1.2 spec (http://www.cdrom.com/pub/png/spec/)
# Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
# and finally the 4-byte width, height
elif ((size >= 24) and data.startswith('\211PNG\r\n\032\n')
and (data[12:16] == 'IHDR')):
content_type = 'image/png'
w, h = struct.unpack(">LL", data[16:24])
width = int(w)
height = int(h)
# Maybe this is for an older PNG version.
elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'):
# Check to see if we have the right content type
content_type = 'image/png'
w, h = struct.unpack(">LL", data[8:16])
width = int(w)
height = int(h)
# handle JPEGs
elif (size >= 2) and data.startswith('\377\330'):
content_type = 'image/jpeg'
jpeg = StringIO(data)
jpeg.read(2)
b = jpeg.read(1)
try:
while (b and ord(b) != 0xDA):
while (ord(b) != 0xFF): b = jpeg.read(1)
while (ord(b) == 0xFF): b = jpeg.read(1)
if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
jpeg.read(3)
h, w = struct.unpack(">HH", jpeg.read(4))
break
else:
jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2)
b = jpeg.read(1)
width = int(w)
height = int(h)
except struct.error:
pass
except ValueError:
pass
return content_type, width, height
class FileFactory:
def __init__(self, context):
self.context = context
def __call__(self, name, content_type, data):
if not content_type and data:
content_type, width, height = getImageInfo(data)
if not content_type:
content_type, encoding = guess_content_type(name, data, '')
if content_type.startswith('image/'):
return Image(data)
return File(data, content_type)
=== Added File Zope3/src/zope/app/file/interfaces.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Basic File interfaces.
$Id: interfaces.py,v 1.1.2.1 2004/02/20 19:39:47 philikon Exp $
"""
import zope.schema
from zope.interface import Interface
from zope.app.i18n import ZopeMessageIDFactory as _
class IReadFile(Interface):
contentType = zope.schema.BytesLine(
title = _(u'Content Type'),
description=_(u'The content type identifies the type of data.'),
default = 'text/plain',
required=False
)
data = zope.schema.Bytes(
title = _(u'Data'),
description = _(u'The actual content of the object.'),
default='',
required=False,
)
def getData():
"""Return the contained data of the object."""
def getContentType():
"""Returns the content type of the file using mime-types syntax."""
def getSize():
"""Return the byte-size of the data of the object."""
class IWriteFile(Interface):
def edit(data, contentType=None):
"""Sets the data and the content type for the object.
Since some implementations will provide their content type
through the data, it is good to leave the argument optional.
"""
def setData(data):
"""Rewrite the 'file'."""
def setContentType(contentType):
"""Sets the content type of the file."""
class IFile(IReadFile, IWriteFile):
"""The basic methods that are required to implement
a file as a Zope Content object.
"""
class IFileContent(Interface):
"""Marker interface for content that can be managed as files.
The default view for file content has effective URLs that don't end in
/. In particular, if the content included HTML, relative links in
the HTML are relative to the container the content is in.
"""
class IImage(IFile):
"""This interface defines an Image that can be displayed.
"""
def getImageSize():
"""Return a tuple (x, y) that describes the dimensions of
the object.
"""
More information about the Zope3-Checkins
mailing list