def test_from_string(self): self.assertEqual(GPSCoordinate.from_string('54,59.380000N'), GPSCoordinate(54, 59, 23, 'N')) self.assertEqual(GPSCoordinate.from_string('1,54.850000W'), GPSCoordinate(1, 54, 51, 'W')) self.assertRaises(ValueError, GPSCoordinate.from_string, '51N') self.assertRaises(ValueError, GPSCoordinate.from_string, '48 24 3 S') self.assertRaises(ValueError, GPSCoordinate.from_string, '48°24\'3"S') self.assertRaises(ValueError, GPSCoordinate.from_string, 'invalid')
def _convert_to_python(self, value, type_): """Convert a raw value to its corresponding python type. Args: value -- the raw value to be converted type_ -- the simple type of the raw value Return: the value converted to its corresponding python type Raise XmpValueError: if the conversion fails """ if type_ == 'Boolean': if value == 'True': return True elif value == 'False': return False else: raise XmpValueError(value, type_) elif type_ == 'Colorant': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ == 'Date': match = self._date_re.match(value) if match is None: raise XmpValueError(value, type_) gd = match.groupdict() if gd['month'] is not None: month = int(gd['month']) else: month = 1 if gd['day'] is not None: day = int(gd['day']) else: day = 1 if gd['time'] is None: try: return datetime.date(int(gd['year']), month, day) except ValueError: raise XmpValueError(value, type_) else: if gd['minutes'] is None: # Malformed time raise XmpValueError(value, type_) if gd['seconds'] is not None: seconds = int(gd['seconds']) else: seconds = 0 if gd['decimal'] is not None: microseconds = int(float('0.%s' % gd['decimal']) * 1E6) else: microseconds = 0 if gd['tzd'] == 'Z': tzinfo = FixedOffset() else: tzinfo = FixedOffset(gd['sign'], int(gd['ohours']), int(gd['ominutes'])) try: return datetime.datetime(int(gd['year']), month, day, int(gd['hours']), int(gd['minutes']), seconds, microseconds, tzinfo) except ValueError: raise XmpValueError(value, type_) elif type_ == 'Dimensions': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ == 'Font': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ == 'GPSCoordinate': try: return GPSCoordinate.from_string(value) except ValueError: raise XmpValueError(value, type_) elif type_ == 'Integer': try: return int(value) except ValueError: raise XmpValueError(value, type_) elif type_ == 'Locale': # TODO # See RFC 3066 raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ == 'MIMEType': if value.count('/') != 1: raise XmpValueError(value, type_) try: return tuple(value.split('/', 1)) except ValueError: raise XmpValueError(value, type_) elif type_ == 'Rational': try: return make_fraction(value) except (ValueError, ZeroDivisionError): raise XmpValueError(value, type_) elif type_ == 'Real': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ in ('AgentName', 'ProperName', 'Text'): if isinstance(value, bytes): try: value = str(value, 'utf-8') except TypeError: raise XmpValueError(value, type_) return value elif type_ == 'Thumbnail': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ in ('URI', 'URL'): if isinstance(value, bytes): try: value = value.decode('utf-8') except UnicodeDecodeError: # Unknow encoding, return the raw value pass return value elif type_ == 'XPath': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) raise NotImplementedError('XMP conversion for type [%s]' % type_)
def test_from_string_invalid(bad_str): with pytest.raises(ValueError): GPS.from_string(bad_str)
def test_from_string_valid(str, deg, min, sec, dir): assert GPS.from_string(str) == GPS(deg, min, sec, dir)
}), ('Xmp.exif.ApertureValue', FRt, FR(8, 1)), ('Xmp.exif.BrightnessValue', FRt, FR(333, 1280)), ('Xmp.exif.ColorSpace', int, 1), ('Xmp.exif.DateTimeOriginal', Dt, DT(2002, 7, 13, 15, 58, 28)), ('Xmp.exif.ExifVersion', str, '0200'), ('Xmp.exif.ExposureBiasValue', FRt, FR(-13, 20)), ('Xmp.exif.ExposureProgram', int, 4), ('Xmp.exif.FNumber', FRt, FR(3, 5)), ('Xmp.exif.FileSource', int, 0), ('Xmp.exif.FlashpixVersion', str, '0100'), ('Xmp.exif.FocalLength', FRt, FR(0, 1)), ('Xmp.exif.FocalPlaneResolutionUnit', int, 2), ('Xmp.exif.FocalPlaneXResolution', FRt, FR(3085, 256)), ('Xmp.exif.FocalPlaneYResolution', FRt, FR(3085, 256)), ('Xmp.exif.GPSLatitude', GPS, GPS.from_string('54,59.380000N')), ('Xmp.exif.GPSLongitude', GPS, GPS.from_string('1,54.850000W')), ('Xmp.exif.GPSMapDatum', str, 'WGS84'), ('Xmp.exif.GPSTimeStamp', Dt, DT(2002, 7, 13, 14, 58, 24)), ('Xmp.exif.GPSVersionID', str, '2.0.0.0'), ('Xmp.exif.ISOSpeedRatings', list, [0]), ('Xmp.exif.MeteringMode', int, 5), ('Xmp.exif.PixelXDimension', int, 2400), ('Xmp.exif.PixelYDimension', int, 1600), ('Xmp.exif.SceneType', int, 0), ('Xmp.exif.SensingMethod', int, 2), ('Xmp.exif.ShutterSpeedValue', FRt, FR(30827, 3245)), ('Xmp.pdf.Keywords', str, 'Communications'), ('Xmp.photoshop.AuthorsPosition', str, 'Photographer'), ('Xmp.photoshop.CaptionWriter', str, 'Ian Britton'), ('Xmp.photoshop.Category', str, 'BUS'),
def _convert_to_python(self, value, type_): """Convert a raw value to its corresponding python type. Args: value -- the raw value to be converted type_ -- the simple type of the raw value Return: the value converted to its corresponding python type Raise XmpValueError: if the conversion fails """ if type_ == 'Boolean': if value == 'True': return True elif value == 'False': return False else: raise XmpValueError(value, type_) elif type_ == 'Colorant': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ == 'Date': match = self._date_re.match(value) if match is None: raise XmpValueError(value, type_) gd = match.groupdict() if gd['month'] is not None: month = int(gd['month']) else: month = 1 if gd['day'] is not None: day = int(gd['day']) else: day = 1 if gd['time'] is None: try: return datetime.date(int(gd['year']), month, day) except ValueError: raise XmpValueError(value, type_) else: if gd['minutes'] is None: # Malformed time raise XmpValueError(value, type_) if gd['seconds'] is not None: seconds = int(gd['seconds']) else: seconds = 0 if gd['decimal'] is not None: microseconds = int(float('0.%s' % gd['decimal']) * 1E6) else: microseconds = 0 if gd['tzd'] is not None and gd[ 'tzd'] == 'Z' and gd['sign'] is None and gd[ 'ohours'] is None and gd['ominutes'] is None: tzinfo = FixedOffset() elif gd['sign'] is not None and gd[ 'ohours'] is not None and gd['ominutes'] is not None: tzinfo = FixedOffset(gd['sign'], int(gd['ohours']), int(gd['ominutes'])) else: tzinfo = None # TODO: Decide if following is way to hande # ====================================================================== # ERROR: test_convert_to_python_date (xmp.TestXmpTag) # ---------------------------------------------------------------------- # Traceback (most recent call last): # File "c:\Users\BSFau\Cloudstation\BSFsoftDev\axternal\py3exiv2\py3exiv2\test\xmp.py", line 91, in test_convert_to_python_date # datetime.datetime(1999, 10, 13, 5, 3, tzinfo=FixedOffset()), # TypeError: can't subtract offset-naive and offset-aware datetimes # ---------------------------------------------------------------------- # u_tm = datetime.datetime.utcfromtimestamp(0) # l_tm = datetime.datetime.fromtimestamp(0) # tzinfo = datetime.timezone(l_tm - u_tm) try: if tzinfo is not None: return datetime.datetime(int(gd['year']), month, day, int(gd['hours']), int(gd['minutes']), seconds, microseconds, tzinfo) else: return datetime.datetime(int(gd['year']), month, day, int(gd['hours']), int(gd['minutes']), seconds, microseconds) except ValueError: raise XmpValueError(value, type_) elif type_ == 'Dimensions': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ == 'Font': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ == 'GPSCoordinate': try: return GPSCoordinate.from_string(value) except ValueError: raise XmpValueError(value, type_) elif type_ == 'Integer': try: return int(value) except ValueError: raise XmpValueError(value, type_) elif type_ == 'Locale': # TODO # See RFC 3066 raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ == 'MIMEType': if value.count('/') != 1: raise XmpValueError(value, type_) try: return tuple(value.split('/', 1)) except ValueError: raise XmpValueError(value, type_) elif type_ == 'Rational': try: return make_fraction(value) except (ValueError, ZeroDivisionError): raise XmpValueError(value, type_) elif type_ == 'Real': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ in ('AgentName', 'ProperName', 'Text'): if isinstance(value, bytes): try: value = str(value, 'utf-8') except TypeError: raise XmpValueError(value, type_) return value elif type_ == 'Thumbnail': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) elif type_ in ('URI', 'URL'): if isinstance(value, bytes): try: value = value.decode('utf-8') except UnicodeDecodeError: # Unknow encoding, return the raw value pass return value elif type_ == 'XPath': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type_) raise NotImplementedError('XMP conversion for type [%s]' % type_)
def _convert_to_python(self, value, type): """ Convert a raw value to its corresponding python type. :param value: the raw value to be converted :type value: string :param type: the simple type of the raw value :type type: string :return: the value converted to its corresponding python type :raise XmpValueError: if the conversion fails """ if type == 'Boolean': if value == 'True': return True elif value == 'False': return False else: raise XmpValueError(value, type) elif type == 'Colorant': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type) elif type == 'Date': match = self._date_re.match(value) if match is None: raise XmpValueError(value, type) gd = match.groupdict() if gd['month'] is not None: month = int(gd['month']) else: month = 1 if gd['day'] is not None: day = int(gd['day']) else: day = 1 if gd['time'] is None: try: return datetime.date(int(gd['year']), month, day) except ValueError: raise XmpValueError(value, type) else: if gd['minutes'] is None: # Malformed time raise XmpValueError(value, type) if gd['seconds'] is not None: seconds = int(gd['seconds']) else: seconds = 0 if gd['decimal'] is not None: microseconds = int(float('0.%s' % gd['decimal']) * 1E6) else: microseconds = 0 if gd['tzd'] == 'Z': tzinfo = FixedOffset() else: tzinfo = FixedOffset(gd['sign'], int(gd['ohours']), int(gd['ominutes'])) try: return datetime.datetime(int(gd['year']), month, day, int(gd['hours']), int(gd['minutes']), seconds, microseconds, tzinfo) except ValueError: raise XmpValueError(value, type) elif type == 'Dimensions': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type) elif type == 'Font': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type) elif type == 'GPSCoordinate': try: return GPSCoordinate.from_string(value) except ValueError: raise XmpValueError(value, type) elif type == 'Integer': try: return int(value) except ValueError: raise XmpValueError(value, type) elif type == 'Locale': # TODO # See RFC 3066 raise NotImplementedError('XMP conversion for type [%s]' % type) elif type == 'MIMEType': if value.count('/') != 1: raise XmpValueError(value, type) try: return tuple(value.split('/', 1)) except ValueError: raise XmpValueError(value, type) elif type == 'Rational': try: return make_fraction(value) except (ValueError, ZeroDivisionError): raise XmpValueError(value, type) elif type == 'Real': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type) elif type in ('AgentName', 'ProperName', 'Text'): try: return unicode(value, 'utf-8') except TypeError: raise XmpValueError(value, type) elif type == 'Thumbnail': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type) elif type in ('URI', 'URL'): return value elif type == 'XPath': # TODO raise NotImplementedError('XMP conversion for type [%s]' % type) raise NotImplementedError('XMP conversion for type [%s]' % type)