Gimp 2.99: how to run in headless/batch mode on a server?

Previously, on a server running Ubuntu 18.04 LTS without Xorg/XWayland/Xvfb, something like

gimp --no-interface -idf --batch-interpreter python-fu-eval -b 'import sys;sys.path=['.']+sys.path;import batch;batch.run()' -b pdb.gimp_quit(1)

would run without issue.

Now, to accommodate another piece of software that requires modern Python, the server was rebuilt with Ubuntu 20.04 LTS, which removed Python 2 support.

The batch script was updated to accommodate the new API in Gimp 2.99 and works locally (where there is Xorg) with Gimp 2.99.14 via flatpak.

On the server, however, flatpak Gimp 2.99.14 does not appear to start without a display. Creating a display with Xvfb and specifying --no-interface yields

$ flatpak run org.gimp.GIMP//beta --no-interface --display=:1 --quit
Unknown option --display=:1

Specifying the display differently does not result in an error, but also does not appear to execute the script when supplied. There is also a difference between the warning messages.

$  DISPLAY=:1 flatpak run org.gimp.GIMP//beta --no-interface -idf  --batch-interpreter python-fu-eval
This is a development version of GIMP.  Debug messages may appear here.

Locally:

 flatpak run org.gimp.GIMP//beta --no-interface -idf  --batch-interpreter python-fu-eval
This is a development version of GIMP.  Debug messages may appear here.


(gimp-2.99:2): Gdk-CRITICAL **: 21:57:42.522: gdk_atom_intern: assertion 'atom_name != NULL' failed

(gimp-2.99:2): Gdk-CRITICAL **: 21:57:42.524: gdk_atom_intern: assertion 'atom_name != NULL' failed
Gtk-Message: 21:57:42.616: Failed to load module "canberra-gtk-module"
Gtk-Message: 21:57:42.617: Failed to load module "pk-gtk-module"
Gtk-Message: 21:57:42.622: Failed to load module "canberra-gtk-module"
Gtk-Message: 21:57:42.622: Failed to load module "pk-gtk-module"

(gimp-2.99:2): Gimp-Core-WARNING **: 21:57:43.275: gimp_finalize: list of contexts not empty upon exit (2 contexts left)

stale context: (null)
stale context: PDB Context

(script-fu:21): LibGimpBase-WARNING **: 21:57:43.289: script-fu: gimp_wire_read(): error

How should Gimp 2.99 be run in headless/batch mode on a server?

With --no-interface, you cannot specify --no-display (from just testing right now, I can reproduce the error message, but it works fine if I remove --no-interface) which makes sense.

Now if you say --no-interface used to run without a display server, it should still work. If it doesn’t, that’s something we’d like to fix.
I’d like to know if this is specific to flatpak (which is primarily made to run GUI software, so maybe something in there assume there must always be one and fail without?) or if there was a change in GIMP (there were quite a few code changes in the start code of GIMP in 2.99, so maybe something is initializing GTK while it shouldn’t have).

Is there a possibility to test a non-flatpak build of 2.99 on your server without display (and without using xvfb of course)? Otherwise Or I could try in a TTY, but you’d have to wait for a few days. :slight_smile:

Flatpak has the bad habit of overriding some environment variables (I don’t know if it’s the case for DISPLAY, but it does override some of the XDG variables). So maybe it’s this. Though once again, it is also probably not expected to be taken into account with --no-interface either. :person_shrugging:

By “locally”, you mean on your desktop machine which runs a display server? Also despite the various shown warnings, the script works?

How should Gimp 2.99 be run in headless/batch mode on a server?

Note that GIMP also has a gimp-console executable which from the start doesn’t link to GTK at all (or any other GUI lib). This would be my first proposition, because even if you could start GIMP from a non-GUI environment in a way so that it doesn’t load GTK, you’d still have to build with a GUI toolkit and the executable will contain more than you’d need (of course you don’t care as you use the flatpak, so you embark all of this anyway! Though if someone wanted a lightweight console-only GIMP for a server, they could just install gimp-console only.

Anyway the problem here is that gimp-console is not present inside the flatpak. The reason was that I assumed that anyone using the flatpak would do so for desktop usage. I didn’t think that people would want to use the flatpak as a server runtime. I’m making a test flatpak containing gimp-console for you to test. The test build is currently running: Keep gimp-console in the flatpak. by Jehan · Pull Request #178 · flathub/org.gimp.GIMP · GitHub

When it’s done, you can install the test build (the build bot will comment on this report and will give the command to run to install the test flatpak; you’ll have to uninstall the beta first since flatpak will say it clashes with this custom build), then run the same flatpak command as you did, except adding --command=gimp-console-2.99. I.e. you’d run:

 flatpak run org.gimp.GIMP//beta --command=gimp-console-2.99 -df  --batch-interpreter python-fu-eval

Hopefully it should run on your server without a display.

Now I’m still interested to fix the --no-interface not needing the display server, because if it used to work, no reason it doesn’t anymore. Also I’d prefer not to make the flatpak bigger (gimp-console really serves the purpose of being run on servers for people who want to install only this). Otherwise it clearly has a redundant usage and the main gimp executable should be enough for a flatpak (it would serve both use cases).

This was the case with Python 2 and the repo version of GIMP (2.8.22 on Ubuntu bionic).

I looked into building from source and found that setting up the dependencies to be a bit involved in terms of changing the server environment and can wait. :slightly_smiling_face:

Your interpretation is correct. The following works despite the warnings and resizes the file “/tmp/full.jpg” to something that is 600 pixels wide and saves the output to “/tmp/600.jpg”.

$ flatpak run org.gimp.GIMP//beta --no-interface -idf  --batch-interpreter python-fu-eval -b "import sys;sys.path=['.']+sys.path;import test;test.run()"

This is a development version of GIMP.  Debug messages may appear here.


(gimp-2.99:2): Gdk-CRITICAL **: 17:37:15.840: gdk_atom_intern: assertion 'atom_name != NULL' failed

(gimp-2.99:2): Gdk-CRITICAL **: 17:37:15.841: gdk_atom_intern: assertion 'atom_name != NULL' failed
Gtk-Message: 17:37:16.051: Failed to load module "canberra-gtk-module"
Gtk-Message: 17:37:16.054: Failed to load module "pk-gtk-module"
Gtk-Message: 17:37:16.094: Failed to load module "canberra-gtk-module"
Gtk-Message: 17:37:16.095: Failed to load module "pk-gtk-module"

(python-eval.py:24): Gdk-CRITICAL **: 17:37:18.207: gdk_atom_intern: assertion 'atom_name != NULL' failed

(python-eval.py:24): Gdk-CRITICAL **: 17:37:18.215: gdk_atom_intern: assertion 'atom_name != NULL' failed
jpeg-save: saving image comment (17 bytes)

(file-jpeg:33): LibGimp-WARNING **: 17:37:23.628: file-jpeg: gimp_flush(): error: Broken pipe
batch command executed successfully

(gimp-2.99:2): Gimp-Core-WARNING **: 17:37:23.698: gimp_finalize: list of contexts not empty upon exit (4 contexts left)

stale context: (null)
stale context: PDB Context
stale context: (null)
stale context: PDB Context

(gimp-2.99:2): GEGL-WARNING **: 17:37:23.703: (../gegl/buffer/gegl-tile-handler-cache.c:1076):gegl_tile_cache_destroy: runtime check failed: (g_queue_is_empty (&cache_queue))
EEEEeEeek! 2 GeglBuffers leaked
To debug GeglBuffer leaks, set the environment variable GEGL_DEBUG to "buffer-alloc"

(script-fu:21): LibGimpBase-WARNING **: 17:37:23.954: script-fu: gimp_wire_read(): error

$ cat test.py 
#!/usr/bin/env python

import gi;
from gi.repository import Gimp;
from gi.repository import GObject,Gio;

def run():
    input_filepath = "/tmp/full.jpg"
    output_filepath = "/tmp/600.jpg"
    desired_width = 600

    file_input = Gio.file_new_for_path(input_filepath);
    file_output = Gio.file_new_for_path(output_filepath);

    image = Gimp.get_pdb().run_procedure('gimp-file-load', [GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE), GObject.Value(Gio.File, file_input)])

    if image.index(0) != Gimp.PDBStatusType.SUCCESS:
      exit()

    image = image.index(1)
    layer = image.pick_correlate_layer(0,0)

    image_width = image.get_width()
    image_height = image.get_height()

    if image_width > desired_width:
        ratio = (desired_width + 0.0) / (image_width + 0.0)
        image_width = desired_width
        image_height = int(ratio * image_height)
        if image_height == 0:
            image_height = 1

    Gimp.get_pdb().run_procedure('gimp-context-set-interpolation', [GObject.Value(GObject.TYPE_INT, 3)]) # no halo

    Gimp.get_pdb().run_procedure('gimp-image-scale', [image, GObject.Value(GObject.TYPE_INT, image_width), GObject.Value(GObject.TYPE_INT, image_height)])

    Gimp.get_pdb().run_procedure('file-jpeg-save', [GObject.Value(Gimp.RunMode, Gimp.RunMode.NONINTERACTIVE), image, GObject.Value(GObject.TYPE_INT, 1),GObject.Value(Gimp.ObjectArray, Gimp.ObjectArray.new(Gimp.Drawable, [layer], False)), GObject.Value(Gio.File, file_output), GObject.Value(GObject.TYPE_DOUBLE, 0.85), GObject.Value(GObject.TYPE_DOUBLE, 0), GObject.Value(GObject.TYPE_BOOLEAN, True), GObject.Value(GObject.TYPE_BOOLEAN, False), GObject.Value(GObject.TYPE_BOOLEAN, False), GObject.Value(GObject.TYPE_INT, 2), GObject.Value(GObject.TYPE_BOOLEAN, True), GObject.Value(GObject.TYPE_INT, 0), GObject.Value(GObject.TYPE_INT, 0)])

Thanks for generating a test build. gimp-console also appears to be interested in GTK:

$ flatpak run --command=gimp-console-2.99 org.gimp.GIMP//beta  -df  --batch-interpreter python-fu-eval

(gimp-console-2.99:2): GLib-GObject-WARNING **: 01:30:05.346: invalid unclassed type 'GimpCoreApp' in class cast to 'GObject'

(gimp-console-2.99:2): GLib-GObject-CRITICAL **: 01:30:05.347: g_object_class_override_property: assertion 'G_IS_OBJECT_CLASS (oclass)' failed

(gimp-console-2.99:2): GLib-GObject-CRITICAL **: 01:30:05.347: g_object_class_override_property: assertion 'G_IS_OBJECT_CLASS (oclass)' failed

(gimp-console-2.99:2): GLib-GObject-CRITICAL **: 01:30:05.348: g_object_class_override_property: assertion 'G_IS_OBJECT_CLASS (oclass)' failed

(gimp-console-2.99:2): GLib-GObject-CRITICAL **: 01:30:05.348: g_object_class_override_property: assertion 'G_IS_OBJECT_CLASS (oclass)' failed

(gimp-console-2.99:2): GLib-GObject-CRITICAL **: 01:30:05.348: g_object_class_override_property: assertion 'G_IS_OBJECT_CLASS (oclass)' failed

(gimp-console-2.99:2): GLib-GObject-CRITICAL **: 01:30:05.348: g_object_class_override_property: assertion 'G_IS_OBJECT_CLASS (oclass)' failed
This is a development version of GIMP.  Debug messages may appear here.


luajit: /app/share/lua/5.1/lgi/namespace.lua:189: gtk_init_check() failed
stack traceback:
	[C]: in function 'error'
	/app/share/lua/5.1/lgi/namespace.lua:189: in function 'require'
	/app/share/lua/5.1/lgi/namespace.lua:170: in function '__index'
	.../org.gimp.extension.goat-exercises/goat-exercise-lua.lua:28: in main chunk
	[C]: at 0x565141e142e0
GIMP-WARNING: gimp-console-2.99: gimp_wire_read(): error


(script-fu:393): LibGimpBase-WARNING **: 01:30:13.510: script-fu: gimp_wire_read(): error

and

$ flatpak run --command=gimp-console-2.99 org.gimp.GIMP//beta  -df  --batch-interpreter python-fu-eval -b "import sys;sys.path=['.']+sys.path;import test;test.run()"

does not produce the expected output file.