add python3 support

This commit is contained in:
John Cupitt 2014-12-10 17:02:51 +00:00
parent 9866127e6a
commit c3102b15bb
6 changed files with 67 additions and 34 deletions

View File

@ -6,6 +6,7 @@
- add .szi as a dzsave zip synonym - add .szi as a dzsave zip synonym
- support tiff XMP metadata - support tiff XMP metadata
- support @density arg to magickload [Lovell] - support @density arg to magickload [Lovell]
- support python3.4 and python2.7 in new python binding
25/7/14 started 7.41.0 25/7/14 started 7.41.0
- start working on --disable-deprecated - start working on --disable-deprecated

View File

@ -1,15 +1,21 @@
# vips8 binding for Python # vips8 binding for Python
This overrides file adds a few small helper functions to the 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 There's a chapter in the libvips API docs on these overrides, see "Using
libvips from Python". 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 Vips.py needs to be in the overrides directory of your gobject-introspection
pygobject area, for example: pygobject area, for example:
sudo cp Vips.py /usr/lib/python2.7/dist-packages/gi/overrides 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

View File

@ -1,9 +1,13 @@
# -*- Mode: Python; py-indent-offset: 4 -*- # -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab # 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/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 # Alternatively, build vips to another prefix, then copy Vips.py and Vips.pyc
# from $prefix/lib/python2.7/dist-packages/gi/overrides to /usr # 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 # 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 sys
import re import re
import logging import logging
@ -113,7 +123,7 @@ class Error(Exception):
Vips.Error = Error Vips.Error = Error
class Argument: class Argument(object):
def __init__(self, op, prop): def __init__(self, op, prop):
self.op = op self.op = op
self.prop = prop self.prop = prop
@ -185,7 +195,7 @@ class Operation(Vips.Operation):
args = [Argument(self, x) for x in self.props] args = [Argument(self, x) for x in self.props]
args = [y for y in args args = [y for y in args
if not y.flags & Vips.ArgumentFlags.DEPRECATED] 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 return args
@ -213,7 +223,7 @@ def _call_base(name, required, optional, self = None, option_string = None):
try: try:
op = Vips.Operation.new(name) op = Vips.Operation.new(name)
except TypeError, e: except TypeError as e:
raise Error('No such operator.') raise Error('No such operator.')
if op.get_flags() & Vips.OperationFlags.DEPRECATED: if op.get_flags() & Vips.OperationFlags.DEPRECATED:
raise Error('No such operator.', 'operator "%s" is deprecated' % name) 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} not x.flags & enm.REQUIRED}
# set optional input args # set optional input args
for key in optional.keys(): for key in list(optional.keys()):
if key in optional_input: if key in optional_input:
optional_input[key].set_value(match_image, optional[key]) optional_input[key].set_value(match_image, optional[key])
elif key in optional_output: 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.append(x.get_value())
out_dict = {} out_dict = {}
for x in optional.keys(): for x in list(optional.keys()):
if x in optional_output: if x in optional_output:
out_dict[x] = optional_output[x].get_value() out_dict[x] = optional_output[x].get_value()
if out_dict != {}: if out_dict != {}:
@ -425,7 +435,7 @@ setattr(Vips.Image, 'new_from_array', vips_image_new_from_array)
def generate_docstring(name): def generate_docstring(name):
try: try:
op = Vips.Operation.new(name) op = Vips.Operation.new(name)
except TypeError, e: except TypeError as e:
raise Error('No such operator.') raise Error('No such operator.')
if op.get_flags() & Vips.OperationFlags.DEPRECATED: if op.get_flags() & Vips.OperationFlags.DEPRECATED:
raise Error('No such operator.', 'operator "%s" is deprecated' % name) raise Error('No such operator.', 'operator "%s" is deprecated' % name)
@ -500,7 +510,7 @@ def generate_docstring(name):
# as well as scalars # as well as scalars
def smap(func, x): def smap(func, x):
if isinstance(x, list): if isinstance(x, list):
return map(func, x) return list(map(func, x))
else: else:
return func(x) return func(x)
@ -595,6 +605,8 @@ class Image(Vips.Image):
def __rmul__(self, other): def __rmul__(self, other):
return self.__mul__(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): def __div__(self, other):
if isinstance(other, Vips.Image): if isinstance(other, Vips.Image):
return self.divide(other) return self.divide(other)
@ -604,6 +616,12 @@ class Image(Vips.Image):
def __rdiv__(self, other): def __rdiv__(self, other):
return (self ** -1) * 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): def __floordiv__(self, other):
if isinstance(other, Vips.Image): if isinstance(other, Vips.Image):
return self.divide(other).floor() return self.divide(other).floor()

View File

@ -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 unittest
import math import math
@ -26,7 +29,7 @@ all_formats = int_formats + float_formats + complex_formats
# the other # the other
def zip_expand(x, y): def zip_expand(x, y):
if isinstance(x, list) and isinstance(y, list): if isinstance(x, list) and isinstance(y, list):
return zip(x, y) return list(zip(x, y))
elif isinstance(x, list): elif isinstance(x, list):
return [[i, y] for i in x] return [[i, y] for i in x]
elif isinstance(y, list): 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)) self.run_cmp(message, im, 10, 10, lambda x: run_fn2(fn, c, x))
def run_arith_const(self, fn, fmt = all_formats): 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] 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]) [1, 2, 3])
for y in fmt] for y in fmt]
@ -102,7 +105,7 @@ class TestArithmetic(unittest.TestCase):
lambda x, y: run_fn2(fn, x, y)) lambda x, y: run_fn2(fn, x, y))
def run_arith(self, fn, fmt = all_formats): 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] for x in self.all_images for y in fmt for z in fmt]
def setUp(self): def setUp(self):
@ -279,7 +282,7 @@ class TestArithmetic(unittest.TestCase):
self.run_cmp(message, im, 10, 10, lambda x: run_fn(fn, x)) self.run_cmp(message, im, 10, 10, lambda x: run_fn(fn, x))
def run_unary(self, images, fn, fmt = all_formats): 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] for x in images for y in fmt]
def test_abs(self): def test_abs(self):
@ -408,7 +411,7 @@ class TestArithmetic(unittest.TestCase):
def test_histfind_indexed(self): def test_histfind_indexed(self):
im = Vips.Image.black(50, 100) im = Vips.Image.black(50, 100)
test = im.insert(im + 10, 50, 0, expand = True) test = im.insert(im + 10, 50, 0, expand = True)
index = test / 10 index = test // 10
for x in noncomplex_formats: for x in noncomplex_formats:
for y in [Vips.BandFormat.UCHAR, Vips.BandFormat.USHORT]: for y in [Vips.BandFormat.UCHAR, Vips.BandFormat.USHORT]:
@ -459,8 +462,8 @@ class TestArithmetic(unittest.TestCase):
v, x, y = hough.maxpos() v, x, y = hough.maxpos()
angle = 360.0 * x / hough.width angle = 360.0 * x // hough.width
distance = test.height * y / hough.height distance = test.height * y // hough.height
self.assertAlmostEqual(angle, 45) self.assertAlmostEqual(angle, 45)
self.assertAlmostEqual(distance, 70) self.assertAlmostEqual(distance, 70)

View File

@ -1,5 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
from builtins import zip
import unittest import unittest
import math import math
@ -40,7 +42,7 @@ all_colourspaces = colour_colourspaces + mono_colourspaces + coded_colourspaces
# the other # the other
def zip_expand(x, y): def zip_expand(x, y):
if isinstance(x, list) and isinstance(y, list): if isinstance(x, list) and isinstance(y, list):
return zip(x, y) return list(zip(x, y))
elif isinstance(x, list): elif isinstance(x, list):
return [[i, y] for i in x] return [[i, y] for i in x]
elif isinstance(y, list): elif isinstance(y, list):

View File

@ -1,5 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
from __future__ import division
from builtins import zip
import unittest import unittest
import math import math
@ -7,6 +9,7 @@ import math
#logging.basicConfig(level = logging.DEBUG) #logging.basicConfig(level = logging.DEBUG)
from gi.repository import Vips from gi.repository import Vips
from functools import reduce
unsigned_formats = [Vips.BandFormat.UCHAR, unsigned_formats = [Vips.BandFormat.UCHAR,
Vips.BandFormat.USHORT, Vips.BandFormat.USHORT,
@ -76,7 +79,7 @@ rot_angle_bonds = [Vips.Angle.D0,
# the other # the other
def zip_expand(x, y): def zip_expand(x, y):
if isinstance(x, list) and isinstance(y, list): if isinstance(x, list) and isinstance(y, list):
return zip(x, y) return list(zip(x, y))
elif isinstance(x, list): elif isinstance(x, list):
return [[i, y] for i in x] return [[i, y] for i in x]
elif isinstance(y, list): elif isinstance(y, list):
@ -123,11 +126,11 @@ class TestConversion(unittest.TestCase):
self.run_cmp_unary(message, im, 10, 10, fn) self.run_cmp_unary(message, im, 10, 10, fn)
def run_unary(self, images, fn, fmt = all_formats): 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] for x in images for y in fmt]
def run_binary(self, images, fn, fmt = all_formats): 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) x.cast(y), x.cast(z), fn)
for x in images for y in fmt for z in fmt] 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): if isinstance(x, Vips.Image):
return x.bandmean() return x.bandmean()
else: else:
return [sum(x) / len(x)] return [sum(x) // len(x)]
self.run_unary([self.colour], bandmean, fmt = noncomplex_formats) 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 # .sort() isn't a function, so we have to run this as a separate
# pass # pass
[x.sort() for x in joined] [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): def bandrank(x, y):
if isinstance(x, Vips.Image) and isinstance(y, Vips.Image): if isinstance(x, Vips.Image) and isinstance(y, Vips.Image):
@ -331,7 +334,7 @@ class TestConversion(unittest.TestCase):
for fmt in noncomplex_formats: for fmt in noncomplex_formats:
mx = max_value[fmt] mx = max_value[fmt]
alpha = mx / 2 alpha = mx / 2.0
nalpha = mx - alpha nalpha = mx - alpha
test = self.colour.bandjoin(black + alpha).cast(fmt) test = self.colour.bandjoin(black + alpha).cast(fmt)
pixel = test.getpoint(30, 30) pixel = test.getpoint(30, 30)
@ -375,7 +378,7 @@ class TestConversion(unittest.TestCase):
exponent = 2.4 exponent = 2.4
for fmt in noncomplex_formats: for fmt in noncomplex_formats:
mx = max_value[fmt] mx = max_value[fmt]
test = (self.colour + mx / 2).cast(fmt) test = (self.colour + mx / 2.0).cast(fmt)
norm = mx ** exponent / mx norm = mx ** exponent / mx
result = test.gamma() result = test.gamma()
@ -390,7 +393,7 @@ class TestConversion(unittest.TestCase):
exponent = 1.2 exponent = 1.2
for fmt in noncomplex_formats: for fmt in noncomplex_formats:
mx = max_value[fmt] mx = max_value[fmt]
test = (self.colour + mx / 2).cast(fmt) test = (self.colour + mx / 2.0).cast(fmt)
norm = mx ** exponent / mx norm = mx ** exponent / mx
result = test.gamma(exponent = 1.0 / 1.2) result = test.gamma(exponent = 1.0 / 1.2)
@ -518,7 +521,7 @@ class TestConversion(unittest.TestCase):
for fmt in unsigned_formats: for fmt in unsigned_formats:
mx = max_value[fmt] mx = max_value[fmt]
size = sizeof_format[fmt] size = sizeof_format[fmt]
test = (self.colour + mx / 8).cast(fmt) test = (self.colour + mx / 8.0).cast(fmt)
im = test.msb() im = test.msb()
before = test.getpoint(10, 10) before = test.getpoint(10, 10)
@ -534,7 +537,7 @@ class TestConversion(unittest.TestCase):
for fmt in signed_formats: for fmt in signed_formats:
mx = max_value[fmt] mx = max_value[fmt]
size = sizeof_format[fmt] size = sizeof_format[fmt]
test = (self.colour + mx / 8).cast(fmt) test = (self.colour + mx / 8.0).cast(fmt)
im = test.msb() im = test.msb()
before = test.getpoint(10, 10) before = test.getpoint(10, 10)
@ -550,7 +553,7 @@ class TestConversion(unittest.TestCase):
for fmt in unsigned_formats: for fmt in unsigned_formats:
mx = max_value[fmt] mx = max_value[fmt]
size = sizeof_format[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) im = test.msb(band = 1)
before = [test.getpoint(10, 10)[1]] before = [test.getpoint(10, 10)[1]]
@ -644,8 +647,8 @@ class TestConversion(unittest.TestCase):
test = self.colour.cast(fmt) test = self.colour.cast(fmt)
im = test.subsample(3, 3) im = test.subsample(3, 3)
self.assertEqual(im.width, test.width / 3) self.assertEqual(im.width, test.width // 3)
self.assertEqual(im.height, test.height / 3) self.assertEqual(im.height, test.height // 3)
before = test.getpoint(60, 60) before = test.getpoint(60, 60)
after = im.getpoint(20, 20) after = im.getpoint(20, 20)