[pitivi] Refactor check.py for faster, better (simpler), stronger dependency checking



commit 248e0a2aa21266dfeffbea3050022fe2533caf24
Author: Jean-FranÃois Fortin Tam <nekohayo gmail com>
Date:   Thu Nov 29 20:32:17 2012 -0500

    Refactor check.py for faster, better (simpler), stronger dependency checking
    
    Run the checks before initializing and importing any modules;
    Remove many translatable strings that are redundant or not meant for users;
    Update dependencies to reflect the current requirements of PiTiVi.

 bin/pitivi.in                   |   67 ++++-----
 pitivi/application.py           |   31 ++---
 pitivi/check.py                 |  320 ++++++++++++++++++++++-----------------
 pitivi/clipproperties.py        |    6 +-
 pitivi/dialogs/depsmanager.py   |    8 +-
 pitivi/dialogs/startupwizard.py |    4 +-
 pitivi/timeline/timeline.py     |    4 +-
 tests/__init__.py               |   11 --
 tests/runtests.py               |   11 ++
 9 files changed, 248 insertions(+), 214 deletions(-)
---
diff --git a/bin/pitivi.in b/bin/pitivi.in
index f5eb7dc..4571926 100644
--- a/bin/pitivi.in
+++ b/bin/pitivi.in
@@ -21,18 +21,15 @@
 # Boston, MA 02110-1301, USA.
 
 import os
-import gi
 import sys
 import string
 import locale
 import gettext
 
-# variables
 CONFIGURED_PYTHONPATH = '@CONFIGURED_PYTHONPATH@'
 CONFIGURED_LD_LIBRARY_PATH = '@CONFIGURED_LD_LIBRARY_PATH@'
 CONFIGURED_GST_PLUGIN_PATH = '@CONFIGURED_GST_PLUGIN_PATH@'
 LIBDIR = '@LIBDIR@'
-
 localedir = ""
 
 # Check if we're in development or installed version
@@ -50,10 +47,12 @@ def _prepend_env_path(name, value):
     os.environ[name] = os.pathsep.join(value +
             os.environ.get(name, "").split(os.pathsep))
 
+
 def jump_through_hoops():
     os.environ["JUMP_THROUGH_HOOPS"] = "1"
     os.execv(sys.argv[0], sys.argv)
 
+
 def _add_pitivi_path():
     global localedir
     dir = os.path.dirname(os.path.abspath(__file__))
@@ -97,54 +96,48 @@ def _add_pitivi_path():
             jump_through_hoops()
 
 
-def _init_gobject_gtk_gst_ges():
-    global localedir
-    try:
-        gi.require_version('Gtk', '3.0')
-        from gi.repository import Gtk
-        Gtk #pyflakes
-    except ImportError, e:
-        raise SystemExit("PyGTK couldn't be found !", str(e))
+def _check_dependencies():
+    from pitivi.check import check_hard_dependencies, check_soft_dependencies
+    missing_hard_deps = check_hard_dependencies()
+    # Yes, check soft deps even if hard ones are OK. We'll need them at runtime:
+    missing_soft_deps = check_soft_dependencies()
 
-    try:
-        gi.require_version('Gst', '1.0')
-        from gi.repository import Gst
-        Gst #pyflakes
-    except ImportError:
-        raise SystemExit("Gst 1.0 couldn't be found!")
-
-    try:
-        gi.require_version('GES', '1.0')
-        from gi.repository import GES
-        GES #pyflakes
-    except ImportError:
-        raise SystemExit("GStreamer Editing Services couldn't be found!")
-
-    from gi.repository import GObject
-
-    GObject.threads_init()
-
-    try:
-        gi.require_version('GooCanvas', '2.0')
-        from gi.repository import GooCanvas
-        GooCanvas # pyflakes
-    except ImportError:
-        raise SystemExit("GooCanvas couldn't be found!")
+    if missing_hard_deps:
+        print "\nERROR - The following hard dependencies are unmet:"
+        print "=================================================="
+        for dep in missing_hard_deps:
+            print "-", dep + ":", missing_hard_deps[dep]
+        if missing_soft_deps:
+            print "\nAdditionally, the following soft dependencies are missing..."
+            for dep in missing_soft_deps:
+                print "-", dep + ":", missing_soft_deps[dep]
+        sys.exit(2)
 
 
 def _run_pitivi():
     import pitivi.application as ptv
+    from gi.repository import GObject
+    # You really need this line if you don't want the UI to lock up as soon as
+    # you start using GStreamer:
+    GObject.threads_init()
 
     # Make it easy for developers to debug the application startup.
     if os.environ.get('PITIVI_DEBUG_NO_UI') == '1':
         print 'Starting Pitivi with no GUI.'
         ptv.GuiPitivi._showGui = lambda *args, **kargs : None
 
+    # Exit the current script and start the real pitivi, with given arguments
     sys.exit(ptv.main(sys.argv))
 
+
 try:
     _add_pitivi_path()
-    _init_gobject_gtk_gst_ges()
+    # Dep checks really have to happen here, not in application.py. Otherwise,
+    # as soon as application.py starts, it will try importing all the code and
+    # the classes in application.py will not even have the opportunity to run.
+    # We do these checks on every startup (even outside the dev environment, for
+    # soft deps); doing imports and gst registry checks has near-zero cost.
+    _check_dependencies()
     _run_pitivi()
 except KeyboardInterrupt:
-    print "Interrupted by user!"
+    print "\tPitivi stopped by user with KeyboardInterrupt!"
diff --git a/pitivi/application.py b/pitivi/application.py
index 37f0a70..8240087 100644
--- a/pitivi/application.py
+++ b/pitivi/application.py
@@ -37,7 +37,6 @@ from optparse import OptionParser
 
 import pitivi.instance as instance
 
-from pitivi.check import initial_checks
 from pitivi.effects import EffectsHandler
 from pitivi.configure import APPNAME, pitivi_version, RELEASES_URL
 from pitivi.settings import GlobalSettings
@@ -55,12 +54,18 @@ import pitivi.utils.loggable as log
 #FIXME GES port disabled it
 #from pitivi.undo.timeline import TimelineLogObserver
 
-# FIXME : Speedup loading time
-# Currently we load everything in one go
-# It would be better if a minimalistic UI could start up ASAP, without loading
-# anything gst-related or that could slow down startup.
-# AND THEN load up the required parts.
-# This will result in a much better end-user experience
+
+"""
+Hierarchy of the whole thing:
+
+Pitivi
+    InteractivePitivi
+    GuiPitivi
+        FullGuiPitivi
+            ProjectCreatorGuiPitivi
+            ProjectLoaderGuiPitivi
+            StartupWizardGuiPitivi
+"""
 
 
 class Pitivi(Loggable, Signallable):
@@ -252,20 +257,9 @@ class InteractivePitivi(Pitivi):
         self.mainloop = GObject.MainLoop()
         self.actioner = None
         self.gui = None
-
-        # Check the dependencies.
-        missing_deps = initial_checks()
-        if missing_deps:
-            message, detail = missing_deps
-            self._showStartupError(message, detail)
-            sys.exit(2)
-
         if debug:
             sys.excepthook = self._excepthook
 
-    def _showStartupError(self, message, detail):
-        self.error("%s %s" % (message, detail))
-
     def _excepthook(self, exc_type, value, tback):
         import traceback
         import pdb
@@ -306,7 +300,6 @@ class GuiPitivi(InteractivePitivi):
     def _showStartupError(self, message, detail):
         dialog = Gtk.MessageDialog(type=Gtk.MessageType.ERROR,
                                    buttons=Gtk.ButtonsType.OK)
-        dialog.set_icon_name("pitivi")
         dialog.set_markup("<b>" + message + "</b>")
         dialog.format_secondary_text(detail)
         dialog.run()
diff --git a/pitivi/check.py b/pitivi/check.py
index edd66b0..94db31a 100644
--- a/pitivi/check.py
+++ b/pitivi/check.py
@@ -1,8 +1,10 @@
+# -*- coding: utf-8 -*-
 # PiTiVi , Non-linear video editor
 #
 #       pitivi/check.py
 #
 # Copyright (c) 2005, Edward Hervey <bilboed bilboed com>
+# Copyright (c) 2012, Jean-FranÃois Fortin Tam <nekohayo gmail com>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -20,28 +22,42 @@
 # Boston, MA 02110-1301, USA.
 
 """
-Runtime checks.
+This file is run by bin/pitivi on startup. Its purpose is to ensure that all
+the important dependencies for running the pitivi UI can be imported and satisfy
+our version number requirements.
+
+The checks here are supposed to take a negligible amount of time (< 0.2 seconds)
+and not impact startup. Module imports have no impact (they get imported later
+by the app anyway). For more complex checks, you can measure (with time.time()),
+when called from application.py instead of bin/pitivi, if it has an impact.
 """
-
-from gi.repository import Gdk
-from gi.repository import Gtk
-from gi.repository import Gst
-from gi.repository import GES
-
+from sys import modules
 from gettext import gettext as _
 
-from pitivi.instance import PiTiVi
-from pitivi.configure import APPNAME, PYGTK_REQ, GTK_REQ, PYGST_REQ, GST_REQ, GNONLIN_REQ, PYCAIRO_REQ
-
-global soft_deps
-soft_deps = {}
-
-
-def initiate_videosinks():
-    """
-    Test if the autovideosink element can initiate, return TRUE if it is the
-    case.
-    """
+# This list is meant to be a complete list for packagers.
+# Unless otherwise noted, modules are accessed through gobject introspection
+HARD_DEPS = {
+    "cairo": "1.10.0",  # using static python bindings
+    "GES": "1.0.0",
+    "gnonlin": "0.11.89.1",
+    "GooCanvas": "2.0",
+    "Gst": "1.0.2",
+    "Gtk": "3.4.0",
+    "xdg": None,  # "pyxdg", using static python bindings
+
+    # The following are not checked, but needed for the rest to work:
+    "gobject-introspection": "1.34.0",
+    "gst-python": "0.10.22",  # we currently require the git version, 02ca5d3ad2
+    "pygobject": "3.4.0",
+}
+# For the list of soft dependencies, see the "check_soft_dependencies" method,
+# near the end of this file.
+global missing_soft_deps
+missing_soft_deps = {}
+
+
+def _initiate_videosinks(Gst):
+    # TODO: eventually switch to a clutter sink
     sink = Gst.ElementFactory.make("autovideosink", None)
     if not sink.set_state(Gst.State.READY):
         return False
@@ -49,11 +65,8 @@ def initiate_videosinks():
     return True
 
 
-def initiate_audiosinks():
-    """
-    Test if the autoaudiosink element can initiate, return TRUE if it is the
-    case.
-    """
+def _initiate_audiosinks(Gst):
+    # Yes, this can still fail, if PulseAudio is non-responsive for example.
     sink = Gst.ElementFactory.make("autoaudiosink", None)
     if not sink.set_state(Gst.State.READY):
         return False
@@ -61,11 +74,7 @@ def initiate_audiosinks():
     return True
 
 
-def __try_import_from_gi__(modulename):
-    """
-    Attempt to load given module.
-    Returns True on success, else False.
-    """
+def _try_import_from_gi(modulename):
     try:
         __import__("gi.repository." + modulename)
         return True
@@ -73,11 +82,7 @@ def __try_import_from_gi__(modulename):
         return False
 
 
-def __try_import__(modulename):
-    """
-    Attempt to load given module.
-    Returns True on success, else False.
-    """
+def _try_import(modulename):
     try:
         __import__(modulename)
         return True
@@ -93,111 +98,154 @@ def _string_to_list(version):
     return [int(x) for x in version.split(".")]
 
 
-def check_required_version(modulename):
+def _check_dependency(modulename, from_gobject_introspection):
     """
-    Checks if the installed module is the required version or more recent.
-    Returns [None, None] if it's recent enough, else will return a list
-    containing the strings of the required version and the installed version.
-    This function does not check for the existence of the given module !
+    Checks if the given module can be imported and is recent enough.
+
+    "modulename" is case-sensitive
+    "from_gobject_introspection" is a mandatory boolean variable.
+
+    Returns: [satisfied, version_required, version_installed]
     """
-    if modulename == "gtk":
-        if list((Gtk.MAJOR_VERSION, Gtk.MINOR_VERSION, Gtk.MICRO_VERSION)) < \
-                _string_to_list(GTK_REQ):
-            return [GTK_REQ, _version_to_string((Gtk.MAJOR_VERSION,
-                       Gtk.MINOR_VERSION, Gtk.MICRO_VERSION))]
+    VERSION_REQ = HARD_DEPS[modulename]
+    # What happens here is that we try to import the module. If it works,
+    # assign it to a "module" variable and check the version reqs with it.
+    module = None
+    if from_gobject_introspection is True:
+        if _try_import_from_gi(modulename):
+            module = modules["gi.repository." + modulename]
+    else:
+        if _try_import(modulename):
+            module = modules[modulename]
+
+    if module is None:
+        # Import failed, the dependency can't be satisfied, don't check versions
+        return [False, VERSION_REQ, None]
+    elif not VERSION_REQ:
+        # Import succeeded but there is no requirement, skip further checks
+        return [True, None, False]
+
+    # The import succeeded and there is a version requirement, so check it out:
+    if modulename == "Gst" or modulename == "GES":
+        if list(module.version()) < _string_to_list(VERSION_REQ):
+            return [False, VERSION_REQ, _version_to_string(module.version())]
+        else:
+            return [True, None, _version_to_string(module.version())]
+    if modulename == "Gtk":
+        gtk_version_tuple = (module.MAJOR_VERSION, module.MINOR_VERSION, module.MICRO_VERSION)
+        if list(gtk_version_tuple) < _string_to_list(VERSION_REQ):
+            return [False, VERSION_REQ, _version_to_string(gtk_version_tuple)]
+        else:
+            return [True, None, _version_to_string(gtk_version_tuple)]
     if modulename == "cairo":
-        import cairo
-        if _string_to_list(cairo.cairo_version_string()) < _string_to_list(PYCAIRO_REQ):
-            return [PYCAIRO_REQ, cairo.cairo_version_string()]
-    if modulename == "gst":
-        if list(Gst.version()) < _string_to_list(GST_REQ):
-            return [GST_REQ, _version_to_string(Gst.version())]
-    if modulename == "gnonlin":
-        gnlver = Gst.Registry.get().find_plugin("gnonlin").get_version()
-        if _string_to_list(gnlver) < _string_to_list(GNONLIN_REQ):
-            return [GNONLIN_REQ, gnlver]
-    return [None, None]
-
-
-def initial_checks():
+        if _string_to_list(module.cairo_version_string()) < _string_to_list(VERSION_REQ):
+            return [False, VERSION_REQ, module.cairo_version_string()]
+        else:
+            return [True, None, module.cairo_version_string()]
+    if modulename == "GooCanvas":
+        # GooCanvas does not provide a function to check the version. BRILLIANT.
+        return [True, VERSION_REQ, False]
+
+    oops = 'Module "%s" is installed, but version checking is not defined in check_dependency' % modulename
+    raise NotImplementedError(oops)
+
+
+def check_hard_dependencies():
+    missing_hard_deps = {}
+
+    satisfied, req, inst = _check_dependency("Gst", True)
+    if not satisfied:
+        missing_hard_deps["GStreamer"] = (req, inst)
+    satisfied, req, inst = _check_dependency("GES", True)
+    if not satisfied:
+        missing_hard_deps["GES"] = (req, inst)
+    satisfied, req, inst = _check_dependency("cairo", False)
+    if not satisfied:
+        missing_hard_deps["Cairo"] = (req, inst)
+    satisfied, req, inst = _check_dependency("GooCanvas", True)
+    if not satisfied:
+        missing_hard_deps["GooCanvas"] = (req, inst)
+    satisfied, req, inst = _check_dependency("xdg", False)
+    if not satisfied:
+        missing_hard_deps["PyXDG"] = (req, inst)
+    satisfied, req, inst = _check_dependency("Gtk", True)
+    if not satisfied:
+        missing_hard_deps["GTK+"] = (req, inst)
+    satisfied, req, inst = _check_dependency("xdg", False)
+    if not satisfied:
+        missing_hard_deps["PyXDG"] = (req, inst)
+
+    # Since we had to check Gst beforehand, we only do the import now:
+    from gi.repository import Gst
     Gst.init(None)
     reg = Gst.Registry.get()
+    # Special case: gnonlin is a plugin, not a python module to be imported,
+    # we can't use check_dependency to determine the version:
+    inst = Gst.Registry.get().find_plugin("gnonlin").get_version()
+    if _string_to_list(inst) < _string_to_list(HARD_DEPS["gnonlin"]):
+        missing_hard_deps["GNonLin"] = (HARD_DEPS["gnonlin"], inst)
+
+    # Prepare the list of hard deps errors to warn about later:
+    for dependency in missing_hard_deps:
+        req = missing_hard_deps[dependency][0]
+        inst = missing_hard_deps[dependency][1]
+        if req and not inst:
+            message = "%s or newer is required, but was not found on your system." % req
+        elif req and inst:
+            message = "%s or newer is required, but only version %s was found." % (req, inst)
+        else:
+            message = "not found on your system."
+        missing_hard_deps[dependency] = message
+
+    # And finally, do a few last checks for basic sanity.
+    # Yes, a broken/dead autoaudiosink is still possible in 2012 with PulseAudio
+    if not _initiate_videosinks(Gst):
+        missing_hard_deps["autovideosink"] = \
+            "Could not initiate video output sink. "\
+            "Make sure you have a valid one (xvimagesink or ximagesink)."
+    if not _initiate_audiosinks(Gst):
+        missing_hard_deps["autoaudiosink"] = \
+            "Could not initiate audio output sink. "\
+            "Make sure you have a valid one (pulsesink, alsasink or osssink)."
+
+    return missing_hard_deps
+
+
+def check_soft_dependencies():
+    """
+    Verify for the presence of optional modules that enhance the user experience
 
-    if PiTiVi:
-        return (_("%s is already running") % APPNAME,
-                _("An instance of %s is already running in this script.") % APPNAME)
-    if not reg.find_plugin("gnonlin"):
-        return (_("Could not find the GNonLin plugins"),
-                _("Make sure the plugins were installed and are available in the GStreamer plugins path."))
-    if not reg.find_plugin("autodetect"):
-        return (_("Could not find the autodetect plugins"),
-                _("Make sure you have installed gst-plugins-good and that it's available in the GStreamer plugin path."))
-    if not hasattr(Gdk.Window, 'cairo_create'):
-        return (_("PyGTK doesn't have cairo support"),
-                _("Please use a version of the GTK+ Python bindings built with cairo support."))
-    if not initiate_videosinks():
-        return (_("Could not initiate the video output plugins"),
-                _("Make sure you have at least one valid video output sink available (xvimagesink or ximagesink)."))
-    if not initiate_audiosinks():
-        return (_("Could not initiate the audio output plugins"),
-                _("Make sure you have at least one valid audio output sink available (alsasink or osssink)."))
-    if not __try_import_from_gi__("cairo"):
-        return (_("Could not import the cairo Python bindings"),
-                _("Make sure you have the cairo Python bindings installed."))
-    if not __try_import_from_gi__("GooCanvas"):
-        return (_("Could not import the goocanvas Python bindings"),
-                _("Make sure you have the goocanvas Python bindings installed."))
-    if not __try_import__("xdg"):
-        return (_("Could not import the xdg Python library"),
-                _("Make sure you have the xdg Python library installed."))
-    req, inst = check_required_version("gtk")
-    if req:
-        return (_("You do not have a recent enough version of GTK+ (your version %s)") % inst,
-                _("Install a version of GTK+ greater than or equal to %s.") % req)
-    req, inst = check_required_version("pygst")
-    if req:
-        return (_("You do not have a recent enough version of GStreamer Python bindings (your version %s)") % inst,
-                _("Install a version of the GStreamer Python bindings greater than or equal to %s.") % req)
-    req, inst = check_required_version("gst")
-    if req:
-        return (_("You do not have a recent enough version of GStreamer (your version %s)") % inst,
-                _("Install a version of the GStreamer greater than or equal to %s.") % req)
-    req, inst = check_required_version("cairo")
-    if req:
-        return (_("You do not have a recent enough version of the cairo Python bindings (your version %s)") % inst,
-                _("Install a version of the cairo Python bindings greater than or equal to %s.") % req)
-    req, inst = check_required_version("gnonlin")
-    if req:
-        return (_("You do not have a recent enough version of the GNonLin GStreamer plugin (your version %s)") % inst,
-                _("Install a version of the GNonLin GStreamer plugin greater than or equal to %s.") % req)
-    if not __try_import_from_gi__("GES"):
-        #FIXME enable version checking in GES
-        return (_("Could not import GStreamer Editing Services "),
-                _("Make sure you have GStreamer Editing Services installed."))
-    if not __try_import__("pkg_resources"):
-        return (_("Could not import the distutils modules"),
-                _("Make sure you have the distutils Python module installed."))
-
-    # The following are soft dependencies
-    # Note that instead of checking for plugins using Gst.Registry.get().find_plugin("foo"),
-    # we could check for elements using Gst.ElementFactory.make("foo")
-    if not __try_import__("numpy"):
-        soft_deps["NumPy"] = _("Enables the autoalign feature")
-    if Gst.ElementFactory.make("frei0r-filter-scale0tilt", None) is None:
-        soft_deps["Frei0r"] = _("Additional video effects")
-    if not Gst.Registry.get().find_plugin("libav"):
-        soft_deps["GStreamer Libav plugin"] = _('Additional multimedia codecs through the Libav library')
-    # Test for gst bad
-    # This is disabled because, by definition, gst bad is a set of plugins that can
-    # move to gst good or ugly, and we don't really have something to rely upon.
-    #if not Gst.Registry.get().find_plugin("swfdec"): # FIXME: find a more representative plugin
-    #    soft_deps["GStreamer bad plugins"] = _('Additional GStreamer plugins whose code is not of good enough quality, or are not considered tested well enough. The licensing may or may not be LGPL')
-    # Test for gst ugly
-    #if not Gst.Registry.get().find_plugin("x264"):
-    #    soft_deps["GStreamer ugly plugins"] = _('Additional good quality GStreamer plugins whose license is not LGPL or with licensing issues')
-
-    if not GES.init():
-        return (_("Could not initialize GStreamer Editing Services"),
-                _("Make sure you have the gst-editing-services installed."))
-
-    return None
+    If those are missing from the system, the user will be notified of their
+    existence by the presence of a "Missing dependencies..." button at startup.
+    """
+    # Importing Gst again (even if we did it in hard deps checks), anyway it
+    # seems to have no measurable performance impact the 2nd time:
+    from gi.repository import Gst
+    Gst.init(None)
+    registry = Gst.Registry.get()
+    # Description strings are translatable as they may be shown in the pitivi UI
+    if not _try_import("numpy"):
+        missing_soft_deps["NumPy"] = _("enables the autoalign feature")
+    if not _try_import("pycanberra"):
+        missing_soft_deps["PyCanberra"] = \
+            _("enables sound notifications when rendering is complete")
+    if not _try_import_from_gi("Notify"):
+        missing_soft_deps["libnotify"] = \
+            _("enables visual notifications when rendering is complete")
+    if not registry.find_plugin("libav"):
+        missing_soft_deps["GStreamer Libav plugin"] = \
+            _('additional multimedia codecs through the Libav library')
+    # Apparently, doing a registry.find_plugin("frei0r") is not enough.
+    # Sometimes it still returns something even when frei0r is uninstalled,
+    # and anyway we're looking specifically for the scale0tilt filter.
+    # Don't use Gst.ElementFactory.make for this check, it's very I/O intensive.
+    # Instead, ask the registry with .lookup_feature or .check_feature_version:
+    if not registry.lookup_feature("frei0r-filter-scale0tilt"):
+        missing_soft_deps["Frei0r"] = \
+            _("additional video effects, clip transformation feature")
+
+    # TODO: we're not actually checking for gst bad and ugly... by definition,
+    # gst bad is a set of plugins that can move to gst good or ugly, and anyway
+    # distro packagers may split/combine the gstreamer plugins into any way they
+    # see fit. We can do a registry.find_plugin for specific encoders, but we
+    # don't really have something generic to rely on; ideas/patches welcome.
diff --git a/pitivi/clipproperties.py b/pitivi/clipproperties.py
index f5b5db6..b802e96 100644
--- a/pitivi/clipproperties.py
+++ b/pitivi/clipproperties.py
@@ -31,7 +31,7 @@ from gi.repository import GES
 
 from gettext import gettext as _
 
-from pitivi.check import soft_deps
+from pitivi.check import missing_soft_deps
 from pitivi.configure import get_ui_dir
 
 from pitivi.dialogs.depsmanager import DepsManager
@@ -485,7 +485,7 @@ class TransformationProperties(Gtk.Expander):
         self.default_values = {}
         self.set_label(_("Transformation"))
 
-        if not "Frei0r" in soft_deps:
+        if not "Frei0r" in missing_soft_deps:
             self.builder = Gtk.Builder()
             self.builder.add_from_file(os.path.join(get_ui_dir(),
                         "cliptransformation.ui"))
@@ -518,7 +518,7 @@ class TransformationProperties(Gtk.Expander):
         self.app.gui.viewer.setZoom(scale.get_value())
 
     def _expandedCb(self, expander, params):
-        if not "Frei0r" in soft_deps:
+        if not "Frei0r" in missing_soft_deps:
             if self._current_tl_obj:
                 self.effect = self._findOrCreateEffect("frei0r-filter-scale0tilt")
                 self._updateSpinButtons()
diff --git a/pitivi/dialogs/depsmanager.py b/pitivi/dialogs/depsmanager.py
index 5173891..8b54028 100644
--- a/pitivi/dialogs/depsmanager.py
+++ b/pitivi/dialogs/depsmanager.py
@@ -24,7 +24,7 @@ from gi.repository import Gtk
 import os
 
 from pitivi.configure import get_ui_dir
-from pitivi.check import soft_deps
+from pitivi.check import missing_soft_deps
 
 
 class DepsManager(object):
@@ -60,7 +60,7 @@ class DepsManager(object):
         self.iface = dbus.Interface(self.obj, self.dbus_interface)
 
         soft_deps_list = []
-        for dep in soft_deps:
+        for dep in missing_soft_deps:
             soft_deps_list.append(dep)
 
         # This line works for testing, but InstallProvideFiles is not really what we want:
@@ -74,8 +74,8 @@ class DepsManager(object):
     def _setDepsLabel(self):
         """Set the contents of the label containing the list of missing dependencies"""
         label_contents = ""
-        for dep in soft_deps:
-            label_contents += "â " + dep + " (" + soft_deps[dep] + ")\n"
+        for dep in missing_soft_deps:
+            label_contents += "â " + dep + " (" + missing_soft_deps[dep] + ")\n"
         self.builder.get_object("pkg_list").set_text(label_contents)
 
     def show(self):
diff --git a/pitivi/dialogs/startupwizard.py b/pitivi/dialogs/startupwizard.py
index 4e02bb6..a5dcfaf 100644
--- a/pitivi/dialogs/startupwizard.py
+++ b/pitivi/dialogs/startupwizard.py
@@ -28,7 +28,7 @@ from gettext import gettext as _
 
 from pitivi.configure import get_ui_dir
 from pitivi.dialogs.depsmanager import DepsManager
-from pitivi.check import soft_deps
+from pitivi.check import missing_soft_deps
 from pitivi.utils.misc import show_user_manual
 
 
@@ -59,7 +59,7 @@ class StartUpWizard(object):
         filter.add_pattern("*.xptv")
         self.recent_chooser.add_filter(filter)
 
-        if not soft_deps:
+        if not missing_soft_deps:
             self.builder.get_object("missing_deps_button").hide()
 
         self.app.projectManager.connect("new-project-failed", self._projectFailedCb)
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index 8af9260..a5ecba1 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -39,7 +39,7 @@ from gettext import gettext as _
 from os.path import join
 
 from pitivi.timeline import ruler
-from pitivi.check import soft_deps
+from pitivi.check import missing_soft_deps
 from pitivi.effects import AUDIO_EFFECT, VIDEO_EFFECT
 from pitivi.autoaligner import AlignmentProgressDialog
 from pitivi.utils.misc import quote_uri, print_ns
@@ -1755,7 +1755,7 @@ class Timeline(Gtk.Table, Loggable, Zoomable):
             self.timeline.enable_update(True)
 
     def alignSelected(self, unused_action):
-        if "NumPy" in soft_deps:
+        if "NumPy" in missing_soft_deps:
             DepsManager(self.app)
 
         elif self.timeline:
diff --git a/tests/__init__.py b/tests/__init__.py
index 0442462..e69de29 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,11 +0,0 @@
-from gi.repository import GObject
-# This call has to be made before any "import Gst" call!
-GObject.threads_init()
-
-from pitivi.check import initial_checks
-
-
-missing_deps = initial_checks()
-if missing_deps:
-    message, detail = missing_deps
-    raise Exception("%s\n%s" % (message, detail))
diff --git a/tests/runtests.py b/tests/runtests.py
index 8b22e85..3ac5c80 100644
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -8,6 +8,17 @@ from gi.repository import GObject
 # because this tool is run directly, as an executable.
 GObject.threads_init()
 
+from pitivi.check import check_hard_dependencies
+missing_hard_deps = check_hard_dependencies()
+# This differs slightly from bin/pitivi.in as we don't check soft deps here:
+if missing_hard_deps:
+    print "\nERROR - The following hard dependencies are unmet:"
+    print "=================================================="
+    for dep in missing_hard_deps:
+        print "-", dep + ":", missing_hard_deps[dep]
+    print ""
+    sys.exit(2)
+
 
 def gettestnames(file_names):
     test_names = [file_name[:-3] for file_name in file_names]



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]