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