Script editor - jython: cannot reassign parameter

Can somebody reproduce this?

# @File(label='select dir', style='directory') input_dir

def run():
	input_dir = input_dir.getPath()

if __name__ == '__main__':
	run()

Throws:

Traceback (most recent call last):
  File "/Users/meyenhof/Desktop/New__.py", line 8, in <module>
    run()
  File "/Users/meyenhof/Desktop/New__.py", line 4, in run
    input_dir = input_dir.getPath()
UnboundLocalError: local variable 'input_dir' referenced before assignment

	at org.python.core.Py.UnboundLocalError(Py.java:289)
	at org.python.core.PyFrame.getlocal(PyFrame.java:240)
	at org.python.pycode._pyx1.run$1(/Users/meyenhof/Desktop/New__.py:5)
	at org.python.pycode._pyx1.call_function(/Users/meyenhof/Desktop/New__.py)
	at org.python.core.PyTableCode.call(PyTableCode.java:167)
	at org.python.core.PyBaseCode.call(PyBaseCode.java:124)
	at org.python.core.PyFunction.__call__(PyFunction.java:403)
	at org.python.pycode._pyx1.f$0(/Users/meyenhof/Desktop/New__.py:8)
	at org.python.pycode._pyx1.call_function(/Users/meyenhof/Desktop/New__.py)
	at org.python.core.PyTableCode.call(PyTableCode.java:167)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1386)
	at org.scijava.plugins.scripting.jython.JythonScriptEngine.eval(JythonScriptEngine.java:75)
	at org.scijava.script.ScriptModule.run(ScriptModule.java:177)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:167)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:126)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:65)
	at org.scijava.thread.DefaultThreadService$2.call(DefaultThreadService.java:191)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

Personally I did not expect this behaviour. I would have though that I can reassign any variable.

Try to initialize input_dir variable.

in the run() function?
In that case I overwrite what the Parameter parser writes in the input_dir… as expercted…

Yes, initializing a new input_dir variable would contort the purpose of the script parameter.

The problem in your case is that you’re trying to define a local variable in the run() function. You can solve it by either:

  • declaring the variable global inside your function:

    # @File(label='select dir', style='directory') input_dir
    
    def run():
    	global input_dir
    	input_dir = input_dir.getPath()
    
    if __name__ == '__main__':
    	run()
    
    

    or:

  • using a different variable name:

    # @File(label='select dir', style='directory') input_dir
    
    def run():
    	my_dir = input_dir.getPath()
    
    if __name__ == '__main__':
    	run()
    

yes, that’s the solution I already opted for.
But still encountering the situation in my initial example and the resulting error messages left me confused before I figured out, that on should not consider input_dir as a variable if it was used in a @Parameter comment in the head of the script. This might be coherent from a Java perspective, but not from a Python perspective, where I can reassign pretty much anything to everything.
Why I bring this up is because it felt somewhat unexpected to me. Maybe it would be worthwhile to make a not in the wiki?

I have to disagree here. Your issue is a pure python issue, as illustrated by the following example that fails in the same way without using any script parameters (and also if you try running it in Python outside ImageJ):

my_var = "Foo"

def run():
	my_var = my_var + "Bar"

if __name__ == '__main__':
	run()

You should simply not confuse global and local variables in Python.


Yes, it would be great if you could add a note where you see fit, to prevent others from running into the same issues. :slight_smile:

1 Like

darn! thanks for disagreeing :slight_smile: I was not aware that you can only modify the global variable if you declare it global in the function itself. Accessing obviously is always possible. I was simply avoiding global variables.
Well, better late than never…
But you are absolutly right that this is consistant with pythons normal behaviour (I might engage them in a discussion about the intuitive comprehension and readability of that part ;))

1 Like

It occurred to me that I forgot to mention a third option:

  • include the variable in the function call:

    # @File(label='select dir', style='directory') input_dir
    
    def run(input_dir):
        input_dir = input_dir.getPath()
    
    if __name__ == '__main__':
        run(input_dir)
    

I think this would be the closest to your original use case, and it explicitly defines the parameter dependency of the run() function.

2 Likes

This looks neat to me. I think I’ll adopt this pattern in the future. Thanks again for elaborating @imagejan.