1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import os, glob, traceback
26 import md5 as __md5
27
28
29 defaultSearchPath = 'SLUM_SEARCH_PATH'
30 defaultOnlineRepositorie = 'http://slum.hradec.com/repositorie'
31
32
34 '''
35 high level function
36 return all classes found on disk/online, that suscessfully passed the runtime/syntax error check.
37 '''
38 return collectSlumClasses().allClasses
39
41 '''
42 high level function
43 force a refresh of all templates from disk/online
44 '''
45 collectSlumClasses(refresh=True)
46
48 '''
49 high level function
50 loads all templates from disk/online
51 '''
52 collectSlumClasses()
53
55 '''
56 high level function
57 returns a updated path for a given template name.
58 '''
59 classes = collectSlumClasses().allClasses
60 return classes[name]['path']
61
63 '''
64 high level function
65 returns a updated path for a given template name.
66 '''
67 classes = collectSlumClasses().allClasses
68 return classes[name]['md5']
69
70
72 '''
73 high level function
74 returns a class obj for the givem template name.
75 The returned object will be the class defined in the template, properly evaluated for runtime/syntax errors.
76 '''
77 classes = collectSlumClasses().allClasses
78 if not _test(classes[name]):
79 return None
80 c = evalSlumClass(classes[name]['code'], name)
81 c.name = name
82 c.md5 = classes[name]['md5']
83 c.path = classes[name]['path']
84 c.refresh = refresh()
85 return c
86
87
89 '''
90 low level class (shouldn't be directly used - refer to high level functions/classes)
91 test methods in a template for runtime/syntax errors.
92
93 todo: only delight() method being test. Need to implement a
94 loop that tests all methods in the given class.
95 '''
96 ret = True
97 c = evalSlumClass(classe['code'], classe['name'])
98
99 try:
100 tmp=c.delight(c._dictParameters(value=True))
101 except:
102 print "Runtime error in %s delight method.\n%s" % (classe['path'], traceback.format_exc())
103 ret = False
104 return ret
105
106
108 '''
109 low level class (shouldn't be directly used - refer to high level functions/classes)
110 reads a template from a file and returns it as a list (one line per record)
111 '''
112 slumCode = open(path).readlines()
113
114 for n in range(len(slumCode)):
115 slumCode[n] = slumCode[n].replace('\r','')
116 return slumCode
117
118
120 '''
121 low level class (shouldn't be directly used - refer to high level functions/classes)
122 execute "code" string, and returns the class object for "classeName"
123 '''
124
125 ret = ''
126 newCode = 'import traceback\n'
127 newCode += 'from slum import *\n'
128 newCode += 'try:\n\t'
129 newCode += code.replace('\n','\n\t')
130 newCode += '\nexcept:\n'
131 newCode += '\traise Exception("Syntax error in slum template: \\n%s" % traceback.format_exc() )\n\n'
132
133
134 try:
135 exec newCode in globals()
136 except:
137 tmp = ""
138 lineNumber = 1
139 for each in newCode.split('\n'):
140 tmp += "%4d: %s\n" % (lineNumber, each)
141 lineNumber += 1
142 raise Exception("Error: %s\n\nOn slum template code: \n%s" % (traceback.format_exc(), tmp ) )
143
144 ret = eval('%s()' % classeName)
145
146 return ret
147
149 '''
150 low level class (shouldn't be directly used - refer to high level functions/classes)
151 calculates md5 for the given code
152 '''
153 return __md5.md5( code ).digest()
154
156 '''
157 low level class (shouldn't be directly used - refer to high level functions/classes)
158 check if the md5 matchs the md5 of the source file.
159 '''
160 return md5data == getMD5( ''.join( _readSlumFile(file) ) )
161
163 '''
164 low level class (shouldn't be directly used - refer to high level functions/classes)
165 class responsible for deal with slum files and classes
166 initializing a new class object will trigger the search of *.slum files in the local search path and online repositories.
167 a cache system is in place to avoid un-necessary re-caching by clients.
168 clients dont need to care about it. just re-create the class and the new object will pick up the data from the cache
169 automatically, unless refresh parameter is True.
170 '''
171 - def __init__(self, refresh=None, searchPath = None, onlineRepositories = None):
172 '''
173 gathers slum classes from local disk/network and online repositories
174
175 this class stores the gathered classes and search paths as global variables, so
176 if the class is re-created, it will retain the data from a previous object. (a sort of cache)
177
178 this is very useful when using this class in clients, so a call for the class in a initialization context
179 will store the data, and the data can be quickly retrieve later in a diferent context, even whitout the original
180 search paths.
181
182 also, a call to _refresh method (or creating the class object using the refresh parameter = True)
183 will refresh the class caches using the cached search paths. This way,
184 the search paths only need to be defined in the initialization context. As the class caches are global, even
185 the _refresh method can be called from a totally separated context.
186
187 This cache mechanism frees the client from the tedious and error prone tasks of store all this data so it can
188 be accessed in diferent contexts. It also speeds up the whole proccess, avoiding multiple un-necessary
189 disk/network accesses.
190 '''
191 global searchPathCache
192 global onlineRepositoriesCache
193
194
195 if searchPath:
196 searchPathCache = searchPath
197 else:
198 searchPathCache = defaultSearchPath
199
200 if onlineRepositories:
201 onlineRepositoriesCache = onlineRepositories
202 else:
203 onlineRepositoriesCache = defaultOnlineRepositorie
204
205
206 self.searchPath = searchPathCache
207 self.onlineRepositories = onlineRepositoriesCache
208
209
210 if type(self.searchPath) == str:
211 self.searchPath = [self.searchPath]
212 if type(self.onlineRepositories) == str:
213 self.onlineRepositories = [self.onlineRepositories]
214
215
216 global localClasses
217 global onlineClasses
218 try:
219 localClasses
220 onlineClasses
221 if refresh or (not localClasses and not onlineClasses):
222 raise
223 except:
224 localClasses, onlineClasses = self._refresh()
225
226
227 self.localClasses = localClasses
228 self.onlineClasses = onlineClasses
229
230
231 self.allClasses = self.localClasses
232 self.allClasses.update( self.onlineClasses )
233
235 global localClasses
236 global onlineClasses
237 def printClasses(classes):
238 idz = []
239 countIDz = {}
240 for classe in classes.keys():
241 id = classes[classe]['ID']
242 idz.append('slum: found ID %4d Class %s' % ( id,classe))
243 if not countIDz.has_key(id):
244 countIDz[id] = []
245 countIDz[id].append(classe)
246
247 idz.sort()
248 clash = None
249 for each in idz:
250 id = int( each.split('ID ')[1].split(' C')[0] )
251 print each,
252 if len(countIDz[id])>1:
253 print "%s> ERROR: ID Clashing -" % ("="*(50-len(each))),countIDz[id]
254 clash = True
255 else:
256 print
257
258 if clash:
259 global localClasses
260 global onlineClasses
261 localClasses = None
262 onlineClasses = None
263 raise Exception("\n\nClash of templates in slum initialization. Fix it!")
264
265 print 'slum: local caching...'
266 localClasses = self.local()
267 printClasses(localClasses)
268 print 'slum: online caching'
269 onlineClasses = self.online()
270 printClasses(onlineClasses )
271 print 'slum: all done.'
272 return ( localClasses, onlineClasses )
273
275 '''
276 based on a string with slum code on it, registers all class names in it into a temp db
277 for each name, it adds a "code" key with the source code, so later a client
278 can execute it and retrieve the class object at runtime.
279
280 this class is a support class for local and online methods!
281 '''
282
283
284
285
286
287
288
289 env = {}
290 registry = None
291 newClasses = None
292 exec 'from slum import *' in env
293 registry = env.copy()
294 exec '\n'.join(slumCode) in env
295 newClasses = env
296
297
298
299
300
301 slumClasses={}
302 idz = []
303 for classe in filter(lambda x: x not in registry, newClasses):
304
305 if not slumClasses.has_key(classe):
306 slumClasses[classe] = {}
307 slumClasses[classe]['code'] = ''.join(slumCode)
308
309
310 slumClasses[classe]['name'] = classe
311 slumClasses[classe]['md5'] = getMD5( slumClasses[classe]['code'] )
312 slumClasses[classe]['path'] = path
313
314
315 if not _test(slumClasses[classe]):
316 del slumClasses[classe]
317
318 slumClasses[classe]['ID'] = evalSlumClass(slumClasses[classe]['code'], classe).ID()
319
320 return slumClasses
321
322
324 '''
325 same as local, but for online repositories.
326 returns data in the same format as local
327 '''
328 return {}
329
332
334 '''
335 returns a dictionary with all classes found in the searchpath
336 the dictionary is organized as:
337
338 { 'class name' :
339 {'code' : 'code for the class'}
340 }
341 '''
342
343 slumClasses={}
344 for searchPath in self.searchPath:
345 if os.environ.has_key(searchPath):
346 env = os.environ[searchPath]
347 for path in env.split(os.path.pathsep):
348 for each in glob.glob( os.path.join( path, '*.slum' ) ):
349 slumClasses.update( self.readSlumFile(each) )
350
351
352 return slumClasses
353