From 084d3d8e8525f6088ef2ac0ba83d93a5bb5c0daf Mon Sep 17 00:00:00 2001 From: Olivier Tilloy Date: Wed, 17 Feb 2010 22:35:35 +0100 Subject: Direct value assignment as an alternative to passing a fully-formed tag. --- src/pyexiv2/metadata.py | 19 ++++++++++++------- test/metadata.py | 18 +++++++++--------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/pyexiv2/metadata.py b/src/pyexiv2/metadata.py index 82fe1f9..0277ca0 100644 --- a/src/pyexiv2/metadata.py +++ b/src/pyexiv2/metadata.py @@ -170,10 +170,11 @@ class ImageMetadata(object): except AttributeError: raise KeyError(key) - def _set_exif_tag(self, tag): + def _set_exif_tag(self, key, tag): # Set an EXIF tag. If the tag already exists, its value is overwritten. if not isinstance(tag, ExifTag): - raise TypeError('Expecting an ExifTag') + # As a handy shortcut, accept direct value assignment. + tag = ExifTag(key, tag) self._image._setExifTagValue(tag.key, tag.raw_value) self._tags['exif'][tag.key] = tag if tag.key not in self.exif_keys: @@ -192,11 +193,12 @@ class ImageMetadata(object): raise TypeError('Expecting a string') self._image._setExifTagValue(key, value) - def _set_iptc_tag(self, tag): + def _set_iptc_tag(self, key, tag): # Set an IPTC tag. If the tag already exists, its values are # overwritten. if not isinstance(tag, IptcTag): - raise TypeError('Expecting an IptcTag') + # As a handy shortcut, accept direct value assignment. + tag = IptcTag(key, tag) self._image._setIptcTagValues(tag.key, tag.raw_values) self._tags['iptc'][tag.key] = tag if tag.key not in self.iptc_keys: @@ -219,10 +221,11 @@ class ImageMetadata(object): raise TypeError('Expecting a list of strings') self._image._setIptcTagValues(key, values) - def _set_xmp_tag(self, tag): + def _set_xmp_tag(self, key, tag): # Set an XMP tag. If the tag already exists, its value is overwritten. if not isinstance(tag, XmpTag): - raise TypeError('Expecting an XmpTag') + # As a handy shortcut, accept direct value assignment. + tag = XmpTag(key, tag) type = tag._tag._getExiv2Type() if type == 'XmpText': self._image._setXmpTagTextValue(tag.key, tag.raw_value) @@ -257,6 +260,8 @@ class ImageMetadata(object): """ Set a metadata tag for a given key. If the tag was previously set, it is overwritten. + As a handy shortcut, a value may be passed instead of a fully formed + tag. The corresponding tag object will be instantiated. :param key: metadata key in the dotted form ``familyName.groupName.tagName`` where ``familyName`` may @@ -270,7 +275,7 @@ class ImageMetadata(object): """ family = key.split('.')[0].lower() try: - return getattr(self, '_set_%s_tag' % family)(tag) + return getattr(self, '_set_%s_tag' % family)(key, tag) except AttributeError: raise KeyError(key) diff --git a/test/metadata.py b/test/metadata.py index 4ef933d..200c6ea 100644 --- a/test/metadata.py +++ b/test/metadata.py @@ -260,7 +260,7 @@ class TestImageMetadata(unittest.TestCase): tag = ExifTag('Exif.Thumbnail.Orientation', 1) self.assertEqual(tag.metadata, None) self.assert_(tag.key not in self.metadata.exif_keys) - self.metadata._set_exif_tag(tag) + self.metadata._set_exif_tag(tag.key, tag) self.assertEqual(tag.metadata, self.metadata) self.assert_(tag.key in self.metadata.exif_keys) self.assertEqual(self.metadata._tags['exif'], {tag.key: tag}) @@ -275,7 +275,7 @@ class TestImageMetadata(unittest.TestCase): # Overwrite an existing tag tag = ExifTag('Exif.Image.DateTime', datetime.datetime(2009, 3, 20, 20, 32, 0)) self.assertEqual(tag.metadata, None) - self.metadata._set_exif_tag(tag) + self.metadata._set_exif_tag(tag.key, tag) self.assertEqual(tag.metadata, self.metadata) self.assertEqual(self.metadata._tags['exif'], {tag.key: tag}) self.assert_(self.metadata._image.tags['exif'].has_key(tag.key)) @@ -292,7 +292,7 @@ class TestImageMetadata(unittest.TestCase): self.assertEqual(self.metadata._tags['exif'][key], tag) new_tag = ExifTag(key, '48 50 50 48 ', _tag=_ExifTagMock(key, 'Undefined', '48 50 50 48 ', '2.20')) self.assertEqual(new_tag.metadata, None) - self.metadata._set_exif_tag(new_tag) + self.metadata._set_exif_tag(key, new_tag) self.assertEqual(new_tag.metadata, self.metadata) self.assertEqual(self.metadata._tags['exif'], {key: new_tag}) self.assert_(self.metadata._image.tags['exif'].has_key(key)) @@ -396,7 +396,7 @@ class TestImageMetadata(unittest.TestCase): tag = IptcTag('Iptc.Application2.Writer', ['Nobody']) self.assertEqual(tag.metadata, None) self.assert_(tag.key not in self.metadata.iptc_keys) - self.metadata._set_iptc_tag(tag) + self.metadata._set_iptc_tag(tag.key, tag) self.assertEqual(tag.metadata, self.metadata) self.assert_(tag.key in self.metadata.iptc_keys) self.assertEqual(self.metadata._tags['iptc'], {tag.key: tag}) @@ -411,7 +411,7 @@ class TestImageMetadata(unittest.TestCase): # Overwrite an existing tag tag = IptcTag('Iptc.Application2.Caption', ['A picture.']) self.assertEqual(tag.metadata, None) - self.metadata._set_iptc_tag(tag) + self.metadata._set_iptc_tag(tag.key, tag) self.assertEqual(tag.metadata, self.metadata) self.assertEqual(self.metadata._tags['iptc'], {tag.key: tag}) self.assert_(self.metadata._image.tags['iptc'].has_key(tag.key)) @@ -428,7 +428,7 @@ class TestImageMetadata(unittest.TestCase): self.assertEqual(self.metadata._tags['iptc'][key], tag) new_tag = IptcTag(key, ['A picture.']) self.assertEqual(new_tag.metadata, None) - self.metadata._set_iptc_tag(new_tag) + self.metadata._set_iptc_tag(key, new_tag) self.assertEqual(new_tag.metadata, self.metadata) self.assertEqual(self.metadata._tags['iptc'], {key: new_tag}) self.assert_(self.metadata._image.tags['iptc'].has_key(key)) @@ -535,7 +535,7 @@ class TestImageMetadata(unittest.TestCase): 'fr-FR': "Ceci n'est pas un titre"}) self.assertEqual(tag.metadata, None) self.assert_(tag.key not in self.metadata.xmp_keys) - self.metadata._set_xmp_tag(tag) + self.metadata._set_xmp_tag(tag.key, tag) self.assertEqual(tag.metadata, self.metadata) self.assert_(tag.key in self.metadata.xmp_keys) self.assertEqual(self.metadata._tags['xmp'], {tag.key: tag}) @@ -550,7 +550,7 @@ class TestImageMetadata(unittest.TestCase): # Overwrite an existing tag tag = XmpTag('Xmp.dc.format', ('image', 'png')) self.assertEqual(tag.metadata, None) - self.metadata._set_xmp_tag(tag) + self.metadata._set_xmp_tag(tag.key, tag) self.assertEqual(tag.metadata, self.metadata) self.assertEqual(self.metadata._tags['xmp'], {tag.key: tag}) self.assert_(self.metadata._image.tags['xmp'].has_key(tag.key)) @@ -567,7 +567,7 @@ class TestImageMetadata(unittest.TestCase): self.assertEqual(self.metadata._tags['xmp'][key], tag) new_tag = XmpTag(key, datetime.datetime(2009, 4, 21, 20, 7, 0, tzinfo=FixedOffset('+', 1, 0))) self.assertEqual(new_tag.metadata, None) - self.metadata._set_xmp_tag(new_tag) + self.metadata._set_xmp_tag(key, new_tag) self.assertEqual(new_tag.metadata, self.metadata) self.assertEqual(self.metadata._tags['xmp'], {key: new_tag}) self.assert_(self.metadata._image.tags['xmp'].has_key(key)) -- cgit From 6587cfa02eddb930dce482332a26b647fedd4d53 Mon Sep 17 00:00:00 2001 From: Olivier Tilloy Date: Wed, 17 Feb 2010 23:34:39 +0100 Subject: Additional unit tests for direct value assignment. --- test/metadata.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/metadata.py b/test/metadata.py index 200c6ea..237a510 100644 --- a/test/metadata.py +++ b/test/metadata.py @@ -300,6 +300,22 @@ class TestImageMetadata(unittest.TestCase): # value. self.assertEqual(self.metadata._image.tags['exif'][key], new_tag.raw_value) + def test_set_exif_tag_direct_value_assignment(self): + self.metadata.read() + self._set_exif_tags() + self.assertEqual(self.metadata._tags['exif'], {}) + # Direct value assignment: pass a value instead of a fully-formed tag + key = 'Exif.Thumbnail.Orientation' + value = 1 + self.metadata._set_exif_tag(key, value) + self.assert_(key in self.metadata.exif_keys) + self.assert_(self.metadata._image.tags['exif'].has_key(key)) + tag = self.metadata._get_exif_tag(key) + self.assertEqual(tag.value, value) + self.assertEqual(tag.metadata, self.metadata) + self.assertEqual(self.metadata._tags['exif'], {key: tag}) + self.assertEqual(self.metadata._image.tags['exif'][key], tag.raw_value) + def test_set_exif_tag_value_inexistent(self): self.metadata.read() self._set_exif_tags() @@ -435,6 +451,22 @@ class TestImageMetadata(unittest.TestCase): self.assertEqual(self.metadata._image.tags['iptc'][key], new_tag.raw_values) + def test_set_iptc_tag_direct_value_assignment(self): + self.metadata.read() + self._set_iptc_tags() + self.assertEqual(self.metadata._tags['iptc'], {}) + # Direct value assignment: pass a value instead of a fully-formed tag + key = 'Iptc.Application2.Writer' + values = ['Nobody'] + self.metadata._set_iptc_tag(key, values) + self.assert_(key in self.metadata.iptc_keys) + self.assert_(self.metadata._image.tags['iptc'].has_key(key)) + tag = self.metadata._get_iptc_tag(key) + self.assertEqual(tag.values, values) + self.assertEqual(tag.metadata, self.metadata) + self.assertEqual(self.metadata._tags['iptc'], {key: tag}) + self.assertEqual(self.metadata._image.tags['iptc'][key], tag.raw_values) + def test_set_iptc_tag_values_inexistent(self): self.metadata.read() self._set_iptc_tags() @@ -574,6 +606,23 @@ class TestImageMetadata(unittest.TestCase): self.assertEqual(self.metadata._image.tags['xmp'][key], new_tag.raw_value) + def test_set_xmp_tag_direct_value_assignment(self): + self.metadata.read() + self._set_xmp_tags() + self.assertEqual(self.metadata._tags['xmp'], {}) + # Direct value assignment: pass a value instead of a fully-formed tag + key = 'Xmp.dc.title' + value = {'x-default': 'This is not a title', + 'fr-FR': "Ceci n'est pas un titre"} + self.metadata._set_xmp_tag(key, value) + self.assert_(key in self.metadata.xmp_keys) + self.assert_(self.metadata._image.tags['xmp'].has_key(key)) + tag = self.metadata._get_xmp_tag(key) + self.assertEqual(tag.value, value) + self.assertEqual(tag.metadata, self.metadata) + self.assertEqual(self.metadata._tags['xmp'], {key: tag}) + self.assertEqual(self.metadata._image.tags['xmp'][key], tag.raw_value) + def test_set_xmp_tag_value_inexistent(self): self.metadata.read() self._set_xmp_tags() -- cgit From b1a8e000faba81e603602d12704f402db13de3d3 Mon Sep 17 00:00:00 2001 From: Olivier Tilloy Date: Wed, 17 Feb 2010 23:42:47 +0100 Subject: Adapted the examples to make use of direct value assignment, which makes the syntax much lighter to application developers. --- src/examples.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/examples.py b/src/examples.py index 1a36758..d393387 100755 --- a/src/examples.py +++ b/src/examples.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -from pyexiv2 import ImageMetadata, ExifTag, IptcTag, XmpTag +from pyexiv2 import ImageMetadata import sys, os from datetime import datetime, date @@ -26,14 +26,14 @@ if __name__ == '__main__': print_key_value(metadata, key) # Set the value of the Exif.Image.DateTime tag - metadata[key].value = datetime.now() + metadata[key] = datetime.now() print_key_value(metadata, key) except KeyError: print '[not set]' # Add a new tag key = 'Exif.Image.Orientation' - metadata[key] = ExifTag(key, 1) + metadata[key] = 1 print_key_value(metadata, key) # Print a list of all the keys of the IPTC tags in the image @@ -45,7 +45,7 @@ if __name__ == '__main__': print_key_value(metadata, key) # Set the value of the Iptc.Application2.DateCreated tag - metadata[key].values = [date.today()] + metadata[key] = [date.today()] print_key_value(metadata, key) except KeyError: print '[not set]' @@ -53,7 +53,7 @@ if __name__ == '__main__': # Add a new tag key = 'Iptc.Application2.Keywords' keywords = ['little', 'big', 'man'] - metadata[key] = IptcTag(key, keywords) + metadata[key] = keywords print_key_value(metadata, key) # Print a list of all the keys of the XMP tags in the image @@ -65,15 +65,14 @@ if __name__ == '__main__': print_key_value(metadata, key) # Set the value of the Xmp.dc.subject tag - metadata[key].value = keywords + metadata[key] = keywords print_key_value(metadata, key) except KeyError: print '[not set]' # Add a new tag key = 'Xmp.dc.title' - value = {'x-default': 'Sunset', 'fr': 'Coucher de soleil'} - metadata[key] = XmpTag(key, value) + metadata[key] = {'x-default': 'Sunset', 'fr': 'Coucher de soleil'} print_key_value(metadata, key) # Write back the metadata to the file -- cgit From 0a20a06d69f6c5ef077505b180c4c54c18d08e71 Mon Sep 17 00:00:00 2001 From: Olivier Tilloy Date: Wed, 17 Feb 2010 23:50:00 +0100 Subject: More explicit parameter names: tag_or_value. --- src/pyexiv2/metadata.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/pyexiv2/metadata.py b/src/pyexiv2/metadata.py index 0277ca0..15b32f3 100644 --- a/src/pyexiv2/metadata.py +++ b/src/pyexiv2/metadata.py @@ -170,11 +170,13 @@ class ImageMetadata(object): except AttributeError: raise KeyError(key) - def _set_exif_tag(self, key, tag): + def _set_exif_tag(self, key, tag_or_value): # Set an EXIF tag. If the tag already exists, its value is overwritten. - if not isinstance(tag, ExifTag): + if isinstance(tag_or_value, ExifTag): + tag = tag_or_value + else: # As a handy shortcut, accept direct value assignment. - tag = ExifTag(key, tag) + tag = ExifTag(key, tag_or_value) self._image._setExifTagValue(tag.key, tag.raw_value) self._tags['exif'][tag.key] = tag if tag.key not in self.exif_keys: @@ -193,12 +195,14 @@ class ImageMetadata(object): raise TypeError('Expecting a string') self._image._setExifTagValue(key, value) - def _set_iptc_tag(self, key, tag): + def _set_iptc_tag(self, key, tag_or_values): # Set an IPTC tag. If the tag already exists, its values are # overwritten. - if not isinstance(tag, IptcTag): + if isinstance(tag_or_values, IptcTag): + tag = tag_or_values + else: # As a handy shortcut, accept direct value assignment. - tag = IptcTag(key, tag) + tag = IptcTag(key, tag_or_values) self._image._setIptcTagValues(tag.key, tag.raw_values) self._tags['iptc'][tag.key] = tag if tag.key not in self.iptc_keys: @@ -221,11 +225,13 @@ class ImageMetadata(object): raise TypeError('Expecting a list of strings') self._image._setIptcTagValues(key, values) - def _set_xmp_tag(self, key, tag): + def _set_xmp_tag(self, key, tag_or_value): # Set an XMP tag. If the tag already exists, its value is overwritten. - if not isinstance(tag, XmpTag): + if isinstance(tag_or_value, XmpTag): + tag = tag_or_value + else: # As a handy shortcut, accept direct value assignment. - tag = XmpTag(key, tag) + tag = XmpTag(key, tag_or_value) type = tag._tag._getExiv2Type() if type == 'XmpText': self._image._setXmpTagTextValue(tag.key, tag.raw_value) @@ -256,7 +262,7 @@ class ImageMetadata(object): else: raise TypeError('Expecting either a string, a list, a tuple or a dict') - def __setitem__(self, key, tag): + def __setitem__(self, key, tag_or_value): """ Set a metadata tag for a given key. If the tag was previously set, it is overwritten. @@ -267,15 +273,17 @@ class ImageMetadata(object): ``familyName.groupName.tagName`` where ``familyName`` may be one of ``exif``, ``iptc`` or ``xmp``. :type key: string - :param tag: an instance of the corresponding family of metadata tag - :type tag: :class:`pyexiv2.exif.ExifTag` or - :class:`pyexiv2.iptc.IptcTag` or :class:`pyexiv2.xmp.XmpTag` + :param tag_or_value: an instance of the corresponding family of metadata + tag, or a value + :type tag_or_value: :class:`pyexiv2.exif.ExifTag` or + :class:`pyexiv2.iptc.IptcTag` or + :class:`pyexiv2.xmp.XmpTag` or any valid value type :raise KeyError: if the key is invalid """ family = key.split('.')[0].lower() try: - return getattr(self, '_set_%s_tag' % family)(key, tag) + return getattr(self, '_set_%s_tag' % family)(key, tag_or_value) except AttributeError: raise KeyError(key) -- cgit From 314907a2796b09d25c99933f6f2c765ac9784784 Mon Sep 17 00:00:00 2001 From: Olivier Tilloy Date: Wed, 17 Feb 2010 23:57:36 +0100 Subject: Tutorial: direct value assignment explained. --- doc/tutorial.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/tutorial.rst b/doc/tutorial.rst index a5bc849..7ecf1e5 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -87,6 +87,13 @@ pair (see exiv2's documentation for a list of valid >>> value = 'This is a useful comment.' >>> metadata[key] = pyexiv2.ExifTag(key, value) +As a handy shortcut, you can always assign a value for a given key regardless +of whether it's already present in the metadata. +If a tag was present, its value is overwritten. +If the tag was not present, one is created and its value is set:: + + >>> metadata[key] = value + Reading and writing IPTC tags ############################# @@ -155,6 +162,13 @@ pair (see exiv2's documentation for a list of valid >>> values = ['John', 'Paul', 'Ringo', 'George'] >>> metadata[key] = pyexiv2.IptcTag(key, values) +As a handy shortcut, you can always assign values for a given key regardless +of whether it's already present in the metadata. +If a tag was present, its values are overwritten. +If the tag was not present, one is created and its values are set:: + + >>> metadata[key] = values + Reading and writing XMP tags ############################ @@ -209,6 +223,13 @@ pair (see exiv2's documentation for a list of valid >>> value = 'A beautiful picture.' >>> metadata[key] = pyexiv2.XmpTag(key, value) +As a handy shortcut, you can always assign a value for a given key regardless +of whether it's already present in the metadata. +If a tag was present, its value is overwritten. +If the tag was not present, one is created and its value is set:: + + >>> metadata[key] = value + Accessing embedded previews ########################### -- cgit