PyXR

c:\projects\bitpim\src \ common.py



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com>
0004 ###
0005 ### This program is free software; you can redistribute it and/or modify
0006 ### it under the terms of the BitPim license as detailed in the LICENSE file.
0007 ###
0008 ### $Id: common.py 4381 2007-08-29 00:19:51Z djpham $
0009 
0010 
0011 # Documentation
0012 """Various classes and functions that are used by GUI and command line versions of BitPim"""
0013 
0014 from __future__ import with_statement
0015 from contextlib import contextmanager
0016 
0017 # standard modules
0018 import string
0019 import cStringIO
0020 import StringIO
0021 import sys
0022 import traceback
0023 import tempfile
0024 import random
0025 import os
0026 import imp
0027 import unicodedata
0028 
0029 class FeatureNotAvailable(Exception):
0030      """The device doesn't support the feature"""
0031      def __init__(self, device, message="The device doesn't support the feature"):
0032           Exception.__init__(self, "%s: %s" % (device, message))
0033           self.device=device
0034           self.message=message
0035 
0036 class IntegrityCheckFailed(Exception):
0037      def __init__(self, device, message):
0038           Exception.__init__(self, "%s: %s" % (device, message))
0039           self.device=device
0040           self.message=message
0041 
0042 class PhoneBookBusyException(Exception):
0043      "The phonebook is busy on the phone"
0044      pass
0045                   
0046 class HelperBinaryNotFound(Exception):
0047      def __init__(self, basename, fullname, paths):
0048           Exception.__init__(self, "Helper binary %s not found.  It should be in one of %s" % (fullname, ", ".join(paths)))
0049           self.basename=basename
0050           self.fullname=fullname
0051           self.paths=paths
0052 
0053 class CommandExecutionFailed(Exception):
0054      def __init__(self, retcode, args, logstr=None):
0055           Exception.__init__(self, "Command execution failed with code %d: %s" % (retcode, " ".join(args)))
0056           self.retcode=retcode
0057           self.args=args
0058           self.logstr=logstr
0059           
0060 class ConversionNotSupported(Exception):
0061      def __init__(self, msg):
0062           Exception.__init__(self, msg)
0063           self.msg=msg
0064 
0065 class InSafeModeException(Exception):
0066      def __init__(self):
0067           Exception.__init__(self, "BitPim is in safe mode - this operation has been blocked")
0068 
0069 # generic comms exception and then various specialisations
0070 class CommsException(Exception):
0071      """Generic commmunications exception"""
0072      def __init__(self, message, device="<>"):
0073         Exception.__init__(self, "%s: %s" % (device, message))
0074         self.device=device
0075         self.message=message
0076 
0077 class CommsNeedConfiguring(CommsException):
0078      """The communication settings need to be configured"""
0079      pass
0080 
0081 class CommsDeviceNeedsAttention(CommsException):
0082      """The communication port or device attached to it needs some
0083      manual intervention"""
0084      pass
0085 
0086 class CommsDataCorruption(CommsException):
0087      """There was some form of data corruption"""
0088      pass
0089 
0090 class CommsTimeout(CommsException):
0091     """Timeout while reading or writing the commport"""
0092     pass
0093 
0094 class CommsOpenFailure(CommsException):
0095     """Failed to open the communications port/device"""
0096     pass
0097 
0098 class CommsWrongPort(CommsException):
0099      """The wrong port has been selected, typically the modem port on an LG composite device"""
0100      pass
0101 
0102 class AutoPortsFailure(CommsException):
0103      """Failed to auto detect a useful port"""
0104      def __init__(self, portstried):
0105           self.device="auto"
0106           self.message="Failed to auto-detect the port to use.  "
0107           if portstried is not None and len(portstried):
0108                self.message+="I tried "+", ".join(portstried)
0109           else:
0110                self.message+="I couldn't detect any candidate ports"
0111           CommsException.__init__(self, self.message, self.device)
0112 
0113 class PhoneStringDecodeException(Exception):
0114     def __init__(self, string=None, codec=None):
0115         self.string=string
0116         self.codec=codec
0117         Exception.__init__(self, "Unable to decode <%s> using codec <%s>" % (`string`, codec))
0118 
0119 class PhoneStringEncodeException(Exception):
0120     def __init__(self, string=None, codec=None):
0121         self.string=string
0122         self.codec=codec
0123         Exception.__init__(self, "Unable to encode <%s> using codec <%s>" % (`string`, codec))
0124 
0125 def datatohexstring(data):
0126     """Returns a pretty printed hexdump of the data
0127 
0128     @rtype: string"""
0129     res=cStringIO.StringIO()
0130     lchar=""
0131     lhex="00000000 "
0132     for count in range(0, len(data)):
0133         b=ord(data[count])
0134         lhex=lhex+"%02x " % (b,)
0135         if b>=32 and string.printable.find(chr(b))>=0:
0136             lchar=lchar+chr(b)
0137         else:
0138             lchar=lchar+'.'
0139 
0140         if (count+1)%16==0:
0141             res.write(lhex+"    "+lchar+"\n")
0142             lhex="%08x " % (count+1,)
0143             lchar=""
0144     if len(data):
0145         while (count+1)%16!=0:
0146             count=count+1
0147             lhex=lhex+"   "
0148         res.write(lhex+"    "+lchar+"\n")
0149     return res.getvalue()
0150 
0151 def hexify(data):
0152      "Turns binary data into a hex string (like the output of MD5/SHA hexdigest)"
0153      return "".join(["%02x" % (ord(x),) for x in data])
0154 
0155 def prettyprintdict(dictionary, indent=0):
0156      """Returns a pretty printed version of the dictionary
0157 
0158      The elements are sorted into alphabetical order, and printed
0159      one per line.  Dictionaries within the values are also pretty
0160      printed suitably indented.
0161 
0162      @rtype: string"""
0163 
0164      res=cStringIO.StringIO()
0165 
0166      # the indent string
0167      istr="  "
0168 
0169      # opening brace
0170      res.write("%s{\n" % (istr*indent,))
0171      indent+=1
0172 
0173      # sort the keys
0174      keys=dictionary.keys()
0175      keys.sort()
0176 
0177      # print each key
0178      for k in keys:
0179           v=dictionary[k]
0180           # is it a dict
0181           if isinstance(v, dict):
0182                res.write("%s%s:\n%s\n" % (istr*indent, `k`, prettyprintdict(v, indent+1)))
0183           else:
0184                # is it a list of dicts?
0185                if isinstance(v, list):
0186                     dicts=0
0187                     for item in v:
0188                          if isinstance(item, dict):
0189                               dicts+=1
0190                     if dicts and dicts==len(v):
0191                          res.write("%s%s:\n%s[\n" % (istr*indent,`k`,istr*(indent+1)))
0192                          for item in v:
0193                               res.write(prettyprintdict(item, indent+2))
0194                          res.write("%s],\n" % (istr*(indent+1)))
0195                          continue
0196                res.write("%s%s: %s,\n" % (istr*indent, `k`, `v`))
0197 
0198      # closing brace
0199      indent-=1
0200      if indent>0:
0201           comma=","
0202      else:
0203           comma=""
0204      res.write("%s}%s\n" % (istr*indent,comma))
0205 
0206      return res.getvalue()
0207 
0208      
0209 class exceptionwrap:
0210      """A debugging assist class that helps in tracking down functions returning exceptions"""
0211      def __init__(self, callable):
0212           self.callable=callable
0213           
0214      def __call__(self, *args, **kwargs):
0215           try:
0216 
0217                return self.callable(*args, **kwargs)
0218           except:
0219                print "in exception wrapped call", `self.callable`
0220                print formatexception()
0221                raise
0222 
0223 def unicode_execfile(filename, dict1=0, dict2=0):
0224      # this version allows the path portion of the filename to 
0225      # contain non-acsii characters, the filename itself cannot contain
0226      # ascii characters, execfile does not work if the filename contains
0227      # non-ascii characters
0228      curdir=os.getcwdu()
0229      filepath, file=os.path.split(filename)
0230      os.chdir(filepath)
0231      if dict1==0:
0232         execfile(file)
0233      elif dict2==0:
0234         execfile(file, dict1)
0235      else:
0236         execfile(file, dict1, dict2)
0237      os.chdir(curdir)
0238 
0239 def readversionedindexfile(filename, dict, versionhandlerfunc, currentversion):
0240      assert currentversion>0
0241      try:
0242          execfile(filename, dict, dict)
0243      except UnicodeError:
0244          unicode_execfile(filename, dict, dict)
0245      if not dict.has_key('FILEVERSION'):
0246           version=0
0247      else:
0248           version=dict['FILEVERSION']
0249           del dict['FILEVERSION']
0250      if version<currentversion:
0251           versionhandlerfunc(dict, version)
0252 
0253 def writeversionindexfile(filename, dict, currentversion):
0254      assert currentversion>0
0255      with file(filename, 'w') as f:
0256           for key in dict:
0257                v=dict[key]
0258                if isinstance(v, type({})):
0259                     f.write("result['%s']=%s\n" % (key, prettyprintdict(dict[key])))
0260                else:
0261                     f.write("result['%s']=%s\n" % (key, `v`))
0262           f.write("FILEVERSION=%d\n" % (currentversion,))
0263 
0264 def formatexceptioneh(*excinfo):
0265      print formatexception(excinfo)
0266 
0267 def formatexception(excinfo=None, lastframes=8):
0268      """Pretty print exception, including local variable information.
0269 
0270      See Python Cookbook, recipe 14.4.
0271 
0272      @param excinfo: tuple of information returned from sys.exc_info when
0273                the exception occurred.  If you don't supply this then
0274                information about the current exception being handled
0275                is used
0276      @param lastframes: local variables are shown for these number of
0277                   frames
0278      @return: A pretty printed string
0279                """
0280      if excinfo is None:
0281           excinfo=sys.exc_info()
0282 
0283      s=StringIO.StringIO()
0284      traceback.print_exception(*excinfo, **{'file': s})
0285      tb=excinfo[2]
0286 
0287      stack=[]
0288 
0289      while tb:
0290           stack.append(tb.tb_frame)
0291           tb=tb.tb_next
0292           
0293      if len(stack)>lastframes:
0294           stack=stack[-lastframes:]
0295      print >>s, "\nVariables by last %d frames, innermost last" % (lastframes,)
0296      for frame in stack:
0297           print >>s, ""
0298           print >>s, "Frame %s in %s at line %s" % (frame.f_code.co_name,
0299                                                     frame.f_code.co_filename,
0300                                                     frame.f_lineno)
0301           for key,value in frame.f_locals.items():
0302                # filter out modules
0303                if type(value)==type(sys):
0304                     continue
0305                print >>s,"%15s = " % (key,),
0306                try:
0307                     if type(value)==type({}):
0308                          kk=value.keys()
0309                          kk.sort()
0310                          print >>s, "Keys",kk
0311                          print >>s, "%15s   " % ("",) ,
0312                     print >>s,`value`[:80]
0313                except:
0314                     print >>s,"(Exception occurred printing value)"
0315      return s.getvalue()
0316                     
0317 def gettempfilename(extension=None):
0318     "Returns a filename to be used for a temporary file"
0319     # safest Python 2.3 method
0320     if extension:
0321         x=tempfile.NamedTemporaryFile(suffix="."+extension)
0322     else:
0323         x=tempfile.NamedTemporaryFile()
0324     n=x.name
0325     x.close()
0326     del x
0327     return n
0328 
0329 def getfullname(name):
0330      """Returns the object corresponding to name.
0331      Imports will be done as necessary to resolve
0332      every part of the name"""
0333      mods=name.split('.')
0334      dict={}
0335      for i in range(len(mods)):
0336           # import everything
0337           try:
0338                exec "import %s" % (".".join(mods[:i])) in dict, dict
0339           except:
0340                pass
0341      # ok, we should have the name now
0342      return eval(name, dict, dict)
0343 
0344 def list_union(*lists):
0345      res=[]
0346      for l in lists:
0347           for item in l:
0348                if item not in res:
0349                     res.append(item)
0350      return res
0351 
0352 def getkv(dict, key, updates=None):
0353      "Gets a key and value from a dict, returning as a dict potentially applying updates"
0354      d={key: dict[key].copy()} 
0355      if updates: 
0356           d[key].update(updates)
0357      return d 
0358 
0359 # some obfuscation
0360 # obfuscate pwd
0361 _magic=[ord(x) for x in "IamAhaPp12&s]"]
0362 
0363 # the oldies are the best
0364 def obfus_encode(str):
0365     res=[]
0366     for i in range(len(str)):
0367         res.append(ord(str[i])^_magic[i%len(_magic)])
0368     return "".join(["%02x" % (x,) for x in res])
0369 
0370 def obfus_decode(str):
0371     res=[]
0372     for i in range(0, len(str), 2):
0373         res.append(int(str[i:i+2], 16))
0374     x=""
0375     for i in range(len(res)):
0376         x+=chr(res[i]^_magic[i%len(_magic)])
0377     return x
0378 
0379 # unicode byte order markers to codecs
0380 # this really should be part of the standard library
0381 
0382 # we try to import the encoding first.  that has the side
0383 # effect of ensuring that the freeze tools pick up the
0384 # right bits of code as well
0385 import codecs
0386 
0387 _boms=[]
0388 # 64 bit 
0389 try:
0390      import encodings.utf_64
0391      _boms.append( (codecs.BOM64_BE, "utf_64") )
0392      _boms.append( (codecs.BOM64_LE, "utf_64") )
0393 except:  pass
0394 
0395 # 32 bit
0396 try:
0397      import encodings.utf_32
0398      _boms.append( (codecs.BOM_UTF32, "utf_32") )
0399      _boms.append( (codecs.BOM_UTF32_BE, "utf_32") )
0400      _boms.append( (codecs.BOM_UTF32_LE, "utf_32") )
0401 except:  pass
0402 
0403 # 16 bit
0404 try:
0405      import encodings.utf_16
0406      _boms.append( (codecs.BOM_UTF16, "utf_16") )
0407      _boms.append( (codecs.BOM_UTF16_BE, "utf_16_be") )
0408      _boms.append( (codecs.BOM_UTF16_LE, "utf_16_le") )
0409 except:  pass
0410 
0411 # 8 bit
0412 try:
0413      import encodings.utf_8
0414      _boms.append( (codecs.BOM_UTF8, "utf_8") )
0415 except: pass
0416 
0417 # Work arounds for the idiots at Apple that decided not to even
0418 # include BOMs
0419 _boms.append( ("\0B\0E\0G\0I\0N\0:\0V\0C\0A\0R\0D", "utf_16_be") )
0420 _boms.append( ("B\0E\0G\0I\0N\0:\0V\0C\0A\0R\0D\0", "utf_16_le") )
0421 
0422 
0423 # NB: the 32 bit and 64 bit versions have the BOM constants defined in Py 2.3
0424 # but no corresponding encodings module.  They are here for completeness.
0425 # The order of above also matters since the first ones have longer
0426 # boms than the latter ones, and we need to be unambiguous
0427 
0428 _maxbomlen=max([len(bom) for bom,codec in _boms])
0429 
0430 def opentextfile(name):
0431      """This function detects unicode byte order markers and if present
0432      uses the codecs module instead to open the file instead with
0433      appropriate unicode decoding, else returns the file using standard
0434      open function"""
0435      with file(name, 'rb') as f:
0436           start=f.read(_maxbomlen)
0437           for bom,codec in _boms:
0438                if start.startswith(bom):
0439                     # some codecs don't do readline, so we have to vector via stringio
0440                     # many postings also claim that the BOM is returned as the first
0441                     # character but that hasn't been the case in my testing
0442                     return StringIO.StringIO(codecs.open(name, "r", codec).read())
0443      return file(name, "rtU")
0444 
0445 
0446 # don't you just love i18n
0447 
0448 # the following function is actually defined in guihelper and
0449 # inserted into this module.  the intention is to ensure this
0450 # module doesn't have to import wx.  The guihelper version
0451 # checks if wx is in unicode mode
0452 
0453 #def strorunicode(s):
0454 #     if isinstance(s, unicode): return s
0455 #     return str(s)
0456 
0457 def forceascii(s):
0458      return get_ascii_string(s, 'replace')
0459 
0460 # The CRC and escaping mechanisms are the same as used in PPP, HDLC and
0461 # various other standards.
0462 
0463 pppterminator="\x7e"
0464 
0465 def pppescape(data):
0466     return data.replace("\x7d", "\x7d\x5d") \
0467            .replace("\x7e", "\x7d\x5e")
0468 
0469 def pppunescape(d):
0470     if d.find("\x7d")<0: return d
0471     res=list(d)
0472     try:
0473         start=0
0474         while True:
0475             p=res.index("\x7d", start)
0476             res[p:p+2]=chr(ord(res[p+1])^0x20)
0477             start=p+1
0478     except ValueError:
0479         return "".join(res)
0480 
0481 # See http://www.repairfaq.org/filipg/LINK/F_crc_v35.html for more info
0482 # on CRC
0483 _crctable=(
0484     0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,   # 0 - 7
0485     0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,   # 8 - 15
0486     0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,   # 16 - 23
0487     0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,   # 24 - 31
0488     0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,   # 32 - 39
0489     0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,   # 40 - 47
0490     0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,   # 48 - 55
0491     0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,   # 56 - 63
0492     0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,   # 64 - 71
0493     0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,   # 72 - 79
0494     0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,   # 80 - 87
0495     0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,   # 88 - 95
0496     0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,   # 96 - 103
0497     0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,   # 104 - 111
0498     0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,   # 112 - 119
0499     0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,   # 120 - 127
0500     0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,   # 128 - 135
0501     0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,   # 136 - 143
0502     0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,   # 144 - 151
0503     0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,   # 152 - 159
0504     0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,   # 160 - 167
0505     0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,   # 168 - 175
0506     0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,   # 176 - 183
0507     0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,   # 184 - 191
0508     0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,   # 192 - 199
0509     0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,   # 200 - 207
0510     0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,   # 208 - 215
0511     0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,   # 216 - 223
0512     0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,   # 224 - 231
0513     0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,   # 232 - 239
0514     0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,   # 240 - 247
0515     0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,   # 248 - 255
0516     )
0517 
0518 def crc(data, initial=0xffff):
0519     "CRC calculation - returns 16 bit integer"
0520     res=initial
0521     for byte in data:
0522         curres=res
0523         res=res>>8  # zero extended
0524         val=(ord(byte)^curres) & 0xff
0525         val=_crctable[val]
0526         res=res^val
0527 
0528     res=(~res)&0xffff
0529     return res
0530 
0531 def crcs(data, initial=0xffff):
0532     "CRC calculation - returns 2 byte string LSB"
0533     r=crc(data, initial)
0534     return "%c%c" % ( r& 0xff, (r>>8)&0xff)
0535 
0536 
0537 
0538 ###
0539 ### Pathname processing (independent of host OS)
0540 ###
0541 
0542 def basename(name):
0543     if name.rfind('\\')>=0 or name.rfind('/')>=0:
0544         pos=max(name.rfind('\\'), name.rfind('/'))
0545         name=name[pos+1:]
0546     return name
0547 
0548 def dirname(name):
0549     if name.rfind('\\')>=0 or name.rfind('/')>=0:
0550         pos=max(name.rfind('\\'), name.rfind('/'))
0551         name=name[:pos]
0552     return name
0553 
0554 def stripext(name):
0555     if name.rfind('.')>=0:
0556         name=name[:name.rfind('.')]
0557     return name
0558 
0559 def getext(name):
0560     if name.rfind('.')>=0:
0561         return name[name.rfind('.')+1:]
0562     return ''
0563 
0564 #-------------------------------------------------------------------------------
0565 # number <-> string conversion routines
0566 
0567 def LSBUint16(v):
0568     if len(v)<2:
0569         return None
0570     return ord(v[0])+(ord(v[1])<<8)
0571 
0572 def LSBUint32(v):
0573     if len(v)<4:
0574         return None
0575     return ord(v[0])+(ord(v[1])<<8)+(ord(v[2])<<16)+(ord(v[3])<<24)
0576     
0577 def MSBUint16(v):
0578     if len(v)<2:
0579         return None
0580     return ord(v[1])+(ord(v[0])<<8)
0581 
0582 def MSBUint32(v):
0583     if len(v)<4:
0584         return None
0585     return ord(v[3])+(ord(v[2])<<8)+(ord(v[1])<<16)+(ord(v[0])<<24)
0586 
0587 def LSBstr16(v):
0588     return chr(v&0xff)+chr((v>>8)&0xff)
0589 
0590 def LSBstr32(v):
0591     return chr(v&0xff)+chr((v>>8)&0xff)+chr((v>>16)&0xff)+chr((v>>24)&0xff)
0592 
0593 def MSBstr16(v):
0594     return chr((v>>8)&0xff)+chr(v&0xff)
0595 
0596 def MSBstr32(v):
0597     return chr((v>>24)&0xff)+chr((v>>16)&0xff)+chr((v>>8)&0xff)+chr(v&0xff)
0598 
0599 
0600 ###
0601 ### Binary handling
0602 ###
0603 
0604 nibbles=("0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111")
0605 
0606 def tobinary(v):
0607      return nibbles[v>>4]+nibbles[v&0x0f]
0608 
0609 def frombinary(v):
0610      res=0
0611      for i in v:
0612           res*=2
0613           res+=bool(i=="1")
0614      return res
0615 
0616 # This could be done more efficiently through clever combinations of
0617 # masks and shifts but it is harder to write and debug (I tried :-)
0618 # and isn't any more effective on the short strings we work with
0619 def decodecharacterbits(bytes, bitsperchar, charconv=chr, terminator=None):
0620      """Decodes the characters out of a string of bytes where each
0621      character takes a fixed number of bits (eg 7)
0622 
0623      @param bytes: atring containing the raw bytes
0624      @param bitsperchar: number of bits making up each character
0625      @param charconv: the function used to convert the integer value
0626                  into a character
0627      @param terminator: if this character is seen then it and the remaining bits are ignored"""
0628      bits="".join([tobinary(ord(c)) for c in bytes])
0629      value=[]
0630      while len(bits):
0631             c=charconv(frombinary(bits[:bitsperchar]))
0632             if c==terminator:
0633                 break
0634             value.append(c)
0635             bits=bits[bitsperchar:]
0636      return "".join(value)
0637   
0638 
0639 def encode_with_degrade(uni_string, codec='ascii', error_handling='strict'):
0640 
0641     temp_str=''
0642     if uni_string is None:
0643         return temp_str
0644     try:
0645         temp_str=uni_string.encode(codec)
0646         return temp_str
0647     except UnicodeError:
0648         pass # we'll have another go with dumbing down    
0649     
0650     temp_str=''
0651     if codec=='ascii':
0652         temp_str=get_ascii_string(uni_string, error_handling)
0653     else:
0654         # go through all characters dumbing down the ones we cannot convert
0655         # we could do all at once but we only want to degrade characters the encoding
0656         # does not support
0657         for i in range(len(uni_string)):
0658             try:
0659                 temp_char=uni_string[i].encode(codec)
0660             except UnicodeError:
0661                 temp_char=get_ascii_string(uni_string[i], error_handling)
0662             temp_str+=temp_char
0663     return temp_str
0664         
0665 def get_ascii_string(uni_string, error_handling='strict'):
0666     "converts any type into an ascii byte string, for unicode string it will degrade if possible"
0667     if uni_string is None:
0668         return None
0669     if not isinstance(uni_string, (str, unicode)):
0670         # convert numbers into strings
0671         return str(uni_string)
0672     if not isinstance(uni_string, unicode):
0673         # we are already a byte string!
0674         return uni_string
0675 
0676     # normalise the string
0677     kd_str=unicodedata.normalize('NFKD', uni_string)
0678     stripped_str=u''
0679     # go through removing all accoutrements from the character and convert some other characters
0680     # defined by unicode standard ver. 3.2.0
0681     replace_chars= {
0682         u"\N{COMBINING GRAVE ACCENT}": u"",
0683         u"\N{COMBINING ACUTE ACCENT}": u"",
0684         u"\N{COMBINING CIRCUMFLEX ACCENT}": u"",
0685         u"\N{COMBINING TILDE}": u"",
0686         u"\N{COMBINING MACRON}": u"",
0687         u"\N{COMBINING OVERLINE}": u"",
0688         u"\N{COMBINING BREVE}": u"",
0689         u"\N{COMBINING DOT ABOVE}": u"",
0690         u"\N{COMBINING DIAERESIS}": u"",
0691         u"\N{COMBINING HOOK ABOVE}": u"",
0692         u"\N{COMBINING RING ABOVE}": u"",
0693         u"\N{COMBINING DOUBLE ACUTE ACCENT}": u"",
0694         u"\N{COMBINING CARON}": u"",
0695         u"\N{COMBINING VERTICAL LINE ABOVE}": u"",
0696         u"\N{COMBINING DOUBLE VERTICAL LINE ABOVE}": u"",
0697         u"\N{COMBINING DOUBLE GRAVE ACCENT}": u"",
0698         u"\N{COMBINING CANDRABINDU}": u"",
0699         u"\N{COMBINING INVERTED BREVE}": u"",
0700         u"\N{COMBINING TURNED COMMA ABOVE}": u"",
0701         u"\N{COMBINING COMMA ABOVE}": u"",
0702         u"\N{COMBINING REVERSED COMMA ABOVE}": u"",
0703         u"\N{COMBINING COMMA ABOVE RIGHT}": u"",
0704         u"\N{COMBINING GRAVE ACCENT BELOW}": u"",
0705         u"\N{COMBINING ACUTE ACCENT BELOW}": u"",
0706         u"\N{COMBINING LEFT TACK BELOW}": u"",
0707         u"\N{COMBINING RIGHT TACK BELOW}": u"",
0708         u"\N{COMBINING LEFT ANGLE ABOVE}": u"",
0709         u"\N{COMBINING HORN}": u"",
0710         u"\N{COMBINING LEFT HALF RING BELOW}": u"",
0711         u"\N{COMBINING UP TACK BELOW}": u"",
0712         u"\N{COMBINING DOWN TACK BELOW}": u"",
0713         u"\N{COMBINING PLUS SIGN BELOW}": u"",
0714         u"\N{COMBINING MINUS SIGN BELOW}": u"",
0715         u"\N{COMBINING PALATALIZED HOOK BELOW}": u"",
0716         u"\N{COMBINING RETROFLEX HOOK BELOW}": u"",
0717         u"\N{COMBINING DOT BELOW}": u"",
0718         u"\N{COMBINING DIAERESIS BELOW}": u"",
0719         u"\N{COMBINING RING BELOW}": u"",
0720         u"\N{COMBINING COMMA BELOW}": u"",
0721         u"\N{COMBINING CEDILLA}": u"",
0722         u"\N{COMBINING OGONEK}": u"",
0723         u"\N{COMBINING VERTICAL LINE BELOW}": u"",
0724         u"\N{COMBINING BRIDGE BELOW}": u"",
0725         u"\N{COMBINING INVERTED DOUBLE ARCH BELOW}": u"",
0726         u"\N{COMBINING CARON BELOW}": u"",
0727         u"\N{COMBINING CIRCUMFLEX ACCENT BELOW}": u"",
0728         u"\N{COMBINING BREVE BELOW}": u"",
0729         u"\N{COMBINING INVERTED BREVE BELOW}": u"",
0730         u"\N{COMBINING TILDE BELOW}": u"",
0731         u"\N{COMBINING MACRON BELOW}": u"",
0732         u"\N{COMBINING LOW LINE}": u"",
0733         u"\N{COMBINING DOUBLE LOW LINE}": u"",
0734         u"\N{COMBINING TILDE OVERLAY}": u"",
0735         u"\N{COMBINING SHORT STROKE OVERLAY}": u"",
0736         u"\N{COMBINING LONG STROKE OVERLAY}": u"",
0737         u"\N{COMBINING SHORT SOLIDUS OVERLAY}": u"",
0738         u"\N{COMBINING LONG SOLIDUS OVERLAY}": u"",
0739         u"\N{COMBINING RIGHT HALF RING BELOW}": u"",
0740         u"\N{COMBINING INVERTED BRIDGE BELOW}": u"",
0741         u"\N{COMBINING SQUARE BELOW}": u"",
0742         u"\N{COMBINING SEAGULL BELOW}": u"",
0743         u"\N{COMBINING X ABOVE}": u"",
0744         u"\N{COMBINING VERTICAL TILDE}": u"",
0745         u"\N{COMBINING DOUBLE OVERLINE}": u"",
0746         u"\N{COMBINING GRAVE TONE MARK}": u"",
0747         u"\N{COMBINING ACUTE TONE MARK}": u"",
0748         u"\N{COMBINING GREEK PERISPOMENI}": u"",
0749         u"\N{COMBINING GREEK KORONIS}": u"",
0750         u"\N{COMBINING GREEK DIALYTIKA TONOS}": u"",
0751         u"\N{COMBINING GREEK YPOGEGRAMMENI}": u"",
0752         u"\N{COMBINING BRIDGE ABOVE}": u"",
0753         u"\N{COMBINING EQUALS SIGN BELOW}": u"",
0754         u"\N{COMBINING DOUBLE VERTICAL LINE BELOW}": u"",
0755         u"\N{COMBINING LEFT ANGLE BELOW}": u"",
0756         u"\N{COMBINING NOT TILDE ABOVE}": u"",
0757         u"\N{COMBINING HOMOTHETIC ABOVE}": u"",
0758         u"\N{COMBINING ALMOST EQUAL TO ABOVE}": u"",
0759         u"\N{COMBINING LEFT RIGHT ARROW BELOW}": u"",
0760         u"\N{COMBINING UPWARDS ARROW BELOW}": u"",
0761         u"\N{COMBINING GRAPHEME JOINER}": u"",
0762         u'\N{BROKEN BAR}': '|',
0763         u'\N{CENT SIGN}': 'c',
0764         u'\N{COPYRIGHT SIGN}': '(C)',
0765         u'\N{DIVISION SIGN}': '/',
0766         u'\N{INVERTED EXCLAMATION MARK}': '!',
0767         u'\N{INVERTED QUESTION MARK}': '?',
0768         u'\N{LATIN CAPITAL LETTER AE}': 'Ae',
0769         u'\N{LATIN CAPITAL LETTER ETH}': 'Th',
0770         u'\N{LATIN CAPITAL LETTER THORN}': 'Th',
0771         u'\N{LATIN SMALL LETTER AE}': 'ae',
0772         u'\N{LATIN SMALL LETTER ETH}': 'th',
0773         u'\N{LATIN SMALL LETTER SHARP S}': 'ss',
0774         u'\N{LATIN SMALL LETTER THORN}': 'th',
0775         u'\N{LEFT-POINTING DOUBLE ANGLE QUOTATION MARK}': '<<',
0776         u'\N{MIDDLE DOT}': '*',
0777         u'\N{MULTIPLICATION SIGN}': '*',
0778         u'\N{PLUS-MINUS SIGN}': '+/-',
0779         u'\N{REGISTERED SIGN}': '(R)',
0780         u'\N{RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK}': '>>',
0781         u'\N{SOFT HYPHEN}': '-',
0782         u'\N{VULGAR FRACTION ONE HALF}': '1/2',
0783         u'\N{VULGAR FRACTION ONE QUARTER}': '1/4',
0784         u'\N{VULGAR FRACTION THREE QUARTERS}': '3/4',
0785         }
0786     for i in range(len(kd_str)):
0787         if kd_str[i] in replace_chars:
0788             stripped_str+=replace_chars[kd_str[i]]
0789         else:
0790             stripped_str+=kd_str[i]
0791     # now we have removed all the stuff we can try converting
0792     # error handling is managed by the calling routine
0793     temp_str=stripped_str.encode('ascii', error_handling)
0794     return temp_str
0795 
0796 ###
0797 ### Cache information against a file
0798 ###
0799 
0800 def statinfo(filename):
0801     """Returns a simplified version of os.stat results that can be used to tell if a file
0802     has changed.  The normal structure returned also has things like last access time
0803     which should not be used to tell if a file has changed."""
0804     try:
0805         s=os.stat(filename)
0806         return (s.st_mode, s.st_ino, s.st_dev, s.st_uid, s.st_gid, s.st_size, s.st_mtime,
0807                 s.st_ctime)
0808     except:
0809         return None
0810 
0811 class FileCache:
0812 
0813      def __init__(self, lowwater=100, hiwater=140):
0814           self.items={}
0815           self.hiwater=hiwater
0816           self.lowwater=lowwater
0817 
0818      def get(self, filename):
0819           v=self.items.get(filename, None)
0820           if v is None: return None
0821           si,value=v
0822           # check freshness
0823           if si==statinfo(filename):
0824                return value
0825           return None
0826 
0827      def set(self, filename, value):
0828           # we deliberately return value make this easy to use in return statement
0829           if len(self.items)>=self.hiwater:
0830                while len(self.items)>self.lowwater:
0831                     del self.items[random.choice(self.items.keys())]
0832           # yes there is a race condition with statinfo changing after program
0833           # has calculated value but before calling this function
0834           self.items[filename]=statinfo(filename),value
0835           return value
0836 
0837 # wrapper for __import__
0838 def importas(name):
0839     return __import__(name, globals(), locals(),
0840                       [name.split('.')[-1]])
0841 
0842 # http://starship.python.net/crew/theller/moin.cgi/HowToDetermineIfRunningFromExe
0843 def main_is_frozen():
0844     """ Return T if running from an exe, F otherwise
0845     """
0846     return (hasattr(sys, "frozen")  # new py2exe
0847            or hasattr(sys, "importers") # old py2exe
0848            or imp.is_frozen("__main__") # tools/freeze
0849             or os.path.isfile(sys.path[0])) # cxfreeze
0850 
0851 def get_main_dir():
0852     """Return the absoluate path of the main BitPim dir
0853     """
0854     if main_is_frozen():
0855         if sys.platform!='win32':
0856              p=sys.path[0]
0857              if os.path.isfile(p):
0858                   p=os.path.dirname(p)
0859              return p
0860         # windows running from exe, return as is
0861         return os.path.abspath(os.path.dirname(sys.executable))
0862     # running from src, up one
0863     return os.path.split(os.path.abspath(os.path.dirname(sys.argv[0])))[0]
0864 
0865 if sys.platform=='win32':
0866     # From Tim Golden's Win32 How Do I ...?
0867     from win32api import GetFileVersionInfo, LOWORD, HIWORD
0868     def get_version_number (filename):
0869         """Return the version of a Windows DLL or EXE file
0870         """
0871         try:
0872             info = GetFileVersionInfo (filename, "\\")
0873             ms = info['FileVersionMS']
0874             ls = info['FileVersionLS']
0875             return HIWORD (ms), LOWORD (ms), HIWORD (ls), LOWORD (ls)
0876         except:
0877             return None
0878 
0879 # simple generator that generates temp file name and del it afterward
0880 @contextmanager
0881 def usetempfile(ext):
0882     _name=gettempfilename(ext)
0883     try:
0884         yield _name
0885     finally:
0886           try:
0887               os.remove(_name)
0888           except OSError:
0889                # ignore if failed to del temp file
0890                pass
0891 

Generated by PyXR 0.9.4