home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 July / CMCD0704.ISO / Software / Shareware / Comunicatii / jyte / jyte.exe / boot.py < prev    next >
Text File  |  2004-04-13  |  9KB  |  309 lines

  1. import errno
  2. import os
  3. import os.path
  4. import re
  5. import shutil
  6. import sys
  7. import site
  8. import zipfile
  9.  
  10. __tag__ = '$Name: BOOT_0_2_11 $'[7:-2]
  11.  
  12. VERSION = None
  13. if __tag__.startswith('BOOT_'):
  14.     parts = __tag__.split('_')
  15.     if len(parts) == 4:
  16.         try:
  17.             parts = map(int, parts[1:])
  18.         except ValueError:
  19.             pass
  20.         else:
  21.             VERSION = tuple(parts)
  22.  
  23. # Initial startup file:
  24. #
  25. # Parse options
  26. # Import platform-dependent startup code
  27. # Find highest-version cc package zip file
  28. # Add to path
  29. # Put references to info that we determined on startup into magnet module
  30. # Jump into magnet main
  31. #
  32. # Catch any exceptions that occur before we can install the
  33. # more-capable handler or after shutdown and report them to the user
  34. # and try to report them to us
  35.  
  36. MIN_VERSION = (0, 7, 0)
  37. DEFAULT_BUG_ADDRESS = 'jytebugs@janrain.com'
  38. DEFAULT_APP_NAME = 'Jyte'
  39. DEFAULT_MAIL_SERVER = 'smtp.janrain.com'
  40. DEFAULT_BUG_REPORT_TEXT = \
  41. """An error occurred that this application could not
  42. handle. Sending a bug report will make this less
  43. frequent in the future."""
  44. BAKED_SEARCH_FILENAME = 'startup.dat'
  45.  
  46. name_pattern = re.compile('^magnet-(\d+)\.(\d+)\.(\d+).zip$')
  47. name_s = 'magnet-%d.%d.%d.zip'
  48.  
  49. def getVersions(dirname):
  50.     try:
  51.         names = os.listdir(dirname)
  52.     except OSError, e:
  53.         if e.errno != errno.ENOENT:
  54.             raise
  55.     else:
  56.         for name in names:
  57.             match = name_pattern.match(name)
  58.             if match is not None:
  59.                 version = tuple(map(int, match.groups()))
  60.                 if version >= MIN_VERSION:
  61.                     yield version
  62.     
  63. def findZip(*locations):
  64.     """Find zip files that should contain magnet releases.
  65.  
  66.     Prefer to get zip files from the directories given in the order
  67.     that they are given. Prefer to get zip files with higher version
  68.     numbers. If the file is not found in the first location passed in,
  69.     then copy the file into that directory so it is there next time."""
  70.  
  71.     # Get the lib path and make sure that it exists
  72.     lib_path = locations[0]
  73.     if not os.path.exists(lib_path):
  74.         os.makedirs(lib_path)
  75.  
  76.     # Sort by version number first, then by the order that locations
  77.     # were passed to us, so that we use the highest-numbered version
  78.     # from the first location in the locations list.
  79.     versions = []   
  80.     for tie_breaker, location in enumerate(locations):
  81.         for version in getVersions(location):
  82.             versions.append((version, tie_breaker, location))
  83.         
  84.     versions.sort()
  85.     versions.reverse()
  86.  
  87.     full_names = []
  88.     for version, unused_tie_breaker, location in versions:
  89.         basename = name_s % version
  90.         source_name = os.path.join(location, basename)
  91.  
  92.         if not zipfile.is_zipfile(source_name):
  93.             print >>sys.stderr, 'Skipping %s -  not a valid zip file' % (
  94.                 source_name,)
  95.  
  96.             continue
  97.  
  98.         if location != lib_path:
  99.             full_name = os.path.join(lib_path, basename)
  100.  
  101.             if not os.path.exists(full_name):
  102.                 print >>sys.stderr, 'Moving file to lib directory: %s' % (
  103.                     source_name)
  104.  
  105.                 scratch_name = full_name + '-scratch'
  106.  
  107.                 # Copy, then move since move is atomic.
  108.                 # This prevents getting a partial copy.
  109.                 shutil.copyfile(source_name, scratch_name)
  110.                 os.rename(scratch_name, full_name)
  111.             
  112.         else:
  113.             full_name = source_name
  114.  
  115.         if full_name not in full_names:
  116.             full_names.append(full_name)
  117.  
  118.     return full_names
  119.  
  120. def getMagnet():
  121.     """Attempt to get the magnet module"""
  122.     try:
  123.         from cc import magnet
  124.     except ImportError:
  125.         return None
  126.     else:
  127.         return magnet
  128.  
  129. def getVersion():
  130.     """Get a version string"""
  131.     try:
  132.         from cc import VERSION as magnet_version
  133.     except ImportError:
  134.         return '(unknown)'
  135.     else:
  136.         return magnet_version
  137.  
  138. def sendErrorReport(text):
  139.     """Attempt to send a bug report with contents text"""
  140.     import smtplib
  141.     from email.MIMEText import MIMEText
  142.  
  143.     boot_version = '(unknown)'
  144.     if VERSION is not None:
  145.         try:
  146.             boot_version = '.'.join(map(str, VERSION))
  147.         except ValueError:
  148.             pass
  149.  
  150.     magnet = getMagnet()
  151.     to_addr = getattr(magnet, 'BUG_ADDRESS', DEFAULT_BUG_ADDRESS)
  152.     app_name = getattr(magnet, 'APP_NAME', DEFAULT_APP_NAME)
  153.     mail_server = getattr(magnet, 'MAIL_SERVER', DEFAULT_MAIL_SERVER)
  154.  
  155.     addr_line = '%s Bugs <%s>' % (app_name, to_addr)
  156.  
  157.     msg = MIMEText(text)
  158.     msg['Subject'] = '%s Crash' % (app_name,)
  159.     msg['To'] = addr_line
  160.     msg['From'] = addr_line
  161.     msg['X-Magnet-Version'] = getVersion()
  162.     msg['X-Magnet-Boot-Version'] = boot_version
  163.  
  164.     server = smtplib.SMTP(mail_server, smtplib.SMTP_PORT)
  165.     server.sendmail(to_addr, [to_addr], msg.as_string())
  166.     server.quit()
  167.  
  168. def showError():
  169.     """Show the user an error dialog
  170.  
  171.     that asks him if he wants to submit a bug report. Returns True if
  172.     he does."""
  173.     
  174.     # It is necessary to have a QApplication instance to display a message box
  175.     import qt
  176.     if qt.QApplication.startingUp() or qt.QApplication.closingDown():
  177.         unused_app = qt.QApplication([])
  178.  
  179.     magnet = getMagnet()
  180.  
  181.     app_name = getattr(magnet, 'APP_NAME', DEFAULT_APP_NAME)
  182.     message_text = getattr(magnet, 'BUG_REPORT_TEXT', DEFAULT_BUG_REPORT_TEXT)
  183.  
  184.     choice = qt.QMessageBox.critical(
  185.         None,                          # parent
  186.         '%s Error' % (app_name,),      # title
  187.         message_text,                  # text
  188.         "Send Report",                 # first button (choice 0) text
  189.         "Don't Send Report")           # second button (choice 1) text
  190.  
  191.     return (choice == 0)
  192.  
  193. def setup():
  194.     if os.name == 'nt':
  195.         # set up win32xxx paths
  196.         sys.path.append(site.here + r'\site-packages\win32') 
  197.         sys.path.append(site.here + r'\site-packages\win32\lib')    
  198.  
  199. def getPath():
  200.     if os.name == 'nt':
  201.         from win32com.shell import shell, shellcon
  202.         app_data_path = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0)
  203.         return os.path.join(app_data_path.encode(), DEFAULT_APP_NAME)        
  204.     elif os.name == 'posix':
  205.         home_dir = os.path.abspath(os.environ.get('HOME', os.getcwd()))
  206.         return os.path.join(home_dir, ('.'+DEFAULT_APP_NAME).lower())
  207.     else:
  208.         raise RuntimeError, 'os not supported: %s' % (os.name,)
  209.     
  210. def getSearchFile():
  211.     d = os.path.split(__file__)[0]
  212.     return os.path.join(d, BAKED_SEARCH_FILENAME)
  213.  
  214. def getZipFiles(locations):
  215.     return findZip(*locations)
  216.  
  217. def bootstrap(options):
  218.     setup()
  219.     path = getPath()
  220.     boot_path = os.path.abspath(os.path.dirname(__file__))
  221.     locations = [os.path.join(path, 'lib'), boot_path]
  222.     if options.path is not None and sys.platform != 'darwin':
  223.         path = options.path
  224.         locations.insert(0, os.path.join(path, 'lib'))
  225.  
  226.     print >>sys.stderr, 'Looking in', locations
  227.  
  228.     tried = []
  229.     sys.path.insert(0, None)
  230.     for zip_name in getZipFiles(locations):
  231.         tried.append(zip_name)
  232.         zip_file = zipfile.ZipFile(zip_name)
  233.  
  234.         sys.path[0] = zip_name
  235.  
  236.         try:
  237.             import cc.magnet
  238.             cc.magnet.BOOT_VERSION = VERSION
  239.             cc.magnet.path = path
  240.             cc.magnet.sendErrorReport = sendErrorReport
  241.             cc.magnet.search_file = getSearchFile()
  242.             import cc.magnet.main
  243.         except ImportError:
  244.             sys.excepthook(*sys.exc_info())
  245.             print >>sys.stderr, 'File %s failed' % (zip_name,)
  246.             continue
  247.         else:
  248.             break
  249.     else:
  250.         raise RuntimeError('Unable to find a good zip file after trying %r' % (
  251.             tried,))
  252.  
  253.     print >>sys.stderr, 'Running from %s' % (zip_name,)
  254.     
  255. def run(options):
  256.     try:
  257.         bootstrap(options)
  258.         from cc.magnet import main
  259.         main.start(log_level=options.log_level, console=options.console,
  260.                    data_path=options.data_path)
  261.     except KeyboardInterrupt:
  262.         pass
  263.     except:
  264.         try:
  265.             import logging
  266.             info = logging.Formatter().formatException(sys.exc_info())
  267.         except:
  268.             info = "Unhandled exception -- getting traceback failed"
  269.  
  270.         print >>sys.stderr, info
  271.         try:
  272.             send_report = showError()
  273.         except:
  274.             send_report = True
  275.  
  276.         if send_report:
  277.             sendErrorReport(info)
  278.     
  279. if __name__ == '__main__':
  280.     import optparse
  281.  
  282.     parser = optparse.OptionParser()
  283.  
  284.     parser.add_option('-p', '--path', dest='path',
  285.                       help='Directory containing code files')
  286.  
  287.     parser.add_option('-d', '--data-path', dest='data_path',
  288.                       help='Directory containing data files')
  289.    
  290.     parser.add_option("-v", "--verbose", dest="console",
  291.                       action="store_true", default=False,
  292.                       help="Print log messages to the console")
  293.     
  294.     parser.add_option("-l", "--log-level",
  295.                       dest="log_level", default="info",
  296.                       choices=["debug",
  297.                                "info",
  298.                                "warning",
  299.                                "error",
  300.                                "critical"],
  301.                       help="Set logging level")
  302.  
  303.     (options, args) = parser.parse_args()
  304.  
  305.     if len(args):
  306.         parser.error('This command takes no arguments')
  307.  
  308.     run(options)
  309.