<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 6 June 2018 at 15:31, Rob Cliffe via Python-Dev <span dir="ltr"><<a href="mailto:python-dev@python.org" target="_blank">python-dev@python.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
  

    
  
  <div text="#000000" bgcolor="#FFFFFF">
    <p>Is this a bug or a feature?<br>
      Consider the following program:<br>
      <br>
      # TestProgram.py<br>
      def Test():<br>
        # global x<br>
          x = 1<br>
          exec(compile('print([x+1,x+2])<wbr>', 'MyTest', 'exec'))<br>
          exec(compile('print([x+i for i in range(1,3)])', 'MyTest',
      'exec'))<br>
      Test()<br>
      <br>
      In Python 2.7.15 the output is<br>
      <br>
      [2, 3]<br>
      [2, 3]<br>
      <br>
      In Python 3.6.5 the output is<br>
      [2, 3]<br>
      Traceback (most recent call last):<br>
        File "TestProgram.py", line 7, in <module><br>
          Test()<br>
        File "TestProgram.py", line 6, in Test<br>
          exec(compile('print([x+i for i in range(1,3)])', 'MyTest',
      'exec'))<br>
        File "MyTest", line 1, in <module><br>
        File "MyTest", line 1, in <listcomp><br>
      NameError: name 'x' is not defined<br>
      <br>
      If the "global x" declaration is uncommented, this "fixes" the
      Python 3.6.5 behaviour,<br>
      i.e. no error occurs and the output is the same as for Python
      2.7.15.<br>
      <br>
      <b>In other words, it looks as if in Python 3.6.5, the compiled
        list comprehension</b><b><br>
      </b><b>can "see" a pre-existing global variable but not a local
        one.</b></p></div></blockquote>Yes, this is expected behaviour - the two-namespace form of exec (which is what you get implicitly when you use it inside a function body) is similar to a class body, and hence nested functions (including the implicit ones created for comprehensions) can't see the top level local variables.</div><div class="gmail_quote"><br></div><div class="gmail_quote">You can override that and force the use of the single-namespace form by passing the locals() namespace into exec() explicitly:</div><div class="gmail_quote"><br></div><div class="gmail_quote">    def explicit_local_namespace():<br></div><div class="gmail_quote">        x = 1<br></div><div class="gmail_quote">        exec(compile('print([x+i for i in range(1,3)])', 'MyTest',
      'exec'), locals())</div><div class="gmail_quote">    explicit_local_namespace()</div><div class="gmail_quote"><br></div><div class="gmail_quote">(Note: you'll then need to use collections.ChainMap instead of separate locals and globals namespaces if you want the exec'ed code to still be able to see the module globals in addition to the function locals)<br></div><br><div class="gmail_quote">Cheers,</div><div class="gmail_quote">Nick.<br clear="all"></div><br>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>   |   Brisbane, Australia</div>
</div></div>