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

Source Code for Module tdi.nodetree

  1  # -*- coding: ascii -*- 
  2  # 
  3  # Copyright 2006 - 2013 
  4  # Andr\xe9 Malo or his licensors, as applicable 
  5  # 
  6  # Licensed under the Apache License, Version 2.0 (the "License"); 
  7  # you may not use this file except in compliance with the License. 
  8  # You may obtain a copy of the License at 
  9  # 
 10  #     http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, software 
 13  # distributed under the License is distributed on an "AS IS" BASIS, 
 14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 15  # See the License for the specific language governing permissions and 
 16  # limitations under the License. 
 17  """ 
 18  ====================== 
 19   Node Tree Structures 
 20  ====================== 
 21   
 22  This module provides node tree management. 
 23  """ 
 24  __author__ = u"Andr\xe9 Malo" 
 25  __docformat__ = "restructuredtext en" 
 26   
 27  from tdi._exceptions import NodeNotFoundError, NodeTreeError 
 28  from tdi import _finalize 
 29  from tdi import _nodetree 
 30  from tdi import util as _util 
 31   
 32   
33 -class RawNode(object):
34 """ 35 Lightweight node for raw content and attribute assignment 36 37 :IVariables: 38 `_udict` : ``dict`` 39 The dict containing node information 40 """ 41 __slots__ = ['content', 'encoder', 'decoder', '_udict'] 42
43 - def __init__(self, node):
44 """ 45 Initialization 46 47 :Parameters: 48 `node` : `Node` 49 The original node 50 """ 51 self._udict = node._udict # pylint: disable = W0212
52
53 - def content():
54 """ 55 Raw content 56 57 :Type: ``str`` 58 """ 59 # pylint: disable = E0211, W0212, W0612, C0111 60 unicode_, str_, isinstance_ = unicode, str, isinstance 61 def fset(self, content): 62 udict = self._udict 63 if isinstance_(content, unicode_): 64 cont = udict['encoder'].encode(content) 65 else: 66 cont = str_(content) 67 udict['content'] = (cont, cont) 68 udict['namedict'] = {}
69 def fget(self): 70 return self._udict['content'][0]
71 return locals() 72 content = _util.Property(content) 73
74 - def encoder():
75 """ 76 Output encoder 77 78 :Type: `EncoderInterface` 79 """ 80 # pylint: disable = E0211, W0212, W0612, C0111 81 def fget(self): 82 return self._udict['encoder']
83 return locals() 84 encoder = _util.Property(encoder) 85
86 - def decoder():
87 """ 88 Input decoder 89 90 :Type: `DecoderInterface` 91 """ 92 # pylint: disable = E0211, W0212, W0612, C0111 93 def fget(self): 94 return self._udict['decoder']
95 return locals() 96 decoder = _util.Property(decoder) 97
98 - def __setitem__(self, name, value):
99 """ 100 Set the attribute `name` to `value` 101 102 The value is *not* encoded according to the model. 103 The original case of `name` is preserved. If the attribute does not 104 occur in the original template, the case of the passed `name` is 105 taken over. Non-string values (including unicode, but not ``None``) 106 are converted to string using ``str()``. 107 108 :Parameters: 109 `name` : ``str`` 110 The attribute name (case insensitive) 111 112 `value` : ``str`` 113 The attribute value (may be ``None`` for short 114 attributes). Objects that are not ``None`` and and not 115 ``unicode`` are stored as their string representation. 116 """ 117 udict = self._udict 118 if value is not None: 119 if isinstance(value, unicode): 120 value = udict['encoder'].encode(value) 121 else: 122 value = str(value) 123 attr = udict['attr'] 124 name = udict['encoder'].name(name) 125 normname = udict['decoder'].normalize(name) 126 realname = attr.get(normname, (name,))[0] 127 attr[normname] = (realname, value)
128
129 - def __getitem__(self, name):
130 """ 131 Determine the value of attribute `name` 132 133 :Parameters: 134 `name` : ``str`` 135 The attribute name 136 137 :Return: The attribute (``None`` for shorttags) 138 :Rtype: ``str`` 139 140 :Exceptions: 141 - `KeyError` : The attribute does not exist 142 """ 143 udict = self._udict 144 return udict['attr'][ 145 udict['decoder'].normalize(udict['encoder'].name(name)) 146 ][1]
147
148 - def __delitem__(self, name):
149 """ 150 Delete attribute `name` 151 152 If the attribute does not exist, no exception is raised. 153 154 :Parameters: 155 `name` : ``str`` 156 The name of the attribute to delete (case insensitive) 157 """ 158 udict = self._udict 159 try: 160 del udict['attr'][ 161 udict['decoder'].normalize(udict['encoder'].name(name)) 162 ] 163 except KeyError: 164 # Ignore, because this is not an error. 165 pass
166 167
168 -class Node(object):
169 """ 170 User visible node object 171 172 :IVariables: 173 `ctx` : ``tuple`` 174 The node context (``None`` if there isn't one). Node contexts 175 are created on repetitions for all (direct and no-direct) subnodes of 176 the repeated node. The context is a ``tuple``, which contains for 177 repeated nodes the position within the loop (starting with ``0``), the 178 actual item and a tuple of the fixed parameters. The last two are also 179 passed to the repeat callback function directly. For separator 180 nodes, ``ctx[1]`` is a tuple containing the items before the separator 181 and after it. Separator indices are starting with ``0``, too. 182 183 `_model` : `ModelAdapterInterface` 184 The template model object 185 186 `_udict` : ``dict`` 187 The dict containing node information 188 """ 189 _usernode = True 190 __slots__ = ['content', 'raw', 'ctx', '_model', '_udict'] 191
192 - def content():
193 """ 194 Node content 195 196 The property can be set to a unicode or str value, which will be 197 escaped and encoded (in case of unicode). It replaces the content or 198 child nodes of the node completely. 199 200 The property can be read and will either return the *raw* content of 201 the node (it may even contain markup) - or ``None`` if the node has 202 subnodes. 203 204 :Type: ``basestring`` or ``None`` 205 """ 206 # pylint: disable = E0211, W0212, W0612, C0111 207 basestring_, isinstance_, str_ = basestring, isinstance, str 208 def fset(self, content): 209 if not isinstance_(content, basestring_): 210 content = str_(content) 211 udict = self._udict 212 udict['content'] = (udict['encoder'].content(content), None) 213 udict['namedict'] = {}
214 def fget(self): 215 return self._udict['content'][0]
216 return locals() 217 content = _util.Property(content) 218
219 - def hiddenelement():
220 """ 221 Hidden node markup? 222 223 :Type: ``bool`` 224 """ 225 # pylint: disable = E0211, W0212, W0612, C0111 226 def fset(self, value): 227 self._udict['noelement'] = value and True or False
228 def fget(self): 229 return self._udict['noelement'] 230 return locals() 231 hiddenelement = _util.Property(hiddenelement) 232
233 - def closedelement():
234 """ 235 Self-closed element? (read-only) 236 237 :Type: ``bool`` 238 """ 239 # pylint: disable = E0211, W0212, W0612, C0111 240 def fget(self): 241 return self._udict['closed']
242 return locals() 243 closedelement = _util.Property(closedelement) 244
245 - def raw():
246 """ 247 Raw node 248 249 :Type: `RawNode` 250 """ 251 # pylint: disable = E0211, W0212, W0612, C0111 252 def fget(self): 253 return RawNode(self)
254 return locals() 255 raw = _util.Property(raw) 256
257 - def __new__(cls, node, model, ctx=None, light=False):
258 """ 259 Construction 260 261 :Parameters: 262 `node` : `Node` or `TemplateNode` 263 The node to clone 264 265 `model` : `ModelAdapterInterface` 266 The template model instance 267 268 `ctx` : ``tuple`` 269 The node context 270 271 `light` : ``bool`` 272 Do a light copy? (In this case just the node context is 273 updated and the *original* node is returned). Do this only if 274 `node` is already a `Node` instance and you do not need another 275 copy! 276 277 :Return: The node instance 278 :Rtype: `Node` 279 """ 280 # pylint: disable = W0212 281 if light: 282 if not node._udict.get('callback'): 283 node.ctx = ctx 284 return node 285 286 self = object.__new__(cls) 287 udict = node._udict.copy() 288 udict['attr'] = udict['attr'].copy() 289 udict['nodes'] = udict['nodes'][:] 290 self._udict = udict 291 self._model = model 292 if udict.get('callback'): 293 self.ctx = node.ctx 294 else: 295 self.ctx = ctx 296 297 return self
298
299 - def __call__(self, name):
300 """ 301 Determine direct subnodes by name 302 303 In contrast to `__getattr__` this works for all names. Also the 304 exception in case of a failed lookup is different. 305 306 :Parameters: 307 `name` : ``str`` 308 The name looked for 309 310 :Return: The found node 311 :Rtype: `Node` 312 313 :Exceptions: 314 - `NodeNotFoundError` : The subnode was not found 315 """ 316 # pylint: disable = W0212 317 udict = self._udict 318 try: 319 name = str(name) 320 idx = udict['namedict'][name] 321 except (UnicodeError, KeyError): 322 raise NodeNotFoundError(name) 323 324 while idx < 0: # walk through transparent "nodes" 325 kind, result = udict['nodes'][-1 - idx] 326 if not result._usernode: 327 result = Node(result, self._model, self.ctx) 328 udict['nodes'][-1 - idx] = (kind, result) 329 udict = result._udict 330 idx = udict['namedict'][name] 331 332 kind, result = udict['nodes'][idx] 333 if not result._usernode: 334 result = Node(result, self._model, self.ctx) 335 udict['nodes'][idx] = (kind, result) 336 else: 337 result.ctx = self.ctx 338 339 return result
340
341 - def __getattr__(self, name):
342 """ 343 Determine direct subnodes by name 344 345 :Parameters: 346 `name` : ``str`` 347 The name looked for 348 349 :Return: The found subnode 350 :Rtype: `Node` 351 352 :Exceptions: 353 - `AttributeError` : The subnode was not found 354 """ 355 try: 356 return self(name) 357 except NodeNotFoundError: 358 raise AttributeError("Attribute %s.%s not found" % ( 359 self.__class__.__name__, name 360 ))
361
362 - def __setitem__(self, name, value):
363 """ 364 Set the attribute `name` to `value` 365 366 The value is encoded according to the model and the original case 367 of `name` is preserved. If the attribute does not occur in the 368 original template, the case of the passed `name` is taken over. 369 Non-string values are converted to string using ``str()``. Unicode 370 values are passed as-is to the model encoder. 371 372 :Parameters: 373 `name` : ``str`` 374 The attribute name (case insensitive) 375 376 `value` : any 377 The attribute value (may be ``None`` for short 378 attributes). Objects that are not ``None`` and and not 379 ``unicode`` are stored as their string representation. 380 """ 381 udict = self._udict 382 if value is not None: 383 if not isinstance(value, basestring): 384 value = str(value) 385 value = udict['encoder'].attribute(value) 386 387 attr = udict['attr'] 388 name = udict['encoder'].name(name) 389 normname = udict['decoder'].normalize(name) 390 realname = attr.get(normname, [name])[0] 391 attr[normname] = (realname, value)
392
393 - def __getitem__(self, name):
394 """ 395 Determine the value of attribute `name` 396 397 :Parameters: 398 `name` : ``str`` 399 The attribute name 400 401 :Return: The attribute (``None`` for shorttags) 402 :Rtype: ``str`` 403 404 :Exceptions: 405 - `KeyError` : The attribute does not exist 406 """ 407 udict = self._udict 408 value = udict['attr'][ 409 udict['decoder'].normalize(udict['encoder'].name(name)) 410 ][1] 411 if value and (value.startswith('"') or value.startswith("'")): 412 value = value[1:-1] 413 414 return value
415
416 - def __delitem__(self, name):
417 """ 418 Delete attribute `name` 419 420 If the attribute does not exist, no exception is raised. 421 422 :Parameters: 423 `name` : ``str`` 424 The name of the attribute to delete (case insensitive) 425 """ 426 udict = self._udict 427 try: 428 del udict['attr'][ 429 udict['decoder'].normalize(udict['encoder'].name(name)) 430 ] 431 except KeyError: 432 # Ignore, because this is not an error. 433 pass
434
435 - def repeat(self, callback, itemlist, *fixed, **kwargs):
436 """ 437 Repeat the snippet ``len(list(itemlist))`` times 438 439 The actually supported signature is:: 440 441 repeat(self, callback, itemlist, *fixed, separate=None) 442 443 Examples: 444 445 >>> def render_foo(self, node): 446 >>> def callback(node, item): 447 >>> ... 448 >>> node.repeat(callback, [1, 2, 3, 4]) 449 450 >>> def render_foo(self, node): 451 >>> def callback(node, item): 452 >>> ... 453 >>> def sep(node): 454 >>> ... 455 >>> node.repeat(callback, [1, 2, 3, 4], separate=sep) 456 457 >>> def render_foo(self, node): 458 >>> def callback(node, item, foo, bar): 459 >>> ... 460 >>> node.repeat(callback, [1, 2, 3, 4], "foo", "bar") 461 462 >>> def render_foo(self, node): 463 >>> def callback(node, item, foo, bar): 464 >>> ... 465 >>> def sep(node): 466 >>> ... 467 >>> node.repeat(callback, [1, 2, 3, 4], "foo", "bar", 468 >>> separate=sep) 469 470 :Parameters: 471 `callback` : ``callable`` 472 The callback function 473 474 `itemlist` : iterable 475 The items to iterate over 476 477 `fixed` : ``tuple`` 478 Fixed parameters to be passed to the repeat methods 479 480 :Keywords: 481 `separate` : ``callable`` 482 Alternative callback function for separator nodes. If omitted or 483 ``None``, ``self.separate_name`` is looked up and called if it 484 exists. 485 """ 486 if 'separate' in kwargs: 487 if len(kwargs) > 1: 488 raise TypeError("Unrecognized keyword parameters") 489 separate = kwargs['separate'] 490 elif kwargs: 491 raise TypeError("Unrecognized keyword parameters") 492 else: 493 separate = None 494 self._udict['repeated'] = (callback, iter(itemlist), fixed, separate)
495
496 - def remove(self):
497 """ 498 Remove the node from the tree 499 500 Tells the system, that the node (and all of its subnodes) should 501 not be rendered. 502 """ 503 self._udict['removed'] = True 504 self._udict['namedict'] = {}
505
506 - def iterate(self, itemlist, separate=None):
507 """ 508 Iterate over repeated nodes 509 510 Iteration works by repeating the original node 511 ``len(list(iteritems))`` times, turning the original node into a 512 container node and appending the generated nodeset to that container. 513 That way, the iterated nodes are virtually indented by one level, but 514 the container node is completely hidden, so it won't be visible. 515 516 All repeated nodes are marked as ``DONE``, so they (and their 517 subnodes) are not processed any further (except explicit callbacks). 518 If there is a separator node assigned, it's put between the 519 repetitions and *not* marked as ``DONE``. The callbacks to them 520 (if any) are executed when the template system gets back to control. 521 522 :Parameters: 523 `itemlist` : iterable 524 The items to iterate over 525 526 `separate` : ``callable`` 527 Alternative callback function for separator nodes. If omitted or 528 ``None``, ``self.separate_name`` is looked up and called if it 529 exists. 530 531 :Return: The repeated nodes and items (``[(node, item), ...]``) 532 :Rtype: iterable 533 """ 534 itemlist = iter(itemlist) 535 node, nodelist = self.copy(), [] 536 537 # This effectively indents the iterated nodeset by one level. 538 # The original node (which was copied from before) only acts as a 539 # container now. 540 self._udict['content'] = (None, None) 541 self._udict['nodes'] = nodelist 542 self._udict['namedict'] = {} 543 self._udict['masked'] = True 544 545 return _nodetree.iterate( 546 node, nodelist, itemlist, separate, Node 547 )
548
549 - def replace(self, callback, other, *fixed):
550 """ 551 Replace the node (and all subnodes) with the copy of another one 552 553 The replacement node is deep-copied, so use it with care 554 (performance-wise). 555 556 :Parameters: 557 `callback` : ``callable`` 558 callback function 559 560 `other` : `Node` 561 The replacement node 562 563 `fixed` : ``tuple`` 564 Fixed parameters for the callback 565 566 :Return: The replaced node (actually the node itself, but with 567 updated parameters) 568 :Rtype: `Node` 569 """ 570 # pylint: disable = E0203 571 # pylint: disable = W0201, W0212 572 udict = other._udict.copy() 573 udict['attr'] = udict['attr'].copy() 574 ctx, deep, TEXT = self.ctx, _nodetree.copydeep, _nodetree.TEXT_NODE 575 model = self._model 576 577 udict['nodes'] = [(kind, (kind != TEXT and node._usernode) and 578 deep(node, model, ctx, Node) or node 579 ) for kind, node in udict['nodes']] 580 581 udict['name'] = self._udict['name'] # name stays the same 582 udict['callback'] = callback 583 udict['complete'] = fixed 584 585 self._udict = udict 586 return self
587
588 - def copy(self):
589 """ 590 Deep copy this node 591 592 :Return: The node copy 593 :Rtype: `Node` 594 """ 595 return _nodetree.copydeep(self, self._model, self.ctx, Node)
596
597 - def render(self, *callback, **kwargs):
598 """ 599 render(self, callback, params, decode=True, decode_errors='strict') 600 601 Render this node only and return the result as string 602 603 Note that callback and params are optional positional parameters:: 604 605 render(self, decode=True, decode_errors='strict') 606 # or 607 render(self, callback, decode=True, decode_errors='strict') 608 # or 609 render(self, callback, param1, paramx, ... decode=True, ...) 610 611 is also possible. 612 613 :Parameters: 614 `callback` : callable or ``None`` 615 Optional callback function and additional parameters 616 617 `params` : ``tuple`` 618 Optional extra parameters for `callback` 619 620 `decode` : ``bool`` 621 Decode the result back to unicode? This uses the encoding of the 622 template. 623 624 `decode_errors` : ``str`` 625 Error handler if decode errors happen. 626 627 :Return: The rendered node, type depends on `decode` parameter 628 :Rtype: ``basestring`` 629 """ 630 # pylint: disable = W0212 631 decode = kwargs.pop('decode', True) 632 decode_errors = kwargs.pop('decode_errors', 'strict') 633 if kwargs: 634 raise TypeError("Unrecognized keyword parameters") 635 node = self.copy() 636 if callback and callback[0] is not None: 637 node.replace(callback[0], node, *callback[1:]) 638 else: 639 node.replace(None, node) 640 res = ''.join(_nodetree.render(node, node._model, Node)) 641 if not decode: 642 return res 643 return res.decode(self._udict['decoder'].encoding, decode_errors)
644 645
646 -class TemplateNode(object):
647 """ 648 Template node 649 650 This is kind of a proto node. During rendering each template node is 651 turned into a user visible `Node` object, which implements the user 652 interface. `TemplateNode` objects provide a tree building interface 653 instead. 654 655 :IVariables: 656 `_udict` : ``dict`` 657 The dict containing node information 658 659 `_finalized` : ``bool`` 660 Was the tree finalized? 661 """ 662 ctx = None 663 _usernode = False 664
665 - def endtag():
666 """ 667 End tag of the node 668 669 :Type: ``str`` 670 """ 671 # pylint: disable = E0211, W0212, W0612, C0111 672 def fset(self, data): 673 if self._finalized: 674 raise NodeTreeError("Tree was already finalized") 675 if self._udict['closed']: 676 raise NodeTreeError( 677 "Self-closing elements cannot have an endtag" 678 ) 679 if not isinstance(data, str): 680 raise NodeTreeError("Endtag data must be a string") 681 self._udict['endtag'] = data
682 def fget(self): 683 return self._udict.get('endtag')
684 return locals() 685 endtag = _util.Property(endtag) 686
687 - def __init__(self, tagname, attr, special, closed):
688 """ 689 Initialization 690 691 :Parameters: 692 `tagname` : ``str`` 693 The name of the accompanying tag 694 695 `attr` : iterable 696 The attribute list (``((name, value), ...)``) 697 698 `special` : ``dict`` 699 Special node information 700 """ 701 scope = special.get('scope') 702 overlay = special.get('overlay') 703 tdi = special.get('attribute') 704 if tdi is None: 705 flags, name = '', None 706 else: 707 flags, name = tdi 708 709 if overlay is None: 710 overlay = False, False, False, None 711 else: 712 overlay = ( 713 '-' in overlay[0], # is_hidden 714 '>' in overlay[0], # is_target 715 '<' in overlay[0], # is_source 716 overlay[1], # name 717 ) 718 719 if scope is None: 720 scope = False, False, None 721 else: 722 scope = ( 723 ('-' in scope[0]), # is_hidden 724 ('=' in scope[0]), # is_absolute 725 scope[1], # name 726 ) 727 if not scope[0] and not scope[1] and not scope[2]: 728 scope = False, False, None 729 730 self._udict = { 731 'sep': None, 732 'nodes': [], 733 'content': (None, None), 734 'attr_': tuple(attr), 735 'removed': False, 736 'repeated': None, 737 'name': name or None, 738 'closed': closed, 739 'tagname': tagname, 740 'noelement': '-' in flags or overlay[0] or scope[0], 741 'noauto': '*' in flags, 742 'masked': False, 743 'overlay': overlay, 744 'scope': scope, 745 } 746 self._finalized = False
747
748 - def append_text(self, content):
749 """ 750 Append a text node 751 752 :Parameters: 753 `content` : ``str`` 754 The text node content 755 756 :Exceptions: 757 - `NodeTreeError` : The tree was already finalized 758 """ 759 if self._finalized: 760 raise NodeTreeError("Tree was already finalized") 761 762 self._udict['nodes'].append((_nodetree.TEXT_NODE, (content, content)))
763
764 - def append_escape(self, escaped, content):
765 """ 766 Append an escaped node 767 768 :Parameters: 769 `escaped` : ``str`` 770 The escaped string (in unescaped form, i.e. the final result) 771 772 `content` : ``str`` 773 The escape string (the whole sequence) 774 775 :Exceptions: 776 - `NodeTreeError` : The tree was already finalized 777 """ 778 if self._finalized: 779 raise NodeTreeError("Tree was already finalized") 780 781 self._udict['nodes'].append((_nodetree.TEXT_NODE, (escaped, content)))
782
783 - def append_node(self, tagname, attr, special, closed):
784 """ 785 Append processable node 786 787 :Parameters: 788 `tagname` : ``str`` 789 The name of the accompanying tag 790 791 `attr` : iterable 792 The attribute list (``((name, value), ...)``) 793 794 `special` : ``dict`` 795 Special attributes. If it's empty, something's wrong. 796 797 `closed` : ``bool`` 798 Closed tag? 799 800 :Return: new `TemplateNode` instance 801 :Rtype: `TemplateNode` 802 803 :Exceptions: 804 - `NodeTreeError` : The tree was already finalized 805 - `AssertionError` : nothing special 806 """ 807 if self._finalized: 808 raise NodeTreeError("Tree was already finalized") 809 810 assert len(special), "Nothing special about this node." 811 812 node = TemplateNode(tagname, attr, special, bool(closed)) 813 tdi = special.get('attribute') 814 if tdi is not None and ':' in tdi[0]: 815 kind = _nodetree.SEP_NODE 816 else: 817 kind = _nodetree.PROC_NODE 818 self._udict['nodes'].append((kind, node)) 819 820 return node
821 822
823 -class Root(TemplateNode):
824 """ 825 Root Node class 826 827 This class has to be used as the initial root of the tree. 828 """ 829 _sources, _targets = None, None 830
831 - def encoder():
832 """ 833 Output encoder 834 835 :Type: `EncoderInterface` 836 """ 837 # pylint: disable = E0211, W0212, W0612, C0111 838 def fget(self): 839 return self._udict['encoder']
840 return locals()
841 encoder = _util.Property(encoder) 842
843 - def decoder():
844 """ 845 Input decoder 846 847 :Type: `DecoderInterface` 848 """ 849 # pylint: disable = E0211, W0212, W0612, C0111 850 def fget(self): 851 return self._udict['decoder']
852 return locals() 853 decoder = _util.Property(decoder) 854
855 - def source_overlay_names():
856 """ 857 Source overlay names 858 859 :Type: iterable 860 """ 861 # pylint: disable = E0211, W0212, W0612, C0111 862 def fget(self): 863 if self._sources is None: 864 return () 865 return self._sources.iterkeys()
866 return locals() 867 source_overlay_names = _util.Property(source_overlay_names) 868
869 - def target_overlay_names():
870 """ 871 Target overlay names 872 873 :Type: iterable 874 """ 875 # pylint: disable = E0211, W0212, W0612, C0111 876 def fget(self): 877 if self._targets is None: 878 return () 879 return self._targets.iterkeys()
880 return locals() 881 target_overlay_names = _util.Property(target_overlay_names) 882
883 - def __init__(self):
884 """ Initialization """ 885 super(Root, self).__init__('', (), {}, False) 886 self.endtag = '' 887 self._udict['is_root'] = True
888
889 - def __str__(self):
890 """ String representation of the tree """ 891 return self.to_string(verbose=True)
892
893 - def to_string(self, verbose=False):
894 """ 895 String representation of the tree 896 897 :Parameters: 898 `verbose` : ``bool`` 899 Show (shortened) text node content and separator nodes? 900 901 :Return: The string representation 902 :Rtype: ``str`` 903 """ 904 if not self._finalized: 905 raise NodeTreeError("Tree was not finalized yet") 906 return '\n'.join(list( 907 _nodetree.represent(self._udict, bool(verbose)) 908 )) + '\n'
909
910 - def finalize(self, encoder, decoder):
911 """ 912 Finalize the tree 913 914 This method assigns separator nodes to their accompanying content 915 nodes, concatenates adjacent text nodes and tries to optimize 916 the tree a bit. 917 918 :Parameters: 919 `encoder` : `EncoderInterface` 920 Encoder instance 921 922 :Exceptions: 923 - `NodeTreeError` : The tree was already finalized or endtag was not 924 set 925 """ 926 if self._finalized: 927 raise NodeTreeError("Tree was already finalized") 928 self._sources, self._targets = \ 929 _finalize.finalize(self._udict, encoder, decoder) 930 self._finalized = True
931
932 - def overlay(self, other):
933 """ 934 Overlay this tree with another one 935 936 :Parameters: 937 `other` : `Root` 938 The tree to lay over 939 940 :Exceptions: 941 - `NodeTreeError` : Finalization error 942 """ 943 # pylint: disable = W0212 944 if not self._finalized: 945 raise NodeTreeError("Tree was not finalized yet.") 946 if not other._finalized: 947 raise NodeTreeError("Overlay tree was not finalized yet.") 948 return _nodetree.overlay( 949 self._udict, other._sources, TemplateNode, Root 950 )
951
952 - def render(self, model, startnode=None):
953 """ 954 Render the tree into chunks, calling `model` for input 955 956 :Parameters: 957 `model` : `ModelAdapterInterface` 958 The model object 959 960 `startnode` : ``str`` 961 Only render this node (and all its children). The node 962 is addressed via a dotted string notation, like ``a.b.c`` (this 963 would render the ``c`` node.) The notation does not describe a 964 strict node chain, though. Between to parts of a node chain may 965 be gaps in the tree. The algorithm looks out for the first 966 matching node. It does no backtracking and so does not cover all 967 branches (yet?), but that works fine for realistic cases :). A 968 non-working example would be (searching for a.b.c):: 969 970 * 971 +- a 972 | `- b - d 973 `- a 974 `- b - c 975 976 :Return: Rendered chunks 977 :Rtype: iterable 978 """ 979 return _nodetree.render( 980 _nodetree.findnode(self, startnode), model, Node 981 )
982 983 984 from tdi import c 985 c = c.load('impl') 986 if c is not None: 987 Root, Node, RawNode, TemplateNode = ( 988 c.Root, c.Node, c.RawNode, c.TemplateNode 989 ) 990 del _nodetree 991 del c 992