| OLD | NEW |
| 1 # Copyright 2012 Benjamin Kalman | 1 # Copyright 2012 Benjamin Kalman |
| 2 # | 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
| 6 # | 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # | 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 def Push(self, context): | 148 def Push(self, context): |
| 149 self._nodes.append(_Contexts._Node(context)) | 149 self._nodes.append(_Contexts._Node(context)) |
| 150 | 150 |
| 151 def Pop(self): | 151 def Pop(self): |
| 152 node = self._nodes.pop() | 152 node = self._nodes.pop() |
| 153 assert len(self._nodes) >= self._first_local | 153 assert len(self._nodes) >= self._first_local |
| 154 for found_key in node.GetKeys(): | 154 for found_key in node.GetKeys(): |
| 155 # [0] is the stack of nodes that |found_key| has been found in. | 155 # [0] is the stack of nodes that |found_key| has been found in. |
| 156 self._value_info[found_key][0].pop() | 156 self._value_info[found_key][0].pop() |
| 157 | 157 |
| 158 def FirstLocal(self): |
| 159 if len(self._nodes) == self._first_local: |
| 160 return None |
| 161 return self._nodes[-1]._value |
| 162 |
| 158 def Resolve(self, path): | 163 def Resolve(self, path): |
| 159 # This method is only efficient at finding |key|; if |tail| has a value (and | 164 # This method is only efficient at finding |key|; if |tail| has a value (and |
| 160 # |key| evaluates to an indexable value) we'll need to descend into that. | 165 # |key| evaluates to an indexable value) we'll need to descend into that. |
| 161 key, tail = path.split('.', 1) if '.' in path else (path, None) | 166 key, tail = path.split('.', 1) if '.' in path else (path, None) |
| 162 found = self._FindNodeValue(key) | 167 found = self._FindNodeValue(key) |
| 163 if tail is None: | 168 if tail is None: |
| 164 return found | 169 return found |
| 165 for part in tail.split('.'): | 170 for part in tail.split('.'): |
| 166 if not hasattr(found, 'get'): | 171 if not hasattr(found, 'get'): |
| 167 return None | 172 return None |
| (...skipping 468 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 636 def Render(self, render_state): | 641 def Render(self, render_state): |
| 637 value = render_state.contexts.Resolve(self._id.name) | 642 value = render_state.contexts.Resolve(self._id.name) |
| 638 if value is None: | 643 if value is None: |
| 639 render_state.AddResolutionError(self._id) | 644 render_state.AddResolutionError(self._id) |
| 640 return | 645 return |
| 641 render_state.text.Append(json.dumps(value, separators=(',',':'))) | 646 render_state.text.Append(json.dumps(value, separators=(',',':'))) |
| 642 | 647 |
| 643 def __repr__(self): | 648 def __repr__(self): |
| 644 return '{{*%s}}' % self._id | 649 return '{{*%s}}' % self._id |
| 645 | 650 |
| 651 # TODO: Better common model of _PartialNodeWithArguments, _PartialNodeInContext, |
| 652 # and _PartialNode. |
| 653 class _PartialNodeWithArguments(_DecoratorNode): |
| 654 def __init__(self, partial, args): |
| 655 if isinstance(partial, Handlebar): |
| 656 # Preserve any get() method that the caller has added. |
| 657 if hasattr(partial, 'get'): |
| 658 self.get = partial.get |
| 659 partial = partial._top_node |
| 660 _DecoratorNode.__init__(self, partial) |
| 661 self._partial = partial |
| 662 self._args = args |
| 663 |
| 664 def Render(self, render_state): |
| 665 render_state.contexts.Scope(self._args, self._partial.Render, render_state) |
| 666 |
| 667 class _PartialNodeInContext(_DecoratorNode): |
| 668 def __init__(self, partial, context): |
| 669 if isinstance(partial, Handlebar): |
| 670 # Preserve any get() method that the caller has added. |
| 671 if hasattr(partial, 'get'): |
| 672 self.get = partial.get |
| 673 partial = partial._top_node |
| 674 _DecoratorNode.__init__(self, partial) |
| 675 self._partial = partial |
| 676 self._context = context |
| 677 |
| 678 def Render(self, render_state): |
| 679 original_contexts = render_state.contexts |
| 680 try: |
| 681 render_state.contexts = self._context |
| 682 render_state.contexts.Scope( |
| 683 # The first local context of |original_contexts| will be the |
| 684 # arguments that were passed to the partial, if any. |
| 685 original_contexts.FirstLocal() or {}, |
| 686 self._partial.Render, render_state) |
| 687 finally: |
| 688 render_state.contexts = original_contexts |
| 689 |
| 646 class _PartialNode(_LeafNode): | 690 class _PartialNode(_LeafNode): |
| 647 '''{{+var:foo}} ... {{/foo}} | 691 '''{{+var:foo}} ... {{/foo}} |
| 648 ''' | 692 ''' |
| 649 def __init__(self, bind_to, id_, content): | 693 def __init__(self, bind_to, id_, content): |
| 650 _LeafNode.__init__(self, id_.line, id_.line) | 694 _LeafNode.__init__(self, id_.line, id_.line) |
| 651 self._bind_to = bind_to | 695 self._bind_to = bind_to |
| 652 self._id = id_ | 696 self._id = id_ |
| 653 self._content = content | 697 self._content = content |
| 654 self._resolved_args = None | |
| 655 self._args = None | 698 self._args = None |
| 656 self._pass_through_id = None | 699 self._pass_through_id = None |
| 657 | 700 |
| 658 @classmethod | 701 @classmethod |
| 659 def Inline(cls, id_): | 702 def Inline(cls, id_): |
| 660 return cls(None, id_, None) | 703 return cls(None, id_, None) |
| 661 | 704 |
| 662 def Render(self, render_state): | 705 def Render(self, render_state): |
| 663 value = render_state.contexts.Resolve(self._id.name) | 706 value = render_state.contexts.Resolve(self._id.name) |
| 664 if value is None: | 707 if value is None: |
| 665 render_state.AddResolutionError(self._id) | 708 render_state.AddResolutionError(self._id) |
| 666 return | 709 return |
| 667 if not isinstance(value, (Handlebar, _Node)): | 710 if not isinstance(value, (Handlebar, _Node)): |
| 668 render_state.AddResolutionError(self._id, description='not a partial') | 711 render_state.AddResolutionError(self._id, description='not a partial') |
| 669 return | 712 return |
| 670 | 713 |
| 671 if isinstance(value, Handlebar): | 714 if isinstance(value, Handlebar): |
| 672 node, name = value._top_node, value._name | 715 node, name = value._top_node, value._name |
| 673 else: | 716 else: |
| 674 node, name = value, None | 717 node, name = value, None |
| 675 | 718 |
| 676 partial_render_state = render_state.ForkPartial(name, self._id) | 719 partial_render_state = render_state.ForkPartial(name, self._id) |
| 677 | 720 |
| 678 arg_context = {} | 721 arg_context = {} |
| 679 if self._pass_through_id is not None: | 722 if self._pass_through_id is not None: |
| 680 context = render_state.contexts.Resolve(self._pass_through_id.name) | 723 context = render_state.contexts.Resolve(self._pass_through_id.name) |
| 681 if context is not None: | 724 if context is not None: |
| 682 arg_context[self._pass_through_id.name] = context | 725 arg_context[self._pass_through_id.name] = context |
| 683 if self._resolved_args is not None: | |
| 684 arg_context.update(self._resolved_args) | |
| 685 if self._args is not None: | 726 if self._args is not None: |
| 686 def resolve_args(args): | 727 def resolve_args(args): |
| 687 resolved = {} | 728 resolved = {} |
| 688 for key, value in args.iteritems(): | 729 for key, value in args.iteritems(): |
| 689 if isinstance(value, dict): | 730 if isinstance(value, dict): |
| 690 assert len(value.keys()) == 1 | 731 assert len(value.keys()) == 1 |
| 691 inner_id, inner_args = value.items()[0] | 732 id_of_partial, partial_args = value.items()[0] |
| 692 inner_partial = render_state.contexts.Resolve(inner_id.name) | 733 partial = render_state.contexts.Resolve(id_of_partial.name) |
| 693 if inner_partial is not None: | 734 if partial is not None: |
| 694 context = _PartialNode(None, inner_id, inner_partial) | 735 resolved[key] = _PartialNodeWithArguments( |
| 695 context.SetResolvedArguments(resolve_args(inner_args)) | 736 partial, resolve_args(partial_args)) |
| 696 resolved[key] = context | |
| 697 else: | 737 else: |
| 698 context = render_state.contexts.Resolve(value.name) | 738 context = render_state.contexts.Resolve(value.name) |
| 699 if context is not None: | 739 if context is not None: |
| 700 resolved[key] = context | 740 resolved[key] = context |
| 701 return resolved | 741 return resolved |
| 702 arg_context.update(resolve_args(self._args)) | 742 arg_context.update(resolve_args(self._args)) |
| 703 if self._bind_to and self._content: | 743 if self._bind_to and self._content: |
| 704 arg_context[self._bind_to.name] = self._content | 744 arg_context[self._bind_to.name] = _PartialNodeInContext( |
| 745 self._content, render_state.contexts) |
| 705 if arg_context: | 746 if arg_context: |
| 706 partial_render_state.contexts.Push(arg_context) | 747 partial_render_state.contexts.Push(arg_context) |
| 707 | 748 |
| 708 node.Render(partial_render_state) | 749 node.Render(partial_render_state) |
| 709 | 750 |
| 710 render_state.Merge( | 751 render_state.Merge( |
| 711 partial_render_state, | 752 partial_render_state, |
| 712 text_transform=lambda text: text[:-1] if text.endswith('\n') else text) | 753 text_transform=lambda text: text[:-1] if text.endswith('\n') else text) |
| 713 | 754 |
| 714 def SetResolvedArguments(self, args): | |
| 715 self._resolved_args = args | |
| 716 | |
| 717 def SetArguments(self, args): | 755 def SetArguments(self, args): |
| 718 self._args = args | 756 self._args = args |
| 719 | 757 |
| 720 def PassThroughArgument(self, id_): | 758 def PassThroughArgument(self, id_): |
| 721 self._pass_through_id = id_ | 759 self._pass_through_id = id_ |
| 722 | 760 |
| 723 def __repr__(self): | 761 def __repr__(self): |
| 724 return '{{+%s}}' % self._id | 762 return '{{+%s}}' % self._id |
| 725 | 763 |
| 726 _TOKENS = {} | 764 _TOKENS = {} |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1085 id_ = _Identifier(tokens.AdvanceOverNextString(excluded=' \n\r\t:()'), | 1123 id_ = _Identifier(tokens.AdvanceOverNextString(excluded=' \n\r\t:()'), |
| 1086 tokens.next_line, | 1124 tokens.next_line, |
| 1087 column_start) | 1125 column_start) |
| 1088 tokens.SkipWhitespace() | 1126 tokens.SkipWhitespace() |
| 1089 return id_ | 1127 return id_ |
| 1090 | 1128 |
| 1091 def Render(self, *user_contexts): | 1129 def Render(self, *user_contexts): |
| 1092 '''Renders this template given a variable number of contexts to read out | 1130 '''Renders this template given a variable number of contexts to read out |
| 1093 values from (such as those appearing in {{foo}}). | 1131 values from (such as those appearing in {{foo}}). |
| 1094 ''' | 1132 ''' |
| 1095 name = self._name or '<root>' | |
| 1096 internal_context = _InternalContext() | 1133 internal_context = _InternalContext() |
| 1097 render_state = _RenderState( | 1134 contexts = list(user_contexts) |
| 1098 name, _Contexts([{'_': internal_context}] + list(user_contexts))) | 1135 contexts.append({ |
| 1136 '_': internal_context, |
| 1137 'false': False, |
| 1138 'true': True, |
| 1139 }) |
| 1140 render_state = _RenderState(self._name or '<root>', _Contexts(contexts)) |
| 1099 internal_context.SetRenderState(render_state) | 1141 internal_context.SetRenderState(render_state) |
| 1100 self._top_node.Render(render_state) | 1142 self._top_node.Render(render_state) |
| 1101 return render_state.GetResult() | 1143 return render_state.GetResult() |
| 1102 | 1144 |
| 1103 def render(self, *contexts): | 1145 def render(self, *contexts): |
| 1104 return self.Render(*contexts) | 1146 return self.Render(*contexts) |
| 1105 | 1147 |
| 1106 def __eq__(self, other): | 1148 def __eq__(self, other): |
| 1107 return self.source == other.source and self._name == other._name | 1149 return self.source == other.source and self._name == other._name |
| 1108 | 1150 |
| 1109 def __ne__(self, other): | 1151 def __ne__(self, other): |
| 1110 return not (self == other) | 1152 return not (self == other) |
| 1111 | 1153 |
| 1112 def __repr__(self): | 1154 def __repr__(self): |
| 1113 return str('%s(%s)' % (type(self).__name__, self._top_node)) | 1155 return str('%s(%s)' % (type(self).__name__, self._top_node)) |
| 1114 | 1156 |
| 1115 def __str__(self): | 1157 def __str__(self): |
| 1116 return repr(self) | 1158 return repr(self) |
| OLD | NEW |