Package advene :: Package model :: Module tales
[hide private]
[frames] | no frames]

Source Code for Module advene.model.tales

  1  """ 
  2  TODO docstring 
  3   
  4  Special TALES attributes 
  5  ======================== 
  6   
  7  Although TALES will search the attributes and item of an object when traversing 
  8  it, it may be useful in some situations to provide TALES with specific 
  9  attributes and methods. 
 10   
 11  Naming convention 
 12  ----------------- 
 13   
 14  When traversing object o with path element "a", TALES with first look for an attribute or method named "_tales_a". This allows classes to provide additional attributes, or even to override existing attributes in TALES. 
 15   
 16  Method decorators 
 17  ----------------- 
 18   
 19  This module provides a set of decorators (all functions named ``tales_X``) to be used with methods (either standard or TALES specific -- see `Naming convention`_) to customize the way TALES uses those methods. 
 20   
 21  Wrapping 
 22  -------- 
 23   
 24  In some cases, the naming convention and method decorators are not sufficient (i.e. when a mixin class should use the TALES specific version of an underlying method). For those cases, classes may provide the ``__wrap_with_tales_context__`` method. TALES will look for this method just before attempting to traverse an object, and if found, will replace that object by the result of the method. 
 25   
 26  Note that the wrapping will happen just before traversing: TALES will never return a wrapped object as the result of an expression. 
 27   
 28  TALES Global Methods 
 29  ==================== 
 30   
 31  TALES global methods are functions that are available anywhere in a TALES path. 
 32   
 33  Warning 
 34  ------- 
 35   
 36  TALES global method should be avoided as much as possible, since they clutter the attribute space in interactive TALES editing, and may induce unexpected behaviours. However, there are some uses to them. 
 37   
 38  """ 
 39  from simpletal import simpleTALES 
 40   
 41  AdveneTalesException=simpleTALES.PathNotFoundException 
42 43 -def tales_full_path_function(f):
44 """ 45 Decorator for TALES full-path-functions. 46 47 A full-path-function will be immediately invoked, with the rest of the path 48 as its sole argument. 49 """ 50 f.tales_type = "full-path-function" 51 return f
52
53 -def tales_path1_function(f):
54 """ 55 Decorator for TALES path1-functions. 56 57 A path1-function will be called with the next path element as its argument, 58 rather than searching for an attribute or key with that name. 59 60 See advene.model.core.meta for an example. 61 """ 62 f.tales_type = "path1-function" 63 return f
64
65 -def tales_context_function(f):
66 """ 67 Decorator for TALES context-functions. 68 69 When the last item of a path, and no-call is not used, a context-function 70 is invoked with the context as its argument (rather than without any 71 argument for other functions). 72 73 :see-also: `tales_use_context` 74 """ 75 f.tales_type = "context-function" 76 return f
77
78 -def tales_property(f):
79 """ 80 Decorator for TALES property. 81 82 A TALES property is similar in use to python's properties: 83 it will automatically be called (with the context as its sole parameter) 84 *even* if it has a subpath or if ``no-call:`` is used. 85 86 :see-also: `tales_use_context` 87 """ 88 f.tales_type = "auto-call" 89 return f
90
91 -def tales_use_as_context(var):
92 """ 93 Decorator with 1 argument to be used with TALES properties and 94 context-functions (or it will have no effect). 95 96 If the function expects a specific context variable rather than the context 97 itself, the name of the variable can be specified with this decorator. 98 99 Example:: 100 @tales_property 101 @tales_use_as_context("package") 102 def some_method(self, a_package): 103 ... 104 """ 105 def the_actual_decorator(f): 106 f.tales_context_variable = var 107 return f
108 return the_actual_decorator 109
110 111 -class AdveneContext(simpleTALES.Context):
112 - def __init__(self, here, options=None):
113 """Creates a tales.AdveneContext object, having a global symbol 'here' 114 with value 'here' and a global symbol 'options' where all the key- 115 value pairs of parameter 'options' are copied. Of course, it also 116 has all the standard TALES global symbols. 117 """ 118 if options is None: 119 options={} 120 simpleTALES.Context.__init__(self, dict(options)) # *copy* dict 'options' 121 self.addGlobal('here', here)
122
123 - def traversePath(self, expr, canCall=1):
124 if expr.startswith('"') or expr.startswith("'"): 125 if expr.endswith('"') or expr.endswith("'"): 126 expr = expr[1:-1] 127 else: 128 expr = expr[1:] 129 elif expr.endswith('"') or expr.endswith("'"): 130 expr = expr[:-1] 131 pathList = expr.split("/") 132 133 val = self._traverse_first(pathList[0]) 134 tales_type = None 135 for i, p in enumerate(pathList[1:]): 136 val = self._traverse_next(val, p) 137 tales_type = getattr(val, "tales_type", None) 138 if tales_type == "full-path-function": 139 # stop traversing, path remaining path to val 140 arg = pathList[i+2:] 141 if arg or canCall: 142 return val(pathList[i+2:]) 143 # else the function will be returned as is 144 elif tales_type == "auto-call": 145 variable_context = getattr(val, "tales_context_variable", None) 146 if variable_context is None: 147 param = self 148 else: 149 param = self._traverse_first(variable_context) 150 val = val(param) 151 if canCall and tales_type != "auto-call": 152 val = self._eval(val) 153 return val
154
155 - def _traverse_first(self, path):
156 if path.startswith("?"): 157 path = self._eval(self._traverse_first(self, path[1:])) 158 159 if self.locals.has_key(path): 160 r = self.locals[path] 161 elif self.globals.has_key(path): 162 r = self.globals[path] 163 else: 164 raise simpleTALES.PATHNOTFOUNDEXCEPTION 165 return r
166
167 - def _traverse_next(self, val, path):
168 # wrap current object with TALES context if possible 169 # (see for example advene.model.core.tag) 170 wrapper = getattr(val, "__wrap_with_tales_context__", None) 171 if wrapper is not None: 172 val = wrapper(self) 173 # search different attributes/method for the given path 174 protected = "_tales_%s" % path 175 if getattr(val, "tales_type", None) == "path1-function": 176 return val(path) 177 elif hasattr(val, protected): 178 return getattr(val, protected) 179 elif hasattr(val, path): 180 return getattr(val, path) 181 else: 182 try: 183 return val[path] 184 except Exception: 185 try: 186 return val[int(path)] 187 except Exception: 188 gm = get_global_method(path) 189 if gm is not None: 190 return gm(val, self) 191 else: 192 raise simpleTALES.PATHNOTFOUNDEXCEPTION
193
194 - def _eval(self, val):
195 if callable(val): 196 if getattr(val, "tales_type", None) == "context-function": 197 variable_context = getattr(val, "tales_context_variable", None) 198 if variable_context is None: 199 param = self 200 else: 201 param = self._traverse_first(variable_context) 202 return val(param) 203 else: 204 return val() 205 else: 206 return val
207
208 209 # global method registration 210 211 -def get_global_method(name):
212 """ 213 Retrieves a global method, or return None if no such global method has been 214 registered. 215 """ 216 global _global_methods 217 return _global_methods.get(name)
218
219 -def iter_global_methods():
220 """ 221 Iter over all the global method names. 222 """ 223 global _global_methods 224 return iter(_global_methods)
225
226 -def register_global_method(f, name=None):
227 """ 228 Register f as a global method, under the given name, or under its own 229 name if no name is provided. 230 231 f must accept two arguments: the object retrieved from the previous TALES 232 path, and the TALES context. 233 """ 234 global _global_methods 235 _global_methods[name or f.func_name] = f
236
237 -def unregister_global_method(f_or_name):
238 """ 239 Unregister a global method. The parameter is the name of the global method, 240 or can be the function if it has been registered under its own name. 241 """ 242 global _global_methods 243 name = getattr(f_or_name, "func_name", f_or_name) 244 del _global_methods[name]
245 246 _global_methods = {}
247 248 -def _gm_repr(obj, context):
249 return repr(obj)
250 251 register_global_method(_gm_repr, "repr")
252 253 254 # absolute_url 255 256 -class WithAbsoluteUrlMixin(object):
257 """ 258 This class provides a common implementation for the ``absolute_url`` tales 259 function. 260 261 ``absolute_url`` is supposed to return an URL where one can retrieve a 262 *description* of an object (this is *not* the URL where a package can be 263 downloaded, nor the URI-ref of an element). It is intensively used in 264 HTML views served by the embedded HTTP server in the Advene tool. 265 266 ``absolute_url`` is expected to consume the rest of the TALES path, and 267 suffix it to its return value. E.g:: 268 some_element/absolute_url/content_data 269 -> http://localhost:1234/packages/p/some_element/content_data 270 271 some_package/absolute_url/annotations 272 -> http://localhost:1234/packages/some_package/annotations 273 274 But if no URL can be constructed, it returns None. 275 276 This mixin provides all the bells and whistles to do so. It relies on 277 the presence of two TALES variables: 278 * options/packages (mandatory) contains a dict whose keys are package 279 names, and whose values are package instances 280 * options/base_url (optional) contains the reference URL 281 282 The returned value will be of the form:: 283 base_url/specific/rest/of/the/path 284 where ``specific`` is computed by a method from the mixed-in class, of the 285 form:: 286 287 def _compute_absolute_url(self, packages) 288 289 and this method should invoke `self._absolute_url_fail()` if it can not 290 construct a URL (resulting in the TALES path failing to evaluate). 291 """ 292 @tales_property
293 - def _tales_absolute_url(self, context):
294 """ 295 See class documentation. 296 """ 297 options = context.evaluate("options|nothing") 298 if options is None: 299 raise AdveneTalesException("Malformed context, options is not defined") 300 base = options.get("package_url", "") 301 abs = self._compute_absolute_url(options['aliases']) 302 return _AbsoluteUrl("%s/%s" % (base, abs))
303
304 - def _absolute_url_fail(self, msg=""):
305 raise AdveneTalesException(msg)
306
307 -class _AbsoluteUrl(unicode):
308 """ 309 Used by `WithAbsoluteUrlMixin`. 310 """
311 - def __new__(self, abs):
312 return unicode.__new__(self, abs)
313 - def __getitem__(self, item):
314 return _AbsoluteUrl(u"%s/%s" % (self, item))
315