diff --git a/.travis.yml b/.travis.yml index 4e87c42..745ca14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,15 @@ language: python dist: trusty python: - "2.7" + - "3.4" virtualenv: system_site_packages: true before_install: - "sudo apt-get update -qq" - "sudo apt-get install python-dev python-pip libimage-exiftool-perl -y" install: - - "sudo pip install -r elodie/tests/requirements.txt" - - "sudo pip install coveralls" + - "pip install -r elodie/tests/requirements.txt" + - "pip install coveralls" # command to run tests # test mapquest key before_script: @@ -25,5 +26,5 @@ before_script: - "cd ${ELODIE_DIR}" after_success: - "coveralls" -script: +script: - "nosetests --with-coverage --cover-package=elodie -w elodie/tests" diff --git a/elodie.py b/elodie.py index 3340153..bcda4a2 100755 --- a/elodie.py +++ b/elodie.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +from __future__ import print_function import os import re import sys @@ -35,16 +36,16 @@ def import_file(_file, destination, album_from_folder, trash): """ if not os.path.exists(_file): if constants.debug: - print 'Could not find %s' % _file - print '{"source":"%s", "error_msg":"Could not find %s"}' % \ - (_file, _file) + print('Could not find %s' % _file) + print('{"source":"%s", "error_msg":"Could not find %s"}' % \ + (_file, _file)) return media = Media.get_class_by_file(_file, [Text, Audio, Photo, Video]) if not media: if constants.debug: - print 'Not a supported file (%s)' % _file - print '{"source":"%s", "error_msg":"Not a supported file"}' % _file + print('Not a supported file (%s)' % _file) + print('{"source":"%s", "error_msg":"Not a supported file"}' % _file) return if media.__name__ == 'Video': @@ -56,7 +57,7 @@ def import_file(_file, destination, album_from_folder, trash): dest_path = FILESYSTEM.process_file(_file, destination, media, allowDuplicate=False, move=False) if dest_path: - print '%s -> %s' % (_file, dest_path) + print('%s -> %s' % (_file, dest_path)) if trash: send2trash(_file) @@ -109,9 +110,9 @@ def update_location(media, file_path, location_name): 'latitude'], location_coords['longitude']) if not location_status: if constants.debug: - print 'Failed to update location' - print ('{"source":"%s",' % file_path, - '"error_msg":"Failed to update location"}') + print('Failed to update location') + print(('{"source":"%s",' % file_path, + '"error_msg":"Failed to update location"}')) sys.exit(1) return True @@ -125,8 +126,8 @@ def update_time(media, file_path, time_string): elif re.match(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}\d{2}$', time_string): msg = ('Invalid time format. Use YYYY-mm-dd hh:ii:ss or YYYY-mm-dd') if constants.debug: - print msg - print '{"source":"%s", "error_msg":"%s"}' % (file_path, msg) + print(msg) + print('{"source":"%s", "error_msg":"%s"}' % (file_path, msg)) sys.exit(1) time = datetime.strptime(time_string, time_format) @@ -150,9 +151,9 @@ def _update(album, location, time, title, files): for file_path in files: if not os.path.exists(file_path): if constants.debug: - print 'Could not find %s' % file_path - print '{"source":"%s", "error_msg":"Could not find %s"}' % \ - (file_path, file_path) + print('Could not find %s' % file_path) + print('{"source":"%s", "error_msg":"Could not find %s"}' % \ + (file_path, file_path)) continue file_path = os.path.expanduser(file_path) @@ -209,9 +210,9 @@ def _update(album, location, time, title, files): dest_path = FILESYSTEM.process_file(file_path, destination, updated_media, move=True, allowDuplicate=True) if constants.debug: - print u'%s -> %s' % (file_path, dest_path) - print '{"source":"%s", "destination":"%s"}' % (file_path, - dest_path) + print(u'%s -> %s' % (file_path, dest_path)) + print('{"source":"%s", "destination":"%s"}' % (file_path, + dest_path)) # If the folder we moved the file out of or its parent are empty # we delete it. FILESYSTEM.delete_directory_if_empty(os.path.dirname(file_path)) diff --git a/elodie/arguments.py b/elodie/arguments.py index fc43f95..f181b18 100644 --- a/elodie/arguments.py +++ b/elodie/arguments.py @@ -1,6 +1,7 @@ """ Command line argument parsing for helper scripts. """ +from __future__ import print_function import getopt import sys @@ -20,13 +21,13 @@ def parse(argv, options, long_options, usage): try: opts, args = getopt.getopt(argv, options, long_options) except getopt.GetoptError: - print usage + print(usage) sys.exit(2) return_arguments = {} for opt, arg in opts: if opt == '-h': - print usage + print(usage) sys.exit() else: return_arguments[sub('^-+', '', opt)] = arg diff --git a/elodie/dependencies.py b/elodie/dependencies.py index 09ab6b8..e575b5d 100644 --- a/elodie/dependencies.py +++ b/elodie/dependencies.py @@ -2,6 +2,7 @@ Helpers for checking for an interacting with external dependencies. These are things that Elodie requires, but aren't installed automatically for the user. """ +from __future__ import print_function import os import sys @@ -43,7 +44,7 @@ def verify_dependencies(): """ exiftool = get_exiftool() if exiftool is None: - print >>sys.stderr, EXIFTOOL_ERROR + print(EXIFTOOL_ERROR, file=sys.stderr) return False return True diff --git a/elodie/external/pyexiftool.py b/elodie/external/pyexiftool.py index 540ecc5..f6850f6 100644 --- a/elodie/external/pyexiftool.py +++ b/elodie/external/pyexiftool.py @@ -273,8 +273,7 @@ class ExifTool(object): """ if not self.running: raise ValueError("ExifTool instance not running.") - cmd_txt = b"\n".join(params + (b"-execute\n",)) - self._process.stdin.write(cmd_txt.encode("utf-8")) + self._process.stdin.write(b"\n".join(params + (b"-execute\n",))) self._process.stdin.flush() output = b"" fd = self._process.stdout.fileno() @@ -403,11 +402,13 @@ class ExifTool(object): "an iterable of strings") params = [] + params_utf8 = [] for tag, value in tags.items(): params.append(u'-%s=%s' % (tag, value)) params.extend(filenames) - return self.execute(*params) + params_utf8 = [x.encode('utf-8') for x in params] + return self.execute(*params_utf8) def set_tags(self, tags, filename): """Writes the values of the specified tags for the given file. diff --git a/elodie/filesystem.py b/elodie/filesystem.py index 50d5b5c..79ca367 100644 --- a/elodie/filesystem.py +++ b/elodie/filesystem.py @@ -3,6 +3,8 @@ General file system methods. .. moduleauthor:: Jaisen Mathai """ +from __future__ import print_function +from builtins import object import os import re @@ -176,7 +178,7 @@ class FileSystem(object): allow_duplicate = kwargs['allowDuplicate'] if(not media.is_valid()): - print '%s is not a valid media file. Skipping...' % _file + print('%s is not a valid media file. Skipping...' % _file) return metadata = media.get_metadata() @@ -191,17 +193,17 @@ class FileSystem(object): checksum = db.checksum(_file) if(checksum is None): if(constants.debug is True): - print 'Could not get checksum for %s. Skipping...' % _file + print('Could not get checksum for %s. Skipping...' % _file) return # If duplicates are not allowed and this hash exists in the db then we # return if(allow_duplicate is False and db.check_hash(checksum) is True): if(constants.debug is True): - print '%s already exists at %s. Skipping...' % ( + print('%s already exists at %s. Skipping...' % ( _file, db.get_hash(checksum) - ) + )) return self.create_directory(dest_directory) diff --git a/elodie/geolocation.py b/elodie/geolocation.py index 158a931..e0f4780 100644 --- a/elodie/geolocation.py +++ b/elodie/geolocation.py @@ -1,32 +1,23 @@ """Look up geolocation information for media objects.""" +from __future__ import print_function +from __future__ import division +from future import standard_library +from past.utils import old_div from os import path -from ConfigParser import ConfigParser -import fractions +from configparser import ConfigParser + +standard_library.install_aliases() # noqa import requests -import urllib +import urllib.request +import urllib.parse +import urllib.error from elodie import constants from elodie.localstorage import Db -class Fraction(fractions.Fraction): - - """Only create Fractions from floats. - - Should be compatible with Python 2.6, though untested. - - >>> Fraction(0.3) - Fraction(3, 10) - >>> Fraction(1.1) - Fraction(11, 10) - """ - - def __new__(cls, value, ignore=None): - return fractions.Fraction.from_float(value).limit_denominator(99999) - - def coordinates_by_name(name): # Try to get cached location first db = Db() @@ -88,7 +79,8 @@ def dms_to_decimal(degrees, minutes, seconds, direction=' '): if(direction[0] in 'WSws'): sign = -1 return ( - float(degrees) + float(minutes) / 60 + float(seconds) / 3600 + float(degrees) + old_div(float(minutes), 60) + + old_div(float(seconds), 3600) ) * sign @@ -159,17 +151,17 @@ def reverse_lookup(lat, lon): headers = {"Accept-Language": constants.accepted_language} r = requests.get( 'http://open.mapquestapi.com/nominatim/v1/reverse.php?%s' % - urllib.urlencode(params), headers=headers + urllib.parse.urlencode(params), headers=headers ) return r.json() except requests.exceptions.RequestException as e: if(constants.debug is True): - print e + print(e) return None except ValueError as e: if(constants.debug is True): - print r.text - print e + print(r.text) + print(e) return None @@ -182,18 +174,18 @@ def lookup(name): try: params = {'format': 'json', 'key': key, 'location': name} if(constants.debug is True): - print 'http://open.mapquestapi.com/geocoding/v1/address?%s' % urllib.urlencode(params) # noqa + print('http://open.mapquestapi.com/geocoding/v1/address?%s' % urllib.parse.urlencode(params)) # noqa r = requests.get( 'http://open.mapquestapi.com/geocoding/v1/address?%s' % - urllib.urlencode(params) + urllib.parse.urlencode(params) ) return r.json() except requests.exceptions.RequestException as e: if(constants.debug is True): - print e + print(e) return None except ValueError as e: if(constants.debug is True): - print r.text - print e + print(r.text) + print(e) return None diff --git a/elodie/localstorage.py b/elodie/localstorage.py index 8f1d837..3ce5f51 100644 --- a/elodie/localstorage.py +++ b/elodie/localstorage.py @@ -1,6 +1,8 @@ """ Methods for interacting with information Elodie caches about stored media. """ +from builtins import map +from builtins import object import hashlib import json @@ -141,17 +143,17 @@ class Db(object): the given latitude and longitude. :returns: str, or None if a matching location couldn't be found. """ - last_d = sys.maxint + last_d = sys.maxsize name = None for data in self.location_db: # As threshold is quite small use simple math # From http://stackoverflow.com/questions/15736995/how-can-i-quickly-estimate-the-distance-between-two-latitude-longitude-points # noqa # convert decimal degrees to radians - lon1, lat1, lon2, lat2 = map( + lon1, lat1, lon2, lat2 = list(map( radians, [longitude, latitude, data['long'], data['lat']] - ) + )) r = 6371000 # radius of the earth in m x = (lon2 - lon1) * cos(0.5 * (lat2 + lat1)) diff --git a/elodie/media/audio.py b/elodie/media/audio.py index fdf2517..8409512 100644 --- a/elodie/media/audio.py +++ b/elodie/media/audio.py @@ -5,8 +5,9 @@ class. .. moduleauthor:: Jaisen Mathai """ +from __future__ import absolute_import -from video import Video +from .video import Video class Audio(Video): diff --git a/elodie/media/base.py b/elodie/media/base.py index 7cf27c4..2d1410b 100644 --- a/elodie/media/base.py +++ b/elodie/media/base.py @@ -13,6 +13,11 @@ are used to represent the actual files. import mimetypes import os +try: # Py3k compatibility + basestring +except NameError: + basestring = (bytes, str) + class Base(object): diff --git a/elodie/media/media.py b/elodie/media/media.py index b3bad78..ae2e82d 100644 --- a/elodie/media/media.py +++ b/elodie/media/media.py @@ -8,6 +8,7 @@ are used to represent the actual files. .. moduleauthor:: Jaisen Mathai """ +from __future__ import print_function # load modules from elodie import constants diff --git a/elodie/media/photo.py b/elodie/media/photo.py index 99b27a5..221438b 100644 --- a/elodie/media/photo.py +++ b/elodie/media/photo.py @@ -4,6 +4,8 @@ image objects (JPG, DNG, etc.). .. moduleauthor:: Jaisen Mathai """ +from __future__ import print_function +from __future__ import absolute_import import imghdr import os @@ -15,7 +17,7 @@ from re import compile from elodie import constants -from media import Media +from .media import Media class Photo(Media): @@ -95,7 +97,7 @@ class Photo(Media): break except BaseException as e: if(constants.debug is True): - print e + print(e) pass if(seconds_since_epoch == 0): diff --git a/elodie/media/text.py b/elodie/media/text.py index 7219591..ccdb6d3 100644 --- a/elodie/media/text.py +++ b/elodie/media/text.py @@ -123,7 +123,7 @@ class Text(Base): self.metadata_line = parsed_json except ValueError: if(constants.debug is True): - print 'Could not parse JSON from first line: %s' % first_line + print('Could not parse JSON from first line: %s' % first_line) pass def write_metadata(self, **kwargs): diff --git a/elodie/media/video.py b/elodie/media/video.py index b49f50a..2becef6 100644 --- a/elodie/media/video.py +++ b/elodie/media/video.py @@ -4,6 +4,9 @@ objects (AVI, MOV, etc.). .. moduleauthor:: Jaisen Mathai """ +from __future__ import print_function +from __future__ import absolute_import +from __future__ import division # load modules from distutils.spawn import find_executable @@ -13,7 +16,7 @@ import os import re import time -from media import Media +from .media import Media class Video(Media): diff --git a/elodie/tests/filesystem_test.py b/elodie/tests/filesystem_test.py index dd6a8d4..dfb13d1 100644 --- a/elodie/tests/filesystem_test.py +++ b/elodie/tests/filesystem_test.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import # Project imports import os import re @@ -11,7 +12,7 @@ import mock sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))) -import helper +from . import helper from elodie.filesystem import FileSystem from elodie.media.media import Media from elodie.media.photo import Photo diff --git a/elodie/tests/geolocation_test.py b/elodie/tests/geolocation_test.py index fb85493..02ab5e3 100644 --- a/elodie/tests/geolocation_test.py +++ b/elodie/tests/geolocation_test.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import +from __future__ import division +from builtins import range +from past.utils import old_div # Project imports import os import random @@ -6,7 +10,7 @@ import sys sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))) -import helper +from . import helper from elodie import geolocation os.environ['TZ'] = 'GMT' @@ -18,7 +22,7 @@ def test_decimal_to_dms(): target_decimal_value = random.uniform(0.0, 180.0) if(x % 2 == 1): target_decimal_value = target_decimal_value * -1 - + dms = geolocation.decimal_to_dms(target_decimal_value) check_value = (dms[0] + dms[1] / 60 + dms[2] / 3600) * dms[3] @@ -34,7 +38,7 @@ def test_dms_string_latitude(): target_decimal_value = random.uniform(0.0, 180.0) if(x % 2 == 1): target_decimal_value = target_decimal_value * -1 - + dms = geolocation.decimal_to_dms(target_decimal_value) dms_string = geolocation.dms_string(target_decimal_value, 'latitude') @@ -49,7 +53,7 @@ def test_dms_string_longitude(): target_decimal_value = random.uniform(0.0, 180.0) if(x % 2 == 1): target_decimal_value = target_decimal_value * -1 - + dms = geolocation.decimal_to_dms(target_decimal_value) dms_string = geolocation.dms_string(target_decimal_value, 'longitude') diff --git a/elodie/tests/helper.py b/elodie/tests/helper.py index 5e07309..102ad29 100644 --- a/elodie/tests/helper.py +++ b/elodie/tests/helper.py @@ -1,3 +1,7 @@ +from __future__ import division +from __future__ import unicode_literals +from builtins import range +from past.utils import old_div import hashlib import os import random @@ -12,7 +16,7 @@ from datetime import timedelta def checksum(file_path, blocksize=65536): hasher = hashlib.sha256() - with open(file_path, 'r') as f: + with open(file_path, 'rb') as f: buf = f.read(blocksize) while len(buf) > 0: @@ -73,7 +77,7 @@ def random_decimal(): def random_coordinate(coordinate, precision): # Here we add to the decimal section of the coordinate by a given precision - return coordinate + ((10.0 / (10.0**precision)) * random_decimal()) + return coordinate + ((old_div(10.0, (10.0**precision))) * random_decimal()) def temp_dir(): return tempfile.gettempdir() @@ -91,8 +95,8 @@ def is_windows(): def path_tz_fix(file_name): if is_windows(): # Calculate the offset between UTC and local time - tz_shift = (datetime.fromtimestamp(0) - - datetime.utcfromtimestamp(0)).seconds/3600 + tz_shift = old_div((datetime.fromtimestamp(0) - + datetime.utcfromtimestamp(0)).seconds,3600) # replace timestamp in file_name m = re.search('(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})',file_name) t_date = datetime.fromtimestamp(time.mktime(time.strptime(m.group(0), '%Y-%m-%d_%H-%M-%S'))) diff --git a/elodie/tests/localstorage_test.py b/elodie/tests/localstorage_test.py index a7077df..fc58e11 100644 --- a/elodie/tests/localstorage_test.py +++ b/elodie/tests/localstorage_test.py @@ -1,10 +1,12 @@ +from __future__ import print_function +from __future__ import absolute_import # Project imports import os import sys sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))) -import helper +from . import helper from elodie.localstorage import Db from elodie import constants @@ -154,10 +156,10 @@ def test_get_location_name_within_threshold(): latitude, longitude, name = helper.get_test_location() db.add_location(latitude, longitude, name) - print latitude + print(latitude) new_latitude = helper.random_coordinate(latitude, 4) new_longitude = helper.random_coordinate(longitude, 4) - print new_latitude + print(new_latitude) # 10 miles retrieved_name = db.get_location_name(new_latitude, new_longitude, 1600*10) diff --git a/elodie/tests/media/audio_test.py b/elodie/tests/media/audio_test.py index 3618525..cfc9cd2 100644 --- a/elodie/tests/media/audio_test.py +++ b/elodie/tests/media/audio_test.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 # Project imports +from __future__ import print_function import os import sys @@ -56,7 +57,6 @@ def test_get_date_taken(): audio = Audio(helper.get_file('audio.m4a')) date_taken = audio.get_date_taken() - print '%r' % date_taken assert date_taken == (2016, 1, 4, 5, 24, 15, 0, 19, 0), date_taken def test_get_exiftool_attributes(): diff --git a/elodie/tests/media/photo_test.py b/elodie/tests/media/photo_test.py index d99f8e1..75ed3d6 100644 --- a/elodie/tests/media/photo_test.py +++ b/elodie/tests/media/photo_test.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 # Project imports +from __future__ import unicode_literals import os import sys diff --git a/requirements.txt b/requirements.txt index 3d208c7..c5489ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ click>=6.2,<7.0 requests>=2.9.1,<3.0 send2trash>=1.3.0,<2.0 +future