From 3eff2c719637a6e360dfcc85e4a6dd514bf8ccf0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 19 Feb 2024 20:14:40 +0000 Subject: [PATCH] new_from_source keeps a ref to the source Since it might be a custom source with callabcks, and the python wrapper for the source class must stay alive. Added another example of custom sources. --- CHANGELOG.rst | 4 +++- examples/stream.py | 27 +++++++++++++++++++++++++++ pyvips/vimage.py | 11 +++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100755 examples/stream.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 82ceb36..8861fc2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,7 +2,9 @@ - ensure compatibility with a single shared libvips library [kleisauke] - add flags_dict(), enum_dict() for better flags introspection -- improve generation of enums.py +- improve generation of `enums.py` +- add `stream.py` example +- fix a missing reference issue with custom sources ## Version 2.2.2 (released 4 Jan 2023) diff --git a/examples/stream.py b/examples/stream.py new file mode 100755 index 0000000..bbda64e --- /dev/null +++ b/examples/stream.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +import sys +import gc +import requests +import pyvips + +URL = "https://cdn.filestackcontent.com/bnTGtQw5ShqMPpxH2tMw" +URLS = [URL] * int(sys.argv[1]) + +session = requests.Session() + +image = pyvips.Image.black(1500, 1500) + +for i, url in enumerate(URLS): + print(f"loading {url} ...") + stream = session.get(url, stream=True).raw + + source = pyvips.SourceCustom() + source.on_read((lambda stream: stream.read)(stream)) + + tile = pyvips.Image.new_from_source(source, "", access="sequential") + image = image.composite2(tile, "over", x= 50 * (i + 1), y= 50 * (i + 1)) + +print(f"writing output.jpg ...") +image.write_to_file("output.jpg") + diff --git a/pyvips/vimage.py b/pyvips/vimage.py index db98b40..1703ee7 100644 --- a/pyvips/vimage.py +++ b/pyvips/vimage.py @@ -664,8 +664,15 @@ def new_from_source(source, options, **kwargs): raise Error('unable to load from source') name = _to_string(pointer) - return pyvips.Operation.call(name, source, - string_options=options, **kwargs) + image = pyvips.Operation.call(name, source, + string_options=options, **kwargs) + + # keep a secret ref to the source object .. we need that to stay + # alive, since it might be a custom source that triggers a python + # callback + image._references.append(source) + + return image @staticmethod def new_temp_file(format):