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, 0xb2b3, # 192 - 199 509 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, # 200 - 207 510 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, # 208 - 215 511 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, # 216 - 223 512 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, # 224 - 231 513 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, # 232 - 239 514 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, # 240 - 247 515 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, # 248 - 255 516 )
517 518 -def crc(data, initial=0xffff):
519 "CRC calculation - returns 16 bit integer" 520 res=initial 521 for byte in data: 522 curres=res 523 res=res>>8 # zero extended 524 val=(ord(byte)^curres) & 0xff 525 val=_crctable[val] 526 res=res^val 527 528 res=(~res)&0xffff 529 return res
530
531 -def crcs(data, initial=0xffff):
532 "CRC calculation - returns 2 byte string LSB" 533 r=crc(data, initial) 534 return "%c%c" % ( r& 0xff, (r>>8)&0xff)
535
536 537 538 ### 539 ### Pathname processing (independent of host OS) 540 ### 541 542 -def basename(name):
543 if name.rfind('\\')>=0 or name.rfind('/')>=0: 544 pos=max(name.rfind('\\'), name.rfind('/')) 545 name=name[pos+1:] 546 return name
547
548 -def dirname(name):
549 if name.rfind('\\')>=0 or name.rfind('/')>=0: 550 pos=max(name.rfind('\\'), name.rfind('/')) 551 name=name[:pos] 552 return name
553
554 -def stripext(name):
555 if name.rfind('.')>=0: 556 name=name[:name.rfind('.')] 557 return name
558
559 -def getext(name):
560 if name.rfind('.')>=0: 561 return name[name.rfind('.')+1:] 562 return ''
563
564 #------------------------------------------------------------------------------- 565 # number <-> string conversion routines 566 567 -def LSBUint16(v):
568 if len(v)<2: 569 return None 570 return ord(v[0])+(ord(v[1])<<8)
571
572 -def LSBUint32(v):
573 if len(v)<4: 574 return None 575 return ord(v[0])+(ord(v[1])<<8)+(ord(v[2])<<16)+(ord(v[3])<<24)
576
577 -def MSBUint16(v):
578 if len(v)<2: 579 return None 580 return ord(v[1])+(ord(v[0])<<8)
581
582 -def MSBUint32(v):
583 if len(v)<4: 584 return None 585 return ord(v[3])+(ord(v[2])<<8)+(ord(v[1])<<16)+(ord(v[0])<<24)
586
587 -def LSBstr16(v):
588 return chr(v&0xff)+chr((v>>8)&0xff)
589
590 -def LSBstr32(v):
591 return chr(v&0xff)+chr((v>>8)&0xff)+chr((v>>16)&0xff)+chr((v>>24)&0xff)
592
593 -def MSBstr16(v):
594 return chr((v>>8)&0xff)+chr(v&0xff)
595
596 -def MSBstr32(v):
597 return chr((v>>24)&0xff)+chr((v>>16)&0xff)+chr((v>>8)&0xff)+chr(v&0xff)
598 599 600 ### 601 ### Binary handling 602 ### 603 604 nibbles=("0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111")
605 606 -def tobinary(v):
607 return nibbles[v>>4]+nibbles[v&0x0f]
608
609 -def frombinary(v):
610 res=0 611 for i in v: 612 res*=2 613 res+=bool(i=="1") 614 return res
615
616 # This could be done more efficiently through clever combinations of 617 # masks and shifts but it is harder to write and debug (I tried :-) 618 # and isn't any more effective on the short strings we work with 619 -def decodecharacterbits(bytes, bitsperchar, charconv=chr, terminator=None):
620 """Decodes the characters out of a string of bytes where each 621 character takes a fixed number of bits (eg 7) 622 623 @param bytes: atring containing the raw bytes 624 @param bitsperchar: number of bits making up each character 625 @param charconv: the function used to convert the integer value 626 into a character 627 @param terminator: if this character is seen then it and the remaining bits are ignored""" 628 bits="".join([tobinary(ord(c)) for c in bytes]) 629 value=[] 630 while len(bits): 631 c=charconv(frombinary(bits[:bitsperchar])) 632 if c==terminator: 633 break 634 value.append(c) 635 bits=bits[bitsperchar:] 636 return "".join(value)
637
638 639 -def encode_with_degrade(uni_string, codec='ascii', error_handling='strict'):
640 641 temp_str='' 642 if uni_string is None: 643 return temp_str 644 try: 645 temp_str=uni_string.encode(codec) 646 return temp_str 647 except UnicodeError: 648 pass # we'll have another go with dumbing down 649 650 temp_str='' 651 if codec=='ascii': 652 temp_str=get_ascii_string(uni_string, error_handling) 653 else: 654 # go through all characters dumbing down the ones we cannot convert 655 # we could do all at once but we only want to degrade characters the encoding 656 # does not support 657 for i in range(len(uni_string)): 658 try: 659 temp_char=uni_string[i].encode(codec) 660 except UnicodeError: 661 temp_char=get_ascii_string(uni_string[i], error_handling) 662 temp_str+=temp_char 663 return temp_str
664
665 -def get_ascii_string(uni_string, error_handling='strict'):
666 "converts any type into an ascii byte string, for unicode string it will degrade if possible" 667 if uni_string is None: 668 return None 669 if not isinstance(uni_string, (str, unicode)): 670 # convert numbers into strings 671 return str(uni_string) 672 if not isinstance(uni_string, unicode): 673 # we are already a byte string! 674 return uni_string 675 676 # normalise the string 677 kd_str=unicodedata.normalize('NFKD', uni_string) 678 stripped_str=u'' 679 # go through removing all accoutrements from the character and convert some other characters 680 # defined by unicode standard ver. 3.2.0 681 replace_chars= { 682 u"\N{COMBINING GRAVE ACCENT}": u"", 683 u"\N{COMBINING ACUTE ACCENT}": u"", 684 u"\N{COMBINING CIRCUMFLEX ACCENT}": u"", 685 u"\N{COMBINING TILDE}": u"", 686 u"\N{COMBINING MACRON}": u"", 687 u"\N{COMBINING OVERLINE}": u"", 688 u"\N{COMBINING BREVE}": u"", 689 u"\N{COMBINING DOT ABOVE}": u"", 690 u"\N{COMBINING DIAERESIS}": u"", 691 u"\N{COMBINING HOOK ABOVE}": u"", 692 u"\N{COMBINING RING ABOVE}": u"", 693 u"\N{COMBINING DOUBLE ACUTE ACCENT}": u"", 694 u"\N{COMBINING CARON}": u"", 695 u"\N{COMBINING VERTICAL LINE ABOVE}": u"", 696 u"\N{COMBINING DOUBLE VERTICAL LINE ABOVE}": u"", 697 u"\N{COMBINING DOUBLE GRAVE ACCENT}": u"", 698 u"\N{COMBINING CANDRABINDU}": u"", 699 u"\N{COMBINING INVERTED BREVE}": u"", 700 u"\N{COMBINING TURNED COMMA ABOVE}": u"", 701 u"\N{COMBINING COMMA ABOVE}": u"", 702 u"\N{COMBINING REVERSED COMMA ABOVE}": u"", 703 u"\N{COMBINING COMMA ABOVE RIGHT}": u"", 704 u"\N{COMBINING GRAVE ACCENT BELOW}": u"", 705 u"\N{COMBINING ACUTE ACCENT BELOW}": u"", 706 u"\N{COMBINING LEFT TACK BELOW}": u"", 707 u"\N{COMBINING RIGHT TACK BELOW}": u"", 708 u"\N{COMBINING LEFT ANGLE ABOVE}": u"", 709 u"\N{COMBINING HORN}": u"", 710 u"\N{COMBINING LEFT HALF RING BELOW}": u"", 711 u"\N{COMBINING UP TACK BELOW}": u"", 712 u"\N{COMBINING DOWN TACK BELOW}": u"", 713 u"\N{COMBINING PLUS SIGN BELOW}": u"", 714 u"\N{COMBINING MINUS SIGN BELOW}": u"", 715 u"\N{COMBINING PALATALIZED HOOK BELOW}": u"", 716 u"\N{COMBINING RETROFLEX HOOK BELOW}": u"", 717 u"\N{COMBINING DOT BELOW}": u"", 718 u"\N{COMBINING DIAERESIS BELOW}": u"", 719 u"\N{COMBINING RING BELOW}": u"", 720 u"\N{COMBINING COMMA BELOW}": u"", 721 u"\N{COMBINING CEDILLA}": u"", 722 u"\N{COMBINING OGONEK}": u"", 723 u"\N{COMBINING VERTICAL LINE BELOW}": u"", 724 u"\N{COMBINING BRIDGE BELOW}": u"", 725 u"\N{COMBINING INVERTED DOUBLE ARCH BELOW}": u"", 726 u"\N{COMBINING CARON BELOW}": u"", 727 u"\N{COMBINING CIRCUMFLEX ACCENT BELOW}": u"", 728 u"\N{COMBINING BREVE BELOW}": u"", 729 u"\N{COMBINING INVERTED BREVE BELOW}": u"", 730 u"\N{COMBINING TILDE BELOW}": u"", 731 u"\N{COMBINING MACRON BELOW}": u"", 732 u"\N{COMBINING LOW LINE}": u"", 733 u"\N{COMBINING DOUBLE LOW LINE}": u"", 734 u"\N{COMBINING TILDE OVERLAY}": u"", 735 u"\N{COMBINING SHORT STROKE OVERLAY}": u"", 736 u"\N{COMBINING LONG STROKE OVERLAY}": u"", 737 u"\N{COMBINING SHORT SOLIDUS OVERLAY}": u"", 738 u"\N{COMBINING LONG SOLIDUS OVERLAY}": u"", 739 u"\N{COMBINING RIGHT HALF RING BELOW}": u"", 740 u"\N{COMBINING INVERTED BRIDGE BELOW}": u"", 741 u"\N{COMBINING SQUARE BELOW}": u"", 742 u"\N{COMBINING SEAGULL BELOW}": u"", 743 u"\N{COMBINING X ABOVE}": u"", 744 u"\N{COMBINING VERTICAL TILDE}": u"", 745 u"\N{COMBINING DOUBLE OVERLINE}": u"", 746 u"\N{COMBINING GRAVE TONE MARK}": u"", 747 u"\N{COMBINING ACUTE TONE MARK}": u"", 748 u"\N{COMBINING GREEK PERISPOMENI}": u"", 749 u"\N{COMBINING GREEK KORONIS}": u"", 750 u"\N{COMBINING GREEK DIALYTIKA TONOS}": u"", 751 u"\N{COMBINING GREEK YPOGEGRAMMENI}": u"", 752 u"\N{COMBINING BRIDGE ABOVE}": u"", 753 u"\N{COMBINING EQUALS SIGN BELOW}": u"", 754 u"\N{COMBINING DOUBLE VERTICAL LINE BELOW}": u"", 755 u"\N{COMBINING LEFT ANGLE BELOW}": u"", 756 u"\N{COMBINING NOT TILDE ABOVE}": u"", 757 u"\N{COMBINING HOMOTHETIC ABOVE}": u"", 758 u"\N{COMBINING ALMOST EQUAL TO ABOVE}": u"", 759 u"\N{COMBINING LEFT RIGHT ARROW BELOW}": u"", 760 u"\N{COMBINING UPWARDS ARROW BELOW}": u"", 761 u"\N{COMBINING GRAPHEME JOINER}": u"", 762 u'\N{BROKEN BAR}': '|', 763 u'\N{CENT SIGN}': 'c', 764 u'\N{COPYRIGHT SIGN}': '(C)', 765 u'\N{DIVISION SIGN}': '/', 766 u'\N{INVERTED EXCLAMATION MARK}': '!', 767 u'\N{INVERTED QUESTION MARK}': '?', 768 u'\N{LATIN CAPITAL LETTER AE}': 'Ae', 769 u'\N{LATIN CAPITAL LETTER ETH}': 'Th', 770 u'\N{LATIN CAPITAL LETTER THORN}': 'Th', 771 u'\N{LATIN SMALL LETTER AE}': 'ae', 772 u'\N{LATIN SMALL LETTER ETH}': 'th', 773 u'\N{LATIN SMALL LETTER SHARP S}': 'ss', 774 u'\N{LATIN SMALL LETTER THORN}': 'th', 775 u'\N{LEFT-POINTING DOUBLE ANGLE QUOTATION MARK}': '<<', 776 u'\N{MIDDLE DOT}': '*', 777 u'\N{MULTIPLICATION SIGN}': '*', 778 u'\N{PLUS-MINUS SIGN}': '+/-', 779 u'\N{REGISTERED SIGN}': '(R)', 780 u'\N{RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK}': '>>', 781 u'\N{SOFT HYPHEN}': '-', 782 u'\N{VULGAR FRACTION ONE HALF}': '1/2', 783 u'\N{VULGAR FRACTION ONE QUARTER}': '1/4', 784 u'\N{VULGAR FRACTION THREE QUARTERS}': '3/4', 785 } 786 for i in range(len(kd_str)): 787 if kd_str[i] in replace_chars: 788 stripped_str+=replace_chars[kd_str[i]] 789 else: 790 stripped_str+=kd_str[i] 791 # now we have removed all the stuff we can try converting 792 # error handling is managed by the calling routine 793 temp_str=stripped_str.encode('ascii', error_handling) 794 return temp_str
795
796 ### 797 ### Cache information against a file 798 ### 799 800 -def statinfo(filename):
801 """Returns a simplified version of os.stat results that can be used to tell if a file 802 has changed. The normal structure returned also has things like last access time 803 which should not be used to tell if a file has changed.""" 804 try: 805 s=os.stat(filename) 806 return (s.st_mode, s.st_ino, s.st_dev, s.st_uid, s.st_gid, s.st_size, s.st_mtime, 807 s.st_ctime) 808 except: 809 return None
810
811 -class FileCache:
812
813 - def __init__(self, lowwater=100, hiwater=140):
814 self.items={} 815 self.hiwater=hiwater 816 self.lowwater=lowwater
817
818 - def get(self, filename):
819 v=self.items.get(filename, None) 820 if v is None: return None 821 si,value=v 822 # check freshness 823 if si==statinfo(filename): 824 return value 825 return None
826
827 - def set(self, filename, value):
828 # we deliberately return value make this easy to use in return statement 829 if len(self.items)>=self.hiwater: 830 while len(self.items)>self.lowwater: 831 del self.items[random.choice(self.items.keys())] 832 # yes there is a race condition with statinfo changing after program 833 # has calculated value but before calling this function 834 self.items[filename]=statinfo(filename),value 835 return value
836
837 # wrapper for __import__ 838 -def importas(name):
839 return __import__(name, globals(), locals(), 840 [name.split('.')[-1]])
841
842 # http://starship.python.net/crew/theller/moin.cgi/HowToDetermineIfRunningFromExe 843 -def main_is_frozen():
844 """ Return T if running from an exe, F otherwise 845 """ 846 return (hasattr(sys, "frozen") # new py2exe 847 or hasattr(sys, "importers") # old py2exe 848 or imp.is_frozen("__main__") # tools/freeze 849 or os.path.isfile(sys.path[0])) # cxfreeze
850
851 -def get_main_dir():
852 """Return the absoluate path of the main BitPim dir 853 """ 854 if main_is_frozen(): 855 if sys.platform!='win32': 856 p=sys.path[0] 857 if os.path.isfile(p): 858 p=os.path.dirname(p) 859 return p 860 # windows running from exe, return as is 861 return os.path.abspath(os.path.dirname(sys.executable)) 862 # running from src, up one 863 return os.path.split(os.path.abspath(os.path.dirname(sys.argv[0])))[0]
864 865 if sys.platform=='win32':
866 # From Tim Golden's Win32 How Do I ...? 867 from win32api import GetFileVersionInfo, LOWORD, HIWORD 868 - def get_version_number (filename):
869 """Return the version of a Windows DLL or EXE file 870 """ 871 try: 872 info = GetFileVersionInfo (filename, "\\") 873 ms = info['FileVersionMS'] 874 ls = info['FileVersionLS'] 875 return HIWORD (ms), LOWORD (ms), HIWORD (ls), LOWORD (ls) 876 except: 877 return None
878
879 # simple generator that generates temp file name and del it afterward 880 @contextmanager 881 -def usetempfile(ext):
882 _name=gettempfilename(ext) 883 try: 884 yield _name 885 finally: 886 try: 887 os.remove(_name) 888 except OSError: 889 # ignore if failed to del temp file 890 pass
891