[Checkins] SVN: z3c.filetype/trunk/ added mimetype converters

Nikolay Kim fafhrd at datacom.kz
Wed Dec 19 08:09:29 EST 2007


Log message for revision 82357:
  added mimetype converters

Changed:
  U   z3c.filetype/trunk/CHANGES.txt
  U   z3c.filetype/trunk/src/z3c/filetype/README.txt
  U   z3c.filetype/trunk/src/z3c/filetype/__init__.py
  U   z3c.filetype/trunk/src/z3c/filetype/adapters.py
  U   z3c.filetype/trunk/src/z3c/filetype/api.py
  U   z3c.filetype/trunk/src/z3c/filetype/configure.zcml
  A   z3c.filetype/trunk/src/z3c/filetype/converter.txt
  A   z3c.filetype/trunk/src/z3c/filetype/converters/
  A   z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py
  A   z3c.filetype/trunk/src/z3c/filetype/converters/configure.zcml
  A   z3c.filetype/trunk/src/z3c/filetype/converters/gif.py
  A   z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py
  A   z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py
  A   z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py
  A   z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py
  A   z3c.filetype/trunk/src/z3c/filetype/converters/png.py
  A   z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py
  A   z3c.filetype/trunk/src/z3c/filetype/converters/utils.py
  D   z3c.filetype/trunk/src/z3c/filetype/event.py
  U   z3c.filetype/trunk/src/z3c/filetype/interfaces/__init__.py
  A   z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py
  A   z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py
  A   z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py
  U   z3c.filetype/trunk/src/z3c/filetype/magic.py
  U   z3c.filetype/trunk/src/z3c/filetype/size.py
  U   z3c.filetype/trunk/src/z3c/filetype/tests.py
  A   z3c.filetype/trunk/src/z3c/filetype/typeablefile.py

-=-
Modified: z3c.filetype/trunk/CHANGES.txt
===================================================================
--- z3c.filetype/trunk/CHANGES.txt	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/CHANGES.txt	2007-12-19 13:09:28 UTC (rev 82357)
@@ -5,7 +5,14 @@
 After
 =====
 
+2007/12/19 1.2.0
+================
 
+ - code cleanup
+ - use IFileData interface for data access
+ - mimetype converting
+
+
 2007/12/06 1.1.0
 ================
 
@@ -13,4 +20,3 @@
  - mimetype extension for audio/mpeg layer 3
  - removed setup.cfg
  - added CHANGES.txt
-

Modified: z3c.filetype/trunk/src/z3c/filetype/README.txt
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/README.txt	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/README.txt	2007-12-19 13:09:28 UTC (rev 82357)
@@ -7,12 +7,14 @@
 This package provides a way to get interfaces that are provided based
 on their content, filename or mime-type.
 
-  >>> from z3c.filetype import api
+  >>> from z3c.filetype import api, typeablefile
+  >>> from zope import event, component
+  >>> from zope.component import eventtesting
 
 We take some files for demonstration from the testdata directory.
 
   >>> import os
-  >>> testData = os.path.join(os.path.dirname(api.__file__),'testdata')
+  >>> testData = os.path.join(os.path.dirname(api.__file__), 'testdata')
 
   >>> fileNames = sorted(os.listdir(testData))
 
@@ -68,7 +70,10 @@
   >>> sorted(api.getInterfacesFor(f))
   [<InterfaceClass z3c.filetype.interfaces.filetypes.ITARFile>]
 
-  >>> sorted(api.getInterfacesFor(filename="x.png"))
+  >>> sorted(api.getInterfacesForFile(os.path.join(testData, 'test.tar')))
+  [<InterfaceClass z3c.filetype.interfaces.filetypes.ITARFile>]
+
+  >>> sorted(api.getInterfacesForFilename('x.png'))
   [<InterfaceClass z3c.filetype.interfaces.filetypes.IPNGFile>]
 
   >>> sorted(api.getInterfacesFor(f, filename="x.png"))
@@ -87,7 +92,7 @@
 
   >>> f.name
   '...test.tar'
-  >>> sorted(api.getInterfacesFor(f.name))
+  >>> sorted(api.getInterfacesForFilename(f.name))
   [<InterfaceClass z3c.filetype.interfaces.filetypes.ITARFile>]
 
 
@@ -98,34 +103,28 @@
 object. This object needs to implement ITypeableFile. So let us setup
 the event handling.
 
-  >>> from zope.component import eventtesting
-  >>> eventtesting.setUp()
-
   >>> from z3c.filetype import interfaces
   >>> from zope import interface
   >>> class Foo(object):
   ...     interface.implements(interfaces.ITypeableFile)
   ...     def __init__(self, f):
-  ...         self.data = f
+  ...         f.seek(0)
+  ...         self.data = f.read()
   >>> foo = Foo(f)
 
 There is also an event handler registered for IObjectCreatedEvent and
-IObjectModified on  ITypeableFile. We register them here in the test.
-
-  >>> from zope import component
-  >>> component.provideHandler(api.handleCreated)
-  >>> component.provideHandler(api.handleModified)
-
+IObjectModified on ITypeableFile.
 So we need to fire an IObjectCreatedEvent. Which is normally done by a
 factory.
 
   >>> from zope.lifecycleevent import ObjectCreatedEvent
   >>> from zope.lifecycleevent import ObjectModifiedEvent
-  >>> from zope.event import notify
+
   >>> eventtesting.clearEvents()
-  >>> notify(ObjectCreatedEvent(foo))
+  >>> event.notify(ObjectCreatedEvent(foo))
+
   >>> sorted(eventtesting.getEvents())
-  [<z3c.filetype.event.FileTypeModifiedEvent object at ...>,
+  [<z3c.filetype.interfaces.FileTypeModifiedEvent object at ...>,
    <zope.app.event.objectevent.ObjectCreatedEvent object at ...>]
 
 The object now implements the according interface. This is achieved by
@@ -146,16 +145,15 @@
 If we change the object the interface changes too. We need to fire
 an IObjectModifiedevent. Which is normally done by the implementation.
 
-  >>> foo.data = file(os.path.join(testData,'test.flv'), 'rb')
+  >>> foo.data = file(os.path.join(testData,'test.flv'), 'rb').read()
   >>> eventtesting.clearEvents()
-  >>> 
-  >>> notify(ObjectModifiedEvent(foo))
+  >>> event.notify(ObjectModifiedEvent(foo))
 
 Now we have two events, one we fired and one from our handler.
 
   >>> eventtesting.getEvents()
   [<zope.app.event.objectevent.ObjectModifiedEvent object at ...>,
-   <z3c.filetype.event.FileTypeModifiedEvent object at ...>]
+   <z3c.filetype.interfaces.FileTypeModifiedEvent object at ...>]
 
 Now the file should implement another filetype.
 
@@ -163,20 +161,22 @@
   [<InterfaceClass z3c.filetype.interfaces.filetypes.IFLVFile>]
 
 
-IFileType adapters
-==================
+IContentType adapters
+=====================
 
-There is also an adapter from ITypedFile to IFileType, which can be
+There is also an adapter from ITypedFile to IContentType, which can be
 used to get the default content type for the interface.
 
   >>> from z3c.filetype import adapters
-  >>> component.provideAdapter(adapters.TypedFileType)
+  >>> component.provideAdapter(adapters.ContentType)
+
   >>> for name in fileNames:
-  ...     if name==".svn": continue
+  ...     if name==".svn":
+  ...        continue
   ...     path = os.path.join(testData, name)
   ...     i =  Foo(file(path, 'rb'))
-  ...     notify(ObjectModifiedEvent(i))
-  ...     print name + " --> " + interfaces.IFileType(i).contentType
+  ...     event.notify(ObjectModifiedEvent(i))
+  ...     print name + " --> " + interfaces.IContentType(i).contentType
   DS_Store --> application/octet-stream
   IMG_0504.JPG --> image/jpeg
   faces_gray.avi --> video/x-msvideo
@@ -210,22 +210,22 @@
   >>> component.provideAdapter(size.PNGFileSized)
   >>> component.provideAdapter(size.JPGFileSized)
 
-  >>> foo.data = file(os.path.join(testData,'thumbnailImage_small.jpeg'), 'rb')
-  >>> notify(ObjectModifiedEvent(foo))
+  >>> foo.data = file(os.path.join(testData,'thumbnailImage_small.jpeg'), 'rb').read()
+  >>> event.notify(ObjectModifiedEvent(foo))
   >>> ISized(foo).sizeForDisplay().mapping
   {'width': '120', 'height': '90', 'size': '3'}
 
-  >>> foo.data = file(os.path.join(testData,'test.png'), 'rb')
-  >>> notify(ObjectModifiedEvent(foo))
+  >>> foo.data = file(os.path.join(testData,'test.png'), 'rb').read()
+  >>> event.notify(ObjectModifiedEvent(foo))
   >>> ISized(foo).sizeForDisplay().mapping
   {'width': '279', 'height': '19', 'size': '4'}
 
-  >>> foo.data = file(os.path.join(testData,'logo.gif'), 'rb')
-  >>> notify(ObjectModifiedEvent(foo))
+  >>> foo.data = file(os.path.join(testData,'logo.gif'), 'rb').read()
+  >>> event.notify(ObjectModifiedEvent(foo))
   >>> ISized(foo).sizeForDisplay().mapping
   {'width': '201', 'height': '54', 'size': '2'}
 
-  >>> foo.data = file(os.path.join(testData,'IMG_0504.JPG'), 'rb')
-  >>> notify(ObjectModifiedEvent(foo))
+  >>> foo.data = file(os.path.join(testData,'IMG_0504.JPG'), 'rb').read()
+  >>> event.notify(ObjectModifiedEvent(foo))
   >>> ISized(foo).sizeForDisplay().mapping
   {'width': '1600', 'height': '1200', 'size': '499'}

Modified: z3c.filetype/trunk/src/z3c/filetype/__init__.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/__init__.py	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/__init__.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -1 +1 @@
-#package
+# This file is necessary to make this directory a package.

Modified: z3c.filetype/trunk/src/z3c/filetype/adapters.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/adapters.py	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/adapters.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -18,12 +18,12 @@
 from zope import interface, component
 
 import interfaces
-from interfaces import filetypes
+from interfaces.filetypes import MT, ITypedFile
 
 
-class TypedFileType(object):
-    component.adapts(filetypes.ITypedFile)
-    interface.implements(interfaces.IFileType)
+class ContentType(object):
+    component.adapts(ITypedFile)
+    interface.implements(interfaces.IContentType)
 
     def __init__(self, context):
         self.context = context
@@ -32,9 +32,13 @@
     def contentType(self):
         decl = interface.Declaration(
             *interface.directlyProvidedBy(self.context))
+
         for iface in decl.flattened():
-            if not issubclass(iface, filetypes.ITypedFile):
+            if not iface.extends(ITypedFile):
                 continue
-            mt = iface.queryTaggedValue(filetypes.MT)
+
+            mt = iface.queryTaggedValue(MT)
             if mt is not None:
                 return mt
+
+        return 'application/octet-stream'

Modified: z3c.filetype/trunk/src/z3c/filetype/api.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/api.py	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/api.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -15,16 +15,15 @@
 
 $Id$
 """
-from StringIO import StringIO
-from zope import interface, component, event
+from zope import interface
+from zope import component, event
 from zope.proxy import removeAllProxies
 from zope.contenttype import guess_content_type
-from zope.lifecycleevent.interfaces import IObjectCreatedEvent
-from zope.lifecycleevent.interfaces import IObjectModifiedEvent
 
 from z3c.filetype import magic, interfaces
+from z3c.filetype.interfaces.api import IAPI
 from z3c.filetype.interfaces import filetypes
-from z3c.filetype.event import FileTypeModifiedEvent
+from z3c.filetype.interfaces.converter import IConverter, ConverterNotFound
 
 
 magicFile = magic.MagicFile()
@@ -43,40 +42,69 @@
     return res
 
 
-def getInterfacesFor(file=None, filename=None, mimeType=None):
+def getInterfacesFor(file, filename=None, mimeType=None):
     """returns a sequence of interfaces that are provided by file like
     objects (file argument) or string with an optional 
-    filename as name or mimeType as mime-type 
+    filename as name or mimeType as mime-type
     """
+    file.seek(0)
+
     ifaces = set()
-    if file is not None:
-        for mt in magicFile.detect(file):
-            ifaces.update(byMimeType(mt))
+    for mt in magicFile.detect(file):
+        ifaces.update(byMimeType(mt))
 
     if mimeType is not None:
         ifaces.update(byMimeType(mimeType))
 
     if filename is not None and not ifaces:
-        t = guess_content_type(filename)[0]
+        mt = guess_content_type(filename)[0]
         # dont trust this here because zope does not recognize some
         # binary files.
-        if t and not t == 'text/x-unknown-content-type':
-            ifaces.update(byMimeType(t))
+        if mt not in ('application/octet-stream', 
+                      'text/x-unknown-content-type'):
+            ifaces.update(byMimeType(mt))
 
     if not ifaces:
         ifaces.add(filetypes.IBinaryFile)
+
     return InterfaceSet(*ifaces)
 
 
+def getInterfacesForFile(filename, mimeType=None):
+    file = open(filename, 'r')
+    try:
+        ifaces = getInterfacesFor(file, filename, mimeType)
+    finally:
+        file.close()
+    return ifaces
+
+
+def getInterfacesForFilename(filename):
+    ifaces = set()
+    mt = guess_content_type(filename)[0]
+    if mt not in ('application/octet-stream', 
+                  'text/x-unknown-content-type'):
+        ifaces.update(byMimeType(mt))
+    if not ifaces:
+        ifaces.add(filetypes.IBinaryFile)
+    return InterfaceSet(*ifaces)        
+
+
 def applyInterfaces(obj):
-    assert(interfaces.ITypeableFile.providedBy(obj))
+    raw = interfaces.IFileData(obj, None)
+    if raw is None:
+        return False
 
-    ifaces = InterfaceSet(*getInterfacesFor(obj.data))
+    data  = raw.open('r')
+
+    ifaces = InterfaceSet(*getInterfacesFor(data))
     provided = set(interface.directlyProvidedBy(obj))
     for iface in provided:
         if not issubclass(iface, filetypes.ITypedFile):
             ifaces.add(iface)
 
+    data.close()
+
     ifaces = set(ifaces)
     if ifaces and (ifaces != provided):
         clean_obj = removeAllProxies(obj)
@@ -85,9 +113,11 @@
             if not iface.isOrExtends(filetypes.ITypedFile):
                 ifaces.add(iface)
 
-        interface.directlyProvides(clean_obj, ifaces)
-        event.notify(FileTypeModifiedEvent(obj))
-        return True
+        if ifaces:
+            interface.directlyProvides(clean_obj, ifaces)
+            event.notify(interfaces.FileTypeModifiedEvent(obj))
+            return True
+
     return False
 
 
@@ -131,16 +161,32 @@
         return iter(self._data)
 
 
- at component.adapter(interfaces.ITypeableFile, IObjectModifiedEvent)
-def handleModified(typeableFile, event):
-    """handles modification of data"""
-    if interfaces.IFileTypeModifiedEvent.providedBy(event):
-        # do nothing if this is already a filetype modification event
-        return
-    applyInterfaces(typeableFile)
+def convert(obj, mimetype, name=None, converter=IConverter, *args, **kw):
+    from z3c.filetype import typeablefile
 
+    dest = typeablefile.TypeableFile()
+    interface.directlyProvides(dest, mimetype)
 
- at component.adapter(interfaces.ITypeableFile, IObjectCreatedEvent)
-def handleCreated(typeableFile, event):
-    """handles modification of data"""
-    applyInterfaces(typeableFile)
+    if name is None:
+        for name, converter in component.getAdapters((obj, dest), converter):
+            converter.convert(*args, **kw)
+            return dest
+        raise ConverterNotFound(
+            "converter not found for (%s, %s)"%(obj, mimetype))
+    else:
+        if isinstance(name, basestring):
+            name = [name]
+
+        for nm in name:
+            converter = component.queryMultiAdapter((obj, dest), converter, nm)
+            if converter is not None:
+                converter.convert(*args, **kw)
+                return dest
+
+        if converter is None:
+            raise ConverterNotFound(
+                "converter not found for (%s, %s)"%(obj, mimetype))
+
+
+interface.moduleProvides(IAPI)
+__all__ = tuple(IAPI)

Modified: z3c.filetype/trunk/src/z3c/filetype/configure.zcml
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/configure.zcml	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/configure.zcml	2007-12-19 13:09:28 UTC (rev 82357)
@@ -1,19 +1,23 @@
 <configure
    xmlns="http://namespaces.zope.org/zope"
+   xmlns:zcml="http://namespaces.zope.org/zcml"
    i18n_domain="zope">
 
-  <subscriber for=".interfaces.ITypeableFile
-                   zope.lifecycleevent.interfaces.IObjectModifiedEvent"
-              handler=".api.handleModified"/>
+  <adapter factory=".adapters.ContentType" />
 
-  <subscriber for=".interfaces.ITypeableFile
-                   zope.lifecycleevent.interfaces.IObjectCreatedEvent"
-              handler=".api.handleCreated"/>
+  <adapter factory=".typeablefile.TypeableFileData" />
+  <subscriber handler=".typeablefile.handleCreated" />
+  <subscriber handler=".typeablefile.handleModified" />
 
-  <adapter factory=".adapters.TypedFileType"/>
-
   <adapter factory=".size.PNGFileSized" />
   <adapter factory=".size.JPGFileSized" />
   <adapter factory=".size.GIFFileSized" />
 
+  <class class="z3c.blobfile.file.File"
+	 zcml:condition="installed z3c.blobfile">
+    <implements interface=".interfaces.IFileData" />
+  </class>
+
+  <include package=".converters" />
+
 </configure>

Added: z3c.filetype/trunk/src/z3c/filetype/converter.txt
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converter.txt	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converter.txt	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,72 @@
+  Converting
+~~~~~~~~~~~~~~
+
+We can convert data, for this we should register converter for required types
+
+  >>> from zope import component, interface
+  >>> from z3c.filetype import api, interfaces
+  >>> from z3c.filetype.interfaces import filetypes
+  >>> from z3c.filetype.converters import jpggif
+
+Now we need load files
+
+  >>> import os.path
+  >>> testData = os.path.join(os.path.dirname(api.__file__), 'testdata')
+
+  >>> from z3c.filetype.typeablefile import TypeableFile
+  >>> img1 = TypeableFile(file(os.path.join(testData, 'logo.gif'), 'rb').read())
+
+  >>> sorted((interface.directlyProvidedBy(img1)))
+  [<InterfaceClass z3c.filetype.interfaces.filetypes.IGIFFile>]
+
+  >>> img2 = api.convert(img1, filetypes.IJPGFile)
+  Traceback (most recent call last):
+  ...
+  ConverterNotFound: ...
+
+Let's register simple image converters
+
+  >>> component.provideAdapter(jpggif.GIFtoJPEGConverter)
+  >>> component.provideAdapter(jpggif.JPEGtoGIFConverter)
+
+  >>> img2 = api.convert(img1, filetypes.IJPGFile)
+  >>> sorted((interface.directlyProvidedBy(img2)))
+  [<InterfaceClass z3c.filetype.interfaces.filetypes.IJPGFile>]
+
+Image converter can scale image
+
+  >>> interfaces.IImageSized(img2).getImageSize()
+  (201, 54)
+
+converter save width/height aspect retio
+
+  >>> img2 = api.convert(img1, filetypes.IJPGFile, width=100, height=54)
+  >>> interfaces.IImageSized(img2).getImageSize()
+  (100, 26)
+
+  >>> img2 = api.convert(img1, filetypes.IJPGFile, width=100, height=40)
+  >>> interfaces.IImageSized(img2).getImageSize()
+  (100, 26)
+
+  >>> img2 = api.convert(img1, filetypes.IJPGFile, scale=0.5)
+  >>> interfaces.IImageSized(img2).getImageSize()
+  (101, 27)
+
+Named converters, we can pass converter `name`. It can be string or sequence 
+of names.
+
+  >>> img2 = api.convert(img1, filetypes.IJPGFile, name='MyConverter')
+  Traceback (most recent call last):
+  ...
+  ConverterNotFound: ...
+
+  >>> component.provideAdapter(jpggif.GIFtoJPEGConverter, name='MyConverter')
+
+Simple case with only one name
+
+  >>> img2 = api.convert(img1, filetypes.IJPGFile, name='MyConverter')
+
+With multple names, it will try get `MyConverter` if it is not 
+exists it will try `PIL` etc.
+
+  >>> img2 = api.convert(img1, filetypes.IJPGFile, name=['MyConverter', 'PIL', ''])

Added: z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/converters/configure.zcml
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/configure.zcml	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/configure.zcml	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,32 @@
+<configure 
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:zcml="http://namespaces.zope.org/zcml">
+
+  <zcml:configure zcml:condition="installed z3c.filetype.converters.jpg">
+    <adapter name="PIL" factory=".jpg.JPEGtoJPEGConverter" />
+  </zcml:configure>
+
+  <zcml:configure zcml:condition="installed z3c.filetype.converters.gif">
+    <adapter name="PIL" factory=".gif.GIFtoGIFConverter" />
+  </zcml:configure>
+
+  <zcml:configure zcml:condition="installed z3c.filetype.converters.png">
+    <adapter name="PIL" factory=".png.PNGtoPNGConverter" />
+  </zcml:configure>
+  
+  <zcml:configure zcml:condition="installed z3c.filetype.converters.jpggif">
+    <adapter name="PIL" factory=".jpggif.GIFtoJPEGConverter" />
+    <adapter name="PIL" factory=".jpggif.JPEGtoGIFConverter" />
+  </zcml:configure>
+
+  <zcml:configure zcml:condition="installed z3c.filetype.converters.jpgpng">
+    <adapter name="PIL" factory=".jpgpng.PNGtoJPEGConverter" />
+    <adapter name="PIL" factory=".jpgpng.JPEGtoPNGConverter" />
+  </zcml:configure>
+
+  <zcml:configure zcml:condition="installed z3c.filetype.converters.pnggif">
+    <adapter name="PIL" factory=".pnggif.PNGtoGIFConverter" />
+    <adapter name="PIL" factory=".pnggif.GIFtoPNGConverter" />
+  </zcml:configure>
+
+</configure>

Added: z3c.filetype/trunk/src/z3c/filetype/converters/gif.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/gif.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/gif.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" JPEG -> JPEG
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+    PIL.Image.core.gif_decoder
+    PIL.Image.core.gif_encoder
+except:
+    log("GIF converters are not available. PIL doesn't support this formats")
+    raise ImportError()
+
+
+class GIFtoGIFConverter(PILImageConverter):
+    component.adapts(filetypes.IGIFFile, filetypes.IGIFFile)
+    
+    _from_format = 'gif'
+    _dest_format = 'gif'


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/gif.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" JPEG -> JPEG
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+    PIL.Image.core.jpeg_decoder
+    PIL.Image.core.jpeg_encoder
+except:
+    log("JPEG converters are not available. PIL doesn't support this formats")
+    raise ImportError()
+
+
+class JPEGtoJPEGConverter(PILImageConverter):
+    component.adapts(filetypes.IJPGFile, filetypes.IJPGFile)
+    
+    _from_format = 'jpeg'
+    _dest_format = 'jpeg'


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" GIF -> JPEG and JPEG - > GIF converters
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+    PIL.Image.core.gif_decoder
+    PIL.Image.core.gif_encoder
+    PIL.Image.core.jpeg_decoder
+    PIL.Image.core.jpeg_encoder
+except:
+    log("JPEG/GIF converters are not available. PIL doesn't support this formats")
+    raise ImportError()
+
+
+class JPEGtoGIFConverter(PILImageConverter):
+    component.adapts(filetypes.IJPGFile, filetypes.IGIFFile)
+    
+    _from_format = 'jpeg'
+    _dest_format = 'gif'
+
+
+class GIFtoJPEGConverter(PILImageConverter):
+    component.adapts(filetypes.IGIFFile, filetypes.IJPGFile)
+    
+    _from_format = 'gif'
+    _dest_format = 'jpeg'
+


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" PNG -> JPEG and JPEG - > PNG converters
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+    PIL.Image.core.png_decoder
+    PIL.Image.core.png_encoder
+    PIL.Image.core.jpeg_decoder
+    PIL.Image.core.jpeg_encoder
+except:
+    log("JPEG/PNG converters are not available. PIL doesn't support this formats")
+    raise ImportError()
+
+
+class JPEGtoPNGConverter(PILImageConverter):
+    component.adapts(filetypes.IJPGFile, filetypes.IPNGFile)
+    
+    _from_format = 'jpeg'
+    _dest_format = 'png'
+
+
+class PNGtoJPEGConverter(PILImageConverter):
+    component.adapts(filetypes.IPNGFile, filetypes.IJPGFile)
+
+    _from_format = 'png'
+    _dest_format = 'jpeg'


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,82 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" PIL based image converter
+
+$Id$
+"""
+from cStringIO import StringIO
+
+from zope import interface
+from z3c.filetype.interfaces import IFileData
+from z3c.filetype.interfaces.converter import \
+    ISimpleImageConverter, ConverterException
+from z3c.filetype.converters.utils import log
+
+try:
+    import PIL.Image
+except:
+    log("Python Imaging library is not available.")
+    raise
+
+
+class PILImageConverter(object):
+    interface.implements(ISimpleImageConverter)
+
+    _from_format = 'raw'
+    _dest_format = 'raw'
+
+    def __init__(self, context, destination):
+        self.context = context
+        self.destination = destination
+
+    def convert(self, width=None, height=None, scale=0, quality=88):
+        """ convert image """
+        file = IFileData(self.context, None)
+        if file is None:
+            raise ConverterException("Can't get data from context.")
+        
+        data = file.open('r')
+        try:
+            image = PIL.Image.open(data)
+
+            if image.mode == '1':
+                image = image.convert('L')
+            elif image.mode == 'P':
+                image = image.convert('RGBA')
+
+            # get width, height
+            orig_size = image.size
+            if width is None: width = orig_size[0]
+            if height is None: height = orig_size[1]
+        
+            # Auto-scaling support
+            if scale:
+                width =  int(round(int(width) * scale))
+                height = int(round(int(height) * scale))
+
+            #convert image
+            pilfilter = PIL.Image.NEAREST
+            if PIL.Image.VERSION >= "1.1.3":
+                pilfilter = PIL.Image.ANTIALIAS
+
+            image.thumbnail((width, height), pilfilter)
+
+            newfile = StringIO()
+            image.save(newfile, self._dest_format, quality=quality)
+            self.destination.data = newfile.getvalue()
+        except Exception, e:
+            data.close()
+            raise ConverterException(str(e))
+
+        data.close()


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/converters/png.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/png.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/png.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" PNG -> PNG
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+    PIL.Image.core.png_decoder
+    PIL.Image.core.png_encoder
+except:
+    log("PNG converters are not available. PIL doesn't support this formats")
+    raise ImportError()
+
+
+class PNGtoPNGConverter(PILImageConverter):
+    component.adapts(filetypes.IPNGFile, filetypes.IPNGFile)
+    
+    _from_format = 'png'
+    _dest_format = 'png'


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/png.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" GIF -> PNG and PNG - > GIF converters
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+    PIL.Image.core.gif_decoder
+    PIL.Image.core.gif_encoder
+    PIL.Image.core.png_decoder
+    PIL.Image.core.png_encoder
+except:
+    log("PNG/GIF converters are not available. PIL doesn't support this formats")
+    raise ImportError()
+
+
+class PNGtoGIFConverter(PILImageConverter):
+    component.adapts(filetypes.IPNGFile, filetypes.IGIFFile)
+
+    _from_format = 'png'
+    _dest_format = 'gif'
+
+
+class GIFtoPNGConverter(PILImageConverter):
+    component.adapts(filetypes.IGIFFile, filetypes.IPNGFile)
+    
+    _from_format = 'gif'
+    _dest_format = 'png'


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/converters/utils.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/utils.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/utils.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" 
+
+$Id$
+"""
+import logging, sys
+
+def log(msg='', subsystem='z3c.filetype.converters'):
+    log = logging.getLogger(subsystem)
+    log.log(logging.INFO, msg)


Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/utils.py
___________________________________________________________________
Name: svn:keywords
   + Id

Deleted: z3c.filetype/trunk/src/z3c/filetype/event.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/event.py	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/event.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -1,24 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2007 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.
-#
-##############################################################################
-"""
-
-$Id$
-"""
-from zope import interface
-from zope.lifecycleevent import ObjectModifiedEvent
-from z3c.filetype import interfaces
-
-
-class FileTypeModifiedEvent(ObjectModifiedEvent):
-    interface.implements(interfaces.IFileTypeModifiedEvent)

Modified: z3c.filetype/trunk/src/z3c/filetype/interfaces/__init__.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/interfaces/__init__.py	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/interfaces/__init__.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -17,45 +17,41 @@
 """
 from zope import interface, schema
 from zope.size.interfaces import ISized
+from zope.lifecycleevent import ObjectModifiedEvent
 from zope.lifecycleevent.interfaces import IObjectModifiedEvent
 
 
-class MagicError(Exception):
-    pass
+class IMimetypeType(interface.interfaces.IInterface):
+    """ mimetype type """
 
 
-class MagicTestError(MagicError):
-    pass
+class IContentType(interface.Interface):
 
+    contentType = schema.TextLine(
+        title = u'Content Type')
 
-class OffsetError(MagicError): 
-    pass
 
+class IFileData(interface.Interface):
 
-class MagicFileError(MagicError): 
-    pass
+    def open(mode='r'):
+        """ Open file, returns file(-like) object for handling the data """
 
 
-class IMimetypeType(interface.interfaces.IInterface):
-    """ mimetype type """
+class IImageSized(ISized):
 
+    def getImageSize():
+        """ return width, height of image """
 
-class IFileType(interface.Interface):
 
-    contentType = schema.TextLine(title = u'Content Type')
-
-
-class IFileTypeModifiedEvent(IObjectModifiedEvent):
-    """This event is fired when the filetypes change on an object"""
-
-
 class ITypeableFile(interface.Interface):
     """A file object that is typeable"""
 
     data = interface.Attribute('Data of the file')
 
 
-class IImageSized(ISized):
+class IFileTypeModifiedEvent(IObjectModifiedEvent):
+    """This event is fired when the filetypes change on an object"""
 
-    def getImageSize():
-        """ return width, height of image """
+
+class FileTypeModifiedEvent(ObjectModifiedEvent):
+    interface.implements(IFileTypeModifiedEvent)

Added: z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,43 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" api interfaces
+
+$Id$
+"""
+from zope import interface
+
+
+class IAPI(interface.Interface):
+    """ filetype api """
+
+    def byMimeType(mt):
+        """returns interfaces implemented by mimeType"""
+
+    def getInterfacesFor(file, filename=None, mimeType=None):
+        """ returns a sequence of interfaces that are provided by file like
+        objects (file argument) with an optional
+        filename as name or mimeType as mime-type """
+
+    def getInterfacesForFile(filename, mimeType=None):
+        """ returns a sequence of interfaces that are provided by file 
+        with filename as name and optional mimeType as mime-type """
+
+    def getInterfacesForFilename(filename):
+        """ returns a sequence of interfaces that are provided by filename """
+
+    def applyInterfaces(obj):
+        """ detect object mimetypes and set mimetype interfaces """
+
+    def convert(obj, mimetype):
+        """ convert obj to destination mimetype """


Property changes on: z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" converter interfaces
+
+$Id$
+"""
+from zope import interface
+
+
+class ConverterException(Exception):
+    """ base converter exception """
+
+
+class ConverterNotFound(Exception):
+    """ required converter not found """
+
+
+class IConverterModule(interface.Interface):
+    """ converter module """
+
+    def convert(context, mimetype):
+        """ convert `context` data to mimetype data, return ITypeableFile object """
+
+
+class IConverter(interface.Interface):
+    """ base interface for data converter """
+
+    def __init__(context, destination):
+        """ adapter """
+
+    def convert():
+        """ convert data """
+
+
+class ISimpleImageConverter(IConverter):
+    """ simple image converter """
+
+    def convert(width=None, height=None, scale=0):
+        """ convert image """


Property changes on: z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" magic file interfaces
+
+$Id$
+"""
+
+class MagicError(Exception):
+    pass
+
+
+class MagicTestError(MagicError):
+    pass
+
+
+class OffsetError(MagicError): 
+    pass
+
+
+class MagicFileError(MagicError): 
+    pass


Property changes on: z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: z3c.filetype/trunk/src/z3c/filetype/magic.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/magic.py	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/magic.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -1,9 +1,11 @@
 # Found on a russian zope mailing list, and modified to fix bugs in parsing
 # the magic file and string making, cleanup
 # -- Daniel Berlin <dberlin at dberlin.org>
-import sys, struct, time, re, pprint, os
+import sys, os.path
+import struct, time, re, pprint
 
-from interfaces import OffsetError, MagicFileError, MagicTestError
+from z3c.filetype.interfaces.magic import \
+    OffsetError, MagicFileError, MagicTestError
 
 default_magic = os.path.join(os.path.dirname(__file__), 'magic.mime')
 
@@ -19,85 +21,53 @@
 
 
 KnownTypes = {
-    'byte':_handle('@b'),
-    'byte':_handle('@B'),
-    'ubyte':_handle('@B'),
-    'string':('s',0,None),
-    'pstring':_handle('p'),
-    'short':_handle('@h'),
-    'beshort':_handle('>h'),
-    'leshort':_handle('<h'),
-    'short':_handle('@H'),
-    'beshort':_handle('>H'),
-    'leshort':_handle('<H'),
-    'ushort':_handle('@H'),
-    'ubeshort':_handle('>H'),
-    'uleshort':_handle('<H'),
+    'byte': _handle('@b'),
+    'byte': _handle('@B'),
+    'ubyte': _handle('@B'),
+    'string': ('s', 0, None),
+    'pstring': _handle('p'),
+    'short': _handle('@h'),
+    'beshort': _handle('>h'),
+    'leshort': _handle('<h'),
+    'short': _handle('@H'),
+    'beshort': _handle('>H'),
+    'leshort': _handle('<H'),
+    'ushort': _handle('@H'),
+    'ubeshort': _handle('>H'),
+    'uleshort': _handle('<H'),
     
-    'long':_handle('@l'),
-    'belong':_handle('>l'),
-    'lelong':_handle('<l'),
-    'ulong':_handle('@L'),
-    'ubelong':_handle('>L'),
-    'ulelong':_handle('<L'),
+    'long': _handle('@l'),
+    'belong': _handle('>l'),
+    'lelong': _handle('<l'),
+    'ulong': _handle('@L'),
+    'ubelong': _handle('>L'),
+    'ulelong': _handle('<L'),
     
-    'date':_handle('=l'),
-    'bedate':_handle('>l'),
-    'ledate':_handle('<l'),
-    'ldate':_handle('=l',_ldate_adjust),
-    'beldate':_handle('>l',_ldate_adjust),
-    'leldate':_handle('<l',_ldate_adjust),
+    'date': _handle('=l'),
+    'bedate': _handle('>l'),
+    'ledate': _handle('<l'),
+    'ldate': _handle('=l', _ldate_adjust),
+    'beldate': _handle('>l', _ldate_adjust),
+    'leldate': _handle('<l', _ldate_adjust),
 }
 
 
 def has_format(s):
     n = 0
     l = None
-    for c in s :
-        if c == '%' :
-            if l == '%' : n -= 1
-            else        : n += 1
+    for c in s:
+        if c == '%':
+            if l == '%': n -= 1
+            else       : n += 1
         l = c
     return n
 
 
-def read_asciiz(file, size=None, pos=None):
-    s = []
-    if pos :
-        file.seek( pos, 0 )
-
-    if size is not None :
-        s = [file.read( size ).split('\0')[0]]
-    else:
-        while 1 :
-            c = file.read(1)
-            if (not c) or (ord(c)==0) or (c=='\n') : break
-            s.append (c)
-
-    return ''.join(s)
-
-
-def a2i(v,base=0):
-    if v[-1:] in 'lL' : v = v[:-1]
-    return int( v, base )
-
-_cmap = {
-        '\\' : '\\',
-        '0' : '\0',
-}
-for c in range(ord('a'),ord('z')+1) :
-    try               : e = eval('"\\%c"' % chr(c))
-    except ValueError : pass
-    else              : _cmap[chr(c)] = e
-else:
-    del c
-    del e
-
 def make_string(s):
     # hack, is this the right way?
-    s = s.replace('\\<','<')
-    s = s.replace('\\ ',' ')
-    return eval( '"'+s.replace('"','\\"')+'"')
+    s = s.replace('\\<', '<')
+    s = s.replace('\\ ', ' ')
+    return eval('"%s"'%s.replace('"', '\\"'))
 
 
 class MagicTest:
@@ -112,25 +82,29 @@
         self.nmod = None
         self.offset, self.type, self.test, self.message = \
             offset, mtype, test, message
-        if self.mtype == 'true' : return # XXX hack to enable level skips
-        if test[-1:]=='\\' and test[-2:]!='\\\\' :
+
+        if self.mtype == 'true':
+            return # XXX hack to enable level skips
+
+        if (test[-1:] == '\\') and (test[-2:] != '\\\\'):
             self.test += 'n' # looks like someone wanted EOL to match?
-        if mtype[:6]=='string' :
+
+        if mtype[:6] == 'string':
             if '/' in mtype : # for strings
                 self.type, self.smod = \
                     mtype[:mtype.find('/')], mtype[mtype.find('/')+1:]
         else:
-            for nm in '&+-' :
-                if nm in mtype : # for integer-based
-                    self.nmod, self.type, self.mask = (
-                            nm,
-                            mtype[:mtype.find(nm)],
-                            # convert mask to int, autodetect base
-                            int( mtype[mtype.find(nm)+1:], 0 )
-                    )
+            for nm in '&+-':
+                if nm in mtype: # for integer-based
+                    self.nmod = nm
+                    self.type = mtype[:mtype.find(nm)]
+
+                    # convert mask to int, autodetect base
+                    self.mask = int(mtype[mtype.find(nm)+1:], 0)
                     break
-        self.struct, self.size, self.cast = KnownTypes[ self.type ]
 
+        self.struct, self.size, self.cast = KnownTypes[self.type]
+
     def __str__(self):
         return '%s %s %s %s' % (
             self.offset, self.mtype, self.mtest, self.message)
@@ -142,15 +116,11 @@
                 '\t'*self.level, pprint.pformat(self.subtests)
         )
 
-    def run(self,file):
+    def run(self, file):
         result = ''
-        do_close = 0
+
         try:
-            if isinstance(file, basestring):
-                file = open(file, 'r', BUFFER_SIZE)
-                do_close = 1
-
-            if self.mtype != 'true' :
+            if self.mtype != 'true':
                 data = self.read(file)
                 try:
                     last = file.tell()
@@ -161,55 +131,74 @@
             else:
                 data = last = None
 
-            if self.check(data) :
-                result = self.message+' '
-                if has_format( result ) : result %= data
-                for test in self.subtests :
+            if self.check(data):
+                result = self.message + ' '
+
+                if has_format(result):
+                    result %= data
+
+                for test in self.subtests:
                     m = test.run(file)
-                    if m is not None : result += m
-                return make_string( result )
-        finally:
-            if do_close :
-                file.close()
+                    if m is not None:
+                        result += m
 
+                return make_string(result)
+        except Exception, e:
+            #log_info('Type detection: %s'%str(e)
+            pass
+
+    def a2i(self, v, base=0):
+        if v[-1:] in 'lL': 
+            v = v[:-1]
+        return int(v, base)
+
     def get_mod_and_value(self):
-        if self.type[-6:] == 'string' :
-            if self.test[0] in '=<>' :
-                mod, value = self.test[0], make_string( self.test[1:] )
+        if self.type[-6:] == 'string':
+            if self.test[0] in '=<>':
+                mod = self.test[0]
+                value = make_string(self.test[1:])
             else:
-                mod, value = '=', make_string( self.test )
+                mod = '='
+                value = make_string( self.test )
         else:
-            if self.test[0] in '=<>&^' :
-                mod, value = self.test[0], a2i(self.test[1:])
+            if self.test[0] in '=<>&^':
+                mod = self.test[0]
+                value = self.a2i(self.test[1:])
             elif self.test[0] == 'x':
                 mod = self.test[0]
                 value = 0
             else:
-                mod, value = '=', a2i(self.test)
+                mod = '='
+                value = self.a2i(self.test)
         return mod, value
 
     def read(self, file):
-        file.seek( self.offset(file), 0 ) # SEEK_SET
+        file.seek(self.offset(file), 0) # SEEK_SET
         try:
             data = rdata = None
             # XXX self.size might be 0 here...
-            if self.size == 0 :
+            if self.size == 0:
                 # this is an ASCIIZ string...
                 size = None
-                if self.test != '>\\0' : # magic's hack for string read...
+                if self.test != '>\\0': # magic's hack for string read...
                     value = self.get_mod_and_value()[1]
                     size = (value=='\0') and None or len(value)
-                rdata = data = read_asciiz( file, size=size )
+
+                rdata = data = self.read_asciiz(file, size=size)
             else:
-                rdata = file.read( self.size )
-                if not rdata or (len(rdata)!=self.size) : return None
-                data = struct.unpack( self.struct, rdata )[0] # XXX hack??
+                rdata = file.read(self.size)
+                if not rdata or (len(rdata) != self.size):
+                    return None
+
+                data = struct.unpack(self.struct, rdata)[0] # XXX hack??
         except:
             raise MagicTestError('@%s struct=%s size=%d rdata=%s' % (
                     self.offset, `self.struct`, self.size, `rdata`))
 
-        if self.cast : data = self.cast( data )
-        if self.mask :
+        if self.cast:
+            data = self.cast(data)
+
+        if self.mask:
             try:
                 if self.nmod == '&':
                     data &= self.mask
@@ -225,7 +214,20 @@
 
         return data
 
-    def check(self,data):
+    def read_asciiz(self, file, size=None):
+        s = []
+        if size is not None :
+            s = [file.read(size).split('\0')[0]]
+        else:
+            while 1:
+                c = file.read(1)
+                if (not c) or (ord(c) == 0) or (c == '\n'):
+                    break
+            s.append (c)
+
+        return ''.join(s)
+
+    def check(self, data):
         if self.mtype == 'true' :
             return '' # not None !
 
@@ -233,17 +235,20 @@
         if self.type[-6:] == 'string' :
             # "something like\tthis\n"
             if self.smod:
-                #import pdb;pdb.set_trace()
                 xdata = data
-                if 'b' in self.smod : # all blanks are optional
-                    xdata = ''.join( data.split() )
-                    value = ''.join( value.split() )
-                if 'c' in self.smod : # all blanks are optional
+
+                if 'b' in self.smod: # all blanks are optional
+                    xdata = ''.join(data.split())
+                    value = ''.join(value.split())
+
+                if 'c' in self.smod: # all blanks are optional
                     xdata = xdata.upper()
                     value = value.upper()
-                if 'B' in self.smod : # compact blanks
-                    data = ' '.join( data.split() )
-                    if ' ' not in data : return None
+
+                if 'B' in self.smod: # compact blanks
+                    data = ' '.join(data.split())
+                    if ' ' not in data:
+                        return None
             else:
                 xdata = data
         try:
@@ -253,40 +258,46 @@
             elif mod == '&' : result = data & value
             elif mod == '^' : result = (data & (~value)) == 0
             elif mod == 'x' : result = 1
-            else            : raise MagicTestError(self.test)
-            if result :
+            else: 
+                raise MagicTestError(self.test)
+
+            if result:
                 zdata, zval = `data`, `value`
-                if self.mtype[-6:]!='string' :
-                    try: zdata, zval = hex(data), hex(value)
-                    except: zdata, zval = `data`, `value`
-                if 0 : print >>sys.stderr, '%s @%s %s:%s %s %s => %s (%s)' % (
-                        '>'*self.level, self.offset,
-                        zdata, self.mtype, `mod`, zval, `result`,
-                        self.message
-                )
+
+                if self.mtype[-6:] != 'string':
+                    try: 
+                        zdata = hex(data)
+                        zval = hex(value)
+                    except: 
+                        zdata = `data`
+                        zval = `value`
+
             return result
         except:
             raise MagicTestError('mtype=%s data=%s mod=%s value=%s' % (
                     `self.mtype`, `data`, `mod`, `value`))
 
     def add(self,mt):
-        if not isinstance(mt,MagicTest) :
-            raise MagicTestError((mt,'incorrect subtest type %s'%(type(mt),)))
-        if mt.level == self.level+1 :
-            self.subtests.append( mt )
-        elif self.subtests :
-            self.subtests[-1].add( mt )
-        elif mt.level > self.level+1 :
+        if not isinstance(mt, MagicTest):
+            raise MagicTestError((mt, 'incorrect subtest type %s'%(type(mt),)))
+
+        if mt.level == self.level+1:
+            self.subtests.append(mt)
+
+        elif self.subtests:
+            self.subtests[-1].add(mt)
+
+        elif mt.level > self.level+1:
             # it's possible to get level 3 just after level 1 !!! :-(
             level = self.level + 1
-            while level < mt.level :
-                xmt = MagicTest(None,'true','x','',line=self.line,level=level)
-                self.add( xmt )
+            while level < mt.level:
+                xmt = MagicTest(None, 'true', 'x', '', line=self.line, level=level)
+                self.add(xmt)
                 level += 1
             else:
-                self.add( mt ) # retry...
+                self.add(mt) # retry...
         else:
-            raise MagicTestError((mt,'incorrect subtest level %s'%(`mt.level`,)))
+            raise MagicTestError((mt, 'incorrect subtest level %s'%(`mt.level`,)))
 
     def last_test(self):
         return self.subtests[-1]
@@ -330,30 +341,40 @@
     def __init__(self,s):
         self.source = s
         self.value  = None
-        self.relative = 0
+        self.relative = False
         self.base = self.type = self.sign = self.offs = None
-        m = Offset.pattern0.match( s )
-        if m : # just a number
-            if s[0] == '&' :
-                self.relative, self.value = 1, int( s[1:], 0 )
+
+        m = Offset.pattern0.match(s)
+        if m: # just a number
+            if s[0] == '&':
+                self.relative = True
+                self.value = int(s[1:], 0)
             else:
                 self.value = int( s, 0 )
             return
-        m = Offset.pattern1.match( s )
-        if m : # real indirect offset
+
+        m = Offset.pattern1.match(s)
+        if m: # real indirect offset
             try:
                 self.base = m.group('base')
-                if self.base[0] == '&' :
-                    self.relative, self.base = 1, int( self.base[1:], 0 )
+                if self.base[0] == '&':
+                    self.relative = True
+                    self.base = int(self.base[1:], 0)
                 else:
-                    self.base = int( self.base, 0 )
-                if m.group('type') : self.type = m.group('type')[1:]
+                    self.base = int(self.base, 0)
+
+                if m.group('type'): 
+                    self.type = m.group('type')[1:]
+
                 self.sign = m.group('sign')
-                if m.group('off') : self.offs = int( m.group('off'), 0 )
-                if self.sign == '-' : self.offs = 0 - self.offs
+                if m.group('off'):
+                    self.offs = int( m.group('off'), 0 )
+
+                if self.sign == '-':
+                    self.offs = 0 - self.offs
             except:
-                print >>sys.stderr, '$$', m.groupdict()
-                raise
+                raise OffsetError(m.groupdict())
+
             return
         raise OffsetError(`s`)
 
@@ -369,7 +390,10 @@
             frmt = Offset.pos_format.get(self.type, 'I')
             size = struct.calcsize(frmt)
             data = struct.unpack(frmt, file.read(size))
-            if self.offs : data += self.offs
+
+            if self.offs:
+                data += self.offs
+
             return data
         finally:
             file.seek(pos, 0)

Modified: z3c.filetype/trunk/src/z3c/filetype/size.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/size.py	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/size.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -15,13 +15,12 @@
 
 $Id$
 """
-import os
-import stat
-import struct
+import os, struct
 import zope.i18nmessageid
 from zope import component, interface
 from zope.size import byteDisplay
-from z3c.filetype.interfaces import filetypes, IImageSized
+from zope.cachedescriptors.property import Lazy
+from z3c.filetype.interfaces import filetypes, IImageSized, IFileData
 
 _ = zope.i18nmessageid.MessageFactory("zope")
 
@@ -29,16 +28,16 @@
 class ImageFileSized(object):
     interface.implements(IImageSized)
     
-    def __init__(self, image):
-        self._image = image
+    def __init__(self, context):
+        self.context = context
 
-    @property
+    @Lazy
     def bytes(self):
-        try:
-            return len(self._image.data)
-        except TypeError:
-            data = self._image.data
-            return int(os.fstat(data.fileno())[stat.ST_SIZE])
+        file = IFileData(self.context).open('r')
+        file.seek(0, 2)
+        len = file.tell()
+        file.close()
+        return len
 
     def sizeForSorting(self):
         '''See `ISized`'''
@@ -54,6 +53,7 @@
             w = '?'
         if h < 0:
             h = '?'
+
         byte_size = byteDisplay(self.bytes)
         mapping = byte_size.mapping
         if mapping is None:
@@ -68,9 +68,11 @@
     component.adapts(filetypes.IGIFFile)
 
     def getImageSize(self):
-        data = self._image.data
-        data.seek(0)
-        data = data.read(24)
+        file = IFileData(self.context).open('r')
+        file.seek(0)
+        data = file.read(24)
+        file.close()
+
         size = len(data)
         width = -1
         height = -1
@@ -79,6 +81,7 @@
             w, h = struct.unpack("<HH", data[6:10])
             width = int(w)
             height = int(h)
+
         return width, height
 
 
@@ -86,9 +89,11 @@
     component.adapts(filetypes.IPNGFile)
 
     def getImageSize(self):
-        data = self._image.data
-        data.seek(0)
-        data = data.read(24)
+        file = IFileData(self.context).open('r')
+        file.seek(0)
+        data = file.read(24)
+        file.close()
+
         size = len(data)
         height = -1
         width = -1
@@ -112,8 +117,9 @@
     component.adapts(filetypes.IJPGFile)
 
     def getImageSize(self):
-        data = self._image.data
+        data = IFileData(self.context).open('r')
         data.seek(2)
+
         size = self.bytes
         height = -1
         width = -1
@@ -122,17 +128,21 @@
             w = -1
             h = -1
             while (b and ord(b) != 0xDA):
-                while (ord(b) != 0xFF): 
+                while (ord(b) != 0xFF):
                     b = data.read(1)
-                while (ord(b) == 0xFF): 
+
+                while (ord(b) == 0xFF):
                     b = data.read(1)
+
                 if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
                     data.read(3)
                     h, w = struct.unpack(">HH", data.read(4))
                     break
                 else:
                     data.read(int(struct.unpack(">H", data.read(2))[0])-2)
+
                 b = data.read(1)
+
             width = int(w)
             height = int(h)
 
@@ -141,4 +151,5 @@
         except ValueError:
             pass
 
+        data.close()
         return width, height

Modified: z3c.filetype/trunk/src/z3c/filetype/tests.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/tests.py	2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/tests.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -15,17 +15,38 @@
 
 $Id$
 """
-import doctest
-import unittest
+import doctest, unittest
+from zope.app.testing import setup
+from zope import component
 
+from z3c.filetype import size, typeablefile
+    
 
+def setUp(test):
+    setup.placelessSetUp()
+
+    component.provideAdapter(size.JPGFileSized)
+    component.provideAdapter(size.GIFFileSized)
+    component.provideAdapter(size.PNGFileSized)
+
+    component.provideAdapter(typeablefile.TypeableFileData)
+    component.provideHandler(typeablefile.handleCreated)
+    component.provideHandler(typeablefile.handleModified)
+
+
 def test_suite():
     return unittest.TestSuite((
             doctest.DocFileSuite(
                 'README.txt',
+                setUp=setUp, tearDown=setup.placelessTearDown,
                 optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                 ),
             doctest.DocFileSuite(
+                'converter.txt',
+                setUp=setUp, tearDown=setup.placelessTearDown,
+                optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+                ),
+            doctest.DocFileSuite(
                 'magic.txt',
                 optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                 ),

Added: z3c.filetype/trunk/src/z3c/filetype/typeablefile.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/typeablefile.py	                        (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/typeablefile.py	2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" ITypeableFile implementation
+
+$Id$
+"""
+from cStringIO import StringIO
+from zope import interface, component
+from zope.lifecycleevent.interfaces import IObjectCreatedEvent
+from zope.lifecycleevent.interfaces import IObjectModifiedEvent
+
+from z3c.filetype import api, interfaces
+
+
+class TypeableFile(object):
+    interface.implements(interfaces.ITypeableFile)
+
+    def __init__(self, data=''):
+        self.data = data
+        if data:
+            api.applyInterfaces(self)
+
+
+class TypeableFileData(object):
+    interface.implements(interfaces.IFileData)
+    component.adapts(interfaces.ITypeableFile)
+
+    def __init__(self, context):
+        self.context = context
+
+    def open(self, mode=''):
+        return StringIO(self.context.data)
+
+
+ at component.adapter(interfaces.ITypeableFile, IObjectCreatedEvent)
+def handleCreated(typeableFile, event):
+    """handles modification of data"""
+    api.applyInterfaces(typeableFile)
+
+
+ at component.adapter(interfaces.ITypeableFile, IObjectModifiedEvent)
+def handleModified(typeableFile, event):
+    """handles modification of data"""
+    if interfaces.IFileTypeModifiedEvent.providedBy(event):
+        return
+    api.applyInterfaces(typeableFile)


Property changes on: z3c.filetype/trunk/src/z3c/filetype/typeablefile.py
___________________________________________________________________
Name: svn:keywords
   + Id



More information about the Checkins mailing list