From d505f3629ac294f96deba27e4667c7f24485d289 Mon Sep 17 00:00:00 2001 From: helmutm Date: Wed, 12 Mar 2008 10:53:52 +0000 Subject: [PATCH] added integrator package: transparent access to filesystem directories and files git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@2441 fd906abe-77d9-0310-91a1-e0d9ade77398 --- integrator/README.txt | 58 +++++++++++ integrator/__init__.py | 3 + integrator/base.py | 134 +++++++++++++++++++++++++ integrator/filesystem.py | 130 ++++++++++++++++++++++++ integrator/interfaces.py | 59 +++++++++++ integrator/testdata/index.html | 9 ++ integrator/testdata/sub/demo.tgz | Bin 0 -> 432 bytes integrator/testdata/sub/index.html | 9 ++ integrator/testdata/sub/loops_logo.png | Bin 0 -> 1774 bytes integrator/tests.py | 28 ++++++ 10 files changed, 430 insertions(+) create mode 100644 integrator/README.txt create mode 100644 integrator/__init__.py create mode 100644 integrator/base.py create mode 100644 integrator/filesystem.py create mode 100644 integrator/interfaces.py create mode 100644 integrator/testdata/index.html create mode 100644 integrator/testdata/sub/demo.tgz create mode 100644 integrator/testdata/sub/index.html create mode 100644 integrator/testdata/sub/loops_logo.png create mode 100755 integrator/tests.py diff --git a/integrator/README.txt b/integrator/README.txt new file mode 100644 index 0000000..ff4e43d --- /dev/null +++ b/integrator/README.txt @@ -0,0 +1,58 @@ +========================================= +Integrating objects from external systems +========================================= + +Integration of external sources. + + ($Id$) + + +Getting started +=============== + +Let's do some basic set up + + >>> from zope import component, interface + + >>> from cybertools.integrator.tests import testDir + >>> from cybertools.integrator.filesystem import ContainerFactory, FileFactory + >>> from cybertools.integrator.interfaces import IContainerFactory + >>> component.provideUtility(ContainerFactory(), name='filesystem') + >>> component.provideUtility(FileFactory(), name='filesystem') + + +Accessing Objects in the Filesystem +======================================= + + >>> top = component.getUtility(IContainerFactory, name='filesystem')(testDir) + >>> sorted(top) + ['index.html', 'sub'] + >>> len(top) + 2 + + >>> sub = top['sub'] + >>> sorted(sub) + ['demo.tgz', 'index.html', 'loops_logo.png'] + + >>> file = sub['demo.tgz'] + >>> file.contentType + 'application/x-tar' + >>> file.getSize() + 432L + + >>> logo = sub['loops_logo.png'] + >>> logo.contentType + 'image/png' + >>> logo.getImageSize() + (145, 42) + + >>> html = top['index.html'] + >>> html.contentType + 'text/html' + >>> print html.data + ... + + Subdirectory + Demo... + ... + diff --git a/integrator/__init__.py b/integrator/__init__.py new file mode 100644 index 0000000..38314f3 --- /dev/null +++ b/integrator/__init__.py @@ -0,0 +1,3 @@ +""" +$Id$ +""" diff --git a/integrator/base.py b/integrator/base.py new file mode 100644 index 0000000..59a99bd --- /dev/null +++ b/integrator/base.py @@ -0,0 +1,134 @@ +# +# Copyright (c) 2008 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Base implementation for accessing external content objects. + +$Id$ +""" + +from zope.app.container.contained import Contained +from zope.cachedescriptors.property import Lazy +from zope import component +from zope.interface import implements + +from cybertools.integrator.interfaces import IContainerFactory, IFileFactory +from cybertools.integrator.interfaces import IReadContainer, IFile, IImage + + +# proxy base (sample) classes + +class ReadContainer(Contained): + + implements(IReadContainer) + + factoryName = 'sample' + + def __init__(self, address, **kw): + self.address = address + + @Lazy + def fileFactory(self): + return component.getUtility(IFileFactory, name=self.factoryName) + + @Lazy + def containerFactory(self): + return component.getUtility(IContainerFactory, name=self.factoryName) + + def keys(self): + return [k for k, v in self.items()] + + def __iter__(self): + return iter(self.keys()) + + def __getitem__(self, key): + if key in self: + return self.get(key) + raise KeyError(key) + + def get(self, key, default=None): + return default + + def values(self): + return [v for k, v in self.items()] + + def __len__(self): + return len(self.keys()) + + def items(self): + return [] + + def __contains__(self, key): + return key in self.keys() + + has_key = __contains__ + + +class File(object): + + implements(IFile) + + contentType = None + data = None + + def __init__(self, address, contentType, **kw): + self.address = address + self.contentType = contentType + for k, v in kw.items(): + setattr(self, k, v) + + def getData(self, num=None): + return '' + + data = property(getData) + + def getSize(self): + return len(self.data) + + +def Image(File): + + implements(IImage) + + def getImageSize(self): + return 0, 0 + + +# factory base (sample) classes + +class Factory(object): + + proxyClass = ReadContainer + + def __call__(self, address, **kw): + return self.proxyClass(address, **kw) + + +class ContainerFactory(Factory): + + implements(IContainerFactory) + + proxyClass = ReadContainer + + +class FileFactory(Factory): + + implements(IFileFactory) + + proxyClass = File # real implementations should also care about images + diff --git a/integrator/filesystem.py b/integrator/filesystem.py new file mode 100644 index 0000000..48934f6 --- /dev/null +++ b/integrator/filesystem.py @@ -0,0 +1,130 @@ +# +# Copyright (c) 2008 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Access to objects in the file system. + +$Id$ +""" + +import os, stat + +from zope import component +from zope.app.file.image import getImageInfo +from zope.cachedescriptors.property import Lazy +from zope.contenttype import guess_content_type +from zope.interface import implements, Attribute + +from cybertools.integrator.base import ContainerFactory, FileFactory +from cybertools.integrator.base import ReadContainer, File, Image +from cybertools.text import mimetypes + + +# proxy classes + +class ReadContainer(ReadContainer): + + factoryName = 'filesystem' + + @Lazy + def filenames(self): + return os.listdir(self.address) + + def keys(self): + return self.filenames + + def __iter__(self): + return iter(self.keys()) + + def __getitem__(self, key): + if key in self: + return self.get(key) + raise KeyError(key) + + def get(self, key, default=None): + if key not in self.keys(): + return default + path = os.path.join(self.address, key) + if os.path.isdir(path): + return self.containerFactory(path) + else: + return self.fileFactory(path) + + def values(self): + return [self.get(k) for k in self] + + def __len__(self): + return len(self.keys()) + + def items(self): + return [(k, self.get(k)) for k in self] + + def __contains__(self, key): + return key in self.keys() + + +class File(File): + + contentType = None + data = None + + def getData(self, num=-1): + f = open(self.address, 'r') + data = f.read(num) + f.close() + return data + + data = property(getData) + + def getSize(self): + return os.stat(self.address)[stat.ST_SIZE] + + +class Image(File): + + width = height = 0 + + def getImageSize(self): + return self.width, self.height + + +# factory classes + +class ContainerFactory(ContainerFactory): + + proxyClass = ReadContainer + + +class FileFactory(FileFactory): + + def __call__(self, address, **kw): + contentType = kw.pop('contentType', None) + width = height = 0 + obj = File(address, contentType, **kw) + if not contentType: + data = obj.getData(50) + contentType, width, height = getImageInfo(data) + if not contentType: + name = os.path.basename(address) + contentType, encoding = guess_content_type(name, data, '') + if contentType.startswith('image/'): + return Image(address, contentType=contentType, + width=width, height=height, **kw) + else: + obj.contentType = contentType + return obj diff --git a/integrator/interfaces.py b/integrator/interfaces.py new file mode 100644 index 0000000..1916eca --- /dev/null +++ b/integrator/interfaces.py @@ -0,0 +1,59 @@ +# +# Copyright (c) 2008 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +External content integration interfaces. + +$Id$ +""" + +from zope.app.container.interfaces import IReadContainer +from zope.app.file.interfaces import IFile, IImage +from zope.app.publication.interfaces import IFileContent +from zope.interface import Interface, Attribute + + +class IFile(IFile, IFileContent): + + def getData(num): + """ Return num bytes from the file`s data. + """ + + +class IProxyFactory(Interface): + """ Creates proxy objects for external objects. + """ + + def __call__(address, **kw): + """ Return a proxy object based on an external object that + can be accessed using the address (and optional + keyword arguments) given. + """ + + +class IContainerFactory(IProxyFactory): + """ Creates container proxy objects for the external specification + given. + """ + + +class IFileFactory(IProxyFactory): + """ Creates file proxy objects for the external specification + given. + """ + diff --git a/integrator/testdata/index.html b/integrator/testdata/index.html new file mode 100644 index 0000000..8527580 --- /dev/null +++ b/integrator/testdata/index.html @@ -0,0 +1,9 @@ + + + + + + Subdirectory + Demo + + diff --git a/integrator/testdata/sub/demo.tgz b/integrator/testdata/sub/demo.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3d53de05fb2581effff38fdcae6b9f083fa0699f GIT binary patch literal 432 zcmV;h0Z;xPiwFQoH4Q}o1MQj3QiCuMfI0gVOz&d;b5Y+VhP0y4AO)xO=?(M`t-uA+ zk?wb4CUAh+{dTiNSDVsik^Z>XcD+niRbiqpou$EuAcqjFzGBHy`?`mr03+@eB`1gn z2y@Ov5TY?(q6_!^stCj2)udTfW}VLs-T$XNCTpEmu?cgf-_^=IE%Y%X7U9G5-L>Bj zn3iL-cQ2-J6+oN3+>Bic8vj|3|C~Di872%#MBTb81d@aQKIrzpoe@w9=1LRh(%N)% z>(31Lf9(9Hf(YpvKX(2LL?rm{gFpN~WdvA1f6md4)SKEyv3;{w>i^DtiX + + + + + Demo + Up + + diff --git a/integrator/testdata/sub/loops_logo.png b/integrator/testdata/sub/loops_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..15123ab009484bfd25a73db3ad2db1c3a103b482 GIT binary patch literal 1774 zcmVqk00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmYE+YT{E+YYWr9XB6000McNliru)&w010}JeK&C~z@275_F zK~yNu?VEY17gZF;KjT&|xnyo*^~yGkh{BdfX<~%EY|%6dDz{ROnOG?pVw9%RVp>X? zhcbl17A-{$L(^Q^+)4y3%QTb5GSjkGZat?z=88Xu`K>cQe?H&+!Vl-&JNKTscfND( z*#rv<3kwSi3kwS;s!>%30vAMd9TJge?Y31v_%3yUGRI?UN}CH*Iv)Gu%v~oB~kSw(B;5b;1b`Z!twBYmwLc7ppEY`3RvoR_=U`0%Wm;$PoUbsRG z9OwvK2fPWiDO83f(dlllcvJzifx|#OV1Vz^5a5j+dVi?vI)!!Cs3k)#FSS9cuFant4 zc8W61X+@>@b{tPR`{ zes3c3S&TW$fx204+a)4fbDXjzFdR55rLDs8*lV=Y0pDd4FcBzAxo10+fJCa=80cb* zwfewqc?{pFs%wFLz*rI4kwH#Npr0XW8PFjQIqOw*2e2BLDI&Wg<{y#a0~lyLnSthI z-x(&6s?IgnJAjEI@=J!bv;iIk+MA}^fitqKVY{k+W3CSYBY{d0`J<%AWts)9Bu$)1 zL>h-Al%@bJ5W}s`Bu)a$>U>s0I5Z^lhtO6Doj}70Y9Jm8$3N$iWYr5mHrjau#A=wc1oN3PbLZ3g^_0%GW{>H@?V5Er54Qb*VfeV2tS@tbT3)MCxX<+Q5 zna0JR0o-C-`#!$QD~?xK)VkPlqM;(95yxP2qXkAMAY(6ZtB9fwWxzUuYK_sr z?ow%@Oy~=|71d!}(lM&)17l*y@PO`71Tu2gscJuCNZ%B5JyW9yWaR8q)#nl*(RsiN zs+#M^wHTNzBFi(n;2tY|mu>_J*q=<>LEmMl@!WQvs%ei|B9iUFj>J{}=?Ikaf~$-g zkBiw^`m2DKa+v1_g6MIkuuFp+e?5SCsyg4q&@vI}Psd|*Dp?$lO^(OUaR^jw64fY} z4`z1`_q}CeHc6ogLEy7{sn%{uR2s}8D8Wb)Pg)YyEUh)jghzfn9oAD!5{=8$X_A;% z#gx%eRV%W{ITvW1z|^E^**`$)|`qYDyDbVUsYQJGt%_m2izM| zMqS`lReh5nFBF9d98Yq_!H25)NCvy77db?tRdo5%jDnHqm^7a?7MKBUIY3(P*QjvGOGCbE;PPk|2F@+8`E* zOyqf5!q2o7cqb(13*eI?XrZ_B)Q*KC1Viv#vCZtu%OVnvP~QW5L{K;%7aM>+BC@zt zBuZD=*wq-uVH`=9KT_2)g4&rrz}0y^$0XppaP~TloN|IB<#>Y7f3El!7zJz$#WoEb Qxc~qF07*qoM6N<$f+`ayqW}N^ literal 0 HcmV?d00001 diff --git a/integrator/tests.py b/integrator/tests.py new file mode 100755 index 0000000..5b04189 --- /dev/null +++ b/integrator/tests.py @@ -0,0 +1,28 @@ +# $Id$ + +import os +import unittest, doctest +from zope.testing.doctestunit import DocFileSuite +from zope.interface.verify import verifyClass + + +baseDir = os.path.dirname(__file__) +testDir = os.path.join(baseDir, 'testdata') + + +class Test(unittest.TestCase): + "Basic tests for the cybertools.integrator package." + + def testSomething(self): + pass + + +def test_suite(): + flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS + return unittest.TestSuite(( + unittest.makeSuite(Test), + DocFileSuite('README.txt', optionflags=flags), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite')