Edgewall Software

Ticket #230: genshi-context.patch

File genshi-context.patch, 7.1 KB (added by dan.colascione@…, 15 years ago)

Patch

  • genshi/template/base.py

     
    3030           'TemplateSyntaxError', 'BadDirectiveError']
    3131__docformat__ = 'restructuredtext en'
    3232
    33 if sys.version_info < (2, 4):
    34     _ctxt2dict = lambda ctxt: ctxt.frames[0]
    35 else:
    36     _ctxt2dict = lambda ctxt: ctxt
    37 
    38 
    3933class TemplateError(Exception):
    4034    """Base exception class for errors related to template processing."""
    4135
     
    133127        """Initialize the template context with the given keyword arguments as
    134128        data.
    135129        """
    136         self.frames = deque([data])
    137         self.pop = self.frames.popleft
    138         self.push = self.frames.appendleft
     130        self.__frames = deque([data])
    139131        self._match_templates = []
    140132        self._choice_stack = []
    141133
     
    152144        data.setdefault('value_of', value_of)
    153145
    154146    def __repr__(self):
    155         return repr(list(self.frames))
     147        return repr(list(self.__frames))
    156148
    157149    def __contains__(self, key):
    158150        """Return whether a variable exists in any of the scopes.
    159151       
    160152        :param key: the name of the variable
    161153        """
    162         return self._find(key)[1] is not None
     154        return self.__find(key)[1] is not None
    163155    has_key = __contains__
    164156
    165     def __delitem__(self, key):
    166         """Remove a variable from all scopes.
    167        
    168         :param key: the name of the variable
    169         """
    170         for frame in self.frames:
    171             if key in frame:
    172                 del frame[key]
    173 
    174157    def __getitem__(self, key):
    175158        """Get a variables's value, starting at the current scope and going
    176159        upward.
     
    179162        :return: the variable value
    180163        :raises KeyError: if the requested variable wasn't found in any scope
    181164        """
    182         value, frame = self._find(key)
     165        value, frame = self.__find(key)
    183166        if frame is None:
    184167            raise KeyError(key)
    185168        return value
     
    197180        :param key: the name of the variable
    198181        :param value: the variable value
    199182        """
    200         self.frames[0][key] = value
     183        self.__frames[0][key] = value
    201184
    202     def _find(self, key, default=None):
     185    def __find(self, key, default=None):
    203186        """Retrieve a given variable's value and the frame it was found in.
    204187
    205         Intended primarily for internal use by directives.
     188        Internal use.
    206189       
    207190        :param key: the name of the variable
    208191        :param default: the default value to return when the variable is not
    209192                        found
    210193        """
    211         for frame in self.frames:
     194        for frame in self.__frames:
    212195            if key in frame:
    213196                return frame[key], frame
    214197        return default, None
     
    221204        :param default: the default value to return when the variable is not
    222205                        found
    223206        """
    224         for frame in self.frames:
     207        for frame in self.__frames:
    225208            if key in frame:
    226209                return frame[key]
    227210        return default
     
    232215        :return: a list of variable names
    233216        """
    234217        keys = []
    235         for frame in self.frames:
     218        for frame in self.__frames:
    236219            keys += [key for key in frame if key not in keys]
    237220        return keys
    238221
     
    246229
    247230    def update(self, mapping):
    248231        """Update the context from the mapping provided."""
    249         self.frames[0].update(mapping)
     232        self.__frames[0].update(mapping)
    250233
    251234    def push(self, data):
    252235        """Push a new scope on the stack.
     
    254237        :param data: the data dictionary to push on the context stack.
    255238        """
    256239
     240        self.__frames.appendleft(data)
     241
    257242    def pop(self):
    258243        """Pop the top-most scope from the stack."""
    259244
     245        return self.__frames.popleft()
    260246
     247    def execdict(self):
     248        """Return an object Python's exec can use for variables"""
     249       
     250        if sys.version_info < (2, 4):
     251            return self.__frames[0]
     252        else:
     253            return self
     254
    261255def _apply_directives(stream, directives, ctxt, **vars):
    262256    """Apply the given directives to the stream.
    263257   
     
    299293    if vars:
    300294        ctxt.push(vars)
    301295        ctxt.push({})
    302     suite.execute(_ctxt2dict(ctxt))
     296    suite.execute(ctxt.execdict())
    303297    if vars:
    304298        top = ctxt.pop()
    305299        ctxt.pop()
    306         ctxt.frames[0].update(top)
     300        ctxt.update(top)
    307301
    308302
    309303class TemplateMeta(type):
  • genshi/template/tests/markup.py

     
    254254          <Item/>
    255255        </Test>""", str(tmpl.generate()))
    256256
     257    def test_included_function_override(self):
     258        dirname = tempfile.mkdtemp(suffix='genshi_test')
     259        try:
     260            file1 = open(os.path.join(dirname, 'tmpl1.html'), 'w')
     261            try:
     262                file1.write("""<div xmlns:xi="http://www.w3.org/2001/XInclude"
     263                                    xmlns:py="http://genshi.edgewall.org/">
     264                  <py:def function="msg">Foo</py:def>
     265                  <xi:include href="tmpl2.html" />${msg()}</div>""")
     266            finally:
     267                file1.close()
     268
     269            file2 = open(os.path.join(dirname, 'tmpl2.html'), 'w')
     270            try:
     271                file2.write("""<py:def
     272                                xmlns:py="http://genshi.edgewall.org/"
     273                                function="msg">Bar</py:def>""")
     274            finally:
     275                file2.close()
     276
     277            loader = TemplateLoader([dirname])
     278            tmpl = loader.load('tmpl1.html')
     279
     280            self.assertEqual("""<div>
     281                  Bar</div>""", tmpl.generate().render())
     282               
     283        finally:
     284            shutil.rmtree(dirname)
     285
    257286    def test_include_in_loop(self):
    258287        dirname = tempfile.mkdtemp(suffix='genshi_test')
    259288        try:
  • genshi/template/directives.py

     
    322322            # Function name can't be set in Python 2.3
    323323            pass
    324324
    325         # Store the function reference in the bottom context frame so that it
    326         # doesn't get popped off before processing the template has finished
    327         # FIXME: this makes context data mutable as a side-effect
    328         ctxt.frames[-1][self.name] = function
     325        ctxt[self.name] = function
    329326
    330327        return []
    331328
     
    731728    attach = classmethod(attach)
    732729
    733730    def __call__(self, stream, directives, ctxt, **vars):
    734         frame = {}
    735         ctxt.push(frame)
     731        ctxt.push({})
     732       
    736733        for targets, expr in self.vars:
    737734            value = _eval_expr(expr, ctxt, **vars)
    738735            for assign in targets:
    739                 assign(frame, value)
     736                assign(ctxt, value)
     737               
    740738        for event in _apply_directives(stream, directives, ctxt, **vars):
    741739            yield event
    742740        ctxt.pop()