Changeset 5311:75467b6660f9 in livinglogic.python.xist

Show
Ignore:
Timestamp:
02/05/13 17:56:45 (7 years ago)
Author:
Walter Doerwald <walter@…>
Branch:
default
Message:

Prevent local variables from leaking into the surrounding scope in UL4 templates.

Files:
2 modified

Legend:

Unmodified
Added
Removed
  • src/ll/ul4c.py

    r5310 r5311  
    217217    def __repr__(self): 
    218218        return "undefined object for key {!r}".format(self.__key) 
     219 
     220 
     221class UndefinedVariable(Undefined): 
     222    def __init__(self, name): 
     223        self.__name = name 
     224 
     225    def __repr__(self): 
     226        return "undefined variable {!r}".format(self.__name) 
    219227 
    220228 
     
    415423    fields = AST.fields.union({"item", "varname", "container", "condition"}) 
    416424 
     425 
    417426    def __init__(self, item=None, varname=None, container=None, condition=None): 
    418427        super().__init__() 
     
    467476 
    468477    def formatpython(self, indent, keepws): 
    469         s = "[{} for {} in {}".format(self.item.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws)) 
     478        s = "(lambda vars: [{} for {} in {}".format(self.item.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws)) 
    470479        if self.condition is not None: 
    471480            s += " if {}".format(self.condition.formatpython(indent, keepws)) 
    472         s += "]" 
     481        s += "])(collections.ChainMap({}, vars))" # Evaluate the listcomp in a new ``ChainMap``, so we can prevent the local variables from leaking 
    473482        return s 
    474483 
     
    611620 
    612621    def formatpython(self, indent, keepws): 
    613         s = "{{{} : {} for {} in {}".format(self.key.formatpython(indent, keepws), self.value.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws)) 
     622        s = "(lambda vars: {{{} : {} for {} in {}".format(self.key.formatpython(indent, keepws), self.value.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws)) 
    614623        if self.condition is not None: 
    615624            s += " if {}".format(self.condition.formatpython(indent, keepws)) 
    616         s += "}" 
     625        s += "})(collections.ChainMap({}, vars))" # Evaluate the dictcomp in a new ``ChainMap``, so we can prevent the local variables from leaking 
    617626        return s 
    618627 
     
    689698 
    690699    def formatpython(self, indent, keepws): 
    691         s = "({} for {} in {}".format(self.item.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws)) 
     700        s = "(lambda vars:({} for {} in {}".format(self.item.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws)) 
    692701        if self.condition is not None: 
    693702            s += " if {}".format(self.condition.formatpython(indent, keepws)) 
    694         s += ")" 
     703        s += "))(collections.ChainMap({}, vars))" # Evaluate the generator expression in a new ``ChainMap``, so we can prevent the local variables from leaking 
    695704        return s 
    696705 
     
    727736 
    728737    def formatpython(self, indent, keepws): 
    729         return "ul4c._getitem(allvars, {!r})".format(self.name) 
     738        return "self._getvar(vars, {!r})".format(self.name) 
    730739 
    731740    def ul4ondump(self, encoder): 
     
    24882497            raise Error(ast.location) from exc 
    24892498 
     2499    def _getvar(self, vars, name): 
     2500        try: 
     2501            return vars[name] 
     2502        except KeyError: 
     2503            try: 
     2504                return self.functions[name] 
     2505            except KeyError: 
     2506                return UndefinedVariable(name) 
     2507 
    24902508    @classmethod 
    24912509    def makefunction(cls, f): 
     
    33033321            v.append("\t\tyield\n") 
    33043322            v.append("\ttry:\n") 
    3305             v.append("\t\tallvars = ul4c._AllVars(vars, self.functions)\n") 
    33063323            for node in self.content: 
    33073324                v.append(node.formatpython(2, self._keepws)) 
     
    34373454            v.append("\tfrom ll import ul4c, color\n") 
    34383455            v.append("\ttry:\n") 
    3439             v.append("\t\tallvars = ul4c._AllVars(vars, self.functions)\n") 
    34403456            for node in self.content: 
    34413457                v.append(node.formatpython(2, self._keepws)) 
     
    35123528### 
    35133529 
    3514 class _AllVars(collections.ChainMap): 
    3515     def __setitem__(self, key, value): 
    3516         if key == "self": 
    3517             raise ValueError("can't assign to self") 
    3518         collections.ChainMap.__setitem__(self, key, value) 
    3519  
    3520  
    35213530def _makedict(*items): 
    35223531    result = {} 
     
    35403549def _formatnestednamepython(name): 
    35413550    if isinstance(name, str): 
    3542         return "allvars[{!r}]".format(name) 
     3551        return "vars[{!r}]".format(name) 
    35433552    elif len(name) == 1: 
    35443553        return "({},)".format(_formatnestednamepython(name[0])) 
  • test/test_ul4.py

    r5308 r5311  
    658658    assert "[0, 2, 4, 6]" == r("<?code d = [2*i for i in range(4)]?><?print d?>") 
    659659 
     660    # Make sure that the loop variables doesn't leak into the surrounding scope 
     661    assert "undefined" == r("<?code d = [2*i for i in range(4)]?><?print type(i)?>") 
     662 
    660663 
    661664@pytest.mark.ul4 
     
    666669    assert "0, 2, 4, 6" == r("<?print ', '.join((str(2*i) for i in range(4)))?>") 
    667670    assert "0:g; 1:r; 2:k" == r("<?for (i, c2) in enumerate(c for c in 'gurk' if c != 'u')?><?if i?>; <?end if?><?print i?>:<?print c2?><?end for?>") 
     671 
     672    # Make sure that the loop variables doesn't leak into the surrounding scope 
     673    assert "undefined" == r("<?code d = (2*i for i in range(4))?><?print type(i)?>") 
    668674 
    669675 
     
    681687    assert 'no' == r('<?if {}?>yes<?else?>no<?end if?>') 
    682688    assert 'yes' == r('<?if {1:2}?>yes<?else?>no<?end if?>') 
     689 
     690    # Make sure that the loop variables doesn't leak into the surrounding scope 
     691    assert "undefined" == r("<?code d = {i: 2*i for i in range(4)}?><?print type(i)?>") 
    683692 
    684693 
     
    30153024def test_def(r): 
    30163025    assert 'foo' == r('<?template lower?><?print x.lower()?><?end template?><?print lower.renders(x="FOO")?>') 
    3017  
    3018  
    3019 @pytest.mark.ul4 
    3020 def test_self(r): 
    3021     with raises("can't assign to self"): 
    3022         assert "42" == r("<?code self = 42?><?print self?>") 
    30233026 
    30243027