Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Gimp 3.0.4 python batch interpreter oddities
#1
Hi,

I use the python-fu-eval batch interpreter on GIMP 3.0.4 AppImage and have noticed some unexpected behaviour. As a trivial example,


User:~$ ~/AppImages/gimp/GIMP-3.0.4-x86_64.AppImage -i --quit --batch-interpreter=python-fu-eval -b 'if True: a="something"; print(a);'
something
batch command executed successfully


But if the "if" statement comes after the declaration of variable "a"

User:~$ ~/AppImages/gimp/GIMP-3.0.4-x86_64.AppImage -i --quit --batch-interpreter=python-fu-eval -b 'a="something";if True: print(a);'
batch command experienced a calling error:
Traceback (most recent call last):
  File "/tmp/.mount_GIMP-3bPBAmP/usr/lib/x86_64-linux-gnu/gimp/3.0/plug-ins/python-eval/python-eval.py", line 42, in code_eval
    exec(code, globals())
  File "<string>", line 1
    a="something";if True: print(a);
                             ^^
SyntaxError: invalid syntax

Similar problems arise with a "with" statement.

I don't understand this. Has anyone else noticed this sort of behaviour?

My OS is linux 6.1.0-37-amd64

Thanks.
Reply
#2
I get the very same behavior if use:
Code:
>python3 -c 'a="something";if True: print(a);'
 File "<string>", line 1
   a="something";if True: print(a);
                 ^^
SyntaxError: invalid syntax

(and same behavior as with Gimp for the first case, too).

I think  you are misunderstanding the power of the semicolon in Python, it is not fully equivalent to a line feed. I refer you to the doc:

Quote:A suite is a group of statements controlled by a clause. A suite can be one or more semicolon-separated simple statements on the same line as the header, following the header’s colon, or it can be one or more indented statements on subsequent lines.
(Emphasis mine)

In other words, you can use the semicolon to make a one-liner with several assignments or even function calls (simple statements), but you cannot separate an if clause (compound statement)  from the rest with a semicolon.

Note however that the conditional assignment (Python's equivalent to the ternary operator in C) in a simple statement. So while you cannot write:

Code:
condition=True; if condition: x=foo ; else x=bar; print(x)

you can write:

Code:
condition=True; x="foo" if condition else "bar"; print(x)
Reply
#3
Thank you very much for your explanation.

How should I change the failing example to work correctly? I don't know how to terminate a statement with other than ; in this context. This does not work either,

User:~$ ~/AppImages/gimp/GIMP-3.0.4-x86_64.AppImage -i --quit --batch-interpreter=python-fu-eval -b 'a="something"' -b 'if True: print(a);'
batch command executed successfully
batch command experienced a calling error:
Traceback (most recent call last):
  File "/tmp/.mount_GIMP-3nIIdNe/usr/lib/x86_64-linux-gnu/gimp/3.0/plug-ins/python-eval/python-eval.py", line 42, in code_eval
    exec(code, globals())
  File "<string>", line 1, in <module>
NameError: name 'a' is not defined
Reply
#4
I don't think there is a solution by just changing the form of the python code (or at least it depends what your actual code wants to do, there is no general solution, and there maybe no solution to many cases). A technique I use since 2.x is to have the -b run import some_code; some_code.execute() and have a separate some_code python module (a bit clumsy but works in Windows). In Linux, you can stuff the python code, newlines and all, in a variable with a "heredoc" and use that variable with the -b

Code:
#! /bin/bash

# Read python code into a variable
# -d '' below necessary to keep the newlines.
read -r -d '' python <<'EOF'
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp

print(f"Running batch in Gimp {Gimp.version()}")  # Just shows that we imported Gimp correctly

if True:
   print("This works!!!")
EOF

# Call Gimp passing the variable's contents in a `-b` argument
gimp -idf --batch-interpreter=python-fu-eval -b "$python" --quit
Reply
#5
(08-10-2025, 08:22 AM)Ofnuts Wrote: I don't think there is a solution by just changing the form of the python code (or at least it depends what your actual code wants to do, there is no general solution, and there maybe no solution to many cases). A technique I use since 2.x is to have the -b run import some_code; some_code.execute() and have a separate some_code python module (a bit clumsy but works in Windows). In Linux, you can stuff the python code, newlines and all, in a variable with a "heredoc" and use that variable with the -b

Code:
#! /bin/bash

# Read python code into a variable
# -d '' below necessary to keep the newlines.
read -r -d '' python <<'EOF'
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp

print(f"Running batch in Gimp {Gimp.version()}")  # Just shows that we imported Gimp correctly

if True:
   print("This works!!!")
EOF

# Call Gimp passing the variable's contents in a `-b` argument
gimp -idf --batch-interpreter=python-fu-eval -b "$python" --quit

Thanks for your response, Offnuts. I had imagined that the batch-interpreter would interpret consecutive commands as though they were typed within the Gimp Python console, but that is obviously not the case. I usually want to run a python script with input arguments and one simple work-around I have used is, for example,

~/AppImages/gimp/GIMP-3.0.4-x86_64.AppImage -i --quit --batch-interpreter=python-fu-eval -b 'with open("path_to_prog.py") as fid:    arg1,arg2,arg3=1,2,"/home/user/.config/GIMP/3.0/progs/prog.py";   exec(fid.read())'

where in this case three example arguments are defined. The called program prog.py can use these arguments directly as they are global. This little prog.py

#! /usr/bin/env python3
#
import gi
gi.require_version("Gimp","3.0")
from gi.repository import Gimp
#
print(f"These are the arguments {arg1}, {arg2}, {arg3}.")

would print the arguments. The declaration of the arguments has to come after the "with" statement for the reasons discussed above. The arguments could be defined within a shell script if they need to be determined programatically e.g.

#! /bin/sh
#
pathtoprog="\"/home/user/.config/GIMP/3.0/progs/prog.py\""
arg1=1
arg2=2
arg3="\"/home/user/GIMP/3.0/configfile.txt\""
#
~/AppImages/gimp/GIMP-3.0.4-x86_64.AppImage -i --quit --batch-interpreter=python-fu-eval -b "with open($pathtoprog) as fid:   arg1,arg2,arg3=$arg1,$arg2,$arg3;   exec(fid.read())"

This is a bit messy. Is there a neater way to do it?
Reply
#6
If you don't put quotes around the marker of the heredoc (EOF here) then bash substitution applies, so you can set variables in your code from bash variables:

Code:
#! /bin/bash

string="the quick brown fox jumps over the lazy dog"
step=4

read -r -d '' python << EOF
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp

string="$string"
step=$step

print(f"Running batch in Gimp {Gimp.version()}")  # Just shows that we imported Gimp correctly

for l in range(0,len(string)+step,step): # len(string)+step insures that we print the full string last
    print(f"{string[:l]}")
EOF

gimp -idf --batch-interpreter=python-fu-eval -b "$python" --quit

Also, since Gimp makes a special case of a single dash - used as an argument to -b to mean "read code  from stdin", you can feed the heredoc directly to Gimp (but the form with a variables could be easier to debug because you can check the result of substitutions before you pass it to Gimp:

Code:
#! /bin/bash

string="the quick brown fox jumps over the lazy dog"
step=4

gimp -idf --batch-interpreter=python-fu-eval -b - --quit << EOF
gi.require_version('Gimp', '3.0')
from gi.repository import Gimp

string="$string"
step=$step

print(f"Running batch in Gimp {Gimp.version()}")  # Just shows that we imported Gimp correctly

for l in range(0,len(string)+step,step): # len(string)+step insures that we print the full string last
    print(f"{string[:l]}")
EOF
Reply


Forum Jump: