Merge pull request #64 from noonat/friendly-dep-errors
Added friendly errors when dependencies are not installed
This commit is contained in:
		
						commit
						1d86856a07
					
				@ -7,7 +7,7 @@ before_install:
 | 
				
			|||||||
  - "sudo apt-get update -qq"
 | 
					  - "sudo apt-get update -qq"
 | 
				
			||||||
  - "sudo apt-get install python-dev python-pip python-pyexiv2 libimage-exiftool-perl -y"
 | 
					  - "sudo apt-get install python-dev python-pip python-pyexiv2 libimage-exiftool-perl -y"
 | 
				
			||||||
install:
 | 
					install:
 | 
				
			||||||
  - "sudo pip install docopt LatLon nose requests"
 | 
					  - "sudo pip install docopt LatLon mock nose requests"
 | 
				
			||||||
# command to run tests
 | 
					# command to run tests
 | 
				
			||||||
# test mapquest key
 | 
					# test mapquest key
 | 
				
			||||||
before_script:
 | 
					before_script:
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,12 @@ import sys
 | 
				
			|||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
from docopt import docopt
 | 
					from docopt import docopt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Verify that external dependencies are present first, so the user gets a
 | 
				
			||||||
 | 
					# more user-friendly error instead of an ImportError traceback.
 | 
				
			||||||
 | 
					from elodie.dependencies import verify_dependencies
 | 
				
			||||||
 | 
					if not verify_dependencies():
 | 
				
			||||||
 | 
					    sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from elodie import constants
 | 
					from elodie import constants
 | 
				
			||||||
from elodie import geolocation
 | 
					from elodie import geolocation
 | 
				
			||||||
from elodie.media.media import Media
 | 
					from elodie.media.media import Media
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										61
									
								
								elodie/dependencies.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								elodie/dependencies.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					"""Helpers for checking external dependencies."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					from distutils.spawn import find_executable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXIFTOOL_ERROR = u"""
 | 
				
			||||||
 | 
					It looks like you don't have exiftool installed, which Elodie requires.
 | 
				
			||||||
 | 
					Please take a look at the installation steps in the readme:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					https://github.com/jmathai/elodie#install-everything-you-need
 | 
				
			||||||
 | 
					""".lstrip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PYEXIV2_ERROR = u"""
 | 
				
			||||||
 | 
					{error_class_name}: {error}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It looks like you don't have pyexiv2 installed, which Elodie requires for
 | 
				
			||||||
 | 
					geolocation. Please take a look at the installation steps in the readme:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					https://github.com/jmathai/elodie#install-everything-you-need
 | 
				
			||||||
 | 
					""".lstrip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_exiftool():
 | 
				
			||||||
 | 
					    """Get path to executable exiftool binary.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    We wrap this since we call it in a few places and we do a fallback.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @returns, None or string
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    path = find_executable('exiftool')
 | 
				
			||||||
 | 
					    # If exiftool wasn't found we try to brute force the homebrew location
 | 
				
			||||||
 | 
					    if path is None:
 | 
				
			||||||
 | 
					        path = '/usr/local/bin/exiftool'
 | 
				
			||||||
 | 
					        if not os.path.isfile(path) or not os.access(path, os.X_OK):
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					    return path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def verify_dependencies():
 | 
				
			||||||
 | 
					    """Verify that external dependencies are installed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Prints a message to stderr and returns False if any dependencies are
 | 
				
			||||||
 | 
					    missing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @returns, bool
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    exiftool = get_exiftool()
 | 
				
			||||||
 | 
					    if exiftool is None:
 | 
				
			||||||
 | 
					        print >>sys.stderr, EXIFTOOL_ERROR
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        import pyexiv2
 | 
				
			||||||
 | 
					    except ImportError as e:
 | 
				
			||||||
 | 
					        print >>sys.stderr, PYEXIV2_ERROR.format(
 | 
				
			||||||
 | 
					            error_class_name=e.__class__.__name__, error=e)
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return True
 | 
				
			||||||
@ -4,8 +4,8 @@ Media package that's a parent class for media objects
 | 
				
			|||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# load modules
 | 
					# load modules
 | 
				
			||||||
from distutils.spawn import find_executable
 | 
					 | 
				
			||||||
from elodie import constants
 | 
					from elodie import constants
 | 
				
			||||||
 | 
					from elodie.dependencies import get_exiftool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import mimetypes
 | 
					import mimetypes
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
@ -48,22 +48,6 @@ class Media(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return exiftool_attributes['album']
 | 
					        return exiftool_attributes['album']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Get path to executable exiftool binary.
 | 
					 | 
				
			||||||
    We wrap this since we call it in a few places and we do a fallback.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @returns, None or string
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def get_exiftool(self):
 | 
					 | 
				
			||||||
        exiftool = find_executable('exiftool')
 | 
					 | 
				
			||||||
        # If exiftool wasn't found we try to brute force the homebrew location
 | 
					 | 
				
			||||||
        if(exiftool is None):
 | 
					 | 
				
			||||||
            exiftool = '/usr/local/bin/exiftool'
 | 
					 | 
				
			||||||
            if(not os.path.isfile(exiftool) or not os.access(exiftool, os.X_OK)):  # noqa
 | 
					 | 
				
			||||||
                return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return exiftool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Get the full path to the video.
 | 
					    Get the full path to the video.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -103,7 +87,7 @@ class Media(object):
 | 
				
			|||||||
        if(self.exiftool_attributes is not None):
 | 
					        if(self.exiftool_attributes is not None):
 | 
				
			||||||
            return self.exiftool_attributes
 | 
					            return self.exiftool_attributes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        exiftool = self.get_exiftool()
 | 
					        exiftool = get_exiftool()
 | 
				
			||||||
        if(exiftool is None):
 | 
					        if(exiftool is None):
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -222,7 +206,7 @@ class Media(object):
 | 
				
			|||||||
        if(name is None):
 | 
					        if(name is None):
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        exiftool = self.get_exiftool()
 | 
					        exiftool = get_exiftool()
 | 
				
			||||||
        if(exiftool is None):
 | 
					        if(exiftool is None):
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,7 @@ import time
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from elodie import constants
 | 
					from elodie import constants
 | 
				
			||||||
from elodie import plist_parser
 | 
					from elodie import plist_parser
 | 
				
			||||||
 | 
					from elodie.dependencies import get_exiftool
 | 
				
			||||||
from media import Media
 | 
					from media import Media
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -145,7 +146,7 @@ class Video(Media):
 | 
				
			|||||||
    @returns, string or None if exiftool is not found
 | 
					    @returns, string or None if exiftool is not found
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    def get_exif(self):
 | 
					    def get_exif(self):
 | 
				
			||||||
        exiftool = self.get_exiftool()
 | 
					        exiftool = get_exiftool()
 | 
				
			||||||
        if(exiftool is None):
 | 
					        if(exiftool is None):
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								elodie/tests/dependencies_test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								elodie/tests/dependencies_test.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					import mock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from elodie.dependencies import get_exiftool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@mock.patch('elodie.dependencies.find_executable')
 | 
				
			||||||
 | 
					@mock.patch('elodie.dependencies.os')
 | 
				
			||||||
 | 
					def test_exiftool(mock_os, mock_find_executable):
 | 
				
			||||||
 | 
					    mock_find_executable.return_value = '/path/to/exiftool'
 | 
				
			||||||
 | 
					    assert get_exiftool() == '/path/to/exiftool'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mock_find_executable.return_value = None
 | 
				
			||||||
 | 
					    mock_os.path.isfile.return_value = True
 | 
				
			||||||
 | 
					    mock_os.path.access.return_value = True
 | 
				
			||||||
 | 
					    assert get_exiftool() == '/usr/local/bin/exiftool'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mock_os.path.isfile.return_value = False
 | 
				
			||||||
 | 
					    assert get_exiftool() is None
 | 
				
			||||||
@ -21,11 +21,6 @@ from elodie.media.video import Video
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
os.environ['TZ'] = 'GMT'
 | 
					os.environ['TZ'] = 'GMT'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_exiftool():
 | 
					 | 
				
			||||||
    media = Media()
 | 
					 | 
				
			||||||
    exiftool = media.get_exiftool()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    assert exiftool is not None, exiftool
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_get_file_path():
 | 
					def test_get_file_path():
 | 
				
			||||||
    media = Media(helper.get_file('plain.jpg'))
 | 
					    media = Media(helper.get_file('plain.jpg'))
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user