mcneel / ghpython

A Grasshopper component for Rhino.Python
http://www.food4rhino.com/project/ghpython
117 stars 34 forks source link

Variables deleted from script still exist in globals() #53

Closed AndersDeleuran closed 6 years ago

AndersDeleuran commented 10 years ago

I have been wondering about this one for some time now actually. If you create a variable, run the script and subsequently delete the variable. The variable still exist in globals().

Is this by design? If so, is there a good reason for this that I might be missing? If not, I propose this issue be considered a bug.

Thanks again for all the hard work,

Anders

piac commented 10 years ago

Hi Anders

you noticed that variables in general stick within globals from one iteration into the other once they are declared, right? This is because the editor is connected to one single interpreter, and does not reset its environment all the time. This allows the scripter to check previous variables, etc.

However, in the case of the variable name of the inputs or outputs, I'd agree that this can be considered a bug. After all, the user did not add that variable, so might expect that variable to disappear. I'll add this to the list of things to work on, thanks

AndersDeleuran commented 10 years ago

I think so. Once a variable is declared it will remain in globals. Even after the variable no longer exists in the script or it has been renamed. The only way to get rid of the "phantom variable" is to go:

del variableName

Alternatively close/open the Grasshopper definition. It is not a major problem, but can lead to debugging issues not revealed until you close/open the definition. Moreover no other Python environment I have used (including the regular EditPythonScript editor) displays this behaviour.

/Anders

piac commented 10 years ago

Yes, I will consider this suggestion and would like to hear more people's opinion. About other environments, I think it's more a perspective. Like this, what we have is just a Python interpreter that is running, and that is attached to another (Grasshopper) interpreter via its variable names. The Python interactive interpreter definitely displays this behavior. See http://blog.datasingularity.com/?p=134 for an example

Alternatively, we could:

  1. force people to write code into a class, and have it similarly structured as the C#/Vb.Net components. This is the normal OOP approach. Note that C# and Vb.Net do not allow code outside of classes, so they are forced to that, but we have the choice. It's a bit scarier for beginners, but it might be a good idea, nevertheless. This might be structured in a way that makes it (for the most part) compatible with existing scripts
  2. have the interpreter delete the scope, remove all variables, or do the fasted among similar operations at the end of each single Python interpreter -> Grasshopper interpreter transition
  3. keep all as it is, and suggest users that do not want to keep variables (it's actually useful at times) to run the following code. This will not break scripts:
for uniquevar in [var for var in globals().copy() if var[0] != "_"]:
    del globals()[uniquevar]

I do not really have a preference myself, as all these options have advantages and disadvantages. For some advanced usages, 1. is best for advanced usages, 2. is often more practical, and 3. allows to do some operations like counters in a very easy way. :)

AndersDeleuran commented 10 years ago

Hi Giulio,

I see that you clearly have given this a lot more thought than I had :)

Didn't consider how having variables stick around might actually be seen as a feature (case in point for counters etc.).

Would say to just leave it as is for now. I understand that you guys are working on consolidating the programming environments for Grasshopper. So I'm sure there are much more important issues to deal with.

Thanks for the thorough reply,

Best,

Anders

piac commented 10 years ago

I will close this for now, mark it as "Needs testing" -- and will still like to hear your and everybody else's impression on this. You, me or everybody else might also just reopen the case once we feel this is slowing down too many people or is too counterintuitive. Thanks,

Giulio

Giulio Piacentino for Robert McNeel & Associates giulio@mcneel.com

mostaphaRoudsari commented 10 years ago

I vote for number 3. It could be even wrapped in a method similar to what MATLAB has as clear (http://www.mathworks.com/help/matlab/ref/clear.html).

I agree that applying number 1 is tricky since many of the beginner users has no idea about the concept of Class and Function and I think it is a good feature to let them script for awhile without knowing that concepts.

AndersDeleuran commented 10 years ago

Perhaps simply adding a button or a menu item to the editor for flushing any unused phantom variables would be an option. Guess this is kind of like suggestion 3, but with handy gui functionality ;)

bengolder commented 10 years ago

I've been wondering about this for a while. I'm a fan of 2, because it is the closest match to the default behavior when running a python module. I've been assuming so far that the default behavior, (option 3) was a bug that would be eventually fixed. I would expect the python interpreter to be connected to the Grasshopper solution, in the sense that a grasshopper solution re-runs the python module written in the grasshopper component, with fresh globals.

It seems like the question at hand is "what is the most intuitive and expected default behavior?". To me this is definitely 2. Though it apparently has interactive behavior, GhPython does not create the same interface that is used in other interactive interpreters, and instead appears to be a text editor that runs python modules. When running a python module, python is typically launched each time the module is run, with a fresh set of global variables. So option 2 is the closest to common python behavior.

Using the global scope to store variables across Grasshopper solutions or for data persistence seems like a messy strategy. There's plenty of ways to store data even if the scope is refreshed: the Rhino or GH documents, or writing to files.

It seems like the confusion of 3 outweighs the advantages I can imagine, while 2 seems like a very straightforward expected behavior. Could 3 be turned on with a switch, with 2 as the default behavior?

mostaphaRoudsari commented 10 years ago

Now that I read @bengolder's comment I think I also agree that number 2 is more close to what a Grasshopper user would assume. I like the option of having a switch for number 3, and having 2 as the default behavior.

saeranv commented 10 years ago

Another vote for 2 as default behaviour. I as well assumed that refreshing the grasshopper component would reset the global variables, because that's the behaviour I'm used to with other python editors.

I'm a little confused now about what exactly each ghPython component is. Is it correct to say they are each self-contained editors that share a single Python interpreter?

piac commented 10 years ago

I am considering an addition to the current behavior to solve this.

Unless we find that it is a bad decision for some other reason, I think this proposal will match both expectations without needing special options. When the user just Tests or Runs the script from the editor, the scope is cleared of all variables. When the script is repeated automatically, to update the solution, nothing special happens.

This will avoid the problem of referencing a name from an old version of the script, and allow to see values across updates. Will this work for you?

(to answer the interpreter question) Rhino only contains one interpreter (it would be very wasteful if it contained many) that is shared by all editors. The editor is just a viewer of text. The "text" code is run in different scopes. This is why you can use modules from the Rhino editor in Grasshopper and viceversa, and share objects via sticky, for example.

mostaphaRoudsari commented 10 years ago

@piac sounds like a really good idea. I also tried your suggestion to remove the global vars. The only issue was that it also removes ghenv which I had to take care of. Except that it does what I need. Thanks for the great work!

saeranv commented 10 years ago

@piac that solution makes sense to me.

Also thank you for answering my question re: the interpreter. So are these the scopes we have to be aware of?

  1. global namespace across different editors i.e. sticky
  2. global namespace specific to each module: i.e. Grasshopper input parameters
  3. local variables specific to our functions.
piac commented 10 years ago

@saeranv There are lots of scopes in Python. The content of a function creates a scope. A module is in its own scope. So variable names don't get mixed up. http://stackoverflow.com/questions/146359/python-scope

There can be an undefined number of nested scopes. In the big picture, it is not the editor that defines scopes. It's modules that do that. You can think of any editor window as its own "module", just with no physical location on the hard drive. I hope this helps,

Giulio

bengolder commented 10 years ago

@piac thank you! This is great.

AndersDeleuran commented 10 years ago

I agree. This looks like a really good solution. Thanks again Giulio..

saeranv commented 10 years ago

@piac Thanks for patiently explaining all this. It helps a lot.