The problem is not that I can't modify the locals() dictionary. It's a
dictionary like any other. The problem is that any modifications to the
locals() dictionary are not reflected back in the local variables of the
function 'foo'. Your code below updates the values in the vars
dictionary, yes. However, I would like to run the tag in the context of
the locals of the foo function, not a copy of it. I want the local 'x'
(a variable in the Spyce script, now transformed into a Python function)
to be updated to the value '2' (by the code of the active tag
implementation). Otherwise, the information flow is one-way. That is,
the tag implementations can read the script variables, but they can't
modify them. I believe that this can lead to confusion and, in that
case, it's better to have two explicit variable contexts. You can always
copy information from one to the other, if you like. If there was some
way to unify these two variable spaces, I most certainly would. My hope,
again, is that there is some other transformation of a Spyce script into
Python code, which is both efficient and lends the variables of the
script to be modifiable by other code blocks. I am beginning to think
that the only way is to implicitly make all Spyce variables global by
performing some code analysis. Making implicit scoping changes, however,
can only lead to trouble down the road and cause unexpected collisions.
The Python locals() dictionary really should be modifiable! ;)
All the best,
On Fri, 28 Nov 2003, Ken Kinder wrote:
>Rimon, thanks for the informative explanation.
>I'm sure you've already thought of this, but is there a reason you
>can't just copy the locals dictionary?
> >>> from copy import copy
> >>> def foo():
> ... x = 1
> ... vars = copy(locals())
> ... exec 'x = 2' in vars
> ... print x, vars['x']
> >>> foo()
> 1 2
>Rimon Barr said:
>> Hi Ken,
>> Yes, Conan is correct. The problem is as follows. Your Spyce script
>> becomes translated into a Python function before being executed. You can
>> actually see this script by running: "spyce -c <script>". The
>> consequence of this transformation is that all your script variables are
>> within the local scope of the function. You can access this dictionary
>> of variables using the locals() function. However, if you read:
>> you'll notice that this dictionary is read-only. Thus, I can exec your
>> tag implementation within the context of this locals() dictionary, but
>> the information-flow is one-way only. That is, your tag functions can
>> read variables from the Spyce script. However, if they change any
>> variables, those changes will not be reflected in the context of the
>> script. That's strange, and so I've opted for two different and explicit
>> contexts. Moreover, I've spoken with Guido about this, and he has told
>> me that locals() is unfortunately going to stay this way for performance
>> However, there may be other transformations of the Spyce script to
>> Python code that do not suffer this problem. In other words, they may
>> not rely on locals(). I have not come up with a transformation that is
>> satisfactory. One possibility is to use global variables instead of
>> local ones, since the dictionary returned by globals() can be safely
>> modified. This could be performed explicitly by the user. In other
>> words, the user would simply write: 'global x, y, z...' for all
>> variables that they would like to modify using tags. They would use
>> globals both in their Spyce script and in their tag implementations.
>> Another alternative is to parse and analyze the Python code, find all
>> assignments to local variables and implicitly insert global bindings.
>> I'm not too happy with this alternative, however, because it polutes the
>> global namespace. The global namespace currently stores the various
>> Spyce modules, and it's easy to clean up after each request. Why do we
>> need to clean up the globals dictionary after each request, you ask?
>> Well, because the global namespace is bound to the code block at compile
>> time. The only way to throw away the global namespace is to reparse the
>> Python code on each request, and that's slow. It's all a bit
>> complicated, I admit. But that's Python for you. ;) If you are
>> interested, you can read:
>> http://www.python.org/doc/current/ref/naming.html and
>> So, what we need is a transformation from Spyce code to Python code that
>> does not suffer from the read-only locals(). Perhaps we could use nested
>> functions. In other words, perhaps we could nested in the tag
>> implementations directly rather than invoking them from a different
>> module. Perhaps we could use Python classes in some interesting way. The
>> problem boils down to this: when you run the following piece of Python
>>> def foo():
>>> x = 1
>>> exec "x=2" in locals()
>>> print x
>> ... you will see it print "1".
>> For now, Conan has given you the exact work-around, albeit akward. That
>> is, the tag variables and the script variables occupy two different
>> contexts and you have to explicitly work with both. I'm all ears for
>> other suggestions on how to aleviate this deficiency that we've
>> inherited from the Python implementation.
>> All the best,
>> ----- Original Message -----
>> From: "Conan Albrecht" <conan_albrecht@...>
>> To: "Ken Kinder" <ken@...>
>> Cc: <spyce-users@...>
>> Sent: Thursday, November 27, 2003 1:36 AM
>> Subject: Re: [Spyce-users] Q: Tags, self.contextEval, Namespaces
>>> This is one of the biggest known issues with taglibs in spyce (IMHO).
>>> I've talked at length with Rimon about it, but the problem is based in
>>> the way python does contexts: globals() and locals() are read only.
>>> Spyce can't modify them! This needs resolving, but it would require
>>> running everything in a custom context and would cause ripples
>>> throughout the code.
>>> The workaround for now is as follows:
>>> [[ food = 'spam and eggs' ]]
>>> [[ taglib.context['food'] = food ]]
>>> <mytaglib.mytag foo="=food">
>>> Note how I set the 'food' variable in the taglib context to the local
>>> context's food variable.
>>> There's got to be a way to do this without the middle statement and
>>> without causing lots of ripples in the codebase. I'm all ears as I've
>>> been using taglibs frequently.
>>> Hope this helps.
>>> On Nov 26, 2003, at 11:08 PM, Ken Kinder wrote:
>>> > I'm struggling a bit here. Let's say I wanted my Spyce code to
>>> define a variable, then pass it to a tag...
>>> > [[ food = 'spam and eggs' ]]
>>> > <mytaglib:mytag foo="=food ">
>>> > Then in my tag lib...
>>> > def begin(self, foo):
>>> > foo = self.contextEval(foo)
>>> > This will produce a NameError. It seems that the "tag context
>>> > dictionary"
>>> > described in the documentation does not include the current spy file
>>> namespace. Can anyone tell me how to get the food variable's data
>>> from within the tag's begin method?
>>> > Thanks
>>> > Ken