conduit r1392 - in trunk: . conduit conduit/dataproviders conduit/datatypes conduit/gtkui conduit/modules conduit/modules/BackpackModule conduit/modules/BansheeModule conduit/modules/BoxDotNetModule conduit/modules/EvolutionModule conduit/modules/FacebookModule conduit/modules/FeedModule conduit/modules/FileModule conduit/modules/FlickrModule conduit/modules/FspotModule conduit/modules/GmailModule conduit/modules/GoogleModule conduit/modules/NetworkModule conduit/modules/PicasaDesktopModule conduit/modules/RhythmboxDBusModule conduit/modules/RhythmboxModule conduit/modules/ShutterflyModule conduit/modules/SmugMugModule conduit/utils scripts test/python-tests



Author: jstowers
Date: Wed Mar 26 09:16:32 2008
New Revision: 1392
URL: http://svn.gnome.org/viewvc/conduit?rev=1392&view=rev

Log:
2008-03-26  John Stowers  <john stowers gmail com>

	* conduit/*.py:
	* test/python-tests/*.py: Move Utils.py to its own module, conduit.utils
	Fixes #520748 (Claude Paroz)



Added:
   trunk/conduit/utils/
   trunk/conduit/utils/CommandLineConverter.py
   trunk/conduit/utils/Makefile.am
   trunk/conduit/utils/Memstats.py
   trunk/conduit/utils/__init__.py
Modified:
   trunk/ChangeLog
   trunk/conduit/Conduit.py
   trunk/conduit/DBus.py
   trunk/conduit/Main.py
   trunk/conduit/Makefile.am
   trunk/conduit/MappingDB.py
   trunk/conduit/TypeConverter.py
   trunk/conduit/Utils.py
   trunk/conduit/dataproviders/DataProvider.py
   trunk/conduit/dataproviders/HalFactory.py
   trunk/conduit/dataproviders/VolumeFactory.py
   trunk/conduit/datatypes/File.py
   trunk/conduit/datatypes/Photo.py
   trunk/conduit/gtkui/Database.py
   trunk/conduit/modules/AudioVideoConverterModule.py
   trunk/conduit/modules/BackpackModule/BackpackModule.py
   trunk/conduit/modules/BansheeModule/BansheeModule.py
   trunk/conduit/modules/BoxDotNetModule/BoxDotNetModule.py
   trunk/conduit/modules/ConverterModule.py
   trunk/conduit/modules/EvolutionModule/EvolutionModule.py
   trunk/conduit/modules/FacebookModule/FacebookModule.py
   trunk/conduit/modules/FeedModule/FeedModule.py
   trunk/conduit/modules/FileModule/FileConfiguration.py
   trunk/conduit/modules/FileModule/FileModule.py
   trunk/conduit/modules/FlickrModule/FlickrModule.py
   trunk/conduit/modules/FspotModule/FspotModule.py
   trunk/conduit/modules/GmailModule/GmailModule.py
   trunk/conduit/modules/GoogleModule/GoogleModule.py
   trunk/conduit/modules/NetworkModule/NetworkModule.py
   trunk/conduit/modules/NetworkModule/XMLRPCUtils.py
   trunk/conduit/modules/PhotoConverterModule.py
   trunk/conduit/modules/PicasaDesktopModule/PicasaDesktopModule.py
   trunk/conduit/modules/RhythmboxDBusModule/RhythmboxDBusModule.py
   trunk/conduit/modules/RhythmboxModule/RhythmboxModule.py
   trunk/conduit/modules/ShutterflyModule/ShutterflyModule.py
   trunk/conduit/modules/SmugMugModule/SmugMugModule.py
   trunk/conduit/modules/TestModule.py
   trunk/conduit/modules/TomboyModule.py
   trunk/conduit/modules/iPodModule.py
   trunk/configure.ac
   trunk/scripts/ChangeLog
   trunk/scripts/run-tests.sh
   trunk/test/python-tests/TestCoreConvert.py
   trunk/test/python-tests/TestCoreConvertAudioVideo.py
   trunk/test/python-tests/TestCoreConvertSubtypesArgs.py
   trunk/test/python-tests/TestCoreDate.py
   trunk/test/python-tests/TestCoreEmail.py
   trunk/test/python-tests/TestCoreFile.py
   trunk/test/python-tests/TestCoreFile2.py
   trunk/test/python-tests/TestCoreMappingDB.py
   trunk/test/python-tests/TestCoreTempFile.py
   trunk/test/python-tests/TestCoreUtil.py
   trunk/test/python-tests/TestCoreVfs.py
   trunk/test/python-tests/TestDataProviderBackpack.py
   trunk/test/python-tests/TestDataProviderBoxDotNet.py
   trunk/test/python-tests/TestDataProviderGmail.py
   trunk/test/python-tests/TestDataProviderGoogle.py
   trunk/test/python-tests/TestDataProviderShutterfly.py
   trunk/test/python-tests/TestDataProviderSmugMug.py
   trunk/test/python-tests/TestDataProviderTomboy.py
   trunk/test/python-tests/TestDataProvideriPod.py
   trunk/test/python-tests/TestSyncEvolutionFolder.py
   trunk/test/python-tests/TestSyncFileFolder.py
   trunk/test/python-tests/TestSyncFolderFolder.py
   trunk/test/python-tests/TestSyncTomboyFolder.py
   trunk/test/python-tests/TestSyncTomboyiPod.py
   trunk/test/python-tests/common.py

Modified: trunk/conduit/Conduit.py
==============================================================================
--- trunk/conduit/Conduit.py	(original)
+++ trunk/conduit/Conduit.py	Wed Mar 26 09:16:32 2008
@@ -9,7 +9,7 @@
 log = logging.getLogger("Conduit")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 CONFLICT_POLICY_NAMES = ("conflict", "deleted")
 CONFLICT_POLICY_VALUES = ("ask","skip","replace")

Modified: trunk/conduit/DBus.py
==============================================================================
--- trunk/conduit/DBus.py	(original)
+++ trunk/conduit/DBus.py	Wed Mar 26 09:16:32 2008
@@ -13,7 +13,7 @@
 log = logging.getLogger("DBus")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Conduit as Conduit
 import conduit.SyncSet as SyncSet
 

Modified: trunk/conduit/Main.py
==============================================================================
--- trunk/conduit/Main.py	(original)
+++ trunk/conduit/Main.py	Wed Mar 26 09:16:32 2008
@@ -8,7 +8,7 @@
 log = logging.getLogger("Main")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 from conduit.Module import ModuleManager
 from conduit.MappingDB import MappingDB
 from conduit.TypeConverter import TypeConverter

Modified: trunk/conduit/Makefile.am
==============================================================================
--- trunk/conduit/Makefile.am	(original)
+++ trunk/conduit/Makefile.am	Wed Mar 26 09:16:32 2008
@@ -1,4 +1,4 @@
-SUBDIRS = datatypes dataproviders modules gtkui hildonui
+SUBDIRS = datatypes dataproviders modules gtkui hildonui utils
 
 conduitbindir = $(bindir)
 conduitbin_SCRIPTS = \
@@ -25,7 +25,6 @@
 	Synchronization.py \
 	SyncSet.py \
 	TypeConverter.py \
-	Utils.py \
 	Vfs.py \
 	Web.py
 	

Modified: trunk/conduit/MappingDB.py
==============================================================================
--- trunk/conduit/MappingDB.py	(original)
+++ trunk/conduit/MappingDB.py	Wed Mar 26 09:16:32 2008
@@ -6,7 +6,7 @@
 
 import conduit
 import conduit.datatypes
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Database as Database
 
 DB_FIELDS = ("sourceUID","sourceDataLUID","sourceDataMtime","sourceDataHash","sinkUID","sinkDataLUID","sinkDataMtime","sinkDataHash")

Modified: trunk/conduit/TypeConverter.py
==============================================================================
--- trunk/conduit/TypeConverter.py	(original)
+++ trunk/conduit/TypeConverter.py	Wed Mar 26 09:16:32 2008
@@ -9,7 +9,7 @@
 log = logging.getLogger("TypeConverter")
 
 import conduit.Exceptions as Exceptions
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 class Converter:
     _module_type_ = "converter"

Modified: trunk/conduit/Utils.py
==============================================================================
--- trunk/conduit/Utils.py	(original)
+++ trunk/conduit/Utils.py	Wed Mar 26 09:16:32 2008
@@ -1,375 +0,0 @@
-"""
-Utility Functions
-
-Part of this code copied from from Listen (c) 2006 Mehdi Abaakouk
-(http://listengnome.free.fr/)
-
-Copyright: John Stowers, 2006
-License: GPLv2
-"""
-import sys
-import os.path
-import socket
-import datetime
-import time
-import re
-import os
-import signal
-import popen2
-import logging
-log = logging.getLogger("Utils")
-
-def get_proportional_resize(desiredW, desiredH, currentW, currentH):
-    """
-    Returns proportionally resized co-ordinates for an image
-    """
-    #Account for 'dont care about this axis sizing'
-    if desiredH == None: desiredH = currentH
-    if desiredW == None: desiredW = currentW
-
-    #Calculate the axis of most change
-    dw = abs(currentW - desiredW)
-    dh = abs(currentH - desiredH)
-
-    if dh > dw:
-        newHeight = float(desiredH)
-        percentage = newHeight / currentH
-        newWidth = currentW * percentage
-    else:
-        newWidth = float(desiredW)
-        percentage = newWidth / currentW
-        newHeight = currentH * percentage
-
-    return int(newWidth), int(newHeight)    
-    
-def program_installed(app):
-    """
-    Check if the given app is installed.
-    """
-    path = os.environ['PATH'] 
-    paths = path.split(os.pathsep)
-    for dir in paths:
-        if os.path.isdir(dir):
-            if os.path.isfile(os.path.join(dir,app)):
-                return True
-    return False
-
-#
-# Temporary file functions
-#
-def new_tempfile(contents, contentsAreText=True):
-    """
-    Returns a new File onject, which has been created in the 
-    system temporary directory, and that has been filled with
-    contents
-    
-    The file is closed when it is returned
-    
-    @param contents: The data to write into the file
-    @param contentsAreText: Indicates to the OS if the file is text (as opposed
-    to a binary type file
-    @param contentsAreText: C{bool}
-    @returns: a L{conduit.datatypes.File}
-    """
-    import conduit.datatypes.File as File
-    return File.TempFile(contents)
-
-def new_tempdir():
-    """
-    Creates a new temporary directory
-    """
-    import tempfile
-    return tempfile.mkdtemp("conduit")
-
-def unique_list(seq):
-    """
-    The fastes way to unique-ify a list while retaining its order, from
-    http://www.peterbe.com/plog/uniqifiers-benchmark
-    """
-    def _f10(listy):
-        seen = set()
-        for x in listy:
-            if x in seen:
-                continue
-            seen.add(x)
-            yield x
-    return list(_f10(seq))
-
-def random_string(length=5):
-    """
-    returns a random string of length
-    """
-    import random
-    s = ""
-    for i in range(0,length):
-        s += str(random.randint(0,10))
-    return s
-
-def dataprovider_add_dir_to_path(dataproviderfile, directory=""):
-    """
-    Adds directory to the python search path.
-
-    From within a dataprovider (FooModule.py) 
-    call with Utils.dataprovider_add_dir_to_path(__file__, some_dir):
-    """
-    path = os.path.join(dataproviderfile, "..", directory)
-    path = os.path.abspath(path)
-    log.debug("Adding %s to python path" % path)
-    sys.path.insert(0,path)
-
-def dataprovider_glade_get_widget(dataproviderfile, gladefilename, widget):
-    """
-    Gets a single gtk widget from a glad file
-    """
-    import gtk.glade
-    path = os.path.join(dataproviderfile, "..", gladefilename)
-    path = os.path.abspath(path)
-    return gtk.glade.XML(path, widget)
-
-def run_dialog(dialog, window=None):
-    """
-    Runs a given dialog, and makes it transient for
-    the given window if any
-    @param dialog: dialog 
-    @param window: gtk window
-    @returns: True if the user clicked OK to exit the dialog
-    """
-    import gtk
-
-    if window:
-        dialog.set_transient_for(window)
-
-    return dialog.run() == gtk.RESPONSE_OK
-
-def md5_string(string):
-    """
-    Returns the md5 of the supplied string in readable hexdigest string format
-    """
-    import md5
-    return md5.new(string).hexdigest()
-
-def uuid_string():
-    """
-    Returns a uuid string
-    """
-    try:
-        import uuid
-        return uuid.uuid4().hex
-    except ImportError:
-        import time, random, md5, socket
-        t = long( time.time() * 1000 )
-        r = long( random.random()*100000000000000000L )
-        try:
-            a = socket.gethostbyname( socket.gethostname() )
-        except:
-            a = random.random()*100000000000000000L
-        data = str(t)+' '+str(r)+' '+str(a)
-        data = md5.md5(data).hexdigest()
-        return data
-
-def dbus_service_available(interface, bus=None):
-    """
-    Checks if a dbus service is available on the given bus
-    @param interface: The interface to look for
-    @param bus: The bus to look on (optional)
-    """
-    try: 
-        import dbus
-    except: 
-        return False
-        
-    if bus == None:        
-        bus = dbus.SessionBus()
-        
-    obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') 
-    dbus_iface = dbus.Interface(obj, 'org.freedesktop.DBus') 
-    avail = dbus_iface.ListNames()
-    return interface in avail
-
-def get_user_string():
-    """
-    Makes a user and machine dependant string in the form
-    username hostname
-    """
-    hostname = socket.gethostname()
-    username = os.environ.get("USER","")
-    return "%s %s" % (username, hostname)
-
-def datetime_from_timestamp(t):
-    """
-    Makes a datetime object from a unix timestamp.
-
-    Note: For the sake of consistancy always drop the
-    fractional (microsecond) part of the timestamp
-    """
-    if type(t) not in [long, int, float]:
-        raise Exception("Timestamp must be a number")
-
-    if t < 0:
-        raise Exception("Timestamps before 1970 are not valid")
-
-    return datetime.datetime.fromtimestamp(long(t))
-
-def datetime_get_timestamp(d):
-    """
-    Returns the unix timestamp for a datetime
-
-    Note: For the sake of consistancy always drop the
-    fractional (microsecond) part of the timestamp
-    """
-    import time, datetime
-    if type(d) != datetime.datetime:
-        raise Exception("Must supply a datetime")
-
-    f = time.mktime(d.timetuple())
-    if f < 0:
-        raise Exception("Timestamps before 1970 are not valid")
-
-    return long(f)
-
-def encode_conversion_args(args):
-    """
-    encodes an args dictionary to a url string in the form
-    param=value&param2=val2
-    """
-    import urllib
-    return urllib.urlencode(args)
-
-def decode_conversion_args(argString):
-    """
-    FIXME: dont import cgi for just one function. Also it doesnt
-    even handle lists
-    """
-    import cgi
-    args = {}
-    for key,val in cgi.parse_qsl(argString):
-        args[key] = val
-    return args
-    
-def log_function_call(log):
-    """
-    A decorator that prints debug message showing the function name and
-    argument types to the supplied logger instance. 
-    
-    Adapted from the accepts/returns decorators at
-    http://wiki.python.org/moin/PythonDecoratorLibrary
-    """
-    def decorator(f):
-        def newf(*args):
-            #Ensure args are tuples with str args - necessart for CPython methods
-            argtypes = map(str,map(type, args))
-            argnames = map(str,f.func_code.co_varnames[:f.func_code.co_argcount])
-            argnamesandtypes = ["%s:%s" % (i,j) for i,j in zip(argnames,argtypes)]
-            msg = "Method Call %s(%s)" % (
-                        f.__name__,
-                        ', '.join(argnamesandtypes)
-                        )
-            log.debug(msg)
-            return f(*args)
-        #Retain information about old function
-        newf.__name__ = f.__name__
-        newf.__doc__ = f.__doc__
-        newf.__dict__.update(f.__dict__)
-        return newf
-    return decorator
-
-def xml_extract_value_from_tag(tag, text):
-    """
-    Returns the contents of the xml tag or None. Uses a simple regex.   
-
-    Taken from:
-    http://immike.net/blog/2007/04/06/5-regular-expressions-every-web-programmer-should-know/
-    """
-    ans = re.compile("<%(tag)s[^>]*>(.*?)</%(tag)s>" % {"tag":tag}).findall(text)
-    if ans:
-        return ans[0]
-    else:
-        return None
-#
-# Memstats
-#
-class Memstats(object):
-    """
-    Memory analysis functions taken from
-    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/286222
-    """
-
-    _proc_status = '/proc/%d/status' % os.getpid()
-    _scale = {'kB': 1024.0, 'mB': 1024.0*1024.0,
-              'KB': 1024.0, 'MB': 1024.0*1024.0}
-              
-    def __init__(self):
-        self.prev = [0.0,0.0,0.0]
-
-    def _VmB(self, VmKey):
-         # get pseudo file  /proc/<pid>/status
-        try:
-            t = open(self._proc_status)
-            v = t.read()
-            t.close()
-        except Exception, err:
-            return 0.0  # non-Linux?
-         # get VmKey line e.g. 'VmRSS:  9999  kB\n ...'
-        i = v.index(VmKey)
-        v = v[i:].split(None, 3)  # whitespace
-        if len(v) < 3:
-            return 0.0  # invalid format?
-         # convert Vm value to bytes
-        return float(v[1]) * self._scale[v[2]]
-        
-    def calculate(self):
-        VmSize = self._VmB('VmSize:') - self.prev[0]
-        VmRSS = self._VmB('VmRSS:') - self.prev [1]
-        VmStack = self._VmB('VmStk:') - self.prev [2]
-        log.debug("Memory Stats: VM=%sMB RSS=%sMB STACK=%sMB" %(
-                                    VmSize  / self._scale["MB"],
-                                    VmRSS   / self._scale["MB"],
-                                    VmStack / self._scale["MB"],
-                                    ))
-        return VmSize,VmRSS,VmStack 
-
-class CommandLineConverter:
-    def __init__(self):
-        self.percentage_match = re.compile('.*')
-
-    def _kill(self, process):
-        log.debug("Killing process")
-        os.kill(process.pid, signal.SIGKILL)
-
-    def build_command(self, command, **params):
-        self.command = command
-        
-    def calculate_percentage(self, val):
-        return float(val)
-
-    def check_cancelled(self):
-        return False
-
-    def convert( self, input_filename, output_filename, callback=None,save_output=False):
-        command = self.command % (input_filename, output_filename)
-        log.debug("Executing %s" % command)
-
-        output = ""
-        process = popen2.Popen4(command)
-        stdout = process.fromchild
-        s = stdout.read(80)
-        if save_output:
-            output += s
-        while s:
-            if callback:
-                for i in self.percentage_match.finditer(s):
-                    val = self.calculate_percentage(i.group(1).strip())
-                    callback(val)
-            if save_output:
-                output += s
-            if self.check_cancelled():
-                self._kill(process)
-            s = stdout.read(80)
-
-        ok = process.wait() == 0
-        if save_output:
-            return ok, output
-        else:
-            return ok
-            
-

Modified: trunk/conduit/dataproviders/DataProvider.py
==============================================================================
--- trunk/conduit/dataproviders/DataProvider.py	(original)
+++ trunk/conduit/dataproviders/DataProvider.py	Wed Mar 26 09:16:32 2008
@@ -13,7 +13,7 @@
 
 import conduit
 import conduit.ModuleWrapper as ModuleWrapper
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Settings as Settings
 
 #Constants used in the sync state machine

Modified: trunk/conduit/dataproviders/HalFactory.py
==============================================================================
--- trunk/conduit/dataproviders/HalFactory.py	(original)
+++ trunk/conduit/dataproviders/HalFactory.py	Wed Mar 26 09:16:32 2008
@@ -2,7 +2,7 @@
 import dbus
 import dbus.glib
 
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.dataproviders.SimpleFactory as SimpleFactory
 
 class HalFactory(SimpleFactory.SimpleFactory):

Modified: trunk/conduit/dataproviders/VolumeFactory.py
==============================================================================
--- trunk/conduit/dataproviders/VolumeFactory.py	(original)
+++ trunk/conduit/dataproviders/VolumeFactory.py	Wed Mar 26 09:16:32 2008
@@ -4,7 +4,7 @@
 
 import conduit
 import conduit.dataproviders.SimpleFactory as SimpleFactory
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Vfs as Vfs
 
 import dbus

Modified: trunk/conduit/datatypes/File.py
==============================================================================
--- trunk/conduit/datatypes/File.py	(original)
+++ trunk/conduit/datatypes/File.py	Wed Mar 26 09:16:32 2008
@@ -131,7 +131,7 @@
             return False
 
     def _set_file_mtime(self, mtime):
-        timestamp = conduit.Utils.datetime_get_timestamp(mtime)
+        timestamp = conduit.utils.datetime_get_timestamp(mtime)
         log.debug("Setting mtime of %s to %s (%s)" % (self.URI, timestamp, type(timestamp)))
         newInfo = gnomevfs.FileInfo()
         newInfo.mtime = timestamp

Modified: trunk/conduit/datatypes/Photo.py
==============================================================================
--- trunk/conduit/datatypes/Photo.py	(original)
+++ trunk/conduit/datatypes/Photo.py	Wed Mar 26 09:16:32 2008
@@ -1,7 +1,7 @@
 import conduit
 
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 PRESET_ENCODINGS = {
     "jpeg":{'formats':'image/jpeg','default-format':'image/jpeg'},

Modified: trunk/conduit/gtkui/Database.py
==============================================================================
--- trunk/conduit/gtkui/Database.py	(original)
+++ trunk/conduit/gtkui/Database.py	Wed Mar 26 09:16:32 2008
@@ -28,7 +28,7 @@
 log = logging.getLogger("gtkui.Database")
 
 import conduit.Database as DB
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 class GenericDBListStore(gtk.GenericTreeModel):
     """

Modified: trunk/conduit/modules/AudioVideoConverterModule.py
==============================================================================
--- trunk/conduit/modules/AudioVideoConverterModule.py	(original)
+++ trunk/conduit/modules/AudioVideoConverterModule.py	Wed Mar 26 09:16:32 2008
@@ -3,7 +3,8 @@
 log = logging.getLogger("modules.AVConverter")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
+import conduit.utils.CommandLineConverter as CommandLineConverter
 import conduit.TypeConverter as TypeConverter
 import conduit.datatypes.File as File
 import conduit.datatypes.Audio as Audio
@@ -16,9 +17,9 @@
 else:
     MODULES = {}
 
-class FFmpegCommandLineConverter(Utils.CommandLineConverter):
+class FFmpegCommandLineConverter(CommandLineConverter.CommandLineConverter):
     def __init__(self, duration=None):
-        Utils.CommandLineConverter.__init__(self)
+        CommandLineConverter.CommandLineConverter.__init__(self)
         self.duration = duration
         self.percentage_match = re.compile('time=?(\d+\.\d+)')
 
@@ -51,9 +52,9 @@
     def check_cancelled(self):
         return conduit.GLOBALS.cancelled
 
-class MencoderCommandLineConverter(Utils.CommandLineConverter):
+class MencoderCommandLineConverter(CommandLineConverter.CommandLineConverter):
     def __init__(self):
-        Utils.CommandLineConverter.__init__(self)
+        CommandLineConverter.CommandLineConverter.__init__(self)
         self.percentage_match = re.compile('(\d+)%')
 
     def build_command(self, **kwargs):
@@ -115,7 +116,7 @@
         input_file = video.get_local_uri()
         
         #run ffmpeg over the video to work out its format, and duration
-        c = Utils.CommandLineConverter()
+        c = CommandLineConverter.CommandLineConverter()
         c.build_command(AudioVideoConverter.VIDEO_INSPECT_COMMAND)
         ok,output = c.convert(input_file,"/dev/null",save_output=True)
 
@@ -175,7 +176,7 @@
         input_file = audio.get_local_uri()
 
         #run ffmpeg over the video to work out its format, and duration
-        c = Utils.CommandLineConverter()
+        c = CommandLineConverter.CommandLineConverter()
         c.build_command(AudioVideoConverter.AUDIO_INSPECT_COMMAND)
         ok,output = c.convert(input_file,"/dev/null",save_output=True)
 

Modified: trunk/conduit/modules/BackpackModule/BackpackModule.py
==============================================================================
--- trunk/conduit/modules/BackpackModule/BackpackModule.py	(original)
+++ trunk/conduit/modules/BackpackModule/BackpackModule.py	Wed Mar 26 09:16:32 2008
@@ -7,7 +7,7 @@
 log = logging.getLogger("modules.Backpack")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.Exceptions as Exceptions
 from conduit.datatypes import Rid

Modified: trunk/conduit/modules/BansheeModule/BansheeModule.py
==============================================================================
--- trunk/conduit/modules/BansheeModule/BansheeModule.py	(original)
+++ trunk/conduit/modules/BansheeModule/BansheeModule.py	Wed Mar 26 09:16:32 2008
@@ -11,7 +11,7 @@
     from sqlite3 import dbapi2 as sqlite
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Exceptions as Exceptions
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.datatypes.Audio as Audio

Modified: trunk/conduit/modules/BoxDotNetModule/BoxDotNetModule.py
==============================================================================
--- trunk/conduit/modules/BoxDotNetModule/BoxDotNetModule.py	(original)
+++ trunk/conduit/modules/BoxDotNetModule/BoxDotNetModule.py	Wed Mar 26 09:16:32 2008
@@ -8,7 +8,7 @@
 log = logging.getLogger("modules.BoxDotNet")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Web as Web
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.Exceptions as Exceptions

Modified: trunk/conduit/modules/ConverterModule.py
==============================================================================
--- trunk/conduit/modules/ConverterModule.py	(original)
+++ trunk/conduit/modules/ConverterModule.py	Wed Mar 26 09:16:32 2008
@@ -2,7 +2,7 @@
 import logging
 log = logging.getLogger("modules.Converter")
 
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.TypeConverter as TypeConverter
 import conduit.datatypes.Contact as Contact
 import conduit.datatypes.Event as Event

Modified: trunk/conduit/modules/EvolutionModule/EvolutionModule.py
==============================================================================
--- trunk/conduit/modules/EvolutionModule/EvolutionModule.py	(original)
+++ trunk/conduit/modules/EvolutionModule/EvolutionModule.py	Wed Mar 26 09:16:32 2008
@@ -20,7 +20,7 @@
 
 import conduit
 import conduit.dataproviders.DataProvider as DataProvider
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Exceptions as Exceptions
 from conduit.datatypes import Rid
 import conduit.datatypes.Contact as Contact

Modified: trunk/conduit/modules/FacebookModule/FacebookModule.py
==============================================================================
--- trunk/conduit/modules/FacebookModule/FacebookModule.py	(original)
+++ trunk/conduit/modules/FacebookModule/FacebookModule.py	Wed Mar 26 09:16:32 2008
@@ -8,7 +8,7 @@
 log = logging.getLogger("modules.Facebook")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Web as Web
 import conduit.dataproviders.Image as Image
 import conduit.Exceptions as Exceptions

Modified: trunk/conduit/modules/FeedModule/FeedModule.py
==============================================================================
--- trunk/conduit/modules/FeedModule/FeedModule.py	(original)
+++ trunk/conduit/modules/FeedModule/FeedModule.py	Wed Mar 26 09:16:32 2008
@@ -13,7 +13,7 @@
 log = logging.getLogger("modules.Feed")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.Exceptions as Exceptions
 import conduit.datatypes.File as File

Modified: trunk/conduit/modules/FileModule/FileConfiguration.py
==============================================================================
--- trunk/conduit/modules/FileModule/FileConfiguration.py	(original)
+++ trunk/conduit/modules/FileModule/FileConfiguration.py	Wed Mar 26 09:16:32 2008
@@ -4,7 +4,7 @@
 log = logging.getLogger("modules.File")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Vfs as Vfs
 import conduit.gtkui.Database as Database
 import conduit.dataproviders.File as FileDataProvider

Modified: trunk/conduit/modules/FileModule/FileModule.py
==============================================================================
--- trunk/conduit/modules/FileModule/FileModule.py	(original)
+++ trunk/conduit/modules/FileModule/FileModule.py	Wed Mar 26 09:16:32 2008
@@ -9,7 +9,7 @@
 import conduit.dataproviders.File as FileDataProvider
 import conduit.dataproviders.VolumeFactory as VolumeFactory
 import conduit.dataproviders.AutoSync as AutoSync
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Vfs as Vfs
 
 MODULES = {

Modified: trunk/conduit/modules/FlickrModule/FlickrModule.py
==============================================================================
--- trunk/conduit/modules/FlickrModule/FlickrModule.py	(original)
+++ trunk/conduit/modules/FlickrModule/FlickrModule.py	Wed Mar 26 09:16:32 2008
@@ -8,7 +8,7 @@
 log = logging.getLogger("modules.Flickr")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Web as Web
 import conduit.dataproviders.Image as Image
 import conduit.Exceptions as Exceptions

Modified: trunk/conduit/modules/FspotModule/FspotModule.py
==============================================================================
--- trunk/conduit/modules/FspotModule/FspotModule.py	(original)
+++ trunk/conduit/modules/FspotModule/FspotModule.py	Wed Mar 26 09:16:32 2008
@@ -5,7 +5,7 @@
 log = logging.getLogger("modules.Fspot")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Exceptions as Exceptions
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.datatypes.Photo as Photo

Modified: trunk/conduit/modules/GmailModule/GmailModule.py
==============================================================================
--- trunk/conduit/modules/GmailModule/GmailModule.py	(original)
+++ trunk/conduit/modules/GmailModule/GmailModule.py	Wed Mar 26 09:16:32 2008
@@ -4,7 +4,7 @@
 log = logging.getLogger("modules.Gmail")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.Exceptions as Exceptions
 from conduit.datatypes import Rid

Modified: trunk/conduit/modules/GoogleModule/GoogleModule.py
==============================================================================
--- trunk/conduit/modules/GoogleModule/GoogleModule.py	(original)
+++ trunk/conduit/modules/GoogleModule/GoogleModule.py	Wed Mar 26 09:16:32 2008
@@ -9,7 +9,7 @@
 import conduit
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.dataproviders.Image as Image
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Exceptions as Exceptions
 from conduit.datatypes import Rid
 import conduit.datatypes.Event as Event

Modified: trunk/conduit/modules/NetworkModule/NetworkModule.py
==============================================================================
--- trunk/conduit/modules/NetworkModule/NetworkModule.py	(original)
+++ trunk/conduit/modules/NetworkModule/NetworkModule.py	Wed Mar 26 09:16:32 2008
@@ -5,7 +5,7 @@
 Copyright: John Stowers, 2006
 License: GPLv2
 """
-import conduit.Utils as Utils
+import conduit.utils as Utils
 Utils.dataprovider_add_dir_to_path(__file__, "")
 
 import Client

Modified: trunk/conduit/modules/NetworkModule/XMLRPCUtils.py
==============================================================================
--- trunk/conduit/modules/NetworkModule/XMLRPCUtils.py	(original)
+++ trunk/conduit/modules/NetworkModule/XMLRPCUtils.py	Wed Mar 26 09:16:32 2008
@@ -20,7 +20,7 @@
 
 import conduit.Exceptions as Exceptions
 import conduit.dataproviders.DataProvider as DataProvider
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 XML_RPC_EASY_EXCEPTIONS = (
     "RefreshError",

Modified: trunk/conduit/modules/PhotoConverterModule.py
==============================================================================
--- trunk/conduit/modules/PhotoConverterModule.py	(original)
+++ trunk/conduit/modules/PhotoConverterModule.py	Wed Mar 26 09:16:32 2008
@@ -2,7 +2,7 @@
 log = logging.getLogger("modules.PhotoConverter")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.TypeConverter as TypeConverter
 import conduit.datatypes.File as File
 import conduit.datatypes.Photo as Photo

Modified: trunk/conduit/modules/PicasaDesktopModule/PicasaDesktopModule.py
==============================================================================
--- trunk/conduit/modules/PicasaDesktopModule/PicasaDesktopModule.py	(original)
+++ trunk/conduit/modules/PicasaDesktopModule/PicasaDesktopModule.py	Wed Mar 26 09:16:32 2008
@@ -5,7 +5,7 @@
 log = logging.getLogger("modules.Picasa")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Vfs as Vfs
 import conduit.Exceptions as Exceptions
 import conduit.dataproviders.DataProvider as DataProvider

Modified: trunk/conduit/modules/RhythmboxDBusModule/RhythmboxDBusModule.py
==============================================================================
--- trunk/conduit/modules/RhythmboxDBusModule/RhythmboxDBusModule.py	(original)
+++ trunk/conduit/modules/RhythmboxDBusModule/RhythmboxDBusModule.py	Wed Mar 26 09:16:32 2008
@@ -15,7 +15,7 @@
 import conduit
 import conduit.Exceptions as Exceptions
 import conduit.dataproviders.DataProvider as DataProvider
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.datatypes.Audio as Audio
 import dbus
 try:

Modified: trunk/conduit/modules/RhythmboxModule/RhythmboxModule.py
==============================================================================
--- trunk/conduit/modules/RhythmboxModule/RhythmboxModule.py	(original)
+++ trunk/conduit/modules/RhythmboxModule/RhythmboxModule.py	Wed Mar 26 09:16:32 2008
@@ -19,7 +19,7 @@
 
 import conduit
 import conduit.dataproviders.DataProvider as DataProvider
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.datatypes.Audio as Audio
 
 from gettext import gettext as _ 

Modified: trunk/conduit/modules/ShutterflyModule/ShutterflyModule.py
==============================================================================
--- trunk/conduit/modules/ShutterflyModule/ShutterflyModule.py	(original)
+++ trunk/conduit/modules/ShutterflyModule/ShutterflyModule.py	Wed Mar 26 09:16:32 2008
@@ -5,7 +5,7 @@
 log = logging.getLogger("modules.Shutterfly")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 from conduit.datatypes import Rid
 import conduit.dataproviders.Image as Image
 import conduit.Exceptions as Exceptions

Modified: trunk/conduit/modules/SmugMugModule/SmugMugModule.py
==============================================================================
--- trunk/conduit/modules/SmugMugModule/SmugMugModule.py	(original)
+++ trunk/conduit/modules/SmugMugModule/SmugMugModule.py	Wed Mar 26 09:16:32 2008
@@ -8,7 +8,7 @@
 log = logging.getLogger("modules.SmugMug")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.dataproviders.Image as Image
 import conduit.Exceptions as Exceptions
 from conduit.datatypes import Rid

Modified: trunk/conduit/modules/TestModule.py
==============================================================================
--- trunk/conduit/modules/TestModule.py	(original)
+++ trunk/conduit/modules/TestModule.py	Wed Mar 26 09:16:32 2008
@@ -8,7 +8,8 @@
 log = logging.getLogger("modules.Test")
 
 import conduit
-import conduit.Utils as Utils
+import conduit.utils as Utils
+import conduit.utils.Memstats as Memstats
 import conduit.TypeConverter as TypeConverter
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.dataproviders.DataProviderCategory as DataProviderCategory
@@ -37,7 +38,7 @@
     "TestSinkNeedConfigure" :   { "type": "dataprovider" },
     "TestFactory" :             { "type": "dataprovider-factory" },
 #    "TestFactoryRemoval" :      { "type": "dataprovider-factory" },
-#    "TestSimpleFactory" :       { "type": "dataprovider-factory" },
+    "TestSimpleFactory" :       { "type": "dataprovider-factory" },
     "TestConverter" :           { "type": "converter" }
 }
 
@@ -748,7 +749,7 @@
     def __init__(self, **kwargs):
         DataProvider.DataProviderFactory.__init__(self, **kwargs)
         self.count = 200
-        self.stats = Utils.Memstats()
+        self.stats = Memstats.Memstats()
         self.cat = DataProviderCategory.DataProviderCategory(
                     "TestHotplug",
                     "emblem-system",
@@ -785,7 +786,7 @@
         SimpleFactory.SimpleFactory.__init__(self, **kwargs)
         gobject.timeout_add(5000, self._added)
         self.count = 200
-        self.stats = Utils.Memstats()
+        self.stats = Memstats.Memstats()
 
     def get_category(self, key, **kwargs):
         return DataProviderCategory.DataProviderCategory(

Modified: trunk/conduit/modules/TomboyModule.py
==============================================================================
--- trunk/conduit/modules/TomboyModule.py	(original)
+++ trunk/conduit/modules/TomboyModule.py	Wed Mar 26 09:16:32 2008
@@ -10,7 +10,7 @@
 import conduit.Exceptions as Exceptions
 import conduit.datatypes.Note as Note
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 MODULES = {
 	"TomboyNoteTwoWay" :        { "type": "dataprovider"    },

Modified: trunk/conduit/modules/iPodModule.py
==============================================================================
--- trunk/conduit/modules/iPodModule.py	(original)
+++ trunk/conduit/modules/iPodModule.py	Wed Mar 26 09:16:32 2008
@@ -19,7 +19,7 @@
 import conduit.dataproviders.DataProvider as DataProvider
 import conduit.dataproviders.DataProviderCategory as DataProviderCategory
 import conduit.dataproviders.VolumeFactory as VolumeFactory
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.datatypes.Note as Note
 import conduit.datatypes.Contact as Contact
 import conduit.datatypes.Event as Event

Added: trunk/conduit/utils/CommandLineConverter.py
==============================================================================
--- (empty file)
+++ trunk/conduit/utils/CommandLineConverter.py	Wed Mar 26 09:16:32 2008
@@ -0,0 +1,50 @@
+import re
+import os
+import signal
+import popen2
+import logging
+log = logging.getLogger("Utils")
+
+class CommandLineConverter:
+    def __init__(self):
+        self.percentage_match = re.compile('.*')
+
+    def _kill(self, process):
+        log.debug("Killing process")
+        os.kill(process.pid, signal.SIGKILL)
+
+    def build_command(self, command, **params):
+        self.command = command
+        
+    def calculate_percentage(self, val):
+        return float(val)
+
+    def check_cancelled(self):
+        return False
+
+    def convert( self, input_filename, output_filename, callback=None,save_output=False):
+        command = self.command % (input_filename, output_filename)
+        log.debug("Executing %s" % command)
+
+        output = ""
+        process = popen2.Popen4(command)
+        stdout = process.fromchild
+        s = stdout.read(80)
+        if save_output:
+            output += s
+        while s:
+            if callback:
+                for i in self.percentage_match.finditer(s):
+                    val = self.calculate_percentage(i.group(1).strip())
+                    callback(val)
+            if save_output:
+                output += s
+            if self.check_cancelled():
+                self._kill(process)
+            s = stdout.read(80)
+
+        ok = process.wait() == 0
+        if save_output:
+            return ok, output
+        else:
+            return ok

Added: trunk/conduit/utils/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/conduit/utils/Makefile.am	Wed Mar 26 09:16:32 2008
@@ -0,0 +1,8 @@
+conduitdir = $(pythondir)/conduit/utils
+conduit_PYTHON = \
+	__init__.py \
+	Memstats.py \
+	CommandLineConverter.py
+
+clean-local:
+	rm -rf *.pyc *.pyo

Added: trunk/conduit/utils/Memstats.py
==============================================================================
--- (empty file)
+++ trunk/conduit/utils/Memstats.py	Wed Mar 26 09:16:32 2008
@@ -0,0 +1,43 @@
+import os
+import logging
+log = logging.getLogger("Utils")
+
+class Memstats:
+    """
+    Memory analysis functions taken from
+    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/286222
+    """
+
+    _proc_status = '/proc/%d/status' % os.getpid()
+    _scale = {'kB': 1024.0, 'mB': 1024.0*1024.0,
+              'KB': 1024.0, 'MB': 1024.0*1024.0}
+              
+    def __init__(self):
+        self.prev = [0.0,0.0,0.0]
+
+    def _VmB(self, VmKey):
+        #get pseudo file  /proc/<pid>/status
+        try:
+            t = open(self._proc_status)
+            v = t.read()
+            t.close()
+        except Exception, err:
+            return 0.0  # non-Linux?
+        #get VmKey line e.g. 'VmRSS:  9999  kB\n ...'
+        i = v.index(VmKey)
+        v = v[i:].split(None, 3)  # whitespace
+        if len(v) < 3:
+            return 0.0  # invalid format?
+        #convert Vm value to bytes
+        return float(v[1]) * self._scale[v[2]]
+        
+    def calculate(self):
+        VmSize = self._VmB('VmSize:') - self.prev[0]
+        VmRSS = self._VmB('VmRSS:') - self.prev [1]
+        VmStack = self._VmB('VmStk:') - self.prev [2]
+        log.info("Memory Stats: VM=%sMB RSS=%sMB STACK=%sMB" %(
+                                    VmSize  / self._scale["MB"],
+                                    VmRSS   / self._scale["MB"],
+                                    VmStack / self._scale["MB"],
+                                    ))
+        return VmSize,VmRSS,VmStack 

Added: trunk/conduit/utils/__init__.py
==============================================================================
--- (empty file)
+++ trunk/conduit/utils/__init__.py	Wed Mar 26 09:16:32 2008
@@ -0,0 +1,285 @@
+"""
+Utility Functions
+
+Part of this code copied from from Listen (c) 2006 Mehdi Abaakouk
+(http://listengnome.free.fr/)
+
+Copyright: John Stowers, 2006
+License: GPLv2
+"""
+import sys
+import os.path
+import socket
+import datetime
+import time
+import re
+import logging
+log = logging.getLogger("Utils")
+
+def get_proportional_resize(desiredW, desiredH, currentW, currentH):
+    """
+    Returns proportionally resized co-ordinates for an image
+    """
+    #Account for 'dont care about this axis sizing'
+    if desiredH == None: desiredH = currentH
+    if desiredW == None: desiredW = currentW
+
+    #Calculate the axis of most change
+    dw = abs(currentW - desiredW)
+    dh = abs(currentH - desiredH)
+
+    if dh > dw:
+        newHeight = float(desiredH)
+        percentage = newHeight / currentH
+        newWidth = currentW * percentage
+    else:
+        newWidth = float(desiredW)
+        percentage = newWidth / currentW
+        newHeight = currentH * percentage
+
+    return int(newWidth), int(newHeight)    
+    
+def program_installed(app):
+    """
+    Check if the given app is installed.
+    """
+    path = os.environ['PATH'] 
+    paths = path.split(os.pathsep)
+    for dir in paths:
+        if os.path.isdir(dir):
+            if os.path.isfile(os.path.join(dir,app)):
+                return True
+    return False
+
+#
+# Temporary file functions
+#
+def new_tempfile(contents, contentsAreText=True):
+    """
+    Returns a new File onject, which has been created in the 
+    system temporary directory, and that has been filled with
+    contents
+    
+    The file is closed when it is returned
+    
+    @param contents: The data to write into the file
+    @param contentsAreText: Indicates to the OS if the file is text (as opposed
+    to a binary type file
+    @param contentsAreText: C{bool}
+    @returns: a L{conduit.datatypes.File}
+    """
+    import conduit.datatypes.File as File
+    return File.TempFile(contents)
+
+def new_tempdir():
+    """
+    Creates a new temporary directory
+    """
+    import tempfile
+    return tempfile.mkdtemp("conduit")
+
+def unique_list(seq):
+    """
+    The fastes way to unique-ify a list while retaining its order, from
+    http://www.peterbe.com/plog/uniqifiers-benchmark
+    """
+    def _f10(listy):
+        seen = set()
+        for x in listy:
+            if x in seen:
+                continue
+            seen.add(x)
+            yield x
+    return list(_f10(seq))
+
+def random_string(length=5):
+    """
+    returns a random string of length
+    """
+    import random
+    s = ""
+    for i in range(0,length):
+        s += str(random.randint(0,10))
+    return s
+
+def dataprovider_add_dir_to_path(dataproviderfile, directory=""):
+    """
+    Adds directory to the python search path.
+
+    From within a dataprovider (FooModule.py) 
+    call with Utils.dataprovider_add_dir_to_path(__file__, some_dir):
+    """
+    path = os.path.join(dataproviderfile, "..", directory)
+    path = os.path.abspath(path)
+    log.debug("Adding %s to python path" % path)
+    sys.path.insert(0,path)
+
+def dataprovider_glade_get_widget(dataproviderfile, gladefilename, widget):
+    """
+    Gets a single gtk widget from a glad file
+    """
+    import gtk.glade
+    path = os.path.join(dataproviderfile, "..", gladefilename)
+    path = os.path.abspath(path)
+    return gtk.glade.XML(path, widget)
+
+def run_dialog(dialog, window=None):
+    """
+    Runs a given dialog, and makes it transient for
+    the given window if any
+    @param dialog: dialog 
+    @param window: gtk window
+    @returns: True if the user clicked OK to exit the dialog
+    """
+    import gtk
+
+    if window:
+        dialog.set_transient_for(window)
+
+    return dialog.run() == gtk.RESPONSE_OK
+
+def md5_string(string):
+    """
+    Returns the md5 of the supplied string in readable hexdigest string format
+    """
+    import md5
+    return md5.new(string).hexdigest()
+
+def uuid_string():
+    """
+    Returns a uuid string
+    """
+    try:
+        import uuid
+        return uuid.uuid4().hex
+    except ImportError:
+        import random, md5, socket
+        t = long( time.time() * 1000 )
+        r = long( random.random()*100000000000000000L )
+        try:
+            a = socket.gethostbyname( socket.gethostname() )
+        except:
+            a = random.random()*100000000000000000L
+        data = str(t)+' '+str(r)+' '+str(a)
+        data = md5.md5(data).hexdigest()
+        return data
+
+def dbus_service_available(interface, bus=None):
+    """
+    Checks if a dbus service is available on the given bus
+    @param interface: The interface to look for
+    @param bus: The bus to look on (optional)
+    """
+    try: 
+        import dbus
+    except: 
+        return False
+        
+    if bus == None:        
+        bus = dbus.SessionBus()
+        
+    obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') 
+    dbus_iface = dbus.Interface(obj, 'org.freedesktop.DBus') 
+    avail = dbus_iface.ListNames()
+    return interface in avail
+
+def get_user_string():
+    """
+    Makes a user and machine dependant string in the form
+    username hostname
+    """
+    hostname = socket.gethostname()
+    username = os.environ.get("USER","")
+    return "%s %s" % (username, hostname)
+
+def datetime_from_timestamp(t):
+    """
+    Makes a datetime object from a unix timestamp.
+
+    Note: For the sake of consistancy always drop the
+    fractional (microsecond) part of the timestamp
+    """
+    if type(t) not in [long, int, float]:
+        raise Exception("Timestamp must be a number")
+
+    if t < 0:
+        raise Exception("Timestamps before 1970 are not valid")
+
+    return datetime.datetime.fromtimestamp(long(t))
+
+def datetime_get_timestamp(d):
+    """
+    Returns the unix timestamp for a datetime
+
+    Note: For the sake of consistancy always drop the
+    fractional (microsecond) part of the timestamp
+    """
+    if type(d) != datetime.datetime:
+        raise Exception("Must supply a datetime")
+
+    f = time.mktime(d.timetuple())
+    if f < 0:
+        raise Exception("Timestamps before 1970 are not valid")
+
+    return long(f)
+
+def encode_conversion_args(args):
+    """
+    encodes an args dictionary to a url string in the form
+    param=value&param2=val2
+    """
+    import urllib
+    return urllib.urlencode(args)
+
+def decode_conversion_args(argString):
+    """
+    FIXME: dont import cgi for just one function. Also it doesnt
+    even handle lists
+    """
+    import cgi
+    args = {}
+    for key,val in cgi.parse_qsl(argString):
+        args[key] = val
+    return args
+    
+def log_function_call(log):
+    """
+    A decorator that prints debug message showing the function name and
+    argument types to the supplied logger instance. 
+    
+    Adapted from the accepts/returns decorators at
+    http://wiki.python.org/moin/PythonDecoratorLibrary
+    """
+    def decorator(f):
+        def newf(*args):
+            #Ensure args are tuples with str args - necessart for CPython methods
+            argtypes = map(str,map(type, args))
+            argnames = map(str,f.func_code.co_varnames[:f.func_code.co_argcount])
+            argnamesandtypes = ["%s:%s" % (i,j) for i,j in zip(argnames,argtypes)]
+            msg = "Method Call %s(%s)" % (
+                        f.__name__,
+                        ', '.join(argnamesandtypes)
+                        )
+            log.debug(msg)
+            return f(*args)
+        #Retain information about old function
+        newf.__name__ = f.__name__
+        newf.__doc__ = f.__doc__
+        newf.__dict__.update(f.__dict__)
+        return newf
+    return decorator
+
+def xml_extract_value_from_tag(tag, text):
+    """
+    Returns the contents of the xml tag or None. Uses a simple regex.   
+
+    Taken from:
+    http://immike.net/blog/2007/04/06/5-regular-expressions-every-web-programmer-should-know/
+    """
+    ans = re.compile("<%(tag)s[^>]*>(.*?)</%(tag)s>" % {"tag":tag}).findall(text)
+    if ans:
+        return ans[0]
+    else:
+        return None
+            
+

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Wed Mar 26 09:16:32 2008
@@ -108,6 +108,7 @@
 Makefile
 conduit/defs.py
 conduit/Makefile
+conduit/utils/Makefile
 conduit/datatypes/Makefile
 conduit/dataproviders/Makefile
 conduit/gtkui/Makefile

Modified: trunk/scripts/run-tests.sh
==============================================================================
--- trunk/scripts/run-tests.sh	(original)
+++ trunk/scripts/run-tests.sh	Wed Mar 26 09:16:32 2008
@@ -185,6 +185,7 @@
     
     CORE_COVERAGE_FILES=`ls \
         conduit/*.py \
+        conduit/utils/*.py \
         conduit/datatypes/*.py \
         conduit/dataproviders/DataProvider.py \
         conduit/modules/TestModule.py \
@@ -192,6 +193,7 @@
         
     ALL_COVERAGE_FILES=`ls \
         conduit/*.py \
+        conduit/utils/*.py \
         conduit/datatypes/*.py \
         conduit/dataproviders/*.py \
         conduit/modules/*.py \

Modified: trunk/test/python-tests/TestCoreConvert.py
==============================================================================
--- trunk/test/python-tests/TestCoreConvert.py	(original)
+++ trunk/test/python-tests/TestCoreConvert.py	Wed Mar 26 09:16:32 2008
@@ -1,7 +1,7 @@
 #common sets up the conduit environment
 from common import *
 
-import conduit.Utils as Utils
+import conduit.utils as Utils
 from conduit.Module import ModuleManager
 from conduit.TypeConverter import TypeConverter
 

Modified: trunk/test/python-tests/TestCoreConvertAudioVideo.py
==============================================================================
--- trunk/test/python-tests/TestCoreConvertAudioVideo.py	(original)
+++ trunk/test/python-tests/TestCoreConvertAudioVideo.py	Wed Mar 26 09:16:32 2008
@@ -5,7 +5,7 @@
 import conduit.datatypes.File as File
 import conduit.datatypes.Video as Video
 import conduit.datatypes.Audio as Audio
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Exceptions as Exceptions
 
 test = SimpleTest()

Modified: trunk/test/python-tests/TestCoreConvertSubtypesArgs.py
==============================================================================
--- trunk/test/python-tests/TestCoreConvertSubtypesArgs.py	(original)
+++ trunk/test/python-tests/TestCoreConvertSubtypesArgs.py	Wed Mar 26 09:16:32 2008
@@ -1,6 +1,6 @@
 #common sets up the conduit environment
 from common import *
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.datatypes.DataType as DataType
 import conduit.Exceptions as Exceptions
 

Modified: trunk/test/python-tests/TestCoreDate.py
==============================================================================
--- trunk/test/python-tests/TestCoreDate.py	(original)
+++ trunk/test/python-tests/TestCoreDate.py	Wed Mar 26 09:16:32 2008
@@ -4,7 +4,7 @@
 import os
 import conduit
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 #check construction arg type checking
 try:

Modified: trunk/test/python-tests/TestCoreEmail.py
==============================================================================
--- trunk/test/python-tests/TestCoreEmail.py	(original)
+++ trunk/test/python-tests/TestCoreEmail.py	Wed Mar 26 09:16:32 2008
@@ -1,7 +1,7 @@
 #common sets up the conduit environment
 from common import *
 import conduit.datatypes.Email as Email
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 e = Email.Email(
             to="me",

Modified: trunk/test/python-tests/TestCoreFile.py
==============================================================================
--- trunk/test/python-tests/TestCoreFile.py	(original)
+++ trunk/test/python-tests/TestCoreFile.py	Wed Mar 26 09:16:32 2008
@@ -3,7 +3,7 @@
 
 import conduit
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Vfs as Vfs
 
 import os

Modified: trunk/test/python-tests/TestCoreFile2.py
==============================================================================
--- trunk/test/python-tests/TestCoreFile2.py	(original)
+++ trunk/test/python-tests/TestCoreFile2.py	Wed Mar 26 09:16:32 2008
@@ -3,7 +3,7 @@
 
 import conduit
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 import os
 import time

Modified: trunk/test/python-tests/TestCoreMappingDB.py
==============================================================================
--- trunk/test/python-tests/TestCoreMappingDB.py	(original)
+++ trunk/test/python-tests/TestCoreMappingDB.py	Wed Mar 26 09:16:32 2008
@@ -2,7 +2,7 @@
 from common import *
 
 import conduit.MappingDB as MappingDB
-import conduit.Utils as Utils
+import conduit.utils as Utils
 from conduit.datatypes import Rid
 
 import datetime

Modified: trunk/test/python-tests/TestCoreTempFile.py
==============================================================================
--- trunk/test/python-tests/TestCoreTempFile.py	(original)
+++ trunk/test/python-tests/TestCoreTempFile.py	Wed Mar 26 09:16:32 2008
@@ -1,6 +1,6 @@
 from common import *
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 import os
 import tempfile

Modified: trunk/test/python-tests/TestCoreUtil.py
==============================================================================
--- trunk/test/python-tests/TestCoreUtil.py	(original)
+++ trunk/test/python-tests/TestCoreUtil.py	Wed Mar 26 09:16:32 2008
@@ -1,6 +1,8 @@
 #common sets up the conduit environment
 from common import *
-import conduit.Utils as Utils
+import conduit.utils as Utils
+import conduit.utils.Memstats as Memstats
+import conduit.utils.CommandLineConverter as CommandLineConverter
 
 import datetime
 import os.path
@@ -42,12 +44,12 @@
 ok("Datetime to unix timestamp", Utils.datetime_get_timestamp(dt) == ts)
 ok("Unix timestamp to datetime", Utils.datetime_from_timestamp(ts) == dt)
 
-m = Utils.Memstats()
+m = Memstats.Memstats()
 VmSize,VmRSS,VmStack = m.calculate()
 ok("Memstats: size:%s rss:%s stack:%s" % (VmSize,VmRSS,VmStack), VmSize > 0 and VmRSS > 0 and VmStack > 0)
 
 # Test the shiny command line executer
-conv = Utils.CommandLineConverter()
+conv = CommandLineConverter.CommandLineConverter()
 conv.build_command("ls %s %s")
 cmdok,output = conv.convert("/tmp","/dev/null",callback=None,save_output=True)
 

Modified: trunk/test/python-tests/TestCoreVfs.py
==============================================================================
--- trunk/test/python-tests/TestCoreVfs.py	(original)
+++ trunk/test/python-tests/TestCoreVfs.py	Wed Mar 26 09:16:32 2008
@@ -1,7 +1,7 @@
 #common sets up the conduit environment
 from common import *
 import conduit.Vfs as Vfs
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 ok("URI make canonical", Vfs.uri_make_canonical("file:///foo/bar/baz/../../bar//") == "file:///foo/bar")
 

Modified: trunk/test/python-tests/TestDataProviderBackpack.py
==============================================================================
--- trunk/test/python-tests/TestDataProviderBackpack.py	(original)
+++ trunk/test/python-tests/TestDataProviderBackpack.py	Wed Mar 26 09:16:32 2008
@@ -5,7 +5,7 @@
 import datetime
 
 import conduit.datatypes.Note as Note
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 if not is_online():
     skip()

Modified: trunk/test/python-tests/TestDataProviderBoxDotNet.py
==============================================================================
--- trunk/test/python-tests/TestDataProviderBoxDotNet.py	(original)
+++ trunk/test/python-tests/TestDataProviderBoxDotNet.py	Wed Mar 26 09:16:32 2008
@@ -3,7 +3,7 @@
 
 import traceback
 
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.datatypes.File as File
 
 if not is_online() or not is_interactive():

Modified: trunk/test/python-tests/TestDataProviderGmail.py
==============================================================================
--- trunk/test/python-tests/TestDataProviderGmail.py	(original)
+++ trunk/test/python-tests/TestDataProviderGmail.py	Wed Mar 26 09:16:32 2008
@@ -5,7 +5,7 @@
 from conduit.TypeConverter import TypeConverter
 import conduit.datatypes.Email as Email
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 if not is_online():
     skip()

Modified: trunk/test/python-tests/TestDataProviderGoogle.py
==============================================================================
--- trunk/test/python-tests/TestDataProviderGoogle.py	(original)
+++ trunk/test/python-tests/TestDataProviderGoogle.py	Wed Mar 26 09:16:32 2008
@@ -2,7 +2,7 @@
 from common import *
 
 import conduit.datatypes.Event as Event
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 import random
 

Modified: trunk/test/python-tests/TestDataProviderShutterfly.py
==============================================================================
--- trunk/test/python-tests/TestDataProviderShutterfly.py	(original)
+++ trunk/test/python-tests/TestDataProviderShutterfly.py	Wed Mar 26 09:16:32 2008
@@ -7,7 +7,7 @@
 from conduit.Module import ModuleManager
 from conduit.TypeConverter import TypeConverter
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 if not is_online():
     skip()

Modified: trunk/test/python-tests/TestDataProviderSmugMug.py
==============================================================================
--- trunk/test/python-tests/TestDataProviderSmugMug.py	(original)
+++ trunk/test/python-tests/TestDataProviderSmugMug.py	Wed Mar 26 09:16:32 2008
@@ -4,7 +4,7 @@
 import traceback
 
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Vfs as Vfs
 
 if not is_online():

Modified: trunk/test/python-tests/TestDataProviderTomboy.py
==============================================================================
--- trunk/test/python-tests/TestDataProviderTomboy.py	(original)
+++ trunk/test/python-tests/TestDataProviderTomboy.py	Wed Mar 26 09:16:32 2008
@@ -2,7 +2,7 @@
 from common import *
 
 import conduit.datatypes.Note as Note
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 import random
 

Modified: trunk/test/python-tests/TestDataProvideriPod.py
==============================================================================
--- trunk/test/python-tests/TestDataProvideriPod.py	(original)
+++ trunk/test/python-tests/TestDataProvideriPod.py	Wed Mar 26 09:16:32 2008
@@ -6,7 +6,7 @@
 import traceback
 
 import conduit.modules.iPodModule as iPodModule
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 
 #simulate an ipod

Modified: trunk/test/python-tests/TestSyncEvolutionFolder.py
==============================================================================
--- trunk/test/python-tests/TestSyncEvolutionFolder.py	(original)
+++ trunk/test/python-tests/TestSyncEvolutionFolder.py	Wed Mar 26 09:16:32 2008
@@ -1,6 +1,6 @@
 #common sets up the conduit environment
 from common import *
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 SAFE_TASK_URI="file:///home/john/.evolution/tasks/local/1204062882 7099 2 nzjrs-desktop"
 SAFE_MEMO_URI="file:///home/john/.evolution/memos/local/1204062871 7099 1 nzjrs-desktop"

Modified: trunk/test/python-tests/TestSyncFileFolder.py
==============================================================================
--- trunk/test/python-tests/TestSyncFileFolder.py	(original)
+++ trunk/test/python-tests/TestSyncFileFolder.py	Wed Mar 26 09:16:32 2008
@@ -1,7 +1,7 @@
 #common sets up the conduit environment
 from common import *
 
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.datatypes.File as File
 from conduit.datatypes import COMPARISON_EQUAL
 

Modified: trunk/test/python-tests/TestSyncFolderFolder.py
==============================================================================
--- trunk/test/python-tests/TestSyncFolderFolder.py	(original)
+++ trunk/test/python-tests/TestSyncFolderFolder.py	Wed Mar 26 09:16:32 2008
@@ -1,7 +1,7 @@
 #common sets up the conduit environment
 from common import *
 
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.datatypes.File as File
 from conduit.datatypes import COMPARISON_EQUAL
 

Modified: trunk/test/python-tests/TestSyncTomboyFolder.py
==============================================================================
--- trunk/test/python-tests/TestSyncTomboyFolder.py	(original)
+++ trunk/test/python-tests/TestSyncTomboyFolder.py	Wed Mar 26 09:16:32 2008
@@ -2,7 +2,7 @@
 from common import *
 
 import conduit.datatypes.File as File
-import conduit.Utils as Utils
+import conduit.utils as Utils
 
 #setup the conduit
 test = SimpleSyncTest()

Modified: trunk/test/python-tests/TestSyncTomboyiPod.py
==============================================================================
--- trunk/test/python-tests/TestSyncTomboyiPod.py	(original)
+++ trunk/test/python-tests/TestSyncTomboyiPod.py	Wed Mar 26 09:16:32 2008
@@ -2,7 +2,7 @@
 from common import *
 
 import conduit.Module as Module
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.datatypes.File as File
 import conduit.Conduit as Conduit
 import conduit.TypeConverter as TypeConverter

Modified: trunk/test/python-tests/common.py
==============================================================================
--- trunk/test/python-tests/common.py	(original)
+++ trunk/test/python-tests/common.py	Wed Mar 26 09:16:32 2008
@@ -17,7 +17,7 @@
 # import main conduit module and datatypes
 import conduit
 import conduit.Logging as Logging
-import conduit.Utils as Utils
+import conduit.utils as Utils
 import conduit.Vfs as Vfs
 import conduit.Module as Module
 import conduit.TypeConverter as TypeConverter



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