- attachment:interceptor.py of MixinClassFileClass
Attachment 'interceptor.py'
Download 1 import gzip, hashlib
2 from roundup import hyperdb
3 from StringIO import StringIO
4
5
6 def interceptor_factory(classname, parent, baseclass) :
7 '''
8 Creates a subclass of parent which is also a subclass of the specified base
9 class. The base class is saved as class variable on the derived
10 subclass.
11 '''
12 attrs = {'super' : baseclass }
13 return type(classname, (parent, baseclass, object), attrs)
14
15
16
17 class ClassInterceptor :
18 '''
19 This class provides hooks to the create, get and get methods of a
20 superclass which is defined at runtime. This allows children of
21 this class to override the set and get methods with their own
22 functionality without re-implementing what has already been
23 written. It is designed to be used with the interceptor_factory()
24 method in this class.
25 '''
26 def super_get(self, *p, **kw):
27 '''
28 Convenience method to call the get() method of the superclass.
29 '''
30 return self.super.get(self, *p, **kw)
31 def super_set(self, *p, **kw):
32 '''
33 Convenience method to call the set() method of the superclass.
34 '''
35 return self.super.set(self, *p, **kw)
36 def super_create(self, *p, **kw):
37 '''
38 Convenience method to call the create() method of the superclass.
39 '''
40 return self.super.create(self, *p, **kw)
41
42
43 class GzipFileClass (ClassInterceptor) :
44 '''
45 This is designed to be a mixin with the FileClass implementation of the
46 backend. It provides transparent compression and decompression of file objects.
47 Note that only the contents of the file are affected (and not the file name
48 on disk, as FileClass does not have control over file naming.
49 '''
50
51 def _compress_content(self, propvalues):
52 '''
53 The compression code is factored out into its own method becuase it
54 is needed for create() and set()
55 '''
56 if 'content' in propvalues :
57 content = propvalues['content']
58 compressed_fobj = StringIO()
59 compressor = gzip.GzipFile(fileobj=compressed_fobj,mode="wb")
60 compressor.write(content)
61 compressor.close()
62 propvalues['content'] = compressed_fobj.getvalue()
63
64 def create(self, **propvalues):
65 '''
66 This method intercepts the "content" property if present. All other
67 properties remain unaffected. The content property is compressed
68 with gzip compression, then handed off to the superclass.
69 '''
70 self._compress_content(propvalues)
71 return self.super_create(**propvalues)
72
73 def set(self, itemid, **propvalues):
74 '''
75 This method intercepts the "content" property if present. All other
76 properties remain unaffected. The content property is compressed with
77 gzip compression, then handed off to the superclass for storage.
78 '''
79 self._compress_content(propvalues)
80 return self.super_set(itemid, **propvalues)
81
82 def get(self, nodeid, propname, *p, **kw):
83 '''
84 This method intercepts the content property if present. No other
85 properties are affected. The content property is uncompressed before it
86 is returned.
87 If the file is not a gzipped file, the content is just directly
88 returned.
89 '''
90 data = self.super_get(nodeid, propname, *p, **kw)
91 if propname == 'content' :
92 compressed_content = data
93 compressed_fobj = StringIO(compressed_content)
94 decompressor = gzip.GzipFile(fileobj=compressed_fobj,mode='rb')
95 try :
96 data = decompressor.read()
97 except IOError :
98 pass
99 decompressor.close()
100
101 return data
102
103 class Md5FileClass (ClassInterceptor):
104 '''
105 This class creates another property of FileClass (md5hash) which is
106 automatically maintained. This new property is the hash of the content
107 property, and is updated whenever the content property is set. The user
108 is never allowed to set the md5hash property directly.
109
110 The md5sum property is visible to the user, so there is no need to
111 override the get() method.
112 '''
113 def __init__(self, db, classname, **properties):
114 '''
115 Add (or overwrite) the md5sum property to ensure that it
116 exists.
117 '''
118 properties['md5hash'] = hyperdb.String(indexme='yes')
119 self.super.__init__(self,db,classname,**properties)
120
121 def _hash_content(self, propvalues):
122 # do not let user set md5hash
123 if 'md5hash' in propvalues :
124 del propvalues['md5hash']
125 if 'content' in propvalues :
126 sh = hashlib.md5()
127 sh.update(propvalues['content'])
128 propvalues['md5hash'] = sh.hexdigest()
129
130 def create(self, **propvalues):
131 self._hash_content(propvalues)
132 return self.super_create(**propvalues)
133
134 def set(self, itemid, **propvalues):
135 self._hash_content(propvalues)
136 return self.super_set(itemid, **propvalues)
137
138 class UniqueFileClass (Md5FileClass):
139 '''
140 This class intercepts requests to create new objects and refuses to create
141 duplicates. If the "new" file has the same content as an existing file,
142 the existing ID is returned. Otherwise, the file is created as specified.
143 Note that the only property used in the determination of "same or different"
144 is the content property. If the content is the same, but something else is
145 different, the new values in the other properties are ignored when the
146 existing ID is returned.
147
148 Note that this only affects the creation of new files. After a file is
149 created, you can set it's content property to the same value as some other
150 file.
151 '''
152 def create(self, **propvalues):
153 '''Intercepts file creation requests and creates only those files
154 which do not already exist in the database. If the file already
155 exists, the existing ID is returned.
156
157 Note that due to the use of old-style classes in Roundup, I had to
158 hard-code the deferred create() method to the Md5FileClass class.
159 '''
160 if 'content' in propvalues :
161 sh = hashlib.md5()
162 sh.update(propvalues['content'])
163 hash = sh.hexdigest()
164 myclass = self.db.getclass(self.classname)
165 existing_id = myclass.filter(None, {'md5hash':hash})
166 if existing_id and len(existing_id) > 0 :
167 return existing_id[0]
168 return Md5FileClass.create(self,**propvalues)
169 #SHA: 615b532ce82cb1a3f8893a1153cbda3aeb82f68d
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.