mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-03 19:17:02 -05:00 
			
		
		
		
	New parallel processing framework. Parallel processes are controlled via TCP/IP sockets making distributed computing possible.
This commit is contained in:
		
							parent
							
								
									233662ebd7
								
							
						
					
					
						commit
						a1afe65f2c
					
				@ -48,7 +48,7 @@ def _check_symlinks_prescript():
 | 
				
			|||||||
    from Authorization import Authorization, kAuthorizationFlagDestroyRights
 | 
					    from Authorization import Authorization, kAuthorizationFlagDestroyRights
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    AUTHTOOL="""#!%(sp)s
 | 
					    AUTHTOOL="""#!%(sp)s
 | 
				
			||||||
import os
 | 
					import os, shutil
 | 
				
			||||||
scripts = %(sp)s
 | 
					scripts = %(sp)s
 | 
				
			||||||
links = %(sp)s
 | 
					links = %(sp)s
 | 
				
			||||||
fonts_conf = %(sp)s
 | 
					fonts_conf = %(sp)s
 | 
				
			||||||
@ -64,7 +64,8 @@ if not os.path.exists('/etc/fonts/fonts.conf'):
 | 
				
			|||||||
    print 'Creating default fonts.conf'
 | 
					    print 'Creating default fonts.conf'
 | 
				
			||||||
    if not os.path.exists('/etc/fonts'):
 | 
					    if not os.path.exists('/etc/fonts'):
 | 
				
			||||||
        os.makedirs('/etc/fonts')
 | 
					        os.makedirs('/etc/fonts')
 | 
				
			||||||
    os.link(fonts_conf, '/etc/fonts/fonts.conf')
 | 
					    shutil.copyfile(fonts_conf, '/etc/fonts/fonts.conf')
 | 
				
			||||||
 | 
					    shutil.copyfile(fonts_conf.replace('conf', 'dtd'), '/etc/fonts/fonts.dtd')
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    dest_path = %(dest_path)s
 | 
					    dest_path = %(dest_path)s
 | 
				
			||||||
@ -80,8 +81,7 @@ if not os.path.exists('/etc/fonts/fonts.conf'):
 | 
				
			|||||||
            continue
 | 
					            continue
 | 
				
			||||||
        bad = True
 | 
					        bad = True
 | 
				
			||||||
        break
 | 
					        break
 | 
				
			||||||
    if not bad:
 | 
					    bad = bad or not os.path.exists('/etc/fonts/fonts.conf')
 | 
				
			||||||
        bad = os.path.exists('/etc/fonts/fonts.conf')
 | 
					 | 
				
			||||||
    if bad:
 | 
					    if bad:
 | 
				
			||||||
        auth = Authorization(destroyflags=(kAuthorizationFlagDestroyRights,))
 | 
					        auth = Authorization(destroyflags=(kAuthorizationFlagDestroyRights,))
 | 
				
			||||||
        fd, name = tempfile.mkstemp('.py')
 | 
					        fd, name = tempfile.mkstemp('.py')
 | 
				
			||||||
@ -280,13 +280,15 @@ sys.frameworks_dir = os.path.join(os.path.dirname(os.environ['RESOURCEPATH']), '
 | 
				
			|||||||
        f.write(src)
 | 
					        f.write(src)
 | 
				
			||||||
        f.close()
 | 
					        f.close()
 | 
				
			||||||
        print 
 | 
					        print 
 | 
				
			||||||
        print 'Adding GUI main.py'
 | 
					        print 'Adding GUI scripts to site-packages'
 | 
				
			||||||
        f = zipfile.ZipFile(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'lib', 'python2.5', 'site-packages.zip'), 'a', zipfile.ZIP_DEFLATED)
 | 
					        f = zipfile.ZipFile(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'lib', 'python2.5', 'site-packages.zip'), 'a', zipfile.ZIP_DEFLATED)
 | 
				
			||||||
        f.write('src/calibre/gui2/main.py', 'calibre/gui2/main.py')
 | 
					        for script in scripts['gui']:
 | 
				
			||||||
 | 
					            f.write(script, script.partition('/')[-1])
 | 
				
			||||||
        f.close()
 | 
					        f.close()
 | 
				
			||||||
        print
 | 
					        print
 | 
				
			||||||
        print 'Adding default fonts.conf'
 | 
					        print 'Adding default fonts.conf'
 | 
				
			||||||
        open(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'fonts.conf'), 'wb').write(open('/etc/fonts/fonts.conf').read())
 | 
					        open(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'fonts.conf'), 'wb').write(open('/etc/fonts/fonts.conf').read())
 | 
				
			||||||
 | 
					        open(os.path.join(self.dist_dir, APPNAME+'.app', 'Contents', 'Resources', 'fonts.dtd'), 'wb').write(open('/etc/fonts/fonts.dtd').read())
 | 
				
			||||||
        print
 | 
					        print
 | 
				
			||||||
        print 'Building disk image'
 | 
					        print 'Building disk image'
 | 
				
			||||||
        BuildAPP.makedmg(os.path.join(self.dist_dir, APPNAME+'.app'), APPNAME+'-'+VERSION)
 | 
					        BuildAPP.makedmg(os.path.join(self.dist_dir, APPNAME+'.app'), APPNAME+'-'+VERSION)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								setup.py
									
									
									
									
									
								
							@ -13,9 +13,6 @@ print 'Setup', APPNAME, 'version:', VERSION
 | 
				
			|||||||
epsrc = re.compile(r'entry_points = (\{.*?\})', re.DOTALL).search(open('src/%s/linux.py'%APPNAME, 'rb').read()).group(1)
 | 
					epsrc = re.compile(r'entry_points = (\{.*?\})', re.DOTALL).search(open('src/%s/linux.py'%APPNAME, 'rb').read()).group(1)
 | 
				
			||||||
entry_points = eval(epsrc, {'__appname__': APPNAME})
 | 
					entry_points = eval(epsrc, {'__appname__': APPNAME})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if 'win32' in sys.platform.lower() or 'win64' in sys.platform.lower():
 | 
					 | 
				
			||||||
    entry_points['console_scripts'].append('parallel = %s.parallel:main'%APPNAME)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _ep_to_script(ep, base='src'):
 | 
					def _ep_to_script(ep, base='src'):
 | 
				
			||||||
    return (base+os.path.sep+re.search(r'.*=\s*(.*?):', ep).group(1).replace('.', '/')+'.py').strip()
 | 
					    return (base+os.path.sep+re.search(r'.*=\s*(.*?):', ep).group(1).replace('.', '/')+'.py').strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -75,6 +75,8 @@ class ColoredFormatter(Formatter):
 | 
				
			|||||||
         
 | 
					         
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def setup_cli_handlers(logger, level):
 | 
					def setup_cli_handlers(logger, level):
 | 
				
			||||||
 | 
					    if os.environ.get('CALIBRE_WORKER', None) is not None and logger.handlers:
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
    logger.setLevel(level)
 | 
					    logger.setLevel(level)
 | 
				
			||||||
    if level == logging.WARNING:
 | 
					    if level == logging.WARNING:
 | 
				
			||||||
        handler = logging.StreamHandler(sys.stdout)
 | 
					        handler = logging.StreamHandler(sys.stdout)
 | 
				
			||||||
@ -88,9 +90,7 @@ def setup_cli_handlers(logger, level):
 | 
				
			|||||||
        handler = logging.StreamHandler(sys.stderr)
 | 
					        handler = logging.StreamHandler(sys.stderr)
 | 
				
			||||||
        handler.setLevel(logging.DEBUG)
 | 
					        handler.setLevel(logging.DEBUG)
 | 
				
			||||||
        handler.setFormatter(logging.Formatter('[%(levelname)s] %(filename)s:%(lineno)s: %(message)s'))
 | 
					        handler.setFormatter(logging.Formatter('[%(levelname)s] %(filename)s:%(lineno)s: %(message)s'))
 | 
				
			||||||
    for hdlr in logger.handlers:
 | 
					    
 | 
				
			||||||
        if hdlr.__class__ == handler.__class__:
 | 
					 | 
				
			||||||
            logger.removeHandler(hdlr)
 | 
					 | 
				
			||||||
    logger.addHandler(handler)
 | 
					    logger.addHandler(handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CustomHelpFormatter(IndentedHelpFormatter):
 | 
					class CustomHelpFormatter(IndentedHelpFormatter):
 | 
				
			||||||
 | 
				
			|||||||
@ -13,13 +13,17 @@
 | 
				
			|||||||
   <string>Details of job</string>
 | 
					   <string>Details of job</string>
 | 
				
			||||||
  </property>
 | 
					  </property>
 | 
				
			||||||
  <property name="windowIcon" >
 | 
					  <property name="windowIcon" >
 | 
				
			||||||
   <iconset resource="../images.qrc" >:/images/view.svg</iconset>
 | 
					   <iconset resource="../images.qrc" >
 | 
				
			||||||
 | 
					    <normaloff>:/images/view.svg</normaloff>:/images/view.svg</iconset>
 | 
				
			||||||
  </property>
 | 
					  </property>
 | 
				
			||||||
  <layout class="QGridLayout" >
 | 
					  <layout class="QGridLayout" >
 | 
				
			||||||
   <item row="0" column="0" >
 | 
					   <item row="0" column="0" >
 | 
				
			||||||
    <widget class="QTextBrowser" name="log" >
 | 
					    <widget class="QTextEdit" name="log" >
 | 
				
			||||||
     <property name="lineWrapMode" >
 | 
					     <property name="undoRedoEnabled" >
 | 
				
			||||||
      <enum>QTextEdit::NoWrap</enum>
 | 
					      <bool>false</bool>
 | 
				
			||||||
 | 
					     </property>
 | 
				
			||||||
 | 
					     <property name="readOnly" >
 | 
				
			||||||
 | 
					      <bool>true</bool>
 | 
				
			||||||
     </property>
 | 
					     </property>
 | 
				
			||||||
    </widget>
 | 
					    </widget>
 | 
				
			||||||
   </item>
 | 
					   </item>
 | 
				
			||||||
 | 
				
			|||||||
@ -126,7 +126,7 @@ class ConversionJob(Job):
 | 
				
			|||||||
    def formatted_error(self):
 | 
					    def formatted_error(self):
 | 
				
			||||||
        if self.exception is None:
 | 
					        if self.exception is None:
 | 
				
			||||||
            return ''
 | 
					            return ''
 | 
				
			||||||
        ans = u'<p><b>%s</b>: %s</p>'%self.exception
 | 
					        ans = u'<p><b>%s</b>:'%self.exception
 | 
				
			||||||
        ans += '<h2>Traceback:</h2><pre>%s</pre>'%self.last_traceback
 | 
					        ans += '<h2>Traceback:</h2><pre>%s</pre>'%self.last_traceback
 | 
				
			||||||
        return ans
 | 
					        return ans
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,12 @@
 | 
				
			|||||||
from calibre.gui2.library import SearchBox
 | 
					from calibre.gui2.library import SearchBox
 | 
				
			||||||
__license__   = 'GPL v3'
 | 
					__license__   = 'GPL v3'
 | 
				
			||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
					__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
				
			||||||
import sys, logging, os, traceback, time, cPickle
 | 
					import sys, logging, os, traceback, time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PyQt4.QtGui import QKeySequence, QPainter, QDialog, QSpinBox, QSlider
 | 
					from PyQt4.QtGui import QKeySequence, QPainter, QDialog, QSpinBox, QSlider
 | 
				
			||||||
from PyQt4.QtCore import Qt, QObject, SIGNAL, QCoreApplication, QThread, \
 | 
					from PyQt4.QtCore import Qt, QObject, SIGNAL, QCoreApplication, QThread
 | 
				
			||||||
                         QVariant
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre import __appname__, __version__, __author__, setup_cli_handlers, islinux, Settings
 | 
					from calibre import __appname__, setup_cli_handlers, islinux, Settings
 | 
				
			||||||
from calibre.ebooks.lrf.lrfparser import LRFDocument
 | 
					from calibre.ebooks.lrf.lrfparser import LRFDocument
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre.gui2 import ORG_NAME, APP_UID, error_dialog, choose_files, Application
 | 
					from calibre.gui2 import ORG_NAME, APP_UID, error_dialog, choose_files, Application
 | 
				
			||||||
@ -57,7 +56,7 @@ class Config(QDialog, Ui_ViewerConfig):
 | 
				
			|||||||
class Main(MainWindow, Ui_MainWindow):
 | 
					class Main(MainWindow, Ui_MainWindow):
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def __init__(self, logger, opts, parent=None):
 | 
					    def __init__(self, logger, opts, parent=None):
 | 
				
			||||||
        MainWindow.__init__(self, parent)
 | 
					        MainWindow.__init__(self, opts, parent)
 | 
				
			||||||
        Ui_MainWindow.__init__(self)        
 | 
					        Ui_MainWindow.__init__(self)        
 | 
				
			||||||
        self.setupUi(self)
 | 
					        self.setupUi(self)
 | 
				
			||||||
        self.setAttribute(Qt.WA_DeleteOnClose)
 | 
					        self.setAttribute(Qt.WA_DeleteOnClose)
 | 
				
			||||||
@ -263,9 +262,12 @@ def file_renderer(stream, opts, parent=None, logger=None):
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def option_parser():
 | 
					def option_parser():
 | 
				
			||||||
    from optparse import OptionParser
 | 
					    from calibre.gui2.main_window import option_parser
 | 
				
			||||||
    parser = OptionParser(usage='%prog book.lrf', version=__appname__+' '+__version__,
 | 
					    parser = option_parser('''\
 | 
				
			||||||
                          epilog='Created by ' + __author__)
 | 
					%prog [options] book.lrf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Read the LRF ebook book.lrf
 | 
				
			||||||
 | 
					''')
 | 
				
			||||||
    parser.add_option('--verbose', default=False, action='store_true', dest='verbose',
 | 
					    parser.add_option('--verbose', default=False, action='store_true', dest='verbose',
 | 
				
			||||||
                      help='Print more information about the rendering process')
 | 
					                      help='Print more information about the rendering process')
 | 
				
			||||||
    parser.add_option('--visual-debug', help='Turn on visual aids to debugging the rendering engine',
 | 
					    parser.add_option('--visual-debug', help='Turn on visual aids to debugging the rendering engine',
 | 
				
			||||||
 | 
				
			|||||||
@ -23,7 +23,7 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
 | 
				
			|||||||
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages
 | 
					from calibre.gui2.cover_flow import CoverFlow, DatabaseImages
 | 
				
			||||||
from calibre.library.database import LibraryDatabase
 | 
					from calibre.library.database import LibraryDatabase
 | 
				
			||||||
from calibre.gui2.update import CheckForUpdates
 | 
					from calibre.gui2.update import CheckForUpdates
 | 
				
			||||||
from calibre.gui2.main_window import MainWindow
 | 
					from calibre.gui2.main_window import MainWindow, option_parser
 | 
				
			||||||
from calibre.gui2.main_ui import Ui_MainWindow
 | 
					from calibre.gui2.main_ui import Ui_MainWindow
 | 
				
			||||||
from calibre.gui2.device import DeviceDetector, DeviceManager
 | 
					from calibre.gui2.device import DeviceDetector, DeviceManager
 | 
				
			||||||
from calibre.gui2.status import StatusBar
 | 
					from calibre.gui2.status import StatusBar
 | 
				
			||||||
@ -58,8 +58,8 @@ class Main(MainWindow, Ui_MainWindow):
 | 
				
			|||||||
        p.end()
 | 
					        p.end()
 | 
				
			||||||
        self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap))
 | 
					        self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap))
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def __init__(self, single_instance, parent=None):
 | 
					    def __init__(self, single_instance, opts, parent=None):
 | 
				
			||||||
        MainWindow.__init__(self, parent)
 | 
					        MainWindow.__init__(self, opts, parent)
 | 
				
			||||||
        self.single_instance = single_instance
 | 
					        self.single_instance = single_instance
 | 
				
			||||||
        if self.single_instance is not None:
 | 
					        if self.single_instance is not None:
 | 
				
			||||||
            self.connect(self.single_instance, SIGNAL('message_received(PyQt_PyObject)'),
 | 
					            self.connect(self.single_instance, SIGNAL('message_received(PyQt_PyObject)'),
 | 
				
			||||||
@ -1079,7 +1079,7 @@ class Main(MainWindow, Ui_MainWindow):
 | 
				
			|||||||
        if getattr(exception, 'only_msg', False):
 | 
					        if getattr(exception, 'only_msg', False):
 | 
				
			||||||
            error_dialog(self, _('Conversion Error'), unicode(exception)).exec_()
 | 
					            error_dialog(self, _('Conversion Error'), unicode(exception)).exec_()
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        msg =  u'<p><b>%s</b>: </p>'%exception
 | 
					        msg =  u'<p><b>%s</b>:'%exception
 | 
				
			||||||
        msg += u'<p>Failed to perform <b>job</b>: '+description
 | 
					        msg += u'<p>Failed to perform <b>job</b>: '+description
 | 
				
			||||||
        msg += u'<p>Detailed <b>traceback</b>:<pre>'
 | 
					        msg += u'<p>Detailed <b>traceback</b>:<pre>'
 | 
				
			||||||
        msg += formatted_traceback + '</pre>'
 | 
					        msg += formatted_traceback + '</pre>'
 | 
				
			||||||
@ -1166,6 +1166,13 @@ def main(args=sys.argv):
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    pid = os.fork() if islinux else -1
 | 
					    pid = os.fork() if islinux else -1
 | 
				
			||||||
    if pid <= 0:
 | 
					    if pid <= 0:
 | 
				
			||||||
 | 
					        parser = option_parser('''\
 | 
				
			||||||
 | 
					%prog [opts] [path_to_ebook]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Launch the main calibre Graphical User Interface and optionally add the ebook at
 | 
				
			||||||
 | 
					path_to_ebook to the database.
 | 
				
			||||||
 | 
					''')
 | 
				
			||||||
 | 
					        opts, args = parser.parse_args(args)
 | 
				
			||||||
        app = Application(args)
 | 
					        app = Application(args)
 | 
				
			||||||
        app.setWindowIcon(QIcon(':/library'))
 | 
					        app.setWindowIcon(QIcon(':/library'))
 | 
				
			||||||
        QCoreApplication.setOrganizationName(ORG_NAME)
 | 
					        QCoreApplication.setOrganizationName(ORG_NAME)
 | 
				
			||||||
@ -1173,7 +1180,7 @@ def main(args=sys.argv):
 | 
				
			|||||||
        single_instance = None if SingleApplication is None else SingleApplication('calibre GUI')
 | 
					        single_instance = None if SingleApplication is None else SingleApplication('calibre GUI')
 | 
				
			||||||
        if not singleinstance('calibre GUI'):
 | 
					        if not singleinstance('calibre GUI'):
 | 
				
			||||||
            if single_instance is not None and single_instance.is_running() and \
 | 
					            if single_instance is not None and single_instance.is_running() and \
 | 
				
			||||||
               single_instance.send_message('launched:'+repr(sys.argv)):
 | 
					               single_instance.send_message('launched:'+repr(args)):
 | 
				
			||||||
                    return 0
 | 
					                    return 0
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            QMessageBox.critical(None, 'Cannot Start '+__appname__,
 | 
					            QMessageBox.critical(None, 'Cannot Start '+__appname__,
 | 
				
			||||||
@ -1181,14 +1188,14 @@ def main(args=sys.argv):
 | 
				
			|||||||
            return 1
 | 
					            return 1
 | 
				
			||||||
        initialize_file_icon_provider()
 | 
					        initialize_file_icon_provider()
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            main = Main(single_instance)
 | 
					            main = Main(single_instance, opts)
 | 
				
			||||||
        except DatabaseLocked, err:
 | 
					        except DatabaseLocked, err:
 | 
				
			||||||
            QMessageBox.critical(None, 'Cannot Start '+__appname__,
 | 
					            QMessageBox.critical(None, 'Cannot Start '+__appname__,
 | 
				
			||||||
            '<p>Another program is using the database. <br/>Perhaps %s is already running?<br/>If not try deleting the file %s'%(__appname__, err.lock_file_path))
 | 
					            '<p>Another program is using the database. <br/>Perhaps %s is already running?<br/>If not try deleting the file %s'%(__appname__, err.lock_file_path))
 | 
				
			||||||
            return 1
 | 
					            return 1
 | 
				
			||||||
        sys.excepthook = main.unhandled_exception
 | 
					        sys.excepthook = main.unhandled_exception
 | 
				
			||||||
        if len(sys.argv) > 1:
 | 
					        if len(args) > 1:
 | 
				
			||||||
            main.add_filesystem_book(sys.argv[1])
 | 
					            main.add_filesystem_book(args)
 | 
				
			||||||
        return app.exec_()
 | 
					        return app.exec_()
 | 
				
			||||||
    return 0
 | 
					    return 0
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -1199,7 +1206,7 @@ if __name__ == '__main__':
 | 
				
			|||||||
    except:
 | 
					    except:
 | 
				
			||||||
        if not iswindows: raise
 | 
					        if not iswindows: raise
 | 
				
			||||||
        from PyQt4.QtGui import QErrorMessage
 | 
					        from PyQt4.QtGui import QErrorMessage
 | 
				
			||||||
        logfile = os.path.expanduser('~/calibre.log')
 | 
					        logfile = os.path.join(os.path.expanduser('~'), 'calibre.log')
 | 
				
			||||||
        if os.path.exists(logfile): 
 | 
					        if os.path.exists(logfile): 
 | 
				
			||||||
            log = open(logfile).read()
 | 
					            log = open(logfile).read()
 | 
				
			||||||
            if log.strip():
 | 
					            if log.strip():
 | 
				
			||||||
 | 
				
			|||||||
@ -3,13 +3,45 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import StringIO, traceback, sys
 | 
					import StringIO, traceback, sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PyQt4.QtGui import QMainWindow
 | 
					from PyQt4.Qt import QMainWindow, QString, Qt, QFont
 | 
				
			||||||
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
 | 
					from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
 | 
				
			||||||
 | 
					from calibre import OptionParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def option_parser(usage='''\
 | 
				
			||||||
 | 
					Usage: %prog [options]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Launch the Graphical User Interface
 | 
				
			||||||
 | 
					'''):
 | 
				
			||||||
 | 
					    parser = OptionParser(usage)
 | 
				
			||||||
 | 
					    parser.add_option('--redirect-console-output', default=False, action='store_true', dest='redirect',
 | 
				
			||||||
 | 
					                      help=_('Redirect console output to a dialog window (both stdout and stderr). Useful on windows where GUI apps do not have a output streams.'))
 | 
				
			||||||
 | 
					    return parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DebugWindow(ConversionErrorDialog):
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __init__(self, parent):
 | 
				
			||||||
 | 
					        ConversionErrorDialog.__init__(self, parent, 'Console output', '')
 | 
				
			||||||
 | 
					        self.setModal(Qt.NonModal)
 | 
				
			||||||
 | 
					        font = QFont()
 | 
				
			||||||
 | 
					        font.setStyleHint(QFont.TypeWriter)
 | 
				
			||||||
 | 
					        self.text.setFont(font)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def write(self, msg):
 | 
				
			||||||
 | 
					        self.text.setPlainText(self.text.toPlainText()+QString(msg))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def flush(self):
 | 
				
			||||||
 | 
					        pass    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MainWindow(QMainWindow):
 | 
					class MainWindow(QMainWindow):
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def __init__(self, parent=None):
 | 
					    def __init__(self, opts, parent=None):
 | 
				
			||||||
        QMainWindow.__init__(self, parent)
 | 
					        QMainWindow.__init__(self, parent)
 | 
				
			||||||
 | 
					        if opts.redirect:
 | 
				
			||||||
 | 
					            self.__console_redirect = DebugWindow(self)
 | 
				
			||||||
 | 
					            sys.stdout = sys.stderr = self.__console_redirect
 | 
				
			||||||
 | 
					            self.__console_redirect.show()
 | 
				
			||||||
 | 
					            print 'testing 1'
 | 
				
			||||||
 | 
					            print 'testing 2'
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def unhandled_exception(self, type, value, tb):
 | 
					    def unhandled_exception(self, type, value, tb):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@ -19,7 +51,7 @@ class MainWindow(QMainWindow):
 | 
				
			|||||||
            print >>sys.stderr, fe
 | 
					            print >>sys.stderr, fe
 | 
				
			||||||
            msg = '<p><b>' + unicode(str(value), 'utf8', 'replace') + '</b></p>'
 | 
					            msg = '<p><b>' + unicode(str(value), 'utf8', 'replace') + '</b></p>'
 | 
				
			||||||
            msg += '<p>Detailed <b>traceback</b>:<pre>'+fe+'</pre>'
 | 
					            msg += '<p>Detailed <b>traceback</b>:<pre>'+fe+'</pre>'
 | 
				
			||||||
            d = ConversionErrorDialog(self, 'ERROR: Unhandled exception', msg)
 | 
					            d = ConversionErrorDialog(self, _('ERROR: Unhandled exception'), msg)
 | 
				
			||||||
            d.exec_()
 | 
					            d.exec_()
 | 
				
			||||||
        except:
 | 
					        except:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
@ -46,9 +46,10 @@ entry_points = {
 | 
				
			|||||||
                             'librarything = calibre.ebooks.metadata.library_thing:main',
 | 
					                             'librarything = calibre.ebooks.metadata.library_thing:main',
 | 
				
			||||||
                             'mobi2oeb  = calibre.ebooks.mobi.reader:main',
 | 
					                             'mobi2oeb  = calibre.ebooks.mobi.reader:main',
 | 
				
			||||||
                             'lrf2html  = calibre.ebooks.lrf.html.convert_to:main',
 | 
					                             'lrf2html  = calibre.ebooks.lrf.html.convert_to:main',
 | 
				
			||||||
                             'calibre-debug = calibre.debug:main',
 | 
					                             'calibre-debug      = calibre.debug:main',
 | 
				
			||||||
                             'calibredb = calibre.library.cli:main',
 | 
					                             'calibredb          = calibre.library.cli:main',
 | 
				
			||||||
                             'calibre-fontconfig = calibre.utils.fontconfig:main',
 | 
					                             'calibre-fontconfig = calibre.utils.fontconfig:main',
 | 
				
			||||||
 | 
					                             'calibre-parallel   = calibre.parallel:main',
 | 
				
			||||||
                           ], 
 | 
					                           ], 
 | 
				
			||||||
        'gui_scripts'    : [ 
 | 
					        'gui_scripts'    : [ 
 | 
				
			||||||
                            __appname__+' = calibre.gui2.main:main',
 | 
					                            __appname__+' = calibre.gui2.main:main',
 | 
				
			||||||
 | 
				
			|||||||
@ -1,75 +1,231 @@
 | 
				
			|||||||
from __future__ import with_statement
 | 
					from __future__ import with_statement
 | 
				
			||||||
__license__   = 'GPL v3'
 | 
					__license__   = 'GPL v3'
 | 
				
			||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
					__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
 | 
				
			||||||
 | 
					__docformat__ = 'restructuredtext en'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
'''
 | 
					'''
 | 
				
			||||||
Used to run jobs in parallel in separate processes.
 | 
					Used to run jobs in parallel in separate processes. Features output streaming,
 | 
				
			||||||
 | 
					support for progress notification as well as job killing. The worker processes
 | 
				
			||||||
 | 
					are controlled via a simple protocol run over TCP/IP sockets. The control happens 
 | 
				
			||||||
 | 
					mainly in two class, :class:`Server` and :class:`Overseer`. The worker is
 | 
				
			||||||
 | 
					encapsulated in the function :function:`worker`. Every worker process
 | 
				
			||||||
 | 
					has the environment variable :envvar:`CALIBRE_WORKER` defined.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The worker control protocol has two modes of operation. In the first mode, the
 | 
				
			||||||
 | 
					worker process listens for commands from the controller process. The controller
 | 
				
			||||||
 | 
					process can either hand off a job to the worker or tell the worker to die. 
 | 
				
			||||||
 | 
					Once a job is handed off to the worker, the protocol enters the second mode, where
 | 
				
			||||||
 | 
					the controller listens for messages from the worker. The worker can send progress updates
 | 
				
			||||||
 | 
					as well as console output (i.e. text that would normally have been written to stdout
 | 
				
			||||||
 | 
					or stderr by the job). Once the job completes (or raises an exception) the worker
 | 
				
			||||||
 | 
					returns the result (or exception) to the controller adnt he protocol reverts to the first mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In the second mode, the controller can also send the worker STOP messages, in which case
 | 
				
			||||||
 | 
					the worker interrupts the job and dies. The sending of progress and console output messages
 | 
				
			||||||
 | 
					is buffered and asynchronous to prevent the job from being IO bound.  
 | 
				
			||||||
'''
 | 
					'''
 | 
				
			||||||
import sys, os, gc, cPickle, traceback, atexit, cStringIO, time, \
 | 
					import sys, os, gc, cPickle, traceback, atexit, cStringIO, time, signal, \
 | 
				
			||||||
       subprocess, socket, collections, binascii
 | 
					       subprocess, socket, collections, binascii, re, tempfile, thread
 | 
				
			||||||
from select import select
 | 
					from select import select
 | 
				
			||||||
from functools import partial
 | 
					from functools import partial
 | 
				
			||||||
from threading import RLock, Thread, Event
 | 
					from threading import RLock, Thread, Event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre.ebooks.lrf.any.convert_from import main as any2lrf
 | 
					 | 
				
			||||||
from calibre.ebooks.lrf.web.convert_from import main as web2lrf
 | 
					 | 
				
			||||||
from calibre.ebooks.lrf.feeds.convert_from import main as feeds2lrf
 | 
					 | 
				
			||||||
from calibre.gui2.lrf_renderer.main import main as lrfviewer
 | 
					 | 
				
			||||||
from calibre.ptempfile import PersistentTemporaryFile
 | 
					from calibre.ptempfile import PersistentTemporaryFile
 | 
				
			||||||
 | 
					from calibre import iswindows, detect_ncpus, isosx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					 | 
				
			||||||
    from calibre.ebooks.lrf.html.table_as_image import do_render as render_table
 | 
					 | 
				
			||||||
except: # Dont fail is PyQt4.4 not present
 | 
					 | 
				
			||||||
    render_table = None
 | 
					 | 
				
			||||||
from calibre import iswindows, islinux, detect_ncpus
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
sa = None
 | 
					 | 
				
			||||||
job_id = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def report_progress(percent, msg=''):
 | 
					 | 
				
			||||||
    if sa is not None and job_id is not None:
 | 
					 | 
				
			||||||
        msg = 'progress:%s:%f:%s'%(job_id, percent, msg)
 | 
					 | 
				
			||||||
        sa.send_message(msg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
_notify = 'fskjhwseiuyweoiu987435935-0342'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: A mapping from job names to functions that perform the jobs
 | 
				
			||||||
PARALLEL_FUNCS = {
 | 
					PARALLEL_FUNCS = {
 | 
				
			||||||
                  'any2lrf'   : partial(any2lrf, gui_mode=True),
 | 
					                  'any2lrf'      : 
 | 
				
			||||||
                  'web2lrf'   : web2lrf,
 | 
					        ('calibre.ebooks.lrf.any.convert_from', 'main', dict(gui_mode=True), None),
 | 
				
			||||||
                  'lrfviewer' : lrfviewer,
 | 
					                  
 | 
				
			||||||
                  'feeds2lrf' : partial(feeds2lrf, notification=_notify),
 | 
					                  'lrfviewer'    : 
 | 
				
			||||||
                  'render_table': render_table,
 | 
					        ('calibre.gui2.lrf_renderer.main', 'main', {}, None),
 | 
				
			||||||
                  }
 | 
					        
 | 
				
			||||||
 | 
					                  'feeds2lrf'    : 
 | 
				
			||||||
 | 
					        ('calibre.ebooks.lrf.feeds.convert_from', 'main', {}, 'notification'),
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					                  'render_table' : 
 | 
				
			||||||
 | 
					        ('calibre.ebooks.lrf.html.table_as_image', 'do_render', {}, None),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
python = sys.executable
 | 
					 | 
				
			||||||
popen = subprocess.Popen
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
if iswindows:
 | 
					isfrozen = hasattr(sys, 'frozen')
 | 
				
			||||||
    if hasattr(sys, 'frozen'):
 | 
					 | 
				
			||||||
        python = os.path.join(os.path.dirname(python), 'parallel.exe')
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
        python = os.path.join(os.path.dirname(python), 'Scripts\\parallel.exe')
 | 
					 | 
				
			||||||
    open = partial(subprocess.Popen, creationflags=0x08) # CREATE_NO_WINDOW=0x08 so that no ugly console is popped up
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
if islinux and hasattr(sys, 'frozen_path'):
 | 
					win32event   = __import__('win32event') if iswindows else None
 | 
				
			||||||
    python = os.path.join(getattr(sys, 'frozen_path'), 'calibre-parallel')
 | 
					win32process = __import__('win32process') if iswindows else None
 | 
				
			||||||
    popen = partial(subprocess.Popen, cwd=getattr(sys, 'frozen_path')) 
 | 
					msvcrt       = __import__('msvcrt') if iswindows else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
prefix = 'import sys; sys.in_worker = True; '
 | 
					class WorkerStatus(object):
 | 
				
			||||||
if hasattr(sys, 'frameworks_dir'):
 | 
					    '''
 | 
				
			||||||
    fd = getattr(sys, 'frameworks_dir')
 | 
					    A platform independent class to control child processes. Provides the
 | 
				
			||||||
    prefix += 'sys.frameworks_dir = "%s"; sys.frozen = "macosx_app"; '%fd
 | 
					    methods:
 | 
				
			||||||
    if fd not in os.environ['PATH']:
 | 
					    
 | 
				
			||||||
        os.environ['PATH'] += ':'+fd
 | 
					    .. method:: WorkerStatus.is_alive()
 | 
				
			||||||
if 'parallel' in python:
 | 
					    
 | 
				
			||||||
    executable          = [python]
 | 
					        Return True is the child process is alive (i.e. it hasn't exited and returned a return code).
 | 
				
			||||||
    worker_command      = '%s:%s'
 | 
					        
 | 
				
			||||||
    free_spirit_command = '%s'
 | 
					    .. method:: WorkerStatus.returncode()
 | 
				
			||||||
else:
 | 
					    
 | 
				
			||||||
    executable          = [python, '-c']
 | 
					        Wait for the child process to exit and return its return code (blocks until child returns).
 | 
				
			||||||
    worker_command      = prefix + 'from calibre.parallel import worker; worker(%s, %s)'
 | 
					        
 | 
				
			||||||
    free_spirit_command = prefix + 'from calibre.parallel import free_spirit; free_spirit(%s)' 
 | 
					    .. method:: WorkerStatus.kill()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        Forcibly terminates child process using operating system specific semantics. 
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __init__(self, obj):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        `obj`: On windows a process handle, on unix a subprocess.Popen object.
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.obj = obj
 | 
				
			||||||
 | 
					        self.win32process = win32process # Needed if kill is called during shutdown of interpreter
 | 
				
			||||||
 | 
					        self.os           = os
 | 
				
			||||||
 | 
					        self.signal       = signal
 | 
				
			||||||
 | 
					        ext = 'windows' if iswindows else 'unix'
 | 
				
			||||||
 | 
					        for func in ('is_alive', 'returncode', 'kill'):
 | 
				
			||||||
 | 
					            setattr(self, func, getattr(self, func+'_'+ext))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def is_alive_unix(self):
 | 
				
			||||||
 | 
					        return self.obj.poll() == None
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def returncode_unix(self):
 | 
				
			||||||
 | 
					        return self.obj.wait()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def kill_unix(self):
 | 
				
			||||||
 | 
					        os.kill(self.obj.pid, self.signal.SIGKILL)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def is_alive_windows(self):
 | 
				
			||||||
 | 
					        return win32event.WaitForSingleObject(self.obj, 0) != win32event.WAIT_OBJECT_0
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def returncode_windows(self):
 | 
				
			||||||
 | 
					        return win32process.GetExitCodeProcess(self.obj)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def kill_windows(self, returncode=-1):
 | 
				
			||||||
 | 
					        self.win32process.TerminateProcess(self.obj, returncode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WorkerMother(object):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Platform independent object for launching child processes. All processes
 | 
				
			||||||
 | 
					    have the environment variable :envvar:`CALIBRE_WORKER` set.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    ..method:: WorkerMother.spawn_free_spirit(arg)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        Launch a non monitored process with argument `arg`.
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    ..method:: WorkerMother.spawn_worker(arg)
 | 
				
			||||||
 | 
					         
 | 
				
			||||||
 | 
					        Launch a monitored and controllable process with argument `arg`. 
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        ext = 'windows' if iswindows else 'osx' if isosx else 'linux'
 | 
				
			||||||
 | 
					        self.os = os # Needed incase cleanup called when interpreter is shutting down
 | 
				
			||||||
 | 
					        if iswindows:
 | 
				
			||||||
 | 
					            self.executable = os.path.join(os.path.dirname(sys.executable),
 | 
				
			||||||
 | 
					                   'calibre-parallel.exe' if isfrozen else 'Scripts\\calibre-parallel.exe')
 | 
				
			||||||
 | 
					        elif isosx:
 | 
				
			||||||
 | 
					            self.executable = sys.executable
 | 
				
			||||||
 | 
					            self.prefix = ''
 | 
				
			||||||
 | 
					            if isfrozen:
 | 
				
			||||||
 | 
					                fd = getattr(sys, 'frameworks_dir')
 | 
				
			||||||
 | 
					                contents = os.path.dirname(fd)
 | 
				
			||||||
 | 
					                resources = os.path.join(contents, 'Resources')
 | 
				
			||||||
 | 
					                sp = os.path.join(resources, 'lib', 'python'+sys.version[:3], 'site-packages.zip')
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                self.prefix += 'import sys; sys.frameworks_dir = "%s"; sys.frozen = "macosx_app"; '%fd
 | 
				
			||||||
 | 
					                self.prefix += 'sys.path.insert(0, %s); '%repr(sp)
 | 
				
			||||||
 | 
					                self.env = {}
 | 
				
			||||||
 | 
					                if fd not in os.environ['PATH']:
 | 
				
			||||||
 | 
					                    self.env['PATH'] = os.environ['PATH']+':'+fd
 | 
				
			||||||
 | 
					                self.env['PYTHONHOME'] = resources
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.executable = os.path.join(getattr(sys, 'frozen_path'), 'calibre-parallel') \
 | 
				
			||||||
 | 
					                                if isfrozen else 'calibre-parallel'
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        self.spawn_worker_windows = lambda arg : self.spawn_free_spirit_windows(arg, type='worker')
 | 
				
			||||||
 | 
					        self.spawn_worker_linux   = lambda arg : self.spawn_free_spirit_linux(arg, type='worker')
 | 
				
			||||||
 | 
					        self.spawn_worker_osx     = lambda arg : self.spawn_free_spirit_osx(arg, type='worker')
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for func in ('spawn_free_spirit', 'spawn_worker'):
 | 
				
			||||||
 | 
					            setattr(self, func, getattr(self, func+'_'+ext))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def cleanup_child_windows(self, child, name=None, fd=None):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            child.kill()
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            if fd is not None:
 | 
				
			||||||
 | 
					                self.os.close(fd)
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            if name is not None and os.path.exists(name):
 | 
				
			||||||
 | 
					                self.os.unlink(name)
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def cleanup_child_linux(self, child):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            child.kill()
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            pass            
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def get_env(self):
 | 
				
			||||||
 | 
					        env = dict(os.environ)
 | 
				
			||||||
 | 
					        env['CALIBRE_WORKER'] = '1'
 | 
				
			||||||
 | 
					        if hasattr(self, 'env'):
 | 
				
			||||||
 | 
					            env.update(self.env)
 | 
				
			||||||
 | 
					        return env
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def spawn_free_spirit_osx(self, arg, type='free_spirit'):
 | 
				
			||||||
 | 
					        script = 'from calibre.parallel import main; main(args=["calibre-parallel", %s]);'%repr(arg)
 | 
				
			||||||
 | 
					        cmdline = [self.executable, '-c', self.prefix+script]
 | 
				
			||||||
 | 
					        child = WorkerStatus(subprocess.Popen(cmdline, env=self.get_env()))
 | 
				
			||||||
 | 
					        atexit.register(self.cleanup_child_linux, child)
 | 
				
			||||||
 | 
					        return child
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def spawn_free_spirit_linux(self, arg, type='free_spirit'):
 | 
				
			||||||
 | 
					        cmdline = [self.executable, arg]
 | 
				
			||||||
 | 
					        child = WorkerStatus(subprocess.Popen(cmdline, env=self.get_env()))
 | 
				
			||||||
 | 
					        atexit.register(self.cleanup_child_linux, child)
 | 
				
			||||||
 | 
					        return child 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def spawn_free_spirit_windows(self, arg, type='free_spirit'):
 | 
				
			||||||
 | 
					        fd, name = tempfile.mkstemp('.log', 'calibre_'+type+'_')
 | 
				
			||||||
 | 
					        handle = msvcrt.get_osfhandle(fd)
 | 
				
			||||||
 | 
					        si = win32process.STARTUPINFO()
 | 
				
			||||||
 | 
					        si.hStdOutput = handle
 | 
				
			||||||
 | 
					        si.hStdError  =  handle
 | 
				
			||||||
 | 
					        cmdline = self.executable + ' ' + str(arg)
 | 
				
			||||||
 | 
					        hProcess = \
 | 
				
			||||||
 | 
					        win32process.CreateProcess(
 | 
				
			||||||
 | 
					            None,    # Application Name
 | 
				
			||||||
 | 
					            cmdline, # Command line
 | 
				
			||||||
 | 
					            None,    # processAttributes
 | 
				
			||||||
 | 
					            None,    # threadAttributes
 | 
				
			||||||
 | 
					            1,       # bInheritHandles
 | 
				
			||||||
 | 
					            win32process.CREATE_NO_WINDOW, # Dont want ugly console popping up
 | 
				
			||||||
 | 
					            self.get_env(), # New environment
 | 
				
			||||||
 | 
					            None,    # Current directory
 | 
				
			||||||
 | 
					            si
 | 
				
			||||||
 | 
					        )[0]
 | 
				
			||||||
 | 
					        child = WorkerStatus(hProcess)
 | 
				
			||||||
 | 
					        atexit.register(self.cleanup_child_windows, child, name, fd)
 | 
				
			||||||
 | 
					        return child
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					mother = WorkerMother()        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def write(socket, msg, timeout=5):
 | 
					def write(socket, msg, timeout=5):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Write a message on socket. If `msg` is unicode, it is encoded in utf-8.
 | 
				
			||||||
 | 
					    Raises a `RuntimeError` if the socket is not ready for writing or the writing fails.
 | 
				
			||||||
 | 
					    `msg` is broken into chunks of size 4096 and sent. The :function:`read` function
 | 
				
			||||||
 | 
					    automatically re-assembles the chunks into whole message. 
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
    if isinstance(msg, unicode):
 | 
					    if isinstance(msg, unicode):
 | 
				
			||||||
        msg = msg.encode('utf-8')
 | 
					        msg = msg.encode('utf-8')
 | 
				
			||||||
    length = None
 | 
					    length = None
 | 
				
			||||||
@ -88,6 +244,11 @@ def write(socket, msg, timeout=5):
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
def read(socket, timeout=5):
 | 
					def read(socket, timeout=5):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Read a message from `socket`. The message must have been sent with the :function:`write`
 | 
				
			||||||
 | 
					    function. Raises a `RuntimeError` if the message is corrpted. Can return an 
 | 
				
			||||||
 | 
					    empty string.
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
    buf = cStringIO.StringIO()
 | 
					    buf = cStringIO.StringIO()
 | 
				
			||||||
    length = None
 | 
					    length = None
 | 
				
			||||||
    while select([socket],[],[],timeout)[0]:
 | 
					    while select([socket],[],[],timeout)[0]:
 | 
				
			||||||
@ -108,6 +269,11 @@ def read(socket, timeout=5):
 | 
				
			|||||||
    return msg    
 | 
					    return msg    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RepeatingTimer(Thread):
 | 
					class RepeatingTimer(Thread):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Calls a specified function repeatedly at a specified interval. Runs in a 
 | 
				
			||||||
 | 
					    daemon thread (i.e. the interpreter can exit while it is still running).
 | 
				
			||||||
 | 
					    Call :meth:`start()` to start it. 
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def repeat(self):
 | 
					    def repeat(self):
 | 
				
			||||||
        while True:
 | 
					        while True:
 | 
				
			||||||
@ -116,25 +282,31 @@ class RepeatingTimer(Thread):
 | 
				
			|||||||
                break
 | 
					                break
 | 
				
			||||||
            self.action()
 | 
					            self.action()
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def __init__(self, interval, func):
 | 
					    def __init__(self, interval, func, name):
 | 
				
			||||||
        self.event    = Event()
 | 
					        self.event    = Event()
 | 
				
			||||||
        self.interval = interval
 | 
					        self.interval = interval
 | 
				
			||||||
        self.action = func  
 | 
					        self.action = func  
 | 
				
			||||||
        Thread.__init__(self, target=self.repeat)
 | 
					        Thread.__init__(self, target=self.repeat, name=name)
 | 
				
			||||||
        self.setDaemon(True)
 | 
					        self.setDaemon(True)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
class ControlError(Exception):
 | 
					class ControlError(Exception):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Overseer(object):
 | 
					class Overseer(object):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Responsible for controlling worker processes. The main interface is the
 | 
				
			||||||
 | 
					    methods, :meth:`initialize_job`, :meth:`control`.
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    KILL_RESULT = 'Server: job killed by user|||#@#$%&*)*(*$#$%#$@&'
 | 
					    KILL_RESULT = 'Server: job killed by user|||#@#$%&*)*(*$#$%#$@&'
 | 
				
			||||||
    INTERVAL    = 0.1
 | 
					    INTERVAL    = 0.1
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def __init__(self, server, port, timeout=5):
 | 
					    def __init__(self, server, port, timeout=5):
 | 
				
			||||||
        self.cmd = worker_command%(repr('127.0.0.1'), repr(port))
 | 
					        self.worker_status = mother.spawn_worker('127.0.0.1:%d'%port)
 | 
				
			||||||
        self.process = popen(executable + [self.cmd])
 | 
					 | 
				
			||||||
        self.socket = server.accept()[0]
 | 
					        self.socket = server.accept()[0]
 | 
				
			||||||
 | 
					        # Needed if terminate called hwen interpreter is shutting down
 | 
				
			||||||
 | 
					        self.os = os
 | 
				
			||||||
 | 
					        self.signal = signal
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        self.working = False
 | 
					        self.working = False
 | 
				
			||||||
        self.timeout = timeout
 | 
					        self.timeout = timeout
 | 
				
			||||||
@ -152,9 +324,7 @@ class Overseer(object):
 | 
				
			|||||||
            raise RuntimeError('Worker sulking')
 | 
					            raise RuntimeError('Worker sulking')
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    def terminate(self):
 | 
					    def terminate(self):
 | 
				
			||||||
        '''
 | 
					        'Kill worker process.'
 | 
				
			||||||
        Kill process.
 | 
					 | 
				
			||||||
        '''
 | 
					 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if self.socket:
 | 
					            if self.socket:
 | 
				
			||||||
                self.write('STOP:')
 | 
					                self.write('STOP:')
 | 
				
			||||||
@ -170,9 +340,8 @@ class Overseer(object):
 | 
				
			|||||||
            except:
 | 
					            except:
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            import signal
 | 
					 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                os.kill(self.worker_pid, signal.SIGKILL)
 | 
					                self.os.kill(self.worker_pid, self.signal.SIGKILL)
 | 
				
			||||||
                time.sleep(0.05)
 | 
					                time.sleep(0.05)
 | 
				
			||||||
            except:
 | 
					            except:
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
@ -188,16 +357,19 @@ class Overseer(object):
 | 
				
			|||||||
        return hasattr(other, 'process') and hasattr(other, 'worker_pid') and self.worker_pid == other.worker_pid
 | 
					        return hasattr(other, 'process') and hasattr(other, 'worker_pid') and self.worker_pid == other.worker_pid
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def __bool__(self):
 | 
					    def __bool__(self):
 | 
				
			||||||
        self.process.poll()
 | 
					        return self.worker_status.is_alive()
 | 
				
			||||||
        return self.process.returncode is None
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    def pid(self):
 | 
					 | 
				
			||||||
        return self.worker_pid
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def select(self, timeout=0):
 | 
					    def select(self, timeout=0):
 | 
				
			||||||
        return select([self.socket], [self.socket], [self.socket], timeout)
 | 
					        return select([self.socket], [self.socket], [self.socket], timeout)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def initialize_job(self, job):
 | 
					    def initialize_job(self, job):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Sends `job` to worker process. Can raise `ControlError` if worker process
 | 
				
			||||||
 | 
					        does not respond appropriately. In this case, this Overseer is useless
 | 
				
			||||||
 | 
					        and should be discarded.
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        `job`: An instance of :class:`Job`.
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
        self.job_id = job.job_id
 | 
					        self.job_id = job.job_id
 | 
				
			||||||
        self.working = True
 | 
					        self.working = True
 | 
				
			||||||
        self.write('JOB:'+cPickle.dumps((job.func, job.args, job.kwdargs), -1))
 | 
					        self.write('JOB:'+cPickle.dumps((job.func, job.args, job.kwdargs), -1))
 | 
				
			||||||
@ -209,40 +381,44 @@ class Overseer(object):
 | 
				
			|||||||
        self.job =  job
 | 
					        self.job =  job
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def control(self):
 | 
					    def control(self):
 | 
				
			||||||
        try:
 | 
					        '''
 | 
				
			||||||
            if select([self.socket],[],[],0)[0]:
 | 
					        Listens for messages from the worker process and dispatches them
 | 
				
			||||||
                msg = self.read()
 | 
					        appropriately. If the worker process dies unexpectedly, returns a result
 | 
				
			||||||
                word, msg = msg.partition(':')[0], msg.partition(':')[-1]
 | 
					        of None with a ControlError indicating the worker died.
 | 
				
			||||||
                if word == 'RESULT':
 | 
					        
 | 
				
			||||||
                    self.write('OK')
 | 
					        Returns a :class:`Result` instance or None, if the worker is still working.
 | 
				
			||||||
                    return Result(cPickle.loads(msg), None, None)
 | 
					        '''
 | 
				
			||||||
                elif word == 'OUTPUT':
 | 
					        if select([self.socket],[],[],0)[0]:
 | 
				
			||||||
                    self.write('OK')
 | 
					            msg = self.read()
 | 
				
			||||||
                    try:
 | 
					            word, msg = msg.partition(':')[0], msg.partition(':')[-1]
 | 
				
			||||||
                        self.output(''.join(cPickle.loads(msg)))
 | 
					            if word == 'RESULT':
 | 
				
			||||||
                    except:
 | 
					                self.write('OK')
 | 
				
			||||||
                        self.output('Bad output message: '+ repr(msg))
 | 
					                return Result(cPickle.loads(msg), None, None)
 | 
				
			||||||
                elif word == 'PROGRESS':
 | 
					            elif word == 'OUTPUT':
 | 
				
			||||||
                    self.write('OK')
 | 
					                self.write('OK')
 | 
				
			||||||
                    percent = None
 | 
					                try:
 | 
				
			||||||
                    try:
 | 
					                    self.output(''.join(cPickle.loads(msg)))
 | 
				
			||||||
                        percent, msg = cPickle.loads(msg)[-1]
 | 
					                except:
 | 
				
			||||||
                    except:
 | 
					                    self.output('Bad output message: '+ repr(msg))
 | 
				
			||||||
                        print 'Bad progress update:', repr(msg)
 | 
					            elif word == 'PROGRESS':
 | 
				
			||||||
                    if self.progress and percent is not None:
 | 
					                self.write('OK')
 | 
				
			||||||
                        self.progress(percent, msg)
 | 
					                percent = None
 | 
				
			||||||
                elif word == 'ERROR':
 | 
					                try:
 | 
				
			||||||
                    self.write('OK')
 | 
					                    percent, msg = cPickle.loads(msg)[-1]
 | 
				
			||||||
                    return Result(None, *cPickle.loads(msg))
 | 
					                except:
 | 
				
			||||||
                else:
 | 
					                    print 'Bad progress update:', repr(msg)
 | 
				
			||||||
                    self.terminate()
 | 
					                if self.progress and percent is not None:
 | 
				
			||||||
                    return Result(None, ControlError('Worker sent invalid msg: %s', repr(msg)), '')
 | 
					                    self.progress(percent, msg)
 | 
				
			||||||
            self.process.poll()
 | 
					            elif word == 'ERROR':
 | 
				
			||||||
            if self.process.returncode is not None:
 | 
					                self.write('OK')
 | 
				
			||||||
                return Result(None, ControlError('Worker process died unexpectedly with returncode: %d'%self.process.returncode), '')
 | 
					                return Result(None, *cPickle.loads(msg))
 | 
				
			||||||
        finally:
 | 
					            else:
 | 
				
			||||||
            self.working = False
 | 
					                self.terminate()
 | 
				
			||||||
            self.last_job_time = time.time()
 | 
					                return Result(None, ControlError('Worker sent invalid msg: %s', repr(msg)), '')
 | 
				
			||||||
 | 
					        if not self.worker_status.is_alive():
 | 
				
			||||||
 | 
					            return Result(None, ControlError('Worker process died unexpectedly with returncode: %d'%self.process.returncode), '')
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
class Job(object):
 | 
					class Job(object):
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -325,14 +501,23 @@ class Server(Thread):
 | 
				
			|||||||
                if len(self.jobs) > 0 and len(self.working) < self.number_of_workers:
 | 
					                if len(self.jobs) > 0 and len(self.working) < self.number_of_workers:
 | 
				
			||||||
                    job = self.jobs.popleft()
 | 
					                    job = self.jobs.popleft()
 | 
				
			||||||
                    with self.pool_lock:
 | 
					                    with self.pool_lock:
 | 
				
			||||||
                        o = self.pool.pop() if self.pool else Overseer(self.server_socket, self.port)
 | 
					 | 
				
			||||||
                    try:
 | 
					 | 
				
			||||||
                        o.initialize_job(job)
 | 
					 | 
				
			||||||
                    except Exception, err:
 | 
					 | 
				
			||||||
                        res = Result(None, unicode(err), traceback.format_exc())
 | 
					 | 
				
			||||||
                        job.done(res)
 | 
					 | 
				
			||||||
                        o.terminate()
 | 
					 | 
				
			||||||
                        o = None
 | 
					                        o = None
 | 
				
			||||||
 | 
					                        while self.pool:
 | 
				
			||||||
 | 
					                            o = self.pool.pop()
 | 
				
			||||||
 | 
					                            try:
 | 
				
			||||||
 | 
					                                o.initialize_job(job)
 | 
				
			||||||
 | 
					                                break
 | 
				
			||||||
 | 
					                            except:
 | 
				
			||||||
 | 
					                                o.terminate()
 | 
				
			||||||
 | 
					                        if o is None:
 | 
				
			||||||
 | 
					                            o = Overseer(self.server_socket, self.port)
 | 
				
			||||||
 | 
					                            try:
 | 
				
			||||||
 | 
					                                o.initialize_job(job)
 | 
				
			||||||
 | 
					                            except Exception, err:
 | 
				
			||||||
 | 
					                                o.terminate()
 | 
				
			||||||
 | 
					                                res = Result(None, unicode(err), traceback.format_exc())
 | 
				
			||||||
 | 
					                                job.done(res)
 | 
				
			||||||
 | 
					                                o = None
 | 
				
			||||||
                    if o:
 | 
					                    if o:
 | 
				
			||||||
                        with self.working_lock:
 | 
					                        with self.working_lock:
 | 
				
			||||||
                            self.working.append(o)
 | 
					                            self.working.append(o)
 | 
				
			||||||
@ -393,8 +578,8 @@ class Server(Thread):
 | 
				
			|||||||
        pt = PersistentTemporaryFile('.pickle', '_IPC_')
 | 
					        pt = PersistentTemporaryFile('.pickle', '_IPC_')
 | 
				
			||||||
        pt.write(cPickle.dumps((func, args, kwdargs)))
 | 
					        pt.write(cPickle.dumps((func, args, kwdargs)))
 | 
				
			||||||
        pt.close()
 | 
					        pt.close()
 | 
				
			||||||
        cmd = free_spirit_command%repr(binascii.hexlify(pt.name))
 | 
					        mother.spawn_free_spirit(binascii.hexlify(pt.name))
 | 
				
			||||||
        popen(executable + [cmd])
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##########################################################################################
 | 
					##########################################################################################
 | 
				
			||||||
##################################### CLIENT CODE #####################################
 | 
					##################################### CLIENT CODE #####################################
 | 
				
			||||||
@ -406,8 +591,7 @@ class BufferedSender(object):
 | 
				
			|||||||
        self.socket = socket
 | 
					        self.socket = socket
 | 
				
			||||||
        self.wbuf, self.pbuf    = [], []
 | 
					        self.wbuf, self.pbuf    = [], []
 | 
				
			||||||
        self.wlock, self.plock   = RLock(), RLock()
 | 
					        self.wlock, self.plock   = RLock(), RLock()
 | 
				
			||||||
        self.timer  = RepeatingTimer(0.5, self.send)
 | 
					        self.timer  = RepeatingTimer(0.5, self.send, 'BufferedSender')
 | 
				
			||||||
        self.prefix = prefix
 | 
					 | 
				
			||||||
        self.timer.start()
 | 
					        self.timer.start()
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    def write(self, msg):
 | 
					    def write(self, msg):
 | 
				
			||||||
@ -417,6 +601,15 @@ class BufferedSender(object):
 | 
				
			|||||||
            self.wbuf.append(msg)
 | 
					            self.wbuf.append(msg)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def send(self):
 | 
					    def send(self):
 | 
				
			||||||
 | 
					        if select([self.socket], [], [], 0)[0]:
 | 
				
			||||||
 | 
					            msg = read(self.socket)
 | 
				
			||||||
 | 
					            if msg == 'PING:':
 | 
				
			||||||
 | 
					                write(self.socket, 'OK')
 | 
				
			||||||
 | 
					            elif msg:
 | 
				
			||||||
 | 
					                self.socket.shutdown(socket.SHUT_RDWR)
 | 
				
			||||||
 | 
					                thread.interrupt_main()
 | 
				
			||||||
 | 
					                time.sleep(1)
 | 
				
			||||||
 | 
					                raise SystemExit
 | 
				
			||||||
        if not select([], [self.socket], [], 30)[1]:
 | 
					        if not select([], [self.socket], [], 30)[1]:
 | 
				
			||||||
            print >>sys.__stderr__, 'Cannot pipe to overseer'
 | 
					            print >>sys.__stderr__, 'Cannot pipe to overseer'
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
@ -442,13 +635,18 @@ class BufferedSender(object):
 | 
				
			|||||||
    def flush(self):
 | 
					    def flush(self):
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_func(name):
 | 
				
			||||||
 | 
					    module, func, kwdargs, notification = PARALLEL_FUNCS[name]
 | 
				
			||||||
 | 
					    module = __import__(module, fromlist=[1])
 | 
				
			||||||
 | 
					    func = getattr(module, func)
 | 
				
			||||||
 | 
					    return func, kwdargs, notification
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def work(client_socket, func, args, kwdargs):
 | 
					def work(client_socket, func, args, kwdargs):
 | 
				
			||||||
    func = PARALLEL_FUNCS[func]
 | 
					    func, kargs, notification = get_func(func)
 | 
				
			||||||
    if hasattr(func, 'keywords'):
 | 
					    if notification is not None and hasattr(sys.stdout, 'notify'):
 | 
				
			||||||
        for key, val in func.keywords.items():
 | 
					        kargs[notification] = sys.stdout.notify
 | 
				
			||||||
            if val == _notify and hasattr(sys.stdout, 'notify'):
 | 
					    kargs.update(kwdargs)
 | 
				
			||||||
                func.keywords[key] = sys.stdout.notify
 | 
					    res = func(*args, **kargs)
 | 
				
			||||||
    res = func(*args, **kwdargs)
 | 
					 | 
				
			||||||
    if hasattr(sys.stdout, 'send'): 
 | 
					    if hasattr(sys.stdout, 'send'): 
 | 
				
			||||||
        sys.stdout.send()
 | 
					        sys.stdout.send()
 | 
				
			||||||
    return res
 | 
					    return res
 | 
				
			||||||
@ -467,6 +665,9 @@ def worker(host, port):
 | 
				
			|||||||
    sys.stderr = sys.stdout
 | 
					    sys.stderr = sys.stdout
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    while True:
 | 
					    while True:
 | 
				
			||||||
 | 
					        if not select([client_socket], [], [], 60)[0]:
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
        msg = read(client_socket, timeout=60)
 | 
					        msg = read(client_socket, timeout=60)
 | 
				
			||||||
        if msg.startswith('JOB:'):
 | 
					        if msg.startswith('JOB:'):
 | 
				
			||||||
            func, args, kwdargs = cPickle.loads(msg[4:])
 | 
					            func, args, kwdargs = cPickle.loads(msg[4:])
 | 
				
			||||||
@ -481,7 +682,10 @@ def worker(host, port):
 | 
				
			|||||||
            if read(client_socket, 10) != 'OK':
 | 
					            if read(client_socket, 10) != 'OK':
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
            gc.collect()
 | 
					            gc.collect()
 | 
				
			||||||
 | 
					        elif msg == 'PING:':
 | 
				
			||||||
 | 
					            write(client_socket, 'OK')    
 | 
				
			||||||
        elif msg == 'STOP:':
 | 
					        elif msg == 'STOP:':
 | 
				
			||||||
 | 
					            client_socket.shutdown(socket.SHUT_RDWR)
 | 
				
			||||||
            return 0
 | 
					            return 0
 | 
				
			||||||
        elif not msg:
 | 
					        elif not msg:
 | 
				
			||||||
            time.sleep(1)
 | 
					            time.sleep(1)
 | 
				
			||||||
@ -490,21 +694,23 @@ def worker(host, port):
 | 
				
			|||||||
            return 1
 | 
					            return 1
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
def free_spirit(path):
 | 
					def free_spirit(path):
 | 
				
			||||||
    func, args, kwdargs = cPickle.load(open(binascii.unhexlify(path), 'rb'))
 | 
					    func, args, kwdargs = cPickle.load(open(path, 'rb'))
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        os.unlink(path)
 | 
					        os.unlink(path)
 | 
				
			||||||
    except:
 | 
					    except:
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
    PARALLEL_FUNCS[func](*args, **kwdargs)
 | 
					    func, kargs = get_func(func)[:2]
 | 
				
			||||||
 | 
					    kargs.update(kwdargs)
 | 
				
			||||||
 | 
					    func(*args, **kargs)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
def main(args=sys.argv):
 | 
					def main(args=sys.argv):
 | 
				
			||||||
    args = args[1].split(':')
 | 
					    args = args[1].split(':')
 | 
				
			||||||
    if len(args) == 1:
 | 
					    if len(args) == 1:
 | 
				
			||||||
        free_spirit(args[0].replace("'", ''))
 | 
					        free_spirit(binascii.unhexlify(re.sub(r'[^a-f0-9A-F]', '', args[0])))
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        worker(args[0].replace("'", ''), int(args[1])) 
 | 
					        worker(args[0].replace("'", ''), int(args[1])) 
 | 
				
			||||||
    return 0
 | 
					    return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    sys.exit(main())
 | 
					    sys.exit(main())
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
__license__   = 'GPL v3'
 | 
					__license__   = 'GPL v3'
 | 
				
			||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
					__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
				
			||||||
import sys, re
 | 
					import sys, re, os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
""" Get information about the terminal we are running in """
 | 
					""" Get information about the terminal we are running in """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -94,7 +94,7 @@ class TerminalController:
 | 
				
			|||||||
        except: return
 | 
					        except: return
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        # If the stream isn't a tty, then assume it has no capabilities.
 | 
					        # If the stream isn't a tty, then assume it has no capabilities.
 | 
				
			||||||
        if hasattr(sys, 'in_worker') or not hasattr(term_stream, 'isatty') or not term_stream.isatty(): return
 | 
					        if os.environ.get('CALIBRE_WORKER', None) is not None or not hasattr(term_stream, 'isatty') or not term_stream.isatty(): return
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        # Check the terminal type.  If we fail, then assume that the
 | 
					        # Check the terminal type.  If we fail, then assume that the
 | 
				
			||||||
        # terminal has no capabilities.
 | 
					        # terminal has no capabilities.
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								upload.py
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								upload.py
									
									
									
									
									
								
							@ -63,17 +63,18 @@ def start_vm(vm, ssh_host, build_script, sleep=75):
 | 
				
			|||||||
    subprocess.check_call(('scp', t.name, ssh_host+':build-'+PROJECT))
 | 
					    subprocess.check_call(('scp', t.name, ssh_host+':build-'+PROJECT))
 | 
				
			||||||
    subprocess.check_call('ssh -t %s bash build-%s'%(ssh_host, PROJECT), shell=True)
 | 
					    subprocess.check_call('ssh -t %s bash build-%s'%(ssh_host, PROJECT), shell=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def build_windows():
 | 
					def build_windows(shutdown=True):
 | 
				
			||||||
    installer = installer_name('exe')
 | 
					    installer = installer_name('exe')
 | 
				
			||||||
    vm = '/vmware/Windows XP/Windows XP Professional.vmx'
 | 
					    vm = '/vmware/Windows XP/Windows XP Professional.vmx'
 | 
				
			||||||
    start_vm(vm, 'windows', BUILD_SCRIPT%('python setup.py develop', 'python','windows_installer.py'))
 | 
					    start_vm(vm, 'windows', BUILD_SCRIPT%('python setup.py develop', 'python','windows_installer.py'))
 | 
				
			||||||
    subprocess.check_call(('scp', 'windows:build/%s/dist/*.exe'%PROJECT, 'dist'))
 | 
					    subprocess.check_call(('scp', 'windows:build/%s/dist/*.exe'%PROJECT, 'dist'))
 | 
				
			||||||
    if not os.path.exists(installer):
 | 
					    if not os.path.exists(installer):
 | 
				
			||||||
        raise Exception('Failed to build installer '+installer)
 | 
					        raise Exception('Failed to build installer '+installer)
 | 
				
			||||||
    subprocess.Popen(('ssh', 'windows', 'shutdown', '-s', '-t', '0'))
 | 
					    if shutdown:
 | 
				
			||||||
 | 
					        subprocess.Popen(('ssh', 'windows', 'shutdown', '-s', '-t', '0'))
 | 
				
			||||||
    return os.path.basename(installer)
 | 
					    return os.path.basename(installer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def build_osx():
 | 
					def build_osx(shutdown=True):
 | 
				
			||||||
    installer = installer_name('dmg')
 | 
					    installer = installer_name('dmg')
 | 
				
			||||||
    vm = '/vmware/Mac OSX/Mac OSX.vmx'
 | 
					    vm = '/vmware/Mac OSX/Mac OSX.vmx'
 | 
				
			||||||
    python = '/Library/Frameworks/Python.framework/Versions/Current/bin/python' 
 | 
					    python = '/Library/Frameworks/Python.framework/Versions/Current/bin/python' 
 | 
				
			||||||
@ -81,18 +82,20 @@ def build_osx():
 | 
				
			|||||||
    subprocess.check_call(('scp', 'osx:build/%s/dist/*.dmg'%PROJECT, 'dist'))
 | 
					    subprocess.check_call(('scp', 'osx:build/%s/dist/*.dmg'%PROJECT, 'dist'))
 | 
				
			||||||
    if not os.path.exists(installer):
 | 
					    if not os.path.exists(installer):
 | 
				
			||||||
        raise Exception('Failed to build installer '+installer)
 | 
					        raise Exception('Failed to build installer '+installer)
 | 
				
			||||||
    subprocess.Popen(('ssh', 'osx', 'sudo', '/sbin/shutdown', '-h', 'now'))
 | 
					    if shutdown:
 | 
				
			||||||
 | 
					        subprocess.Popen(('ssh', 'osx', 'sudo', '/sbin/shutdown', '-h', 'now'))
 | 
				
			||||||
    return os.path.basename(installer)
 | 
					    return os.path.basename(installer)
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def build_linux():
 | 
					def build_linux(shutdown=True):
 | 
				
			||||||
    installer = installer_name('tar.bz2')
 | 
					    installer = installer_name('tar.bz2')
 | 
				
			||||||
    vm = '/vmware/linux/libprs500-gentoo.vmx'
 | 
					    vm = '/vmware/linux/libprs500-gentoo.vmx'
 | 
				
			||||||
    start_vm(vm, 'linux', BUILD_SCRIPT%('sudo python setup.py develop', 'python','linux_installer.py'))
 | 
					    start_vm(vm, 'linux', BUILD_SCRIPT%('sudo python setup.py develop', 'python','linux_installer.py'))
 | 
				
			||||||
    subprocess.check_call(('scp', 'linux:/tmp/%s'%os.path.basename(installer), 'dist'))
 | 
					    subprocess.check_call(('scp', 'linux:/tmp/%s'%os.path.basename(installer), 'dist'))
 | 
				
			||||||
    if not os.path.exists(installer):
 | 
					    if not os.path.exists(installer):
 | 
				
			||||||
        raise Exception('Failed to build installer '+installer)
 | 
					        raise Exception('Failed to build installer '+installer)
 | 
				
			||||||
    subprocess.Popen(('ssh', 'linux', 'sudo', '/sbin/poweroff'))
 | 
					    if shutdown:
 | 
				
			||||||
 | 
					        subprocess.Popen(('ssh', 'linux', 'sudo', '/sbin/poweroff'))
 | 
				
			||||||
    return os.path.basename(installer)
 | 
					    return os.path.basename(installer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def build_installers():
 | 
					def build_installers():
 | 
				
			||||||
 | 
				
			|||||||
@ -412,7 +412,7 @@ SectionEnd
 | 
				
			|||||||
                                                   version=VERSION, 
 | 
					                                                   version=VERSION, 
 | 
				
			||||||
                                                   outpath=os.path.abspath(output_dir))
 | 
					                                                   outpath=os.path.abspath(output_dir))
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    def build(self):        
 | 
					    def build(self):
 | 
				
			||||||
        f = open('installer.nsi', 'w')
 | 
					        f = open('installer.nsi', 'w')
 | 
				
			||||||
        path = f.name
 | 
					        path = f.name
 | 
				
			||||||
        f.write(self.installer)
 | 
					        f.write(self.installer)
 | 
				
			||||||
@ -420,7 +420,7 @@ SectionEnd
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            subprocess.check_call('"C:\Program Files\NSIS\makensis.exe" /V2 ' + path, shell=True)
 | 
					            subprocess.check_call('"C:\Program Files\NSIS\makensis.exe" /V2 ' + path, shell=True)
 | 
				
			||||||
        except:
 | 
					        except:
 | 
				
			||||||
            print path 
 | 
					            print path
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            os.remove(path)
 | 
					            os.remove(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -537,18 +537,18 @@ class BuildEXE(build_exe):
 | 
				
			|||||||
def main():
 | 
					def main():
 | 
				
			||||||
    sys.argv[1:2] = ['py2exe']
 | 
					    sys.argv[1:2] = ['py2exe']
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    console = [dict(dest_base=basenames['console'][i], script=scripts['console'][i]) 
 | 
					    console = [dict(dest_base=basenames['console'][i], script=scripts['console'][i])
 | 
				
			||||||
               for i in range(len(scripts['console']))]
 | 
					               for i in range(len(scripts['console']))]# if not 'parallel.py' in scripts['console'][i] ]
 | 
				
			||||||
    sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
 | 
					    sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
 | 
				
			||||||
    setup(
 | 
					    setup(
 | 
				
			||||||
          cmdclass = {'py2exe': BuildEXE},
 | 
					          cmdclass = {'py2exe': BuildEXE},
 | 
				
			||||||
          windows = [
 | 
					          windows = [
 | 
				
			||||||
                     {'script'          : scripts['gui'][0], 
 | 
					                     {'script'          : scripts['gui'][0],
 | 
				
			||||||
                      'dest_base'       : APPNAME,
 | 
					                      'dest_base'       : APPNAME,
 | 
				
			||||||
                      'icon_resources'  : [(1, 'icons/library.ico')],
 | 
					                      'icon_resources'  : [(1, 'icons/library.ico')],
 | 
				
			||||||
                      'other_resources' : [BuildEXE.manifest(APPNAME)],
 | 
					                      'other_resources' : [BuildEXE.manifest(APPNAME)],
 | 
				
			||||||
                      },
 | 
					                      },
 | 
				
			||||||
                      {'script'         : scripts['gui'][1], 
 | 
					                      {'script'         : scripts['gui'][1],
 | 
				
			||||||
                      'dest_base'       : 'lrfviewer',
 | 
					                      'dest_base'       : 'lrfviewer',
 | 
				
			||||||
                      'icon_resources'  : [(1, 'icons/viewer.ico')],
 | 
					                      'icon_resources'  : [(1, 'icons/viewer.ico')],
 | 
				
			||||||
                      'other_resources' : [BuildEXE.manifest('lrfviewer')],
 | 
					                      'other_resources' : [BuildEXE.manifest('lrfviewer')],
 | 
				
			||||||
@ -561,11 +561,13 @@ def main():
 | 
				
			|||||||
                                  'includes'  : [
 | 
					                                  'includes'  : [
 | 
				
			||||||
                                             'sip', 'pkg_resources', 'PyQt4.QtSvg', 
 | 
					                                             'sip', 'pkg_resources', 'PyQt4.QtSvg', 
 | 
				
			||||||
                                             'mechanize', 'ClientForm', 'wmi', 
 | 
					                                             'mechanize', 'ClientForm', 'wmi', 
 | 
				
			||||||
                                             'win32file', 'pythoncom', 'rtf2xml', 
 | 
					                                             'win32file', 'pythoncom', 'rtf2xml',
 | 
				
			||||||
 | 
					                                             'win32process', 'win32api', 'msvcrt',
 | 
				
			||||||
 | 
					                                             'win32event', 
 | 
				
			||||||
                                             'lxml', 'lxml._elementpath', 'genshi',
 | 
					                                             'lxml', 'lxml._elementpath', 'genshi',
 | 
				
			||||||
                                             'path', 'pydoc', 'IPython.Extensions.*',
 | 
					                                             'path', 'pydoc', 'IPython.Extensions.*',
 | 
				
			||||||
                                             'calibre.web.feeds.recipes.*', 'PyQt4.QtWebKit',
 | 
					                                             'calibre.web.feeds.recipes.*', 'PyQt4.QtWebKit',
 | 
				
			||||||
                                             ],                                
 | 
					                                             ],
 | 
				
			||||||
                                  'packages'  : ['PIL'],
 | 
					                                  'packages'  : ['PIL'],
 | 
				
			||||||
                                  'excludes'  : ["Tkconstants", "Tkinter", "tcl", 
 | 
					                                  'excludes'  : ["Tkconstants", "Tkinter", "tcl", 
 | 
				
			||||||
                                                 "_imagingtk", "ImageTk", "FixTk"
 | 
					                                                 "_imagingtk", "ImageTk", "FixTk"
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user