diff options
-rw-r--r-- | src/pyexiv2.py | 38 | ||||
-rw-r--r-- | test/iptc.py | 235 |
2 files changed, 255 insertions, 18 deletions
diff --git a/src/pyexiv2.py b/src/pyexiv2.py index 4bdba93..88d08f2 100644 --- a/src/pyexiv2.py +++ b/src/pyexiv2.py @@ -771,8 +771,13 @@ class IptcTag(MetadataTag): def __init__(self, key, name, label, description, type, values): super(IptcTag, self).__init__(key, name, label, description, type, values) + self._init_values() + + def _init_values(self): + # Initial conversion of the raw values to their corresponding python + # types. + values = map(self._convert_to_python, self.raw_value) # Make values a notifying list - values = map(lambda x: IptcTag._convert_to_python(x, type), values) self._values = NotifyingList(values) self._values.register_listener(self) @@ -808,34 +813,31 @@ class IptcTag(MetadataTag): # The following is a quick, non optimal solution. self._set_values(self._values) - @staticmethod - def _convert_to_python(value, xtype): + def _convert_to_python(self, value): """ Convert a raw value to its corresponding python type. @param value: the raw value to be converted @type value: C{str} - @param xtype: the IPTC type of the value - @type xtype: C{str} @return: the value converted to its corresponding python type - @rtype: depends on xtype (DOCME) + @rtype: depends on C{self.type} (DOCME) @raise IptcValueError: if the conversion fails """ - if xtype == 'Short': + if self.type == 'Short': try: return int(value) except ValueError: - raise IptcValueError(value, xtype) + raise IptcValueError(value, self.type) - elif xtype == 'String': + elif self.type == 'String': try: return unicode(value, 'utf-8') except TypeError: - raise IptcValueError(value, xtype) + raise IptcValueError(value, self.type) - elif xtype == 'Date': + elif self.type == 'Date': # According to the IPTC specification, the format for a string field # representing a date is '%Y%m%d'. However, the string returned by # exiv2 using method DateValue::toString() is formatted using @@ -845,33 +847,33 @@ class IptcTag(MetadataTag): t = time.strptime(value, format) return datetime.date(*t[:3]) except ValueError: - raise IptcValueError(value, xtype) + raise IptcValueError(value, self.type) - elif xtype == 'Time': + elif self.type == 'Time': # According to the IPTC specification, the format for a string field # representing a time is '%H%M%S±%H%M'. However, the string returned # by exiv2 using method TimeValue::toString() is formatted using # pattern '%H:%M:%S±%H:%M'. match = IptcTag._time_re.match(value) if match is None: - raise IptcValueError(value, xtype) + raise IptcValueError(value, self.type) gd = match.groupdict() try: tzinfo = FixedOffset(gd['sign'], int(gd['ohours']), int(gd['ominutes'])) except TypeError: - raise IptcValueError(value, xtype) + raise IptcValueError(value, self.type) try: return datetime.time(int(gd['hours']), int(gd['minutes']), int(gd['seconds']), tzinfo=tzinfo) except (TypeError, ValueError): - raise IptcValueError(value, xtype) + raise IptcValueError(value, self.type) - elif xtype == 'Undefined': + elif self.type == 'Undefined': # Binary data, return it unmodified return value - raise IptcValueError(value, xtype) + raise IptcValueError(value, self.type) @staticmethod def _convert_to_string(value, xtype): diff --git a/test/iptc.py b/test/iptc.py new file mode 100644 index 0000000..a630c24 --- /dev/null +++ b/test/iptc.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- + +# ****************************************************************************** +# +# Copyright (C) 2009 Olivier Tilloy <olivier@tilloy.net> +# +# This file is part of the pyexiv2 distribution. +# +# pyexiv2 is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# pyexiv2 is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyexiv2; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. +# +# Author: Olivier Tilloy <olivier@tilloy.net> +# +# ****************************************************************************** + +import unittest +from pyexiv2 import IptcTag, IptcValueError, FixedOffset +import datetime + + +class IptcTagMock(IptcTag): + + def __init__(self, key, type): + super(IptcTagMock, self).__init__(key, '', '', '', type, []) + + def _init_values(self): + pass + + +class ImageMetadataMock(object): + + tags = {} + + def _set_iptc_tag_values(self, key, values): + self.tags[key] = values + + def _delete_iptc_tag(self, key): + try: + del self.tags[key] + except KeyError: + pass + + +class TestIptcTag(unittest.TestCase): + + def test_convert_to_python_short(self): + type = 'Short' + + # Valid values + tag = IptcTagMock('Iptc.Envelope.FileFormat', type) + self.assertEqual(tag._convert_to_python('23'), 23) + self.assertEqual(tag._convert_to_python('+5628'), 5628) + self.assertEqual(tag._convert_to_python('-4'), -4) + + # Invalid values + self.failUnlessRaises(IptcValueError, tag._convert_to_python, 'abc') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '5,64') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '47.0001') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '1E3') + + def test_convert_to_string_short(self): + xtype = 'Short' + # Valid values + self.assertEqual(IptcTag._convert_to_string(123, xtype), '123') + self.assertEqual(IptcTag._convert_to_string(-57, xtype), '-57') + # Invalid values + self.failUnlessRaises(IptcValueError, IptcTag._convert_to_string, 'invalid', xtype) + self.failUnlessRaises(IptcValueError, IptcTag._convert_to_string, 3.14, xtype) + + def test_convert_to_python_string(self): + type = 'String' + + # Valid values + tag = IptcTagMock('Iptc.Application2.Subject', type) + self.assertEqual(tag._convert_to_python('Some text.'), 'Some text.') + self.assertEqual(tag._convert_to_python('Some text with exotic chàräctérʐ.'), + u'Some text with exotic chàräctérʐ.') + + # Invalid values + self.failUnlessRaises(IptcValueError, tag._convert_to_python, None) + + def test_convert_to_string_string(self): + xtype = 'String' + # Valid values + self.assertEqual(IptcTag._convert_to_string(u'Some text', xtype), 'Some text') + self.assertEqual(IptcTag._convert_to_string(u'Some text with exotic chàräctérʐ.', xtype), + 'Some text with exotic chàräctérʐ.') + self.assertEqual(IptcTag._convert_to_string('Some text with exotic chàräctérʐ.', xtype), + 'Some text with exotic chàräctérʐ.') + # Invalid values + self.failUnlessRaises(IptcValueError, IptcTag._convert_to_string, None, xtype) + + def test_convert_to_python_date(self): + type = 'Date' + + # Valid values + tag = IptcTagMock('Iptc.Envelope.DateSent', type) + self.assertEqual(tag._convert_to_python('1999-10-13'), + datetime.date(1999, 10, 13)) + + # Invalid values + self.failUnlessRaises(IptcValueError, tag._convert_to_python, 'invalid') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '11/10/1983') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '-1000') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '2009-02') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '2009-10-32') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '2009-02-24T22:12:54') + + def test_convert_to_string_date(self): + xtype = 'Date' + # Valid values + self.assertEqual(IptcTag._convert_to_string(datetime.date(2009, 2, 4), xtype), + '20090204') + self.assertEqual(IptcTag._convert_to_string(datetime.datetime(1999, 10, 13), xtype), + '19991013') + self.assertEqual(IptcTag._convert_to_string(datetime.datetime(2009, 2, 4), xtype), + '20090204') + self.assertEqual(IptcTag._convert_to_string(datetime.datetime(2009, 2, 4, 10, 52, 37), xtype), + '20090204') + # Invalid values + self.failUnlessRaises(IptcValueError, IptcTag._convert_to_string, 'invalid', xtype) + self.failUnlessRaises(IptcValueError, IptcTag._convert_to_string, None, xtype) + + def test_convert_to_python_time(self): + type = 'Time' + + # Valid values + tag = IptcTagMock('Iptc.Envelope.TimeSent', type) + self.assertEqual(tag._convert_to_python('05:03:54+00:00'), + datetime.time(5, 3, 54, tzinfo=FixedOffset())) + self.assertEqual(tag._convert_to_python('05:03:54+06:00'), + datetime.time(5, 3, 54, tzinfo=FixedOffset('+', 6, 0))) + self.assertEqual(tag._convert_to_python('05:03:54-10:30'), + datetime.time(5, 3, 54, tzinfo=FixedOffset('-', 10, 30))) + + # Invalid values + self.failUnlessRaises(IptcValueError, tag._convert_to_python, 'invalid') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '23:12:42') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '25:12:42+00:00') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '21:77:42+00:00') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '21:12:98+00:00') + self.failUnlessRaises(IptcValueError, tag._convert_to_python, '081242+0000') + + def test_convert_to_string_time(self): + xtype = 'Time' + # Valid values + self.assertEqual(IptcTag._convert_to_string(datetime.time(10, 52, 4), xtype), + '105204+0000') + self.assertEqual(IptcTag._convert_to_string(datetime.time(10, 52, 4, 574), xtype), + '105204+0000') + self.assertEqual(IptcTag._convert_to_string(datetime.time(10, 52, 4, tzinfo=FixedOffset()), xtype), + '105204+0000') + self.assertEqual(IptcTag._convert_to_string(datetime.time(10, 52, 4, tzinfo=FixedOffset('+', 5, 30)), xtype), + '105204+0530') + self.assertEqual(IptcTag._convert_to_string(datetime.time(10, 52, 4, tzinfo=FixedOffset('-', 4, 0)), xtype), + '105204-0400') + self.assertEqual(IptcTag._convert_to_string(datetime.datetime(2007, 2, 7, 10, 52, 4), xtype), + '105204+0000') + self.assertEqual(IptcTag._convert_to_string(datetime.datetime(2007, 2, 7, 10, 52, 4, 478), xtype), + '105204+0000') + self.assertEqual(IptcTag._convert_to_string(datetime.datetime(2007, 2, 7, 10, 52, 4, tzinfo=FixedOffset()), xtype), + '105204+0000') + self.assertEqual(IptcTag._convert_to_string(datetime.datetime(2007, 2, 7, 10, 52, 4, tzinfo=FixedOffset('+', 5, 30)), xtype), + '105204+0530') + self.assertEqual(IptcTag._convert_to_string(datetime.datetime(2007, 2, 7, 10, 52, 4, tzinfo=FixedOffset('-', 4, 0)), xtype), + '105204-0400') + # Invalid values + self.failUnlessRaises(IptcValueError, IptcTag._convert_to_string, 'invalid', xtype) + + def test_convert_to_python_undefined(self): + type = 'Undefined' + + # Valid values + tag = IptcTagMock('Iptc.Envelope.CharacterSet', type) + self.assertEqual(tag._convert_to_python('Some binary data.'), + 'Some binary data.') + self.assertEqual(tag._convert_to_python('�lj1�eEϟ�u����ᒻ;C(�SpI]���QI�}'), + '�lj1�eEϟ�u����ᒻ;C(�SpI]���QI�}') + + def test_convert_to_string_undefined(self): + xtype = 'Undefined' + # Valid values + self.assertEqual(IptcTag._convert_to_string('Some binary data.', xtype), + 'Some binary data.') + self.assertEqual(IptcTag._convert_to_string('�lj1�eEϟ�u����ᒻ;C(�SpI]���QI�}', xtype), + '�lj1�eEϟ�u����ᒻ;C(�SpI]���QI�}') + # Invalid values + self.failUnlessRaises(IptcValueError, IptcTag._convert_to_string, None, xtype) + + def test_set_values_no_metadata(self): + tag = IptcTag('Iptc.Application2.City', 'City', 'City', 'Identifies ' \ + 'city of object data origin according to guidelines ' \ + 'established by the provider.', 'String', ['Seattle']) + old_values = tag.values + tag.values = ['Barcelona'] + self.failIfEqual(tag.values, old_values) + + def test_set_values_with_metadata(self): + tag = IptcTag('Iptc.Application2.City', 'City', 'City', 'Identifies ' \ + 'city of object data origin according to guidelines ' \ + 'established by the provider.', 'String', ['Seattle']) + tag.metadata = ImageMetadataMock() + old_values = tag.values + tag.values = ['Barcelona'] + self.failIfEqual(tag.values, old_values) + self.assertEqual(tag.metadata.tags[tag.key], ['Barcelona']) + + def test_del_values_no_metadata(self): + tag = IptcTag('Iptc.Application2.City', 'City', 'City', 'Identifies ' \ + 'city of object data origin according to guidelines ' \ + 'established by the provider.', 'String', ['Seattle']) + del tag.values + self.failIf(hasattr(tag, 'values')) + + def test_del_values_with_metadata(self): + tag = IptcTag('Iptc.Application2.City', 'City', 'City', 'Identifies ' \ + 'city of object data origin according to guidelines ' \ + 'established by the provider.', 'String', ['Seattle']) + tag.metadata = ImageMetadataMock() + tag.metadata._set_iptc_tag_values(tag.key, tag.to_string()) + self.assertEqual(tag.metadata.tags, {tag.key: ['Seattle']}) + del tag.values + self.failIf(hasattr(tag, 'values')) + self.failIf(tag.metadata.tags.has_key(tag.key)) |