Re: Weird scoping issue in options file
Michael Haggerty <[email protected]>
| Newsgroups | gmane.comp.version-control.subversion.cvs2svn.devel |
|---|---|
| Message-ID | <[email protected]> |
Greg Ward wrote:
> I want to write a custom subclass in a cvs2svn options file, say for
> example this:
>
> """
> class MyOutputOption(NewRepositoryOutputOption):
> def __init__(self, *args, **kwargs):
> NewRepositoryOutputOption.__init__(self, *args, **kwargs)
> # line 135 in stack trace below
> """
>
> [...]
> NameError: global name 'NewRepositoryOutputOption' is not defined
> """
>
> WTF? Python is OK with me using 'NewRepositoryOutputOption' at file
> scope to define the subclass, but *not* OK with it at function scope?
> Colour me confused. A gross workaround is
How can this be? The NameError occurs when the __init__() method is
*defined*; the code *within* __init__() is not even executed at this
time. Could it have something to do with the way that local variables
are optimized by the Python interpreter (it tries to allocate fixed
slots to them rather than doing a name lookup each time the variable is
accessed)?
I cannot reproduce a simple test case similar to your first one, but the
attached file demonstrates similar NameErrors. Cases 1 and 4 produce
NameErrors; cases 2 and 3 execute without errors.
I think the problem is that the local context that is used for the outer
scope of the execfile'd file is not inherited by the class scope. The
file-level imports go into the locals dictionary, not the globals
dictionary, and so are not visible within nested scopes.
> Anyways: this all smells like there's something funny with the
> execfile() call in run_options.py. And indeed I can make my problems
> disappear with this patch:
>
> --- a/cvs2svn_lib/run_options.py
> +++ b/cvs2svn_lib/run_options.py
> @@ -1018,12 +1018,11 @@
>
> Store the run options to SELF."""
>
> - g = {}
> - l = {
> + g = {
> 'ctx' : Ctx(),
> 'run_options' : self,
> }
> - execfile(options_filename, g, l)
> + execfile(options_filename, g)
>
> def usage(self):
> self.parser.print_help()
>
> ...or, in English, make 'ctx' and 'run_options' global variables in
> the options file rather than local variables.
>
> That's all very nice, but does anyone understand what is going on
> here? I thought I knew Python pretty well, but I don't understand why
> this fix works. Apparently I don't know everything there is to know
> about execfile()!
I think that your fix has nothing to do with where ctx and run_options
are stored. Rather it has to do with the fact that you pass only one
dictionary to execfile(), which means that a single dictionary is used
for both globals and locals. Therefore the file-level imports end up
(also) in the globals dictionary, and are visible from within nested scopes.
I have committed your suggested patch (as trunk r4879), since it seems
like the right thing to do (even aside from the fact that it actually
works).
Michael
------------------------------------------------------
http://cvs2svn.tigris.org/ds/viewMessage.do?dsForumId=1667&dsMessageId=2385839
To unsubscribe from this discussion, e-mail: [[email protected]].
scope.py
(text/x-python, 292 B)
#! /usr/bin/python
code = """\
x = 1
class C:
x
"""
case = 1
if case == 1:
# NameError:
exec code in {}, {}
if case == 2:
# Works:
exec code in {}
if case == 3:
# Works:
exec code
if case == 4:
def blah():
exec code
# NameError:
blah()