Module common
[hide private]
[frames] | no frames]

Source Code for Module common

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 
  4  ### 
  5  ### This program is free software; you can redistribute it and/or modify 
  6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  7  ### 
  8  ### $Id: common.py 4381 2007-08-29 00:19:51Z djpham $ 
  9   
 10   
 11  # Documentation 
 12  """Various classes and functions that are used by GUI and command line versions of BitPim""" 
 13   
 14  from __future__ import with_statement 
 15  from contextlib import contextmanager 
 16   
 17  # standard modules 
 18  import string 
 19  import cStringIO 
 20  import StringIO 
 21  import sys 
 22  import traceback 
 23  import tempfile 
 24  import random 
 25  import os 
 26  import imp 
 27  import unicodedata 
28 29 -class FeatureNotAvailable(Exception):
30 """The device doesn't support the feature"""
31 - def __init__(self, device, message="The device doesn't support the feature"):
32 Exception.__init__(self, "%s: %s" % (device, message)) 33 self.device=device 34 self.message=message
35
36 -class IntegrityCheckFailed(Exception):
37 - def __init__(self, device, message):
38 Exception.__init__(self, "%s: %s" % (device, message)) 39 self.device=device 40 self.message=message
41
42 -class PhoneBookBusyException(Exception):
43 "The phonebook is busy on the phone" 44 pass
45
46 -class HelperBinaryNotFound(Exception):
47 - def __init__(self, basename, fullname, paths):
48 Exception.__init__(self, "Helper binary %s not found. It should be in one of %s" % (fullname, ", ".join(paths))) 49 self.basename=basename 50 self.fullname=fullname 51 self.paths=paths
52
53 -class CommandExecutionFailed(Exception):
54 - def __init__(self, retcode, args, logstr=None):
55 Exception.__init__(self, "Command execution failed with code %d: %s" % (retcode, " ".join(args))) 56 self.retcode=retcode 57 self.args=args 58 self.logstr=logstr
59
60 -class ConversionNotSupported(Exception):
61 - def __init__(self, msg):
62 Exception.__init__(self, msg) 63 self.msg=msg
64
65 -class InSafeModeException(Exception):
66 - def __init__(self):
67 Exception.__init__(self, "BitPim is in safe mode - this operation has been blocked")
68
69 # generic comms exception and then various specialisations 70 -class CommsException(Exception):
71 """Generic commmunications exception"""
72 - def __init__(self, message, device="<>"):
73 Exception.__init__(self, "%s: %s" % (device, message)) 74 self.device=device 75 self.message=message
76
77 -class CommsNeedConfiguring(CommsException):
78 """The communication settings need to be configured""" 79 pass
80
81 -class CommsDeviceNeedsAttention(CommsException):
82 """The communication port or device attached to it needs some 83 manual intervention""" 84 pass
85
86 -class CommsDataCorruption(CommsException):
87 """There was some form of data corruption""" 88 pass
89
90 -class CommsTimeout(CommsException):
91 """Timeout while reading or writing the commport""" 92 pass
93
94 -class CommsOpenFailure(CommsException):
95 """Failed to open the communications port/device""" 96 pass
97
98 -class CommsWrongPort(CommsException):
99 """The wrong port has been selected, typically the modem port on an LG composite device""" 100 pass
101
102 -class AutoPortsFailure(CommsException):
103 """Failed to auto detect a useful port"""
104 - def __init__(self, portstried):
105 self.device="auto" 106 self.message="Failed to auto-detect the port to use. " 107 if portstried is not None and len(portstried): 108 self.message+="I tried "+", ".join(portstried) 109 else: 110 self.message+="I couldn't detect any candidate ports" 111 CommsException.__init__(self, self.message, self.device)
112
113 -class PhoneStringDecodeException(Exception):
114 - def __init__(self, string=None, codec=None):
115 self.string=string 116 self.codec=codec 117 Exception.__init__(self, "Unable to decode <%s> using codec <%s>" % (`string`, codec))
118
119 -class PhoneStringEncodeException(Exception):
120 - def __init__(self, string=None, codec=None):
121 self.string=string 122 self.codec=codec 123 Exception.__init__(self, "Unable to encode <%s> using codec <%s>" % (`string`, codec))
124
125 -def datatohexstring(data):
126 """Returns a pretty printed hexdump of the data 127 128 @rtype: string""" 129 res=cStringIO.StringIO() 130 lchar="" 131 lhex="00000000 " 132 for count in range(0, len(data)): 133 b=ord(data[count]) 134 lhex=lhex+"%02x " % (b,) 135 if b>=32 and string.printable.find(chr(b))>=0: 136 lchar=lchar+chr(b) 137 else: 138 lchar=lchar+'.' 139 140 if (count+1)%16==0: 141 res.write(lhex+" "+lchar+"\n") 142 lhex="%08x " % (count+1,) 143 lchar="" 144 if len(data): 145 while (count+1)%16!=0: 146 count=count+1 147 lhex=lhex+" " 148 res.write(lhex+" "+lchar+"\n") 149 return res.getvalue()
150
151 -def hexify(data):
152 "Turns binary data into a hex string (like the output of MD5/SHA hexdigest)" 153 return "".join(["%02x" % (ord(x),) for x in data])
154
155 -def prettyprintdict(dictionary, indent=0):
156 """Returns a pretty printed version of the dictionary 157 158 The elements are sorted into alphabetical order, and printed 159 one per line. Dictionaries within the values are also pretty 160 printed suitably indented. 161 162 @rtype: string""" 163 164 res=cStringIO.StringIO() 165 166 # the indent string 167 istr=" " 168 169 # opening brace 170 res.write("%s{\n" % (istr*indent,)) 171 indent+=1 172 173 # sort the keys 174 keys=dictionary.keys() 175 keys.sort() 176 177 # print each key 178 for k in keys: 179 v=dictionary[k] 180 # is it a dict 181 if isinstance(v, dict): 182 res.write("%s%s:\n%s\n" % (istr*indent, `k`, prettyprintdict(v, indent+1))) 183 else: 184 # is it a list of dicts? 185 if isinstance(v, list): 186 dicts=0 187 for item in v: 188 if isinstance(item, dict): 189 dicts+=1 190 if dicts and dicts==len(v): 191 res.write("%s%s:\n%s[\n" % (istr*indent,`k`,istr*(indent+1))) 192 for item in v: 193 res.write(prettyprintdict(item, indent+2)) 194 res.write("%s],\n" % (istr*(indent+1))) 195 continue 196 res.write("%s%s: %s,\n" % (istr*indent, `k`, `v`)) 197 198 # closing brace 199 indent-=1 200 if indent>0: 201 comma="," 202 else: 203 comma="" 204 res.write("%s}%s\n" % (istr*indent,comma)) 205 206 return res.getvalue()
207
208 209 -class exceptionwrap:
210 """A debugging assist class that helps in tracking down functions returning exceptions"""
211 - def __init__(self, callable):
212 self.callable=callable
213
214 - def __call__(self, *args, **kwargs):
215 try: 216 217 return self.callable(*args, **kwargs) 218 except: 219 print "in exception wrapped call", `self.callable` 220 print formatexception() 221 raise
222
223 -def unicode_execfile(filename, dict1=0, dict2=0):
224 # this version allows the path portion of the filename to 225 # contain non-acsii characters, the filename itself cannot contain 226 # ascii characters, execfile does not work if the filename contains 227 # non-ascii characters 228 curdir=os.getcwdu() 229 filepath, file=os.path.split(filename) 230 os.chdir(filepath) 231 if dict1==0: 232 execfile(file) 233 elif dict2==0: 234 execfile(file, dict1) 235 else: 236 execfile(file, dict1, dict2) 237 os.chdir(curdir)
238
239 -def readversionedindexfile(filename, dict, versionhandlerfunc, currentversion):
240 assert currentversion>0 241 try: 242 execfile(filename, dict, dict) 243 except UnicodeError: 244 unicode_execfile(filename, dict, dict) 245 if not dict.has_key('FILEVERSION'): 246 version=0 247 else: 248 version=dict['FILEVERSION'] 249 del dict['FILEVERSION'] 250 if version<currentversion: 251 versionhandlerfunc(dict, version)
252
253 -def writeversionindexfile(filename, dict, currentversion):
254 assert currentversion>0 255 with file(filename, 'w') as f: 256 for key in dict: 257 v=dict[key] 258 if isinstance(v, type({})): 259 f.write("result['%s']=%s\n" % (key, prettyprintdict(dict[key]))) 260 else: 261 f.write("result['%s']=%s\n" % (key, `v`)) 262 f.write("FILEVERSION=%d\n" % (currentversion,))
263
264 -def formatexceptioneh(*excinfo):
265 print formatexception(excinfo)
266
267 -def formatexception(excinfo=None, lastframes=8):
268 """Pretty print exception, including local variable information. 269 270 See Python Cookbook, recipe 14.4. 271 272 @param excinfo: tuple of information returned from sys.exc_info when 273 the exception occurred. If you don't supply this then 274 information about the current exception being handled 275 is used 276 @param lastframes: local variables are shown for these number of 277 frames 278 @return: A pretty printed string 279 """ 280 if excinfo is None: 281 excinfo=sys.exc_info() 282 283 s=StringIO.StringIO() 284 traceback.print_exception(*excinfo, **{'file': s}) 285 tb=excinfo[2] 286 287 stack=[] 288 289 while tb: 290 stack.append(tb.tb_frame) 291 tb=tb.tb_next 292 293 if len(stack)>lastframes: 294 stack=stack[-lastframes:] 295 print >>s, "\nVariables by last %d frames, innermost last" % (lastframes,) 296 for frame in stack: 297 print >>s, "" 298 print >>s, "Frame %s in %s at line %s" % (frame.f_code.co_name, 299 frame.f_code.co_filename, 300 frame.f_lineno) 301 for key,value in frame.f_locals.items(): 302 # filter out modules 303 if type(value)==type(sys): 304 continue 305 print >>s,"%15s = " % (key,), 306 try: 307 if type(value)==type({}): 308 kk=value.keys() 309 kk.sort() 310 print >>s, "Keys",kk 311 print >>s, "%15s " % ("",) , 312 print >>s,`value`[:80] 313 except: 314 print >>s,"(Exception occurred printing value)" 315 return s.getvalue()
316
317 -def gettempfilename(extension=None):
318 "Returns a filename to be used for a temporary file" 319 # safest Python 2.3 method 320 if extension: 321 x=tempfile.NamedTemporaryFile(suffix="."+extension) 322 else: 323 x=tempfile.NamedTemporaryFile() 324 n=x.name 325 x.close() 326 del x 327 return n
328
329 -def getfullname(name):
330 """Returns the object corresponding to name. 331 Imports will be done as necessary to resolve 332 every part of the name""" 333 mods=name.split('.') 334 dict={} 335 for i in range(len(mods)): 336 # import everything 337 try: 338 exec "import %s" % (".".join(mods[:i])) in dict, dict 339 except: 340 pass 341 # ok, we should have the name now 342 return eval(name, dict, dict)
343
344 -def list_union(*lists):
345 res=[] 346 for l in lists: 347 for item in l: 348 if item not in res: 349 res.append(item) 350 return res
351
352 -def getkv(dict, key, updates=None):
353 "Gets a key and value from a dict, returning as a dict potentially applying updates" 354 d={key: dict[key].copy()} 355 if updates: 356 d[key].update(updates) 357 return d
358 359 # some obfuscation 360 # obfuscate pwd 361 _magic=[ord(x) for x in "IamAhaPp12&s]"]
362 363 # the oldies are the best 364 -def obfus_encode(str):
365 res=[] 366 for i in range(len(str)): 367 res.append(ord(str[i])^_magic[i%len(_magic)]) 368 return "".join(["%02x" % (x,) for x in res])
369
370 -def obfus_decode(str):
371 res=[] 372 for i in range(0, len(str), 2): 373 res.append(int(str[i:i+2], 16)) 374 x="" 375 for i in range(len(res)): 376 x+=chr(res[i]^_magic[i%len(_magic)]) 377 return x
378 379 # unicode byte order markers to codecs 380 # this really should be part of the standard library 381 382 # we try to import the encoding first. that has the side 383 # effect of ensuring that the freeze tools pick up the 384 # right bits of code as well 385 import codecs 386 387 _boms=[] 388 # 64 bit 389 try: 390 import encodings.utf_64 391 _boms.append( (codecs.BOM64_BE, "utf_64") ) 392 _boms.append( (codecs.BOM64_LE, "utf_64") ) 393 except: pass 394 395 # 32 bit 396 try: 397 import encodings.utf_32 398 _boms.append( (codecs.BOM_UTF32, "utf_32") ) 399 _boms.append( (codecs.BOM_UTF32_BE, "utf_32") ) 400 _boms.append( (codecs.BOM_UTF32_LE, "utf_32") ) 401 except: pass 402 403 # 16 bit 404 try: 405 import encodings.utf_16 406 _boms.append( (codecs.BOM_UTF16, "utf_16") ) 407 _boms.append( (codecs.BOM_UTF16_BE, "utf_16_be") ) 408 _boms.append( (codecs.BOM_UTF16_LE, "utf_16_le") ) 409 except: pass 410 411 # 8 bit 412 try: 413 import encodings.utf_8 414 _boms.append( (codecs.BOM_UTF8, "utf_8") ) 415 except: pass 416 417 # Work arounds for the idiots at Apple that decided not to even 418 # include BOMs 419 _boms.append( ("\0B\0E\0G\0I\0N\0:\0V\0C\0A\0R\0D", "utf_16_be") ) 420 _boms.append( ("B\0E\0G\0I\0N\0:\0V\0C\0A\0R\0D\0", "utf_16_le") ) 421 422 423 # NB: the 32 bit and 64 bit versions have the BOM constants defined in Py 2.3 424 # but no corresponding encodings module. They are here for completeness. 425 # The order of above also matters since the first ones have longer 426 # boms than the latter ones, and we need to be unambiguous 427 428 _maxbomlen=max([len(bom) for bom,codec in _boms])
429 430 -def opentextfile(name):
431 """This function detects unicode byte order markers and if present 432 uses the codecs module instead to open the file instead with 433 appropriate unicode decoding, else returns the file using standard 434 open function""" 435 with file(name, 'rb') as f: 436 start=f.read(_maxbomlen) 437 for bom,codec in _boms: 438 if start.startswith(bom): 439 # some codecs don't do readline, so we have to vector via stringio 440 # many postings also claim that the BOM is returned as the first 441 # character but that hasn't been the case in my testing 442 return StringIO.StringIO(codecs.open(name, "r", codec).read()) 443 return file(name, "rtU")
444
445 446 # don't you just love i18n 447 448 # the following function is actually defined in guihelper and 449 # inserted into this module. the intention is to ensure this 450 # module doesn't have to import wx. The guihelper version 451 # checks if wx is in unicode mode 452 453 #def strorunicode(s): 454 # if isinstance(s, unicode): return s 455 # return str(s) 456 457 -def forceascii(s):
458 return get_ascii_string(s, 'replace')
459 460 # The CRC and escaping mechanisms are the same as used in PPP, HDLC and 461 # various other standards. 462 463 pppterminator="\x7e"
464 465 -def pppescape(data):
466 return data.replace("\x7d", "\x7d\x5d") \ 467 .replace("\x7e", "\x7d\x5e")
468
469 -def pppunescape(d):
470 if d.find("\x7d")<0: return d 471 res=list(d) 472 try: 473 start=0 474 while True: 475 p=res.index("\x7d", start) 476 res[p:p+2]=chr(ord(res[p+1])^0x20) 477 start=p+1 478 except ValueError: 479 return "".join(res)
480 481 # See http://www.repairfaq.org/filipg/LINK/F_crc_v35.html for more info 482 # on CRC 483 _crctable=( 484 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, # 0 - 7 485 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, # 8 - 15 486 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, # 16 - 23 487 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, # 24 - 31 488 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, # 32 - 39 489 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, # 40 - 47 490 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, # 48 - 55 491 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, # 56 - 63 492 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, # 64 - 71 493 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, # 72 - 79 494 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, # 80 - 87 495 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, # 88 - 95 496 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, # 96 - 103 497 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, # 104 - 111 498 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, # 112 - 119 499 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, # 120 - 127 500 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, # 128 - 135 501 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, # 136 - 143 502 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, # 144 - 151 503 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, # 152 - 159 504 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, # 160 - 167 505 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, # 168 - 175 506 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, # 176 - 183 507 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, # 184 - 191 508 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a,