Package tdi :: Module model_adapters
[frames] | no frames]

Source Code for Module tdi.model_adapters

  1  # -*- coding: ascii -*- 
  2  r""" 
  3  :Copyright: 
  4   
  5   Copyright 2006 - 2015 
  6   Andr\xe9 Malo or his licensors, as applicable 
  7   
  8  :License: 
  9   
 10   Licensed under the Apache License, Version 2.0 (the "License"); 
 11   you may not use this file except in compliance with the License. 
 12   You may obtain a copy of the License at 
 13   
 14       http://www.apache.org/licenses/LICENSE-2.0 
 15   
 16   Unless required by applicable law or agreed to in writing, software 
 17   distributed under the License is distributed on an "AS IS" BASIS, 
 18   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 19   See the License for the specific language governing permissions and 
 20   limitations under the License. 
 21   
 22  ================ 
 23   Model Adapters 
 24  ================ 
 25   
 26  Model adapter implementations. 
 27  """ 
 28  if __doc__: 
 29      # pylint: disable = redefined-builtin 
 30      __doc__ = __doc__.encode('ascii').decode('unicode_escape') 
 31  __author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape') 
 32  __docformat__ = "restructuredtext en" 
 33   
 34  from ._exceptions import ModelMissingError 
 35  from . import interfaces as _interfaces 
36 37 38 -class RenderAdapter(object):
39 """ 40 Regular Render-Adapter implementation 41 42 :See: `ModelAdapterInterface` 43 """ 44 __implements__ = [_interfaces.ModelAdapterInterface] 45
46 - def __new__(cls, model, requiremethods=False, requirescopes=False):
47 """ 48 Construct 49 50 :Parameters: 51 `model` : any 52 User model 53 54 `requiremethods` : ``bool`` 55 Require methods to exist? 56 57 `requirescopes` : ``bool`` 58 Require scopes to exist? 59 60 :Return: Render adapter 61 :Rtype: `ModelAdapterInterface` 62 """ 63 self = object.__new__(cls) 64 65 requiremethods = bool(requiremethods) 66 requirescopes = bool(requirescopes) 67 getattr_ = getattr 68 models = {'': model} 69 70 class unset(object): 71 # pylint: disable = invalid-name, missing-docstring 72 pass
73 unset = unset() 74 75 def new(model): 76 """ Create adapter for a new model """ 77 return cls( 78 model, 79 requiremethods=requiremethods, 80 requirescopes=requirescopes, 81 )
82 83 def modelmethod(prefix, name, scope, noauto): 84 """ 85 Build the method name from prefix and node name and resolve 86 87 This implements the default look up. 88 89 :Parameters: 90 `prefix` : ``str`` 91 The method prefix (``render``, or ``separate``) 92 93 `name` : ``str`` 94 The node name 95 96 `scope` : ``str`` 97 Scope 98 99 `noauto` : ``bool`` 100 No automatic method calling? 101 102 :Return: The method or ``None`` 103 :Rtype: ``callable`` 104 105 :Exceptions: 106 - `ModelMissingError` : The method was not found, but all 107 methods are required 108 """ 109 if name is None or noauto: 110 return None 111 if scope in models: 112 model = models[scope] 113 else: 114 model = models[''] 115 scope_part = None 116 for part in scope.split('.'): 117 if not scope_part: 118 scope_part = part 119 else: 120 scope_part = '%s.%s' % (scope_part, part) 121 if scope_part in models: 122 model = models[scope_part] 123 else: 124 model = getattr_(model, 'scope_' + part, unset) 125 if model is unset: 126 if requirescopes: 127 raise ModelMissingError(scope_part) 128 model = None 129 models[scope_part] = model 130 131 method = getattr_(model, "%s_%s" % (prefix, name), unset) 132 if method is unset: 133 if requiremethods: 134 raise ModelMissingError("%s_%s" % (prefix, name)) 135 method = None 136 return method 137 138 self.modelmethod = modelmethod 139 self.new = new 140 self.emit_escaped = False 141 142 return self 143 144 @classmethod
145 - def for_prerender(cls, model, attr=None):
146 """ 147 Create prerender adapter from model 148 149 :Parameters: 150 `model` : any 151 User model 152 153 `attr` : ``dict`` 154 Attribute name mapping. The keys 'scope' and 'tdi' are recognized. 155 If omitted or ``None``, the default attribute names are applied 156 ('tdi:scope' and 'tdi'). 157 158 :Return: Prerender adapter 159 :Rtype: `ModelAdapterInterface` 160 """ 161 return PreRenderWrapper(cls(model), attr=attr)
162
163 164 -class PreRenderWrapper(object):
165 """ 166 Pre-render wrapper adapter 167 168 :See: `ModelAdapterInterface` 169 """ 170 __implements__ = [_interfaces.ModelAdapterInterface] 171
172 - def __new__(cls, adapter, attr=None):
173 """ 174 Construct 175 176 :Parameters: 177 `adapter` : `ModelAdapterInterface` 178 model adapter for resolving methods 179 180 `attr` : ``dict`` 181 Attribute name mapping. The keys 'scope' and 'tdi' are recognized. 182 If omitted or ``None``, the default attribute names are applied 183 ('tdi:scope' and 'tdi'). 184 185 :Return: Render adapter 186 :Rtype: `ModelAdapterInterface` 187 """ 188 self = object.__new__(cls) 189 190 scope_attr = 'tdi:scope' 191 tdi_attr = 'tdi' 192 if attr is not None: 193 scope_attr = attr.get('scope', scope_attr) 194 tdi_attr = attr.get('tdi', tdi_attr) 195 attr = dict(tdi=tdi_attr, scope=scope_attr) 196 197 def new(model): 198 """ Create adapter for a new model """ 199 return cls(adapter.new(model), attr=attr)
200 201 def modelmethod(prefix, name, scope, noauto): 202 """ 203 Build the method name from prefix and node name and resolve 204 205 This asks the passed adapter and if the particular method is not 206 found it generates its own, which restores the tdi attributes 207 (but not tdi:overlay). 208 209 :Parameters: 210 `prefix` : ``str`` 211 The method prefix (``render``, or ``separate``) 212 213 `name` : ``str`` 214 The node name 215 216 `scope` : ``str`` 217 Scope 218 219 `noauto` : ``bool`` 220 No automatic method calling? 221 222 :Return: The method or ``None`` 223 :Rtype: ``callable`` 224 """ 225 try: 226 method = adapter.modelmethod(prefix, name, scope, noauto) 227 except ModelMissingError: 228 pass 229 else: 230 if method is not None: 231 return method 232 233 # These methods we only see of the model repeats a node, but 234 # doesn't define a separator logic. We do not want to write out 235 # the special node stuff in this case (since the separators would 236 # be alone after that). 237 if prefix == 'separate': 238 return None 239 240 # The node is repeated in order to get our hands on 241 # a possible separator. The second iteration of the node is simply 242 # removed, so we keep the node itself and its separator. 243 244 # However, by repeating the node we override an existing context 245 # of the node. So we pass it explicitly and override it again. 246 def repeat(node, item, ctx): 247 """ Repeater """ 248 if item: 249 return node.remove() 250 node.ctx = ctx
251 252 def setscope(node, scope=scope): 253 """ Special attribute helper """ 254 node[scope_attr] = ( 255 '=' + (node.hiddenelement and '-' or '+') + scope 256 ) 257 258 def render(node, name=name, sep=False): 259 """ Generated render method """ 260 try: 261 toremove = node['tdi:prerender'] == 'remove-node' 262 del node['tdi:prerender'] 263 except KeyError: 264 toremove = False 265 266 setscope(node) 267 if not toremove: 268 if name is not None: 269 flags = node.hiddenelement and '-' or '+' 270 if noauto: 271 flags += '*' 272 if sep: 273 flags += ':' 274 node[tdi_attr] = flags + name 275 node.hiddenelement = False 276 277 def separate(node, ctx): 278 """ Separator """ 279 node.ctx = ctx 280 return render(node, sep=True) 281 282 node.repeat(repeat, (0, 1), node.ctx, separate=separate) 283 284 if name is None: 285 return setscope 286 return render 287 288 self.modelmethod = modelmethod 289 self.new = new 290 self.emit_escaped = True 291 292 return self 293 294 295 from . import c 296 c = c.load('impl') 297 if c is not None: 298 RenderAdapter = c.RenderAdapter # noqa 299 PreRenderWrapper = c.PreRenderWrapper # noqa 300 del c 301