Edgewall Software

Ticket #84: python_pi.3.diff

File python_pi.3.diff, 23.2 KB (added by cmlenz, 5 years ago)

Updated patch, candidate for merge

  • genshi/tests/builder.py

     
    4545    def test_stream_as_child(self): 
    4646        xml = list(tag.span(XML('<b>Foo</b>')).generate()) 
    4747        self.assertEqual(5, len(xml)) 
    48         self.assertEqual((Stream.START, ('span', ()), (None, -1, -1)), xml[0]) 
    49         self.assertEqual((Stream.START, ('b', ()), (None, 1, 0)), xml[1]) 
    50         self.assertEqual((Stream.TEXT, 'Foo', (None, 1, 3)), xml[2]) 
    51         self.assertEqual((Stream.END, 'b', (None, 1, 6)), xml[3]) 
    52         self.assertEqual((Stream.END, 'span', (None, -1, -1)), xml[4]) 
     48        self.assertEqual((Stream.START, ('span', ())), xml[0][:2]) 
     49        self.assertEqual((Stream.START, ('b', ())), xml[1][:2]) 
     50        self.assertEqual((Stream.TEXT, 'Foo'), xml[2][:2]) 
     51        self.assertEqual((Stream.END, 'b'), xml[3][:2]) 
     52        self.assertEqual((Stream.END, 'span'), xml[4][:2]) 
    5353 
    5454 
    5555def suite(): 
  • genshi/path.py

     
    156156        >>> test = Path('child').test() 
    157157        >>> for event in xml: 
    158158        ...     if test(event, {}, {}): 
    159         ...         print event 
    160         ('START', (QName(u'child'), Attrs([(QName(u'id'), u'2')])), (None, 1, 34)) 
     159        ...         print event[0], repr(event[1]) 
     160        START (QName(u'child'), Attrs([(QName(u'id'), u'2')])) 
    161161        """ 
    162162        paths = [(p, len(p), [0], [], [0] * len(p)) for p in [ 
    163163            (ignore_context and [_DOTSLASHSLASH] or []) + p for p in self.paths 
  • genshi/template/core.py

     
    107107    def __repr__(self): 
    108108        return repr(list(self.frames)) 
    109109 
     110    def __contains__(self, key): 
     111        """Return whether a variable exists in any of the scopes.""" 
     112        return self._find(key)[1] is not None 
     113 
     114    def __getitem__(self, key): 
     115        """Get a variables's value, starting at the current scope and going 
     116        upward. 
     117         
     118        Raises `KeyError` if the requested variable wasn't found in any scope. 
     119        """ 
     120        value, frame = self._find(key) 
     121        if frame is None: 
     122            raise KeyError(key) 
     123        return value 
     124 
    110125    def __setitem__(self, key, value): 
    111126        """Set a variable in the current scope.""" 
    112127        self.frames[0][key] = value 
     
    129144            if key in frame: 
    130145                return frame[key] 
    131146        return default 
    132     __getitem__ = get 
    133147 
     148    def keys(self): 
     149        keys = [] 
     150        for frame in self.frames: 
     151            keys += [key for key in frame if key not in keys] 
     152        return keys 
     153 
     154    def items(self): 
     155        return [(key, self.get(key)) for key in self.keys()] 
     156 
    134157    def push(self, data): 
    135158        """Push a new scope on the stack.""" 
    136159 
  • genshi/template/tests/markup.py

     
    195195          \xf6 
    196196        </div>""", unicode(tmpl.generate())) 
    197197 
     198    def test_exec_import(self): 
     199        tmpl = MarkupTemplate(u"""<?python from datetime import timedelta ?> 
     200        <div xmlns:py="http://genshi.edgewall.org/"> 
     201          ${timedelta(days=2)} 
     202        </div>""") 
     203        self.assertEqual(u"""<div> 
     204          2 days, 0:00:00 
     205        </div>""", str(tmpl.generate())) 
     206 
     207    def test_exec_def(self): 
     208        tmpl = MarkupTemplate(u""" 
     209        <?python 
     210        def foo(): 
     211            return 42 
     212        ?> 
     213        <div xmlns:py="http://genshi.edgewall.org/"> 
     214          ${foo()} 
     215        </div>""") 
     216        self.assertEqual(u"""<div> 
     217          42 
     218        </div>""", str(tmpl.generate())) 
     219 
    198220    def test_include_in_loop(self): 
    199221        dirname = tempfile.mkdtemp(suffix='genshi_test') 
    200222        try: 
  • genshi/template/tests/eval.py

     
    1515import sys 
    1616import unittest 
    1717 
    18 from genshi.template.eval import Expression, Undefined 
     18from genshi.template.eval import Expression, Suite, Undefined 
    1919 
    2020 
    2121class ExpressionTestCase(unittest.TestCase): 
     
    382382        self.assertRaises(TypeError, expr.evaluate, {'nothing': object()}) 
    383383 
    384384 
     385class SuiteTestCase(unittest.TestCase): 
     386 
     387    def test_assign(self): 
     388        suite = Suite("foo = 42") 
     389        data = {} 
     390        suite.execute(data) 
     391        self.assertEqual(42, data['foo']) 
     392 
     393    def test_def(self): 
     394        suite = Suite("def donothing(): pass") 
     395        data = {} 
     396        suite.execute(data) 
     397        assert 'donothing' in data 
     398        self.assertEqual(None, data['donothing']()) 
     399 
     400    def test_delete(self): 
     401        suite = Suite("""foo = 42 
     402del foo 
     403""") 
     404        data = {} 
     405        suite.execute(data) 
     406        assert 'foo' not in data 
     407 
     408    def test_class(self): 
     409        suite = Suite("class plain(object): pass") 
     410        data = {} 
     411        suite.execute(data) 
     412        assert 'plain' in data 
     413 
     414    def test_import(self): 
     415        suite = Suite("from itertools import ifilter") 
     416        data = {} 
     417        suite.execute(data) 
     418        assert 'ifilter' in data 
     419 
     420    def test_for(self): 
     421        suite = Suite("""x = [] 
     422for i in range(3): 
     423    x.append(i**2) 
     424""") 
     425        data = {} 
     426        suite.execute(data) 
     427        self.assertEqual([0, 1, 4], data['x']) 
     428 
     429    def test_if(self): 
     430        suite = Suite("""if foo == 42: 
     431    x = True 
     432""") 
     433        data = {'foo': 42} 
     434        suite.execute(data) 
     435        self.assertEqual(True, data['x']) 
     436 
     437    def test_raise(self): 
     438        suite = Suite("""raise NotImplementedError""") 
     439        self.assertRaises(NotImplementedError, suite.execute, {}) 
     440 
     441    def test_try_except(self): 
     442        suite = Suite("""try: 
     443    import somemod 
     444except ImportError: 
     445    somemod = None 
     446else: 
     447    somemod.dosth()""") 
     448        data = {} 
     449        suite.execute(data) 
     450        self.assertEqual(None, data['somemod']) 
     451 
     452    def test_finally(self): 
     453        suite = Suite("""try: 
     454    x = 2 
     455finally: 
     456    x = None 
     457""") 
     458        data = {} 
     459        suite.execute(data) 
     460        self.assertEqual(None, data['x']) 
     461 
     462    def test_while_break(self): 
     463        suite = Suite("""x = 0 
     464while x < 5: 
     465    x += step 
     466    if x == 4: 
     467        break 
     468""") 
     469        data = {'step': 2} 
     470        suite.execute(data) 
     471        self.assertEqual(4, data['x']) 
     472 
     473 
    385474def suite(): 
    386475    suite = unittest.TestSuite() 
    387476    suite.addTest(doctest.DocTestSuite(Expression.__module__)) 
    388477    suite.addTest(unittest.makeSuite(ExpressionTestCase, 'test')) 
     478    suite.addTest(unittest.makeSuite(SuiteTestCase, 'test')) 
    389479    return suite 
    390480 
    391481if __name__ == '__main__': 
  • genshi/template/markup.py

     
    1414"""Markup templating engine.""" 
    1515 
    1616from itertools import chain 
     17import sys 
     18from textwrap import dedent 
    1719 
    1820from genshi.core import Attrs, Namespace, Stream, StreamEventKind 
    19 from genshi.core import START, END, START_NS, END_NS, TEXT, COMMENT 
     21from genshi.core import START, END, START_NS, END_NS, TEXT, PI, COMMENT 
    2022from genshi.input import XMLParser 
    2123from genshi.template.core import BadDirectiveError, Template, \ 
    22                                  _apply_directives, SUB 
     24                                 TemplateSyntaxError, _apply_directives, SUB 
     25from genshi.template.eval import Suite 
    2326from genshi.template.loader import TemplateNotFound 
    2427from genshi.template.directives import * 
    2528 
     29if sys.version_info < (2, 4): 
     30    _ctxt2dict = lambda ctxt: ctxt.frames[0] 
     31else: 
     32    _ctxt2dict = lambda ctxt: ctxt 
    2633 
     34 
    2735class MarkupTemplate(Template): 
    2836    """Implementation of the template language for XML-based templates. 
    2937     
     
    3543      <li>1</li><li>2</li><li>3</li> 
    3644    </ul> 
    3745    """ 
     46    EXEC = StreamEventKind('EXEC') 
    3847    INCLUDE = StreamEventKind('INCLUDE') 
    3948 
    4049    DIRECTIVE_NAMESPACE = Namespace('http://genshi.edgewall.org/') 
     
    5968        Template.__init__(self, source, basedir=basedir, filename=filename, 
    6069                          loader=loader, encoding=encoding) 
    6170 
    62         self.filters.append(self._match) 
     71        self.filters += [self._exec, self._match] 
    6372        if loader: 
    6473            self.filters.append(self._include) 
    6574 
     
    169178                    stream[start_offset:] = [(SUB, (directives, substream), 
    170179                                              pos)] 
    171180 
     181            elif kind is PI and data[0] == 'python': 
     182                try: 
     183                    # As Expat doesn't report whitespace between the PI target 
     184                    # and the data, we have to jump through some hoops here to 
     185                    # get correctly indented Python code 
     186                    # Unfortunately, we'll still probably not get the line 
     187                    # number quite right 
     188                    lines = [line.expandtabs() for line in data[1].splitlines()] 
     189                    first = lines[0] 
     190                    rest = dedent('\n'.join(lines[1:])) 
     191                    if first.rstrip().endswith(':') and not rest[0].isspace(): 
     192                        rest = '\n'.join(['    ' + line for line 
     193                                          in rest.splitlines()]) 
     194                    source = '\n'.join([first, rest]) 
     195                    suite = Suite(source, self.filepath, pos[1]) 
     196                except SyntaxError, err: 
     197                    raise TemplateSyntaxError(err, self.filepath, 
     198                                              pos[1] + (err.lineno or 1) - 1, 
     199                                              pos[2] + (err.offset or 0)) 
     200                stream.append((EXEC, suite, pos)) 
     201 
    172202            elif kind is TEXT: 
    173203                for kind, data, pos in self._interpolate(data, self.basedir, 
    174204                                                         *pos): 
     
    190220                data = data[0], list(self._prepare(data[1])) 
    191221            yield kind, data, pos 
    192222 
     223    def _exec(self, stream, ctxt): 
     224        """Internal stream filter that executes code in <?python ?> processing 
     225        instructions. 
     226        """ 
     227        for event in stream: 
     228            if event[0] is EXEC: 
     229                event[1].execute(_ctxt2dict(ctxt)) 
     230            else: 
     231                yield event 
     232 
    193233    def _include(self, stream, ctxt): 
    194234        """Internal stream filter that performs inclusion of external 
    195235        template files. 
     
    291331                yield event 
    292332 
    293333 
     334EXEC = MarkupTemplate.EXEC 
    294335INCLUDE = MarkupTemplate.INCLUDE 
  • genshi/template/eval.py

     
    1515 
    1616import __builtin__ 
    1717from compiler import ast, parse 
    18 from compiler.pycodegen import ExpressionCodeGenerator 
     18from compiler.pycodegen import ExpressionCodeGenerator, ModuleCodeGenerator 
    1919import new 
    2020try: 
    2121    set 
    2222except NameError: 
    2323    from sets import Set as set 
     24import sys 
    2425 
    2526from genshi.util import flatten 
    2627 
    27 __all__ = ['Expression', 'Undefined'] 
     28if sys.version_info < (2, 4): 
     29    _locals = dict 
     30else: 
     31    _locals = lambda x: x 
    2832 
     33__all__ = ['Expression', 'Suite', 'Undefined'] 
    2934 
    30 class Expression(object): 
     35 
     36class Code(object): 
     37    """Abstract base class for the `Expression` and `Suite` classes.""" 
     38    __slots__ = ['source', 'code'] 
     39 
     40    def __init__(self, source, filename=None, lineno=-1): 
     41        """Create the code object, either from a string, or from an AST node. 
     42         
     43        @param source: either a string containing the source code, or an AST 
     44            node 
     45        @param filename: the (preferably absolute) name of the file containing 
     46            the code 
     47        @param lineno: the number of the line on which the code was found 
     48        """ 
     49        if isinstance(source, basestring): 
     50            self.source = source 
     51            node = _parse(source, mode=self.mode) 
     52        else: 
     53            assert isinstance(source, ast.Node) 
     54            self.source = '?' 
     55            if self.mode == 'eval': 
     56                node = ast.Expression(source) 
     57            else: 
     58                node = ast.Module(None, source) 
     59 
     60        self.code = _compile(node, self.source, mode=self.mode, 
     61                             filename=filename, lineno=lineno) 
     62 
     63    def __eq__(self, other): 
     64        return (type(other) == type(self)) and (self.code == other.code) 
     65 
     66    def __hash__(self): 
     67        return hash(self.code) 
     68 
     69    def __ne__(self, other): 
     70        return not self == other 
     71 
     72    def __repr__(self): 
     73        return '%s(%r)' % (self.__class__.__name__, self.source) 
     74 
     75 
     76class Expression(Code): 
    3177    """Evaluates Python expressions used in templates. 
    3278 
    3379    >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'}) 
     
    68114    >>> Expression('len(items)').evaluate(data) 
    69115    3 
    70116    """ 
    71     __slots__ = ['source', 'code'] 
     117    __slots__ = [] 
     118    mode = 'eval' 
    72119 
    73     def __init__(self, source, filename=None, lineno=-1): 
    74         """Create the expression, either from a string, or from an AST node. 
    75          
    76         @param source: either a string containing the source code of the 
    77             expression, or an AST node 
    78         @param filename: the (preferably absolute) name of the file containing 
    79             the expression 
    80         @param lineno: the number of the line on which the expression was found 
    81         """ 
    82         if isinstance(source, basestring): 
    83             self.source = source 
    84             self.code = _compile(_parse(source), self.source, filename=filename, 
    85                                  lineno=lineno) 
    86         else: 
    87             assert isinstance(source, ast.Node) 
    88             self.source = '?' 
    89             self.code = _compile(ast.Expression(source), filename=filename, 
    90                                  lineno=lineno) 
    91  
    92     def __eq__(self, other): 
    93         return (type(other) == Expression) and (self.code == other.code) 
    94  
    95     def __hash__(self): 
    96         return hash(self.code) 
    97  
    98     def __ne__(self, other): 
    99         return not self == other 
    100  
    101     def __repr__(self): 
    102         return 'Expression(%r)' % self.source 
    103  
    104120    def evaluate(self, data): 
    105121        """Evaluate the expression against the given data dictionary. 
    106122         
     
    110126        return eval(self.code, {'data': data, 
    111127                                '_lookup_name': _lookup_name, 
    112128                                '_lookup_attr': _lookup_attr, 
    113                                 '_lookup_item': _lookup_item}, 
    114                                {'data': data}) 
     129                                '_lookup_item': _lookup_item}, _locals(data)) 
    115130 
    116131 
     132class Suite(Code): 
     133    """Executes Python statements used in templates. 
     134 
     135    >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'}) 
     136    >>> Suite('foo = dict.some').execute(data) 
     137    >>> data['foo'] 
     138    'thing' 
     139    """ 
     140    __slots__ = [] 
     141    mode = 'exec' 
     142 
     143    def execute(self, data): 
     144        """Execute the suite in the given data dictionary. 
     145         
     146        @param data: a mapping containing the data to execute in 
     147        """ 
     148        exec self.code in {'data': data, 
     149                           '_lookup_name': _lookup_name, 
     150                           '_lookup_attr': _lookup_attr, 
     151                           '_lookup_item': _lookup_item}, data 
     152 
     153 
    117154class Undefined(object): 
    118155    """Represents a reference to an undefined variable. 
    119156     
     
    176213        source = '\xef\xbb\xbf' + source.encode('utf-8') 
    177214    return parse(source, mode) 
    178215 
    179 def _compile(node, source=None, filename=None, lineno=-1): 
    180     tree = ExpressionASTTransformer().visit(node) 
     216def _compile(node, source=None, mode='eval', filename=None, lineno=-1): 
     217    tree = TemplateASTTransformer().visit(node) 
    181218    if isinstance(filename, unicode): 
    182219        # unicode file names not allowed for code objects 
    183220        filename = filename.encode('utf-8', 'replace') 
     
    187224    if lineno <= 0: 
    188225        lineno = 1 
    189226 
    190     gen = ExpressionCodeGenerator(tree) 
     227    if mode == 'eval': 
     228        gen = ExpressionCodeGenerator(tree) 
     229        name = '<Expression %s>' % (repr(source or '?').replace("'", '"')) 
     230    else: 
     231        gen = ModuleCodeGenerator(tree) 
     232        name = '<Suite>' 
    191233    gen.optimized = True 
    192234    code = gen.getCode() 
    193235 
     
    195237    # clone the code object while adjusting the line number 
    196238    return new.code(0, code.co_nlocals, code.co_stacksize, 
    197239                    code.co_flags | 0x0040, code.co_code, code.co_consts, 
    198                     code.co_names, code.co_varnames, filename, 
    199                     '<Expression %s>' % (repr(source or '?').replace("'", '"')), 
    200                     lineno, code.co_lnotab, (), ()) 
     240                    code.co_names, code.co_varnames, filename, name, lineno, 
     241                    code.co_lnotab, (), ()) 
    201242 
    202243BUILTINS = __builtin__.__dict__.copy() 
    203244BUILTINS['Undefined'] = Undefined 
     
    231272        key = key[0] 
    232273    try: 
    233274        return obj[key] 
    234     except (KeyError, IndexError, TypeError), e: 
     275    except (AttributeError, KeyError, IndexError, TypeError), e: 
    235276        if isinstance(key, basestring): 
    236277            val = getattr(obj, key, _UNDEF) 
    237278            if val is _UNDEF: 
     
    249290    _visitors = {} 
    250291 
    251292    def visit(self, node): 
     293        if node is None: 
     294            return None 
    252295        v = self._visitors.get(node.__class__) 
    253296        if not v: 
    254             v = getattr(self, 'visit%s' % node.__class__.__name__) 
     297            v = getattr(self.__class__, 'visit%s' % node.__class__.__name__, 
     298                        self.__class__._visitDefault) 
    255299            self._visitors[node.__class__] = v 
    256         return v(node) 
     300        return v(self, node) 
    257301 
     302    def _visitDefault(self, node): 
     303        return node 
     304 
    258305    def visitExpression(self, node): 
    259306        node.node = self.visit(node.node) 
    260307        return node 
    261308 
    262     # Functions & Accessors 
     309    def visitModule(self, node): 
     310        node.node = self.visit(node.node) 
     311        return node 
    263312 
     313    def visitStmt(self, node): 
     314        node.nodes = [self.visit(x) for x in node.nodes] 
     315        return node 
     316 
     317    # Classes, Functions & Accessors 
     318 
    264319    def visitCallFunc(self, node): 
    265320        node.node = self.visit(node.node) 
    266321        node.args = [self.visit(x) for x in node.args] 
     
    270325            node.dstar_args = self.visit(node.dstar_args) 
    271326        return node 
    272327 
    273     def visitLambda(self, node): 
     328    def visitClass(self, node): 
     329        node.bases = [self.visit(x) for x in node.bases] 
    274330        node.code = self.visit(node.code) 
    275331        node.filename = '<string>' # workaround for bug in pycodegen 
    276332        return node 
    277333 
     334    def visitFunction(self, node): 
     335        if hasattr(node, 'decorators'): 
     336            node.decorators = self.visit(node.decorators) 
     337        node.defaults = [self.visit(x) for x in node.defaults] 
     338        node.code = self.visit(node.code) 
     339        node.filename = '<string>' # workaround for bug in pycodegen 
     340        return node 
     341 
    278342    def visitGetattr(self, node): 
    279343        node.expr = self.visit(node.expr) 
    280344        return node 
    281345 
     346    def visitLambda(self, node): 
     347        node.code = self.visit(node.code) 
     348        node.filename = '<string>' # workaround for bug in pycodegen 
     349        return node 
     350 
    282351    def visitSubscript(self, node): 
    283352        node.expr = self.visit(node.expr) 
    284353        node.subs = [self.visit(x) for x in node.subs] 
    285354        return node 
    286355 
     356    # Statements 
     357 
     358    def visitAssert(self, node): 
     359        node.test = self.visit(node.test) 
     360        node.fail = self.visit(node.fail) 
     361        return node 
     362 
     363    def visitAssign(self, node): 
     364        node.nodes = [self.visit(x) for x in node.nodes] 
     365        node.expr = self.visit(node.expr) 
     366        return node 
     367 
     368    def visitDecorators(self, node): 
     369        node.nodes = [self.visit(x) for x in node.nodes] 
     370        return node 
     371 
     372    def visitFor(self, node): 
     373        node.assign = self.visit(node.assign) 
     374        node.list = self.visit(node.list) 
     375        node.body = self.visit(node.body) 
     376        node.else_ = self.visit(node.else_) 
     377        return node 
     378 
     379    def visitIf(self, node): 
     380        node.tests = [self.visit(x) for x in node.tests] 
     381        node.else_ = self.visit(node.else_) 
     382        return node 
     383 
     384    def _visitPrint(self, node): 
     385        node.nodes = [self.visit(x) for x in node.nodes] 
     386        node.dest = self.visit(node.dest) 
     387        return node 
     388    visitPrint = visitPrintnl = _visitPrint 
     389 
     390    def visitRaise(self, node): 
     391        node.expr1 = self.visit(node.expr1) 
     392        node.expr2 = self.visit(node.expr2) 
     393        node.expr3 = self.visit(node.expr3) 
     394        return node 
     395 
     396    def visitTryExcept(self, node): 
     397        node.body = self.visit(node.body) 
     398        node.handlers = self.visit(node.handlers) 
     399        node.else_ = self.visit(node.else_) 
     400        return node 
     401 
     402    def visitTryFinally(self, node): 
     403        node.body = self.visit(node.body) 
     404        node.final = self.visit(node.final) 
     405        return node 
     406 
     407    def visitWhile(self, node): 
     408        node.test = self.visit(node.test) 
     409        node.body = self.visit(node.body) 
     410        node.else_ = self.visit(node.else_) 
     411        return node 
     412 
     413    def visitWith(self, node): 
     414        node.expr = self.visit(node.expr) 
     415        node.vars = [self.visit(x) for x in node.vars] 
     416        node.body = self.visit(node.body) 
     417        return node 
     418 
     419    def visitYield(self, node): 
     420        node.value = self.visit(node.value) 
     421        return node 
     422 
    287423    # Operators 
    288424 
    289425    def _visitBoolOp(self, node): 
     
    308444        node.expr = self.visit(node.expr) 
    309445        return node 
    310446    visitUnaryAdd = visitUnarySub = visitNot = visitInvert = _visitUnaryOp 
    311     visitBackquote = _visitUnaryOp 
     447    visitBackquote = visitDiscard = _visitUnaryOp 
    312448 
    313449    # Identifiers, Literals and Comprehensions 
    314450 
    315     def _visitDefault(self, node): 
    316         return node 
    317     visitAssName = visitConst = visitName = _visitDefault 
    318  
    319451    def visitDict(self, node): 
    320452        node.items = [(self.visit(k), 
    321453                       self.visit(v)) for k, v in node.items] 
     
    381513        return node 
    382514 
    383515 
    384 class ExpressionASTTransformer(ASTTransformer): 
     516class TemplateASTTransformer(ASTTransformer): 
    385517    """Concrete AST transformer that implements the AST transformations needed 
    386     for template expressions. 
     518    for code embedded in templates. 
    387519    """ 
    388520 
    389521    def __init__(self): 
     
    398530        return node 
    399531 
    400532    def visitAssName(self, node): 
    401         self.locals[-1].add(node.name) 
     533        if self.locals: 
     534            self.locals[-1].add(node.name) 
    402535        return node 
    403536 
     537    def visitClass(self, node): 
     538        self.locals.append(set()) 
     539        node = ASTTransformer.visitClass(self, node) 
     540        self.locals.pop() 
     541        return node 
     542 
     543    def visitFor(self, node): 
     544        self.locals.append(set()) 
     545        node = ASTTransformer.visitFor(self, node) 
     546        self.locals.pop() 
     547        return node 
     548 
     549    def visitFunction(self, node): 
     550        self.locals.append(set(node.argnames)) 
     551        node = ASTTransformer.visitFunction(self, node) 
     552        self.locals.pop() 
     553        return node 
     554 
    404555    def visitGenExpr(self, node): 
    405556        self.locals.append(set()) 
    406557        node = ASTTransformer.visitGenExpr(self, node)