diff --git a/ChangeLog b/ChangeLog index 02e7f729..36217b0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,7 @@ - add .szi as a dzsave zip synonym - support tiff XMP metadata - support @density arg to magickload [Lovell] +- support python3.4 and python2.7 in new python binding 25/7/14 started 7.41.0 - start working on --disable-deprecated diff --git a/python/README.md b/python/README.md index b61ba7d3..94411c0a 100644 --- a/python/README.md +++ b/python/README.md @@ -1,15 +1,21 @@ # vips8 binding for Python This overrides file adds a few small helper functions to the -gobject-introspection binding for libvips. +gobject-introspection binding for libvips. It works with python2.7 and +python3.4. There's a chapter in the libvips API docs on these overrides, see "Using libvips from Python". -vips-x.y.z/test has a test suite. +vips-x.y.z/test has a test suite. Again, the test suite works with python2.7 +and python3.4. Vips.py needs to be in the overrides directory of your gobject-introspection pygobject area, for example: sudo cp Vips.py /usr/lib/python2.7/dist-packages/gi/overrides +or + + sudo cp Vips.py /usr/lib/python3.4/dist-packages/gi/overrides + diff --git a/python/Vips.py b/python/Vips.py index 872859ab..8ba4235d 100644 --- a/python/Vips.py +++ b/python/Vips.py @@ -1,9 +1,13 @@ # -*- Mode: Python; py-indent-offset: 4 -*- # vim: tabstop=4 shiftwidth=4 expandtab -# copy this file to /usr/lib/python2.7/dist-packages/gi/overrides, eg. +# overrides for pygobject gobject-introspection binding for libvips, tested +# with python2.7 and python3.4 + +# copy this file to dist-packages/gi/overrides, eg. # # sudo cp Vips.py /usr/lib/python2.7/dist-packages/gi/overrides +# sudo cp Vips.py /usr/lib/python3/dist-packages/gi/overrides # # Alternatively, build vips to another prefix, then copy Vips.py and Vips.pyc # from $prefix/lib/python2.7/dist-packages/gi/overrides to /usr @@ -26,6 +30,12 @@ # # These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk +from __future__ import division +from builtins import map +from builtins import str +from builtins import range +from builtins import object + import sys import re import logging @@ -113,7 +123,7 @@ class Error(Exception): Vips.Error = Error -class Argument: +class Argument(object): def __init__(self, op, prop): self.op = op self.prop = prop @@ -185,7 +195,7 @@ class Operation(Vips.Operation): args = [Argument(self, x) for x in self.props] args = [y for y in args if not y.flags & Vips.ArgumentFlags.DEPRECATED] - args.sort(lambda a, b: a.priority - b.priority) + args.sort(key = lambda x: x.priority) return args @@ -213,7 +223,7 @@ def _call_base(name, required, optional, self = None, option_string = None): try: op = Vips.Operation.new(name) - except TypeError, e: + except TypeError as e: raise Error('No such operator.') if op.get_flags() & Vips.OperationFlags.DEPRECATED: raise Error('No such operator.', 'operator "%s" is deprecated' % name) @@ -285,7 +295,7 @@ def _call_base(name, required, optional, self = None, option_string = None): not x.flags & enm.REQUIRED} # set optional input args - for key in optional.keys(): + for key in list(optional.keys()): if key in optional_input: optional_input[key].set_value(match_image, optional[key]) elif key in optional_output: @@ -322,7 +332,7 @@ def _call_base(name, required, optional, self = None, option_string = None): out.append(x.get_value()) out_dict = {} - for x in optional.keys(): + for x in list(optional.keys()): if x in optional_output: out_dict[x] = optional_output[x].get_value() if out_dict != {}: @@ -425,7 +435,7 @@ setattr(Vips.Image, 'new_from_array', vips_image_new_from_array) def generate_docstring(name): try: op = Vips.Operation.new(name) - except TypeError, e: + except TypeError as e: raise Error('No such operator.') if op.get_flags() & Vips.OperationFlags.DEPRECATED: raise Error('No such operator.', 'operator "%s" is deprecated' % name) @@ -500,7 +510,7 @@ def generate_docstring(name): # as well as scalars def smap(func, x): if isinstance(x, list): - return map(func, x) + return list(map(func, x)) else: return func(x) @@ -595,6 +605,8 @@ class Image(Vips.Image): def __rmul__(self, other): return self.__mul__(other) + # a / const has always been a float in vips, so div and truediv are the + # same def __div__(self, other): if isinstance(other, Vips.Image): return self.divide(other) @@ -604,6 +616,12 @@ class Image(Vips.Image): def __rdiv__(self, other): return (self ** -1) * other + def __truediv__(self, other): + return self.__div__(other) + + def __rtruediv__(self, other): + return self.__rdiv__(other) + def __floordiv__(self, other): if isinstance(other, Vips.Image): return self.divide(other).floor() diff --git a/test/test_arithmetic.py b/test/test_arithmetic.py index 0002e6b7..9f5ae399 100755 --- a/test/test_arithmetic.py +++ b/test/test_arithmetic.py @@ -1,5 +1,8 @@ -#!/usr/bin/python +#!/usr/bin/python3 +from __future__ import division +from builtins import zip +from builtins import range import unittest import math @@ -26,7 +29,7 @@ all_formats = int_formats + float_formats + complex_formats # the other def zip_expand(x, y): if isinstance(x, list) and isinstance(y, list): - return zip(x, y) + return list(zip(x, y)) elif isinstance(x, list): return [[i, y] for i in x] elif isinstance(y, list): @@ -77,9 +80,9 @@ class TestArithmetic(unittest.TestCase): self.run_cmp(message, im, 10, 10, lambda x: run_fn2(fn, c, x)) def run_arith_const(self, fn, fmt = all_formats): - [self.run_testconst(fn.func_name + ' scalar', fn, x.cast(y), 2) + [self.run_testconst(fn.__name__ + ' scalar', fn, x.cast(y), 2) for x in self.all_images for y in fmt] - [self.run_testconst(fn.func_name + ' vector', fn, self.colour.cast(y), + [self.run_testconst(fn.__name__ + ' vector', fn, self.colour.cast(y), [1, 2, 3]) for y in fmt] @@ -102,7 +105,7 @@ class TestArithmetic(unittest.TestCase): lambda x, y: run_fn2(fn, x, y)) def run_arith(self, fn, fmt = all_formats): - [self.run_test2(fn.func_name + ' image', x.cast(y), x.cast(z), fn) + [self.run_test2(fn.__name__ + ' image', x.cast(y), x.cast(z), fn) for x in self.all_images for y in fmt for z in fmt] def setUp(self): @@ -279,7 +282,7 @@ class TestArithmetic(unittest.TestCase): self.run_cmp(message, im, 10, 10, lambda x: run_fn(fn, x)) def run_unary(self, images, fn, fmt = all_formats): - [self.run_testunary(fn.func_name + ' image', x.cast(y), fn) + [self.run_testunary(fn.__name__ + ' image', x.cast(y), fn) for x in images for y in fmt] def test_abs(self): @@ -408,7 +411,7 @@ class TestArithmetic(unittest.TestCase): def test_histfind_indexed(self): im = Vips.Image.black(50, 100) test = im.insert(im + 10, 50, 0, expand = True) - index = test / 10 + index = test // 10 for x in noncomplex_formats: for y in [Vips.BandFormat.UCHAR, Vips.BandFormat.USHORT]: @@ -459,8 +462,8 @@ class TestArithmetic(unittest.TestCase): v, x, y = hough.maxpos() - angle = 360.0 * x / hough.width - distance = test.height * y / hough.height + angle = 360.0 * x // hough.width + distance = test.height * y // hough.height self.assertAlmostEqual(angle, 45) self.assertAlmostEqual(distance, 70) diff --git a/test/test_colour.py b/test/test_colour.py index f314b6cc..06085bd0 100755 --- a/test/test_colour.py +++ b/test/test_colour.py @@ -1,5 +1,7 @@ #!/usr/bin/python +from builtins import zip + import unittest import math @@ -40,7 +42,7 @@ all_colourspaces = colour_colourspaces + mono_colourspaces + coded_colourspaces # the other def zip_expand(x, y): if isinstance(x, list) and isinstance(y, list): - return zip(x, y) + return list(zip(x, y)) elif isinstance(x, list): return [[i, y] for i in x] elif isinstance(y, list): diff --git a/test/test_conversion.py b/test/test_conversion.py index 8efec111..cdded776 100755 --- a/test/test_conversion.py +++ b/test/test_conversion.py @@ -1,5 +1,7 @@ #!/usr/bin/python +from __future__ import division +from builtins import zip import unittest import math @@ -7,6 +9,7 @@ import math #logging.basicConfig(level = logging.DEBUG) from gi.repository import Vips +from functools import reduce unsigned_formats = [Vips.BandFormat.UCHAR, Vips.BandFormat.USHORT, @@ -76,7 +79,7 @@ rot_angle_bonds = [Vips.Angle.D0, # the other def zip_expand(x, y): if isinstance(x, list) and isinstance(y, list): - return zip(x, y) + return list(zip(x, y)) elif isinstance(x, list): return [[i, y] for i in x] elif isinstance(y, list): @@ -123,11 +126,11 @@ class TestConversion(unittest.TestCase): self.run_cmp_unary(message, im, 10, 10, fn) def run_unary(self, images, fn, fmt = all_formats): - [self.run_testunary(fn.func_name + (' %s' % y), x.cast(y), fn) + [self.run_testunary(fn.__name__ + (' %s' % y), x.cast(y), fn) for x in images for y in fmt] def run_binary(self, images, fn, fmt = all_formats): - [self.run_testbinary(fn.func_name + (' %s %s' % (y, z)), + [self.run_testbinary(fn.__name__ + (' %s %s' % (y, z)), x.cast(y), x.cast(z), fn) for x in images for y in fmt for z in fmt] @@ -178,7 +181,7 @@ class TestConversion(unittest.TestCase): if isinstance(x, Vips.Image): return x.bandmean() else: - return [sum(x) / len(x)] + return [sum(x) // len(x)] self.run_unary([self.colour], bandmean, fmt = noncomplex_formats) @@ -188,7 +191,7 @@ class TestConversion(unittest.TestCase): # .sort() isn't a function, so we have to run this as a separate # pass [x.sort() for x in joined] - return [x[len(x) / 2] for x in joined] + return [x[len(x) // 2] for x in joined] def bandrank(x, y): if isinstance(x, Vips.Image) and isinstance(y, Vips.Image): @@ -331,7 +334,7 @@ class TestConversion(unittest.TestCase): for fmt in noncomplex_formats: mx = max_value[fmt] - alpha = mx / 2 + alpha = mx / 2.0 nalpha = mx - alpha test = self.colour.bandjoin(black + alpha).cast(fmt) pixel = test.getpoint(30, 30) @@ -350,7 +353,7 @@ class TestConversion(unittest.TestCase): im = test.flatten(background = [100, 100, 100]) pixel = test.getpoint(30, 30) - predict = [int(x) * alpha / mx + (100 * nalpha) / mx + predict = [int(x) * alpha / mx + (100 * nalpha) / mx for x in pixel[:-1]] self.assertEqual(im.bands, 3) @@ -375,7 +378,7 @@ class TestConversion(unittest.TestCase): exponent = 2.4 for fmt in noncomplex_formats: mx = max_value[fmt] - test = (self.colour + mx / 2).cast(fmt) + test = (self.colour + mx / 2.0).cast(fmt) norm = mx ** exponent / mx result = test.gamma() @@ -390,7 +393,7 @@ class TestConversion(unittest.TestCase): exponent = 1.2 for fmt in noncomplex_formats: mx = max_value[fmt] - test = (self.colour + mx / 2).cast(fmt) + test = (self.colour + mx / 2.0).cast(fmt) norm = mx ** exponent / mx result = test.gamma(exponent = 1.0 / 1.2) @@ -518,7 +521,7 @@ class TestConversion(unittest.TestCase): for fmt in unsigned_formats: mx = max_value[fmt] size = sizeof_format[fmt] - test = (self.colour + mx / 8).cast(fmt) + test = (self.colour + mx / 8.0).cast(fmt) im = test.msb() before = test.getpoint(10, 10) @@ -534,7 +537,7 @@ class TestConversion(unittest.TestCase): for fmt in signed_formats: mx = max_value[fmt] size = sizeof_format[fmt] - test = (self.colour + mx / 8).cast(fmt) + test = (self.colour + mx / 8.0).cast(fmt) im = test.msb() before = test.getpoint(10, 10) @@ -550,7 +553,7 @@ class TestConversion(unittest.TestCase): for fmt in unsigned_formats: mx = max_value[fmt] size = sizeof_format[fmt] - test = (self.colour + mx / 8).cast(fmt) + test = (self.colour + mx / 8.0).cast(fmt) im = test.msb(band = 1) before = [test.getpoint(10, 10)[1]] @@ -644,8 +647,8 @@ class TestConversion(unittest.TestCase): test = self.colour.cast(fmt) im = test.subsample(3, 3) - self.assertEqual(im.width, test.width / 3) - self.assertEqual(im.height, test.height / 3) + self.assertEqual(im.width, test.width // 3) + self.assertEqual(im.height, test.height // 3) before = test.getpoint(60, 60) after = im.getpoint(20, 20)