diff options
author | Olivier Tilloy <olivier@tilloy.net> | 2010-12-26 21:09:29 +0100 |
---|---|---|
committer | Olivier Tilloy <olivier@tilloy.net> | 2010-12-26 21:09:29 +0100 |
commit | 71116952f8a4714228155c755b55e588e17d5e6b (patch) | |
tree | 1b20734e849b77da34fdef024efb214b4576f73e | |
parent | 27f94886b6c043609366dd2f5604566d1264852d (diff) | |
parent | 3ece0524d87364f9edb40b70b95d8c39937572b9 (diff) | |
download | pyexiv2-71116952f8a4714228155c755b55e588e17d5e6b.tar.gz |
Use fractions.Fraction when available in the standard library (Python ≥ 2.6),
or fallback on the custom Rational class in a transparent manner.
-rw-r--r-- | src/pyexiv2/exif.py | 18 | ||||
-rw-r--r-- | src/pyexiv2/utils.py | 54 | ||||
-rw-r--r-- | src/pyexiv2/xmp.py | 10 | ||||
-rw-r--r-- | test/ReadMetadataTestCase.py | 61 | ||||
-rwxr-xr-x | test/TestsRunner.py | 3 | ||||
-rw-r--r-- | test/exif.py | 21 | ||||
-rw-r--r-- | test/metadata.py | 4 | ||||
-rw-r--r-- | test/pickling.py | 8 | ||||
-rw-r--r-- | test/utils.py | 50 | ||||
-rw-r--r-- | test/xmp.py | 13 |
10 files changed, 158 insertions, 84 deletions
diff --git a/src/pyexiv2/exif.py b/src/pyexiv2/exif.py index 4f3fe03..b0c6374 100644 --- a/src/pyexiv2/exif.py +++ b/src/pyexiv2/exif.py @@ -30,7 +30,7 @@ EXIF specific code. import libexiv2python -from pyexiv2.utils import Rational, Fraction, \ +from pyexiv2.utils import is_fraction, make_fraction, fraction_to_string, \ NotifyingList, ListenerInterface, \ undefined_to_string, string_to_undefined @@ -72,7 +72,8 @@ class ExifTag(ListenerInterface): - Comment: string - Long, SLong: [list of] long - Short, SShort: [list of] int - - Rational, SRational: [list of] :class:`pyexiv2.utils.Rational` + - Rational, SRational: [list of] :class:`fractions.Fraction` if available + (Python ≥ 2.6) or :class:`pyexiv2.utils.Rational` - Undefined: string """ @@ -316,7 +317,7 @@ class ExifTag(ListenerInterface): elif self.type in ('Rational', 'SRational'): try: - r = Rational.from_string(value) + r = make_fraction(value) except (ValueError, ZeroDivisionError): raise ExifValueError(value, self.type) else: @@ -424,17 +425,14 @@ class ExifTag(ListenerInterface): raise ExifValueError(value, self.type) elif self.type == 'Rational': - if (isinstance(value, Rational) or \ - (Fraction is not None and isinstance(value, Fraction))) \ - and value.numerator >= 0: - return str(value) + if is_fraction(value) and value.numerator >= 0: + return fraction_to_string(value) else: raise ExifValueError(value, self.type) elif self.type == 'SRational': - if isinstance(value, Rational) or \ - (Fraction is not None and isinstance(value, Fraction)): - return str(value) + if is_fraction(value): + return fraction_to_string(value) else: raise ExifValueError(value, self.type) diff --git a/src/pyexiv2/utils.py b/src/pyexiv2/utils.py index 5b0a3d5..4fcebf9 100644 --- a/src/pyexiv2/utils.py +++ b/src/pyexiv2/utils.py @@ -31,11 +31,10 @@ Utilitary classes and functions. import datetime import re -# Support for fractions.Fraction as a replacement for the Rational class is not -# implemented yet as we have to support versions of Python < 2.6 -# (see https://launchpad.net/bugs/514415). -# However, it doesn’t hurt to accept Fraction objects as values when the module -# is available (see https://launchpad.net/bugs/683232). +# pyexiv2 uses fractions.Fraction when available (Python ≥ 2.6), or falls back +# on the custom Rational class. This should be transparent to the application +# developer as both classes have a similar API. +# This module contains convenience functions to ease manipulation of fractions. try: from fractions import Fraction except ImportError: @@ -250,6 +249,51 @@ class Rational(object): return '%d/%d' % (self._numerator, self._denominator) +def is_fraction(obj): + """ + Test whether the object is a valid fraction. + """ + if Fraction is not None and isinstance(obj, Fraction): + return True + elif isinstance(obj, Rational): + return True + else: + return False + + +def make_fraction(*args): + """ + Make a fraction. + + The type of the returned object depends on the availability of the + fractions module in the standard library (Python ≥ 2.6). + """ + if Fraction is not None: + return Fraction(*args) + else: + if len(args) == 1: + return Rational.from_string(*args) + else: + return Rational(*args) + + +def fraction_to_string(fraction): + """ + Return a string representation of a fraction, suitable to pass to libexiv2. + + The returned string is always in the form '[numerator]/[denominator]'. + + :raise TypeError: if the argument is not a valid fraction + """ + if Fraction is not None and isinstance(fraction, Fraction): + # fractions.Fraction.__str__ returns '0' for a null numerator. + return '%s/%s' % (fraction.numerator, fraction.denominator) + elif isinstance(fraction, Rational): + return str(fraction) + else: + raise TypeError('Not a fraction') + + class ListenerInterface(object): """ diff --git a/src/pyexiv2/xmp.py b/src/pyexiv2/xmp.py index ab2edeb..469af98 100644 --- a/src/pyexiv2/xmp.py +++ b/src/pyexiv2/xmp.py @@ -30,7 +30,7 @@ XMP specific code. import libexiv2python -from pyexiv2.utils import FixedOffset, Rational, Fraction, GPSCoordinate +from pyexiv2.utils import FixedOffset, is_fraction, make_fraction, GPSCoordinate import datetime import re @@ -74,7 +74,8 @@ class XmpTag(object): - Integer: int - Locale: *[not implemented yet]* - MIMEType: 2-tuple of strings - - Rational: :class:`pyexiv2.utils.Rational` + - Rational: :class:`fractions.Fraction` if available (Python ≥ 2.6) or + :class:`pyexiv2.utils.Rational` - Real: *[not implemented yet]* - AgentName, ProperName, Text: unicode string - Thumbnail: *[not implemented yet]* @@ -338,7 +339,7 @@ class XmpTag(object): elif type == 'Rational': try: - return Rational.from_string(value) + return make_fraction(value) except (ValueError, ZeroDivisionError): raise XmpValueError(value, type) @@ -435,8 +436,7 @@ class XmpTag(object): raise XmpValueError(value, type) elif type == 'Rational': - if isinstance(value, Rational) or \ - (Fraction is not None and isinstance(value, Fraction)): + if is_fraction(value): return str(value) else: raise XmpValueError(value, type) diff --git a/test/ReadMetadataTestCase.py b/test/ReadMetadataTestCase.py index 40f0843..3bf8e09 100644 --- a/test/ReadMetadataTestCase.py +++ b/test/ReadMetadataTestCase.py @@ -25,12 +25,19 @@ # # ****************************************************************************** +import pyexiv2 +from pyexiv2.utils import is_fraction, make_fraction + import unittest -import testutils import os.path -import pyexiv2 import datetime +import testutils + + +FRACTION = 'fraction' + + class ReadMetadataTestCase(unittest.TestCase): """ @@ -38,7 +45,10 @@ class ReadMetadataTestCase(unittest.TestCase): """ def check_type_and_value(self, tag, etype, evalue): - self.assert_(isinstance(tag.value, etype)) + if etype == FRACTION: + self.assert_(is_fraction(tag.value)) + else: + self.assert_(isinstance(tag.value, etype)) self.assertEqual(tag.value, evalue) def check_type_and_values(self, tag, etype, evalues): @@ -68,8 +78,8 @@ class ReadMetadataTestCase(unittest.TestCase): # Exhaustive tests on the values of EXIF metadata exifTags = [('Exif.Image.ImageDescription', str, 'Well it is a smiley that happens to be green'), - ('Exif.Image.XResolution', pyexiv2.Rational, pyexiv2.Rational(72, 1)), - ('Exif.Image.YResolution', pyexiv2.Rational, pyexiv2.Rational(72, 1)), + ('Exif.Image.XResolution', FRACTION, make_fraction(72, 1)), + ('Exif.Image.YResolution', FRACTION, make_fraction(72, 1)), ('Exif.Image.ResolutionUnit', int, 2), ('Exif.Image.Software', str, 'ImageReady'), ('Exif.Image.DateTime', datetime.datetime, datetime.datetime(2004, 7, 13, 21, 23, 44)), @@ -115,36 +125,22 @@ class ReadMetadataTestCase(unittest.TestCase): ('Xmp.dc.source', unicode, u'FreeFoto.com'), ('Xmp.dc.subject', list, [u'Communications']), ('Xmp.dc.title', dict, {u'x-default': u'Communications'}), - ('Xmp.exif.ApertureValue', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(8, 1)), - ('Xmp.exif.BrightnessValue', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(333, 1280)), + ('Xmp.exif.ApertureValue', FRACTION, make_fraction(8, 1)), + ('Xmp.exif.BrightnessValue', FRACTION, make_fraction(333, 1280)), ('Xmp.exif.ColorSpace', int, 1), ('Xmp.exif.DateTimeOriginal', datetime.datetime, datetime.datetime(2002, 7, 13, 15, 58, 28, tzinfo=pyexiv2.utils.FixedOffset())), ('Xmp.exif.ExifVersion', unicode, u'0200'), - ('Xmp.exif.ExposureBiasValue', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(-13, 20)), + ('Xmp.exif.ExposureBiasValue', FRACTION, make_fraction(-13, 20)), ('Xmp.exif.ExposureProgram', int, 4), - ('Xmp.exif.FNumber', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(3, 5)), + ('Xmp.exif.FNumber', FRACTION, make_fraction(3, 5)), ('Xmp.exif.FileSource', int, 0), ('Xmp.exif.FlashpixVersion', unicode, u'0100'), - ('Xmp.exif.FocalLength', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(0, 1)), + ('Xmp.exif.FocalLength', FRACTION, make_fraction(0, 1)), ('Xmp.exif.FocalPlaneResolutionUnit', int, 2), - ('Xmp.exif.FocalPlaneXResolution', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(3085, 256)), - ('Xmp.exif.FocalPlaneYResolution', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(3085, 256)), + ('Xmp.exif.FocalPlaneXResolution', FRACTION, make_fraction(3085, 256)), + ('Xmp.exif.FocalPlaneYResolution', FRACTION, make_fraction(3085, 256)), ('Xmp.exif.GPSLatitude', pyexiv2.utils.GPSCoordinate, pyexiv2.utils.GPSCoordinate.from_string('54,59.380000N')), @@ -162,9 +158,7 @@ class ReadMetadataTestCase(unittest.TestCase): ('Xmp.exif.PixelYDimension', int, 1600), ('Xmp.exif.SceneType', int, 0), ('Xmp.exif.SensingMethod', int, 2), - ('Xmp.exif.ShutterSpeedValue', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(30827, 3245)), + ('Xmp.exif.ShutterSpeedValue', FRACTION, make_fraction(30827, 3245)), ('Xmp.pdf.Keywords', unicode, u'Communications'), ('Xmp.photoshop.AuthorsPosition', unicode, u'Photographer'), ('Xmp.photoshop.CaptionWriter', unicode, u'Ian Britton'), @@ -191,13 +185,9 @@ class ReadMetadataTestCase(unittest.TestCase): ('Xmp.tiff.Orientation', int, 1), ('Xmp.tiff.ResolutionUnit', int, 2), ('Xmp.tiff.Software', unicode, u'Adobe Photoshop 7.0'), - ('Xmp.tiff.XResolution', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(300, 1)), + ('Xmp.tiff.XResolution', FRACTION, make_fraction(300, 1)), ('Xmp.tiff.YCbCrPositioning', int, 2), - ('Xmp.tiff.YResolution', - pyexiv2.utils.Rational, - pyexiv2.utils.Rational(300, 1)), + ('Xmp.tiff.YResolution', FRACTION, make_fraction(300, 1)), ('Xmp.xmp.CreateDate', datetime.datetime, datetime.datetime(2002, 7, 13, 15, 58, 28, tzinfo=pyexiv2.utils.FixedOffset())), @@ -215,3 +205,4 @@ class ReadMetadataTestCase(unittest.TestCase): self.assertEqual(image.xmp_keys, [tag[0] for tag in xmpTags]) for key, ktype, value in xmpTags: self.check_type_and_value(image[key], ktype, value) + diff --git a/test/TestsRunner.py b/test/TestsRunner.py index 198bb27..ba5162c 100755 --- a/test/TestsRunner.py +++ b/test/TestsRunner.py @@ -38,7 +38,7 @@ from xmp import TestXmpTag, TestXmpNamespaces from metadata import TestImageMetadata from buffer import TestBuffer from encoding import TestEncodings -from utils import TestConversions +from utils import TestConversions, TestFractions from usercomment import TestUserCommentReadWrite, TestUserCommentAdd from pickling import TestPicklingTags @@ -58,6 +58,7 @@ def run_unit_tests(): suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestBuffer)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestEncodings)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestConversions)) + suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestFractions)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestUserCommentReadWrite)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestUserCommentAdd)) suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(TestPicklingTags)) diff --git a/test/exif.py b/test/exif.py index 1f929b7..38e19b7 100644 --- a/test/exif.py +++ b/test/exif.py @@ -27,7 +27,7 @@ import unittest from pyexiv2.exif import ExifTag, ExifValueError -from pyexiv2.utils import Rational, Fraction +from pyexiv2.utils import make_fraction import datetime @@ -260,7 +260,7 @@ class TestExifTag(unittest.TestCase): # Valid values tag = ExifTag('Exif.Image.XResolution') self.assertEqual(tag.type, 'Rational') - self.assertEqual(tag._convert_to_python('5/3'), Rational(5, 3)) + self.assertEqual(tag._convert_to_python('5/3'), make_fraction(5, 3)) # Invalid values self.failUnlessRaises(ExifValueError, tag._convert_to_python, 'invalid') @@ -272,21 +272,19 @@ class TestExifTag(unittest.TestCase): # Valid values tag = ExifTag('Exif.Image.XResolution') self.assertEqual(tag.type, 'Rational') - self.assertEqual(tag._convert_to_string(Rational(5, 3)), '5/3') - if Fraction is not None: - self.assertEqual(tag._convert_to_string(Fraction('1.6')), '8/5') + self.assertEqual(tag._convert_to_string(make_fraction(5, 3)), '5/3') # Invalid values self.failUnlessRaises(ExifValueError, tag._convert_to_string, 'invalid') self.failUnlessRaises(ExifValueError, - tag._convert_to_string, Rational(-5, 3)) + tag._convert_to_string, make_fraction(-5, 3)) def test_convert_to_python_srational(self): # Valid values tag = ExifTag('Exif.Image.BaselineExposure') self.assertEqual(tag.type, 'SRational') - self.assertEqual(tag._convert_to_python('5/3'), Rational(5, 3)) - self.assertEqual(tag._convert_to_python('-5/3'), Rational(-5, 3)) + self.assertEqual(tag._convert_to_python('5/3'), make_fraction(5, 3)) + self.assertEqual(tag._convert_to_python('-5/3'), make_fraction(-5, 3)) # Invalid values self.failUnlessRaises(ExifValueError, tag._convert_to_python, 'invalid') @@ -297,11 +295,8 @@ class TestExifTag(unittest.TestCase): # Valid values tag = ExifTag('Exif.Image.BaselineExposure') self.assertEqual(tag.type, 'SRational') - self.assertEqual(tag._convert_to_string(Rational(5, 3)), '5/3') - self.assertEqual(tag._convert_to_string(Rational(-5, 3)), '-5/3') - if Fraction is not None: - self.assertEqual(tag._convert_to_string(Fraction('1.6')), '8/5') - self.assertEqual(tag._convert_to_string(Fraction('-1.6')), '-8/5') + self.assertEqual(tag._convert_to_string(make_fraction(5, 3)), '5/3') + self.assertEqual(tag._convert_to_string(make_fraction(-5, 3)), '-5/3') # Invalid values self.failUnlessRaises(ExifValueError, tag._convert_to_string, 'invalid') diff --git a/test/metadata.py b/test/metadata.py index bc4d1ff..177ec9d 100644 --- a/test/metadata.py +++ b/test/metadata.py @@ -28,7 +28,7 @@ from pyexiv2.metadata import ImageMetadata from pyexiv2.exif import ExifTag from pyexiv2.iptc import IptcTag from pyexiv2.xmp import XmpTag -from pyexiv2.utils import FixedOffset, Rational +from pyexiv2.utils import FixedOffset, make_fraction import datetime import os @@ -501,7 +501,7 @@ class TestImageMetadata(unittest.TestCase): self.metadata.read() # Set new tags key = 'Exif.Photo.ExposureBiasValue' - tag = ExifTag(key, Rational(0, 3)) + tag = ExifTag(key, make_fraction(0, 3)) self.metadata[key] = tag self.failUnless(key in self.metadata._tags['exif']) self.failUnlessEqual(self.metadata._tags['exif'][key], tag) diff --git a/test/pickling.py b/test/pickling.py index d98c146..8d0101b 100644 --- a/test/pickling.py +++ b/test/pickling.py @@ -27,7 +27,7 @@ from pyexiv2.exif import ExifTag from pyexiv2.iptc import IptcTag from pyexiv2.xmp import XmpTag -from pyexiv2.utils import Rational, FixedOffset +from pyexiv2.utils import make_fraction, FixedOffset import unittest import pickle @@ -49,8 +49,8 @@ class TestPicklingTags(unittest.TestCase): tags.append(ExifTag('Exif.Image.TimeZoneOffset', 7)) tags.append(ExifTag('Exif.Image.ImageWidth', 7492)) tags.append(ExifTag('Exif.OlympusCs.ManometerReading', 29)) - tags.append(ExifTag('Exif.Image.XResolution', Rational(7, 3))) - tags.append(ExifTag('Exif.Image.BaselineExposure', Rational(-7, 3))) + tags.append(ExifTag('Exif.Image.XResolution', make_fraction(7, 3))) + tags.append(ExifTag('Exif.Image.BaselineExposure', make_fraction(-7, 3))) tags.append(ExifTag('Exif.Photo.ExifVersion', '0100')) for tag in tags: s = pickle.dumps(tag) @@ -102,7 +102,7 @@ class TestPicklingTags(unittest.TestCase): tags.append(XmpTag('Xmp.dc.source', 'bleh')) tags.append(XmpTag('Xmp.xmpMM.DocumentID', 'http://example.com')) tags.append(XmpTag('Xmp.xmp.BaseURL', 'http://example.com')) - tags.append(XmpTag('Xmp.xmpDM.videoPixelAspectRatio', Rational(5, 3))) + tags.append(XmpTag('Xmp.xmpDM.videoPixelAspectRatio', make_fraction(5, 3))) for tag in tags: s = pickle.dumps(tag) t = pickle.loads(s) diff --git a/test/utils.py b/test/utils.py index 2f0d19b..11b1766 100644 --- a/test/utils.py +++ b/test/utils.py @@ -26,7 +26,9 @@ import unittest -from pyexiv2.utils import undefined_to_string, string_to_undefined +from pyexiv2.utils import undefined_to_string, string_to_undefined, \ + Rational, Fraction, \ + is_fraction, make_fraction, fraction_to_string class TestConversions(unittest.TestCase): @@ -48,3 +50,49 @@ class TestConversions(unittest.TestCase): value = "48 50 50 49" self.assertEqual(string_to_undefined(undefined_to_string(value)), value) + +class TestFractions(unittest.TestCase): + + def test_is_fraction(self): + if Fraction is not None: + self.failUnless(is_fraction(Fraction())) + self.failUnless(is_fraction(Fraction(3, 5))) + self.failUnless(is_fraction(Fraction(Fraction(4, 5)))) + self.failUnless(is_fraction(Fraction('3/2'))) + self.failUnless(is_fraction(Fraction('-4/5'))) + self.failUnless(is_fraction(Rational(3, 5))) + self.failUnless(is_fraction(Rational(-4, 5))) + self.failUnless(is_fraction(Rational.from_string("3/5"))) + self.failUnless(is_fraction(Rational.from_string("-4/5"))) + + self.failIf(is_fraction(3 / 5)) + self.failIf(is_fraction('3/5')) + self.failIf(is_fraction('2.7')) + self.failIf(is_fraction(2.7)) + self.failIf(is_fraction('notafraction')) + self.failIf(is_fraction(None)) + + def test_make_fraction(self): + if Fraction is not None: + self.assertEqual(make_fraction(3, 5), Fraction(3, 5)) + self.assertEqual(make_fraction(-3, 5), Fraction(-3, 5)) + self.assertEqual(make_fraction('3/2'), Fraction(3, 2)) + self.assertEqual(make_fraction('-3/4'), Fraction(-3, 4)) + else: + self.assertEqual(make_fraction(3, 5), Rational(3, 5)) + self.assertEqual(make_fraction(-3, 5), Rational(-3, 5)) + self.assertEqual(make_fraction('3/2'), Rational(3, 2)) + self.assertEqual(make_fraction('-3/4'), Rational(-3, 4)) + + self.assertRaises(ZeroDivisionError, make_fraction, 3, 0) + self.assertRaises(ZeroDivisionError, make_fraction, '3/0') + self.assertRaises(TypeError, make_fraction, 5, 3, 2) + self.assertRaises(TypeError, make_fraction, None) + + def test_fraction_to_string(self): + self.assertEqual(fraction_to_string(make_fraction(3, 5)), '3/5') + self.assertEqual(fraction_to_string(make_fraction(-3, 5)), '-3/5') + self.assertEqual(fraction_to_string(make_fraction(0, 1)), '0/1') + self.assertRaises(TypeError, fraction_to_string, None) + self.assertRaises(TypeError, fraction_to_string, 'invalid') + diff --git a/test/xmp.py b/test/xmp.py index 3379851..f3fa4c3 100644 --- a/test/xmp.py +++ b/test/xmp.py @@ -28,7 +28,7 @@ import unittest from pyexiv2.xmp import XmpTag, XmpValueError, register_namespace, \ unregister_namespace, unregister_namespaces -from pyexiv2.utils import FixedOffset, Rational, Fraction +from pyexiv2.utils import FixedOffset, make_fraction from pyexiv2.metadata import ImageMetadata import datetime @@ -288,8 +288,8 @@ class TestXmpTag(unittest.TestCase): # Valid values tag = XmpTag('Xmp.xmpDM.videoPixelAspectRatio') self.assertEqual(tag.type, 'Rational') - self.assertEqual(tag._convert_to_python('5/3', 'Rational'), Rational(5, 3)) - self.assertEqual(tag._convert_to_python('-5/3', 'Rational'), Rational(-5, 3)) + self.assertEqual(tag._convert_to_python('5/3', 'Rational'), make_fraction(5, 3)) + self.assertEqual(tag._convert_to_python('-5/3', 'Rational'), make_fraction(-5, 3)) # Invalid values self.failUnlessRaises(XmpValueError, tag._convert_to_python, 'invalid', 'Rational') @@ -300,11 +300,8 @@ class TestXmpTag(unittest.TestCase): # Valid values tag = XmpTag('Xmp.xmpDM.videoPixelAspectRatio') self.assertEqual(tag.type, 'Rational') - self.assertEqual(tag._convert_to_string(Rational(5, 3), 'Rational'), '5/3') - self.assertEqual(tag._convert_to_string(Rational(-5, 3), 'Rational'), '-5/3') - if Fraction is not None: - self.assertEqual(tag._convert_to_string(Fraction('1.6'), 'Rational'), '8/5') - self.assertEqual(tag._convert_to_string(Fraction('-1.6'), 'Rational'), '-8/5') + self.assertEqual(tag._convert_to_string(make_fraction(5, 3), 'Rational'), '5/3') + self.assertEqual(tag._convert_to_string(make_fraction(-5, 3), 'Rational'), '-5/3') # Invalid values self.failUnlessRaises(XmpValueError, tag._convert_to_string, 'invalid', 'Rational') |