Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new release when & ship with libvips? #443

Open
SteveHawk opened this issue Jan 4, 2024 · 14 comments
Open

new release when & ship with libvips? #443

SteveHawk opened this issue Jan 4, 2024 · 14 comments

Comments

@SteveHawk
Copy link

Hi, I'm just wondering, is there going to be a new release of pyvips soon?

libvips 8.15 introduces highway for simd which is exciting, however current pyvips v2.2.1 on PyPI only supports libvips up to 8.13. Master branch of pyvips does work with 8.15 in my setup, but it would be really nice to have a stable version to lock in.

Oh and, will it be possible for pyvips to ship with prebuilt libvips in the future? As my understanding, net-vips and sharp has been doing this for years, I tried the prebuilt package from kleisauke/libvips-packaging with pyvips and it works like a charm. Using pyvips without needing to install many dozens of system dependencies is a lifesaver in deployment.

Thanks!

@jcupitt
Copy link
Member

jcupitt commented Jan 4, 2024

Hi @SteveHawk,

pyvips works with any libvips version -- it introspects the libvips binary and exposes the API it finds. pyvips v2.2.1 should work fine with 8.15 and 8.16.

Yes, we've talked about including a binary, and you're right it'd be pretty simple. Someone just needs to do the work :( pyvips still uses the prehistoric setup.py, so really the whole installer needs redoing and updating.

One slight issue is that pyvips supports ABI (portable but slow and leaky) and API (fast and stable) modes, but with a prebuilt libvips binary we'd be stuck in ABI mode. Maybe the benefits are worth it.

Using pyvips without needing to install many dozens of system dependencies is a lifesaver in deployment.

You should just need the libvips-dev package (or equivalent), I think. python-dev as well I guess, and a C compiler.

@SteveHawk
Copy link
Author

pyvips works with any libvips version

Oh that's weird. I asked because I tried to use pyvips v2.2.1 with 8.15, but didn't work and throw errors immediately (seems to be related to glib). The master branch works though, that's why I thought v2.2.1 is not compatible with 8.15.

Here's how I did it:

  • Using a freshly started python:3.11-slim-bullseye docker container, do apt update; apt install pkg-config build-essential
  • Download the precompiled libvips-dev, extract it under /root/lib/, insert proper pkgconfig .pc files and run
    export LD_LIBRARY_PATH=/root/lib/libvips-8.15.0-linux-x64/lib/
    export PKG_CONFIG_PATH=/root/lib/libvips-8.15.0-linux-x64/lib/pkgconfig/
  • run pip install pyvips, then pip show reports: Summary: binding for the libvips image processing library, API mode

After setting up the environment, enter python shell and run:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> from pyvips import Image
DEBUG:pyvips:Binary module load failed: /usr/local/lib/python3.11/site-packages/_libvips.abi3.so: undefined symbol: vips_path_mode7
DEBUG:pyvips:Falling back to ABI mode
DEBUG:pyvips:Loaded lib <cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x7fa8935a3550>
DEBUG:pyvips:Loaded lib <cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x7fa8936f3710>
DEBUG:pyvips:Inited libvips
>>> Image.new_from_file("/root/lib/test.png")
DEBUG:pyvips.voperation:VipsOperation.call: operation_name = VipsForeignLoadPngFile
DEBUG:pyvips.voperation:VipsOperation.call: match_image = None
DEBUG:pyvips.vobject:VipsObject.set: name = filename, value = /root/lib/test.png

(process:875): GLib-CRITICAL **: 09:49:38.370: g_datalist_id_set_data_full: assertion 'key_id > 0' failed

(process:875): GLib-GObject-CRITICAL **: 09:49:38.370: g_param_spec_pool_lookup: assertion 'pool != NULL' failed

(process:875): GLib-GObject-WARNING **: 09:49:38.370: g_object_set_is_valid_property: object class '(null)' has no property named 'filename'
Segmentation fault (core dumped)

That crashed. I also tried thumbnail_buffer, with no luck:

>>> Image.thumbnail_buffer(b"test", 128)
DEBUG:pyvips.voperation:VipsOperation.call: operation_name = thumbnail_buffer
DEBUG:pyvips.voperation:VipsOperation.call: match_image = None
DEBUG:pyvips.vobject:VipsObject.set: name = buffer, value = b'test'
DEBUG:pyvips.error:Error unsupported gtype for set NULL, fundamental GBoxed 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.11/site-packages/pyvips/vimage.py", line 256, in call_function
    return pyvips.Operation.call(name, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyvips/voperation.py", line 282, in call
    op.set(name, intro.details[name]['flags'], match_image, value)
  File "/usr/local/lib/python3.11/site-packages/pyvips/voperation.py", line 216, in set
    super(Operation, self).set(name, value)
  File "/usr/local/lib/python3.11/site-packages/pyvips/vobject.py", line 122, in set
    gv.set(value)
  File "/usr/local/lib/python3.11/site-packages/pyvips/gvalue.py", line 242, in set
    raise Error('unsupported gtype for set {0}, fundamental {1}'.
pyvips.error.Error: unsupported gtype for set NULL, fundamental GBoxed

When I uninstall the pypi version and install the master branch, pip show still reports API mode, and everything works as expected:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> from pyvips import Image
DEBUG:pyvips:Loaded binary module _libvips
DEBUG:pyvips:Module generated for libvips 8.15
DEBUG:pyvips:Linked to libvips 8.15
DEBUG:pyvips:Inited libvips
>>> Image.new_from_file("/root/lib/test.png")
DEBUG:pyvips.voperation:VipsOperation.call: operation_name = VipsForeignLoadPngFile
DEBUG:pyvips.voperation:VipsOperation.call: match_image = None
DEBUG:pyvips.vobject:VipsObject.set: name = filename, value = /root/lib/test.png
DEBUG:pyvips.vobject:VipsObject.get: name = out
DEBUG:pyvips.vobject:VipsObject.get: name = interpretation
DEBUG:pyvips.vobject:VipsObject.get: name = width
DEBUG:pyvips.vobject:VipsObject.get: name = height
DEBUG:pyvips.vobject:VipsObject.get: name = format
DEBUG:pyvips.vobject:VipsObject.get: name = bands
DEBUG:pyvips.vobject:VipsObject.get: name = interpretation
DEBUG:pyvips.voperation:VipsOperation.call: result = <pyvips.Image 1240x1754 uchar, 1 bands, b-w>
DEBUG:pyvips.vobject:VipsObject.get: name = interpretation
DEBUG:pyvips.vobject:VipsObject.get: name = width
DEBUG:pyvips.vobject:VipsObject.get: name = height
DEBUG:pyvips.vobject:VipsObject.get: name = format
DEBUG:pyvips.vobject:VipsObject.get: name = bands
DEBUG:pyvips.vobject:VipsObject.get: name = interpretation
<pyvips.Image 1240x1754 uchar, 1 bands, b-w>
>>> Image.thumbnail_buffer(b"test", 128)
DEBUG:pyvips.voperation:VipsOperation.call: operation_name = thumbnail_buffer
DEBUG:pyvips.voperation:VipsOperation.call: match_image = None
DEBUG:pyvips.vobject:VipsObject.set: name = buffer, value = b'test'
DEBUG:pyvips.vobject:VipsObject.set: name = width, value = 128
INFO:pyvips:VIPS: thumbnailing 4 bytes of data
DEBUG:pyvips.error:Error unable to call thumbnail_buffer VipsForeignLoad: buffer is not in a known format

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.11/site-packages/pyvips/vimage.py", line 256, in call_function
    return pyvips.Operation.call(name, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyvips/voperation.py", line 305, in call
    raise Error('unable to call {0}'.format(operation_name))
pyvips.error.Error: unable to call thumbnail_buffer
  VipsForeignLoad: buffer is not in a known format

but with a prebuilt libvips binary we'd be stuck in ABI mode

I installed pyvips with the prebuilt libvips-dev from kleisauke/libvips-packaging (with proper LD_LIBRARY_PATH and PKG_CONFIG_PATH set) and pip show reports the installation is using API mode. So I guess this might not be true?

You should just need the libvips-dev package

Actually the package manager will download a LOT of dependencies (over 100 using conda, over 200! using apt, including libjpg, libpng etc.), taking up more disk spaces than statically linked libvips-dev package, and also taking more time to download and install. More importantly, package managers like apt usually don't come with newer versions of these libraries, so that would be another down side.

@jcupitt
Copy link
Member

jcupitt commented Jan 4, 2024

Huh strange, it ought to work. I'll try to make a dockerfile for this.

Actually the package manager will download a LOT of dependencies (over 100 using conda, over 200! using apt, including libjpg, libpng etc.),

Did you use --no-install-recommends? The deb package recommends the libvips GUI, and that in turn pulls in most of X11 and gtk2 (!!).

@jcupitt
Copy link
Member

jcupitt commented Jan 4, 2024

I installed pyvips with the prebuilt libvips-dev from kleisauke/libvips-packaging (with proper LD_LIBRARY_PATH and PKG_CONFIG_PATH set) and pip show reports the installation is using API mode. So I guess this might not be true?

Ah if you use the libvips-dev precompiled binary it'd work, yes. But that's probably no smaller than just installing libvips-dev from your package manager, and will be annoying to maintain.

@jcupitt
Copy link
Member

jcupitt commented Jan 4, 2024

I updated this dockerfile:

https://github.com/jcupitt/docker-builds/blob/master/pyvips-alpine/Dockerfile

And it seems to work fine. That's libvips 8.15.1 and pyvips 2.2.1 on alpine.

I've not tried with those precompiled libvips binaries. If you need a newer libvips than is in your package manager, I'd expect building from source yourself to be much more likely to work, should be smaller (it can use the platform libjpeg etc.) and can be deployed pretty easily with docker or whatever.

@SteveHawk
Copy link
Author

Did you use --no-install-recommends

Yes i did, in a python:3.11-slim-bullseye docker, run apt install --no-install-recommends libvips-dev resulted in:

1 upgraded, 260 newly installed, 0 to remove and 15 not upgraded.
Need to get 117 MB of archives.
After this operation, 475 MB of additional disk space will be used.

Without the --no-install-recommends flag that would be even more madness:

1 upgraded, 419 newly installed, 0 to remove and 15 not upgraded.
Need to get 260 MB of archives.
After this operation, 1002 MB of additional disk space will be used.

But that's probably no smaller than just installing libvips-dev from your package manager

The precompiled libvips-dev package is a 8MB tarball and only 26MB uncompressed on disk. I believe it only includes necessary .so files, so it's actually much smaller in size ;)

@kleisauke
Copy link
Member

The prebuilt binaries available at https://github.com/kleisauke/libvips-packaging would only work with pyvips' API mode since GLib is statically-linked.

Here's a Dockerfile to reproduce this issue:

Details
# Build and run with:
# docker build -t pyvips-libvips-packaging .
# docker run -it --rm pyvips-libvips-packaging
FROM python:3.12-slim-bookworm

LABEL maintainer="Kleis Auke Wolthuizen <[email protected]>"

ARG VIPS_VERSION=8.15.0

# https://gist.github.com/kleisauke/91ef7315adbe1ca7455a38b24d28081e
ARG VIPS_PC_FILE=https://gist.github.com/kleisauke/91ef7315adbe1ca7455a38b24d28081e/raw/8b780945bacb66d7e79efa1ecc1c9a783b2d1f47/vips.pc

WORKDIR /opt/libvips

# Update packages
RUN apt-get update -q \
    # Install needed dependencies
    && apt-get install -qy --no-install-recommends \
        build-essential \
        ca-certificates \
        curl \
        pkg-config \
    # Download pre-built libvips binaries
    && curl -Ls https://github.com/kleisauke/libvips-packaging/releases/download/v$VIPS_VERSION/libvips-$VIPS_VERSION-linux-x64.tar.gz | tar -xzC . \
    # Download pkgconfig file
    && mkdir -p lib/pkgconfig \
    && cd lib/pkgconfig \
    && curl -LO $VIPS_PC_FILE

WORKDIR /app

# Update the PKG_CONFIG_PATH environment variable,
# since libvips is installed in a non-standard prefix
ENV PKG_CONFIG_PATH=/opt/libvips/lib/pkgconfig \
    # Ensure dynamic linker finds the pre-built libvips binaries
    LD_LIBRARY_PATH=/opt/libvips/lib

RUN pip install --verbose --user pyvips \
    && pip show pyvips

CMD ["python"]

The salient part of the error log is:

DEBUG:pyvips:Binary module load failed: /root/.local/lib/python3.12/site-packages/_libvips.abi3.so: undefined symbol: vips_path_mode7
DEBUG:pyvips:Falling back to ABI mode
OSError: cannot load library 'libgobject-2.0.so.0': libgobject-2.0.so.0: cannot open shared object file: No such file or directory.  Additionally, ctypes.util.find_library() did not manage to locate a library called 'libgobject-2.0.so.0'

So, it tries to fallback to ABI mode since the deprecated vips_path_mode7 symbol is not available, this was fixed with commit 350a459.

@jcupitt
Copy link
Member

jcupitt commented Jan 4, 2024

Ah that had popped out of my brain, thanks Kleis.

Then I agree, a pyvips 2.2.2 would be useful. I'll do it now.

@jcupitt
Copy link
Member

jcupitt commented Jan 4, 2024

We might have v2.2.2, any testing very welcome.

@kleisauke
Copy link
Member

Great! I just opened PR #444 to support this in ABI mode.

FWIW, looks like v2.2.2 was miss-tagged as v2.2.0:
https://github.com/libvips/pyvips/releases/tag/v2.2.0

@kleisauke
Copy link
Member

pyvips still uses the prehistoric setup.py, so really the whole installer needs redoing and updating.

I'll have a look at this somewhere tomorrow.

@jcupitt
Copy link
Member

jcupitt commented Jan 4, 2024

FWIW, looks like v2.2.2 was miss-tagged as v2.2.0:

🤦 fixed.

@SteveHawk
Copy link
Author

We might have v2.2.2, any testing very welcome.

Nice! It works well on my codes so far, will do more tests next week. Thanks!

@kleisauke
Copy link
Member

pyvips still uses the prehistoric setup.py, so really the whole installer needs redoing and updating.

PR #445 should fix this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants