~openerp-groupes/openobject-server/6.0-fix-setup-windows

« back to all changes in this revision

Viewing changes to bin/reportlab/lib/utils.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#Copyright ReportLab Europe Ltd. 2000-2004
 
2
#see license.txt for license details
 
3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/utils.py
 
4
__version__=''' $Id$ '''
 
5
 
 
6
import string, os, sys, imp
 
7
from types import *
 
8
from reportlab.lib.logger import warnOnce
 
9
SeqTypes = (ListType,TupleType)
 
10
if sys.hexversion<0x2030000:
 
11
    True = 1
 
12
    False = 0
 
13
 
 
14
def _findFiles(dirList,ext='.ttf'):
 
15
    from os.path import isfile, isdir, join as path_join
 
16
    from os import listdir
 
17
    ext = ext.lower()
 
18
    R = []
 
19
    A = R.append
 
20
    for D in dirList:
 
21
        if not isdir(D): continue
 
22
        for fn in listdir(D):
 
23
            fn = path_join(D,fn)
 
24
            if isfile(fn) and (not ext or fn.lower().endswith(ext)): A(fn)
 
25
    return R
 
26
 
 
27
try:
 
28
    _UserDict = dict
 
29
except:
 
30
    from UserDict import UserDict as _UserDict
 
31
 
 
32
class CIDict(_UserDict):
 
33
    def __init__(self,*a,**kw):
 
34
        map(self.update, a)
 
35
        self.update(kw)
 
36
 
 
37
    def update(self,D):
 
38
        for k,v in D.items(): self[k] = v
 
39
 
 
40
    def __setitem__(self,k,v):
 
41
        try:
 
42
            k = k.lower()
 
43
        except:
 
44
            pass
 
45
        _UserDict.__setitem__(self,k,v)
 
46
 
 
47
    def __getitem__(self,k):
 
48
        try:
 
49
            k = k.lower()
 
50
        except:
 
51
            pass
 
52
        return _UserDict.__getitem__(self,k)
 
53
 
 
54
    def __delitem__(self,k):
 
55
        try:
 
56
            k = k.lower()
 
57
        except:
 
58
            pass
 
59
        return _UserDict.__delitem__(self,k)
 
60
 
 
61
    def get(self,k,dv=None):
 
62
        try:
 
63
            return self[k]
 
64
        except KeyError:
 
65
            return dv
 
66
 
 
67
    def has_key(self,k):
 
68
        try:
 
69
            self[k]
 
70
            return True
 
71
        except:
 
72
            return False
 
73
 
 
74
    def pop(self,k,*a):
 
75
        try:
 
76
            k = k.lower()
 
77
        except:
 
78
            pass
 
79
        return _UserDict.pop(*((self,k)+a))
 
80
 
 
81
    def setdefault(self,k,*a):
 
82
        try:
 
83
            k = k.lower()
 
84
        except:
 
85
            pass
 
86
        return _UserDict.setdefault(*((self,k)+a))
 
87
 
 
88
if os.name == 'mac':
 
89
    #with the Mac, we need to tag the file in a special
 
90
    #way so the system knows it is a PDF file.
 
91
    #This supplied by Joe Strout
 
92
    import macfs, macostools
 
93
    _KNOWN_MAC_EXT = {
 
94
        'BMP' : ('ogle','BMP '),
 
95
        'EPS' : ('ogle','EPSF'),
 
96
        'EPSF': ('ogle','EPSF'),
 
97
        'GIF' : ('ogle','GIFf'),
 
98
        'JPG' : ('ogle','JPEG'),
 
99
        'JPEG': ('ogle','JPEG'),
 
100
        'PCT' : ('ttxt','PICT'),
 
101
        'PICT': ('ttxt','PICT'),
 
102
        'PNG' : ('ogle','PNGf'),
 
103
        'PPM' : ('ogle','.PPM'),
 
104
        'TIF' : ('ogle','TIFF'),
 
105
        'TIFF': ('ogle','TIFF'),
 
106
        'PDF' : ('CARO','PDF '),
 
107
        'HTML': ('MSIE','TEXT'),
 
108
        }
 
109
    def markfilename(filename,creatorcode=None,filetype=None,ext='PDF'):
 
110
        try:
 
111
            if creatorcode is None or filetype is None and ext is not None:
 
112
                try:
 
113
                    creatorcode, filetype = _KNOWN_MAC_EXT[string.upper(ext)]
 
114
                except:
 
115
                    return
 
116
            macfs.FSSpec(filename).SetCreatorType(creatorcode,filetype)
 
117
            macostools.touched(filename)
 
118
        except:
 
119
            pass
 
120
else:
 
121
    def markfilename(filename,creatorcode=None,filetype=None):
 
122
        pass
 
123
 
 
124
import reportlab
 
125
__RL_DIR=os.path.dirname(reportlab.__file__)    #possibly relative
 
126
_RL_DIR=os.path.isabs(__RL_DIR) and __RL_DIR or os.path.abspath(__RL_DIR)
 
127
del reportlab
 
128
 
 
129
#Attempt to detect if this copy of reportlab is running in a
 
130
#file system (as opposed to mostly running in a zip or McMillan
 
131
#archive or Jar file).  This is used by test cases, so that
 
132
#we can write test cases that don't get activated in a compiled
 
133
try:
 
134
    __file__
 
135
except:
 
136
    __file__ = sys.argv[0]
 
137
import glob, fnmatch
 
138
try:
 
139
    _isFSD = not __loader__
 
140
    _archive = __loader__.archive
 
141
    _archivepfx = _archive + os.sep
 
142
    _archivedir = os.path.dirname(__loader__.archive)
 
143
    _archivedirpfx = _archivedir + os.sep
 
144
    _archivepfxlen = len(_archivepfx)
 
145
    _archivedirpfxlen = len(_archivedirpfx)
 
146
    def __startswith_rl(fn,
 
147
                    _archivepfx=os.path.normcase(_archivepfx),
 
148
                    _archivedirpfx=os.path.normcase(_archivedirpfx),
 
149
                    _archive=os.path.normcase(_archive),
 
150
                    _archivedir=os.path.normcase(_archivedir),
 
151
                    os_path_normpath=os.path.normpath,
 
152
                    os_path_normcase=os.path.normcase,
 
153
                    os_getcwd=os.getcwd,
 
154
                    os_sep=os.sep,
 
155
                    os_sep_len = len(os.sep)):
 
156
        '''if the name starts with a known prefix strip it off'''
 
157
        fn = os_path_normpath(fn.replace('/',os_sep))
 
158
        nfn = os_path_normcase(fn)
 
159
        if nfn in (_archivedir,_archive): return 1,''
 
160
        if nfn.startswith(_archivepfx): return 1,fn[_archivepfxlen:]
 
161
        if nfn.startswith(_archivedirpfx): return 1,fn[_archivedirpfxlen:]
 
162
        cwd = os_path_normcase(os_getcwd())
 
163
        n = len(cwd)
 
164
        if nfn.startswith(cwd):
 
165
            if fn[n:].startswith(os_sep): return 1, fn[n+os_sep_len:]
 
166
            if n==len(fn): return 1,''
 
167
        return not os.path.isabs(fn),fn
 
168
 
 
169
    def _startswith_rl(fn):
 
170
        return __startswith_rl(fn)[1]
 
171
 
 
172
    def rl_glob(pattern,glob=glob.glob,fnmatch=fnmatch.fnmatch, _RL_DIR=_RL_DIR,pjoin=os.path.join):
 
173
        c, pfn = __startswith_rl(pattern)
 
174
        r = glob(pfn)
 
175
        if c or r==[]:
 
176
            r += map(lambda x,D=_archivepfx,pjoin=pjoin: pjoin(_archivepfx,x),filter(lambda x,pfn=pfn,fnmatch=fnmatch: fnmatch(x,pfn),__loader__._files.keys()))
 
177
        return r
 
178
except:
 
179
    _isFSD = os.path.isfile(__file__)   #slight risk of wrong path
 
180
    __loader__ = None
 
181
    def _startswith_rl(fn):
 
182
        return fn
 
183
    def rl_glob(pattern,glob=glob.glob):
 
184
        return glob(pattern)
 
185
del glob, fnmatch
 
186
_isFSSD = _isFSD and os.path.isfile(os.path.splitext(__file__)[0] +'.py')
 
187
 
 
188
def isFileSystemDistro():
 
189
    '''return truth if a file system distribution'''
 
190
    return _isFSD
 
191
 
 
192
def isCompactDistro():
 
193
    '''return truth if not a file system distribution'''
 
194
    return not _isFSD
 
195
 
 
196
def isSourceDistro():
 
197
    '''return truth if a source file system distribution'''
 
198
    return _isFSSD
 
199
 
 
200
try:
 
201
    #raise ImportError
 
202
    ### NOTE!  FP_STR SHOULD PROBABLY ALWAYS DO A PYTHON STR() CONVERSION ON ARGS
 
203
    ### IN CASE THEY ARE "LAZY OBJECTS".  ACCELLERATOR DOESN'T DO THIS (YET)
 
204
    try:
 
205
        from _rl_accel import fp_str                # in case of builtin version
 
206
    except ImportError:
 
207
        from reportlab.lib._rl_accel import fp_str  # specific
 
208
except ImportError:
 
209
    from math import log
 
210
    _log_10 = lambda x,log=log,_log_e_10=log(10.0): log(x)/_log_e_10
 
211
    _fp_fmts = "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f"
 
212
    import re
 
213
    _tz_re = re.compile('0+$')
 
214
    del re
 
215
    def fp_str(*a):
 
216
        if len(a)==1 and type(a[0]) in SeqTypes: a = a[0]
 
217
        s = []
 
218
        A = s.append
 
219
        for i in a:
 
220
            sa =abs(i)
 
221
            if sa<=1e-7: A('0')
 
222
            else:
 
223
                l = sa<=1 and 6 or min(max(0,(6-int(_log_10(sa)))),6)
 
224
                n = _fp_fmts[l]%i
 
225
                if l:
 
226
                    n = _tz_re.sub('',n)
 
227
                    try:
 
228
                        if n[-1]=='.': n = n[:-1]
 
229
                    except:
 
230
                        print i, n
 
231
                        raise
 
232
                A((n[0]!='0' or len(n)==1) and n or n[1:])
 
233
        return string.join(s)
 
234
 
 
235
#hack test for comma users
 
236
if ',' in fp_str(0.25):
 
237
    _FP_STR = fp_str
 
238
    def fp_str(*a):
 
239
        return string.replace(apply(_FP_STR,a),',','.')
 
240
 
 
241
_rl_tempdir=None
 
242
def get_rl_tempdir(*subdirs):
 
243
    global _rl_tempdir
 
244
    if _rl_tempdir is None:
 
245
        import tempfile
 
246
        _rl_tempdir = os.path.join(tempfile.gettempdir(),'ReportLab_tmp%s' % (sys.platform=='unix' and `os.getuid()` or ''))
 
247
    d = _rl_tempdir
 
248
    if subdirs: d = os.path.join(*((d,)+subdirs))
 
249
    try:
 
250
        os.makedirs(d)
 
251
    except:
 
252
        pass
 
253
    return d
 
254
 
 
255
def get_rl_tempfile(fn=None):
 
256
    if not fn:
 
257
        import tempfile
 
258
        fn = tempfile.mktemp()
 
259
    return os.path.join(get_rl_tempdir(),fn)
 
260
 
 
261
def recursiveImport(modulename, baseDir=None, noCWD=0, debug=0):
 
262
    """Dynamically imports possible packagized module, or raises ImportError"""
 
263
    normalize = lambda x: os.path.normcase(os.path.abspath(os.path.normpath(x)))
 
264
    path = map(normalize,sys.path)
 
265
    if baseDir:
 
266
        if type(baseDir) not in SeqTypes:
 
267
            tp = [baseDir]
 
268
        else:
 
269
            tp = filter(None,list(baseDir))
 
270
        for p in tp:
 
271
            p = normalize(p)
 
272
            if p not in path: path.insert(0,p)
 
273
 
 
274
    if noCWD:
 
275
        for p in ('','.',normalize('.')):
 
276
            while p in path:
 
277
                if debug: print 'removed "%s" from path' % p
 
278
                path.remove(p)
 
279
    elif '.' not in path:
 
280
            path.insert(0,'.')
 
281
 
 
282
    if debug:
 
283
        import pprint
 
284
        pp = pprint.pprint
 
285
        print 'path=',
 
286
        pp(path)
 
287
 
 
288
    #make import errors a bit more informative
 
289
    opath = sys.path
 
290
    try:
 
291
        sys.path = path
 
292
        exec 'import %s\nm = %s\n' % (modulename,modulename) in locals()
 
293
        sys.path = opath
 
294
        return m
 
295
    except ImportError:
 
296
        sys.path = opath
 
297
        msg = "recursiveimport(%s,baseDir=%s) failed" % (modulename,baseDir)
 
298
        if baseDir:
 
299
            msg = msg + " under paths '%s'" % `path`
 
300
        raise ImportError, msg
 
301
 
 
302
def recursiveGetAttr(obj, name):
 
303
    "Can call down into e.g. object1.object2[4].attr"
 
304
    return eval(name, obj.__dict__)
 
305
 
 
306
def recursiveSetAttr(obj, name, value):
 
307
    "Can call down into e.g. object1.object2[4].attr = value"
 
308
    #get the thing above last.
 
309
    tokens = string.split(name, '.')
 
310
    if len(tokens) == 1:
 
311
        setattr(obj, name, value)
 
312
    else:
 
313
        most = string.join(tokens[:-1], '.')
 
314
        last = tokens[-1]
 
315
        parent = recursiveGetAttr(obj, most)
 
316
        setattr(parent, last, value)
 
317
 
 
318
def import_zlib():
 
319
    try:
 
320
        import zlib
 
321
    except ImportError:
 
322
        zlib = None
 
323
        from reportlab.rl_config import ZLIB_WARNINGS
 
324
        if ZLIB_WARNINGS: warnOnce('zlib not available')
 
325
    return zlib
 
326
 
 
327
 
 
328
# Image Capability Detection.  Set a flag haveImages
 
329
# to tell us if either PIL or Java imaging libraries present.
 
330
# define PIL_Image as either None, or an alias for the PIL.Image
 
331
# module, as there are 2 ways to import it
 
332
 
 
333
if sys.platform[0:4] == 'java':
 
334
    try:
 
335
        import javax.imageio
 
336
        import java.awt.image
 
337
        haveImages = 1
 
338
    except:
 
339
        haveImages = 0
 
340
else:
 
341
    try:
 
342
        from PIL import Image
 
343
    except ImportError:
 
344
        try:
 
345
            import Image
 
346
        except ImportError:
 
347
            Image = None
 
348
    haveImages = Image is not None
 
349
    if haveImages: del Image
 
350
 
 
351
 
 
352
__StringIO=None
 
353
def getStringIO(buf=None):
 
354
    '''unified StringIO instance interface'''
 
355
    global __StringIO
 
356
    if not __StringIO:
 
357
        try:
 
358
            from cStringIO import StringIO
 
359
        except ImportError:
 
360
            from StringIO import StringIO
 
361
        __StringIO = StringIO
 
362
    return buf is not None and __StringIO(buf) or __StringIO()
 
363
 
 
364
class ArgvDictValue:
 
365
    '''A type to allow clients of getArgvDict to specify a conversion function'''
 
366
    def __init__(self,value,func):
 
367
        self.value = value
 
368
        self.func = func
 
369
 
 
370
def getArgvDict(**kw):
 
371
    ''' Builds a dictionary from its keyword arguments with overrides from sys.argv.
 
372
        Attempts to be smart about conversions, but the value can be an instance
 
373
        of ArgDictValue to allow specifying a conversion function.
 
374
    '''
 
375
    def handleValue(v,av,func):
 
376
        if func:
 
377
            v = func(av)
 
378
        else:
 
379
            t = type(v)
 
380
            if t is StringType:
 
381
                v = av
 
382
            elif t is FloatType:
 
383
                v = float(av)
 
384
            elif t is IntType:
 
385
                v = int(av)
 
386
            elif t is ListType:
 
387
                v = list(eval(av))
 
388
            elif t is TupleType:
 
389
                v = tuple(eval(av))
 
390
            else:
 
391
                raise TypeError, "Can't convert string '%s' to %s" % (av,str(t))
 
392
        return v
 
393
 
 
394
    A = sys.argv[1:]
 
395
    R = {}
 
396
    for k, v in kw.items():
 
397
        if isinstance(v,ArgvDictValue):
 
398
            v, func = v.value, v.func
 
399
        else:
 
400
            func = None
 
401
        handled = 0
 
402
        ke = k+'='
 
403
        for a in A:
 
404
            if string.find(a,ke)==0:
 
405
                av = a[len(ke):]
 
406
                A.remove(a)
 
407
                R[k] = handleValue(v,av,func)
 
408
                handled = 1
 
409
                break
 
410
 
 
411
        if not handled: R[k] = handleValue(v,v,func)
 
412
 
 
413
    return R
 
414
 
 
415
def getHyphenater(hDict=None):
 
416
    try:
 
417
        from reportlab.lib.pyHnj import Hyphen
 
418
        if hDict is None: hDict=os.path.join(os.path.dirname(__file__),'hyphen.mashed')
 
419
        return Hyphen(hDict)
 
420
    except ImportError, errMsg:
 
421
        if str(errMsg)!='No module named pyHnj': raise
 
422
        return None
 
423
 
 
424
def _className(self):
 
425
    '''Return a shortened class name'''
 
426
    try:
 
427
        name = self.__class__.__name__
 
428
        i=string.rfind(name,'.')
 
429
        if i>=0: return name[i+1:]
 
430
        return name
 
431
    except AttributeError:
 
432
        return str(self)
 
433
 
 
434
def open_for_read_by_name(name,mode='b'):
 
435
    if 'r' not in mode: mode = 'r'+mode
 
436
    try:
 
437
        return open(name,mode)
 
438
    except IOError:
 
439
        if _isFSD or __loader__ is None: raise
 
440
        #we have a __loader__, perhaps the filename starts with
 
441
        #the dirname(reportlab.__file__) or is relative
 
442
        name = _startswith_rl(name)
 
443
        s = __loader__.get_data(name)
 
444
        if 'b' not in mode and os.linesep!='\n': s = s.replace(os.linesep,'\n')
 
445
        return getStringIO(s)
 
446
 
 
447
import urllib
 
448
def open_for_read(name,mode='b', urlopen=urllib.urlopen):
 
449
    '''attempt to open a file or URL for reading'''
 
450
    if hasattr(name,'read'): return name
 
451
    try:
 
452
        return open_for_read_by_name(name,mode)
 
453
    except:
 
454
        try:
 
455
            return getStringIO(urlopen(name).read())
 
456
        except:
 
457
            raise IOError('Cannot open resource "%s"' % name)
 
458
del urllib
 
459
 
 
460
def open_and_read(name,mode='b'):
 
461
    return open_for_read(name,mode).read()
 
462
 
 
463
def open_and_readlines(name,mode='t'):
 
464
    return open_and_read(name,mode).split('\n')
 
465
 
 
466
def rl_isfile(fn,os_path_isfile=os.path.isfile):
 
467
    if hasattr(fn,'read'): return True
 
468
    if os_path_isfile(fn): return True
 
469
    if _isFSD or __loader__ is None: return False
 
470
    fn = _startswith_rl(fn)
 
471
    return fn in __loader__._files.keys()
 
472
 
 
473
def rl_isdir(pn,os_path_isdir=os.path.isdir,os_path_normpath=os.path.normpath):
 
474
    if os_path_isdir(pn): return True
 
475
    if _isFSD or __loader__ is None: return False
 
476
    pn = _startswith_rl(os_path_normpath(pn))
 
477
    if not pn.endswith(os.sep): pn += os.sep
 
478
    return len(filter(lambda x,pn=pn: x.startswith(pn),__loader__._files.keys()))>0
 
479
 
 
480
def rl_get_module(name,dir):
 
481
    if sys.modules.has_key(name):
 
482
        om = sys.modules[name]
 
483
        del sys.modules[name]
 
484
    else:
 
485
        om = None
 
486
    try:
 
487
        f = None
 
488
        try:
 
489
            f, p, desc= imp.find_module(name,[dir])
 
490
            return imp.load_module(name,f,p,desc)
 
491
        except:
 
492
            if isCompactDistro():
 
493
                #attempt a load from inside the zip archive
 
494
                import zipimport
 
495
                dir = _startswith_rl(dir)
 
496
                dir = (dir=='.' or not dir) and _archive or os.path.join(_archive,dir.replace('/',os.sep))
 
497
                zi = zipimport.zipimporter(dir)
 
498
                return zi.load_module(name)
 
499
            raise ImportError('%s[%s]' % (name,dir))
 
500
    finally:
 
501
        if om: sys.modules[name] = om
 
502
        del om
 
503
        if f: f.close()
 
504
 
 
505
class ImageReader:
 
506
    "Wraps up either PIL or Java to get data from bitmaps"
 
507
    def __init__(self, fileName):
 
508
        if not haveImages:
 
509
            warnOnce('Imaging Library not available, unable to import bitmaps')
 
510
            return
 
511
        #start wih lots of null private fields, to be populated by
 
512
        #the relevant engine.
 
513
        self.fileName = fileName
 
514
        self._image = None
 
515
        self._width = None
 
516
        self._height = None
 
517
        self._transparent = None
 
518
        self._data = None
 
519
        self.fp = open_for_read(fileName,'b')
 
520
 
 
521
        #detect which library we are using and open the image
 
522
        if sys.platform[0:4] == 'java':
 
523
            from javax.imageio import ImageIO
 
524
            self._image = ImageIO.read(self.fp)
 
525
        else:
 
526
            import PIL.Image
 
527
            self._image = PIL.Image.open(self.fp)
 
528
 
 
529
    def getSize(self):
 
530
        if (self._width is None or self._height is None):
 
531
            if sys.platform[0:4] == 'java':
 
532
                self._width = self._image.getWidth()
 
533
                self._height = self._image.getHeight()
 
534
            else:
 
535
                self._width, self._height = self._image.size
 
536
        return (self._width, self._height)
 
537
 
 
538
    def getRGBData(self):
 
539
        "Return byte array of RGB data as string"
 
540
        if self._data is None:
 
541
            if sys.platform[0:4] == 'java':
 
542
                import jarray
 
543
                from java.awt.image import PixelGrabber
 
544
                width, height = self.getSize()
 
545
                buffer = jarray.zeros(width*height, 'i')
 
546
                pg = PixelGrabber(self._image, 0,0,width,height,buffer,0,width)
 
547
                pg.grabPixels()
 
548
                # there must be a way to do this with a cast not a byte-level loop,
 
549
                # I just haven't found it yet...
 
550
                pixels = []
 
551
                a = pixels.append
 
552
                for i in range(len(buffer)):
 
553
                    rgb = buffer[i]
 
554
                    a(chr((rgb>>16)&0xff))
 
555
                    a(chr((rgb>>8)&0xff))
 
556
                    a(chr(rgb&0xff))
 
557
                self._data = ''.join(pixels)
 
558
            else:
 
559
                rgb = self._image.convert('RGB')
 
560
                self._data = rgb.tostring()
 
561
        return self._data
 
562
 
 
563
    def getImageData(self):
 
564
        width, height = self.getSize()
 
565
        return width, height, self.getRGBData()
 
566
 
 
567
    def getTransparent(self):
 
568
        if sys.platform[0:4] == 'java':
 
569
            return None
 
570
        else:
 
571
            if self._image.info.has_key("transparency"):
 
572
                transparency = self._image.info["transparency"] * 3
 
573
                palette = self._image.palette
 
574
                try:
 
575
                    palette = palette.palette
 
576
                except:
 
577
                    palette = palette.data
 
578
                return map(ord, palette[transparency:transparency+3])
 
579
            else:
 
580
                return None
 
581
 
 
582
def getImageData(imageFileName):
 
583
    "Get width, height and RGB pixels from image file.  Wraps Java/PIL"
 
584
    return ImageReader.getImageData(imageFileName)
 
585
 
 
586
class DebugMemo:
 
587
    '''Intended as a simple report back encapsulator
 
588
 
 
589
    Typical usages
 
590
    1) To record error data
 
591
        dbg = DebugMemo(fn='dbgmemo.dbg',myVar=value)
 
592
        dbg.add(anotherPayload='aaaa',andagain='bbb')
 
593
        dbg.dump()
 
594
 
 
595
    2) To show the recorded info
 
596
        dbg = DebugMemo(fn='dbgmemo.dbg',mode='r')
 
597
        dbg.load()
 
598
        dbg.show()
 
599
 
 
600
    3) To re-use recorded information
 
601
        dbg = DebugMemo(fn='dbgmemo.dbg',mode='r')
 
602
            dbg.load()
 
603
        myTestFunc(dbg.payload('myVar'),dbg.payload('andagain'))
 
604
 
 
605
    in addition to the payload variables the dump records many useful bits
 
606
    of information which are also printed in the show() method.
 
607
    '''
 
608
    def __init__(self,fn='rl_dbgmemo.dbg',mode='w',getScript=1,modules=(),capture_traceback=1, stdout=None, **kw):
 
609
        import time, socket
 
610
        self.fn = fn
 
611
        if mode!='w': return
 
612
        if not stdout: 
 
613
            self.stdout = sys.stdout
 
614
        else:
 
615
            if hasattr(stdout,'write'):
 
616
                self.stdout = stdout
 
617
            else:
 
618
                self.stdout = open(stdout,'w')
 
619
        self.store = store = {}
 
620
        if capture_traceback and sys.exc_info() != (None,None,None):
 
621
            import traceback
 
622
            s = getStringIO()
 
623
            traceback.print_exc(None,s)
 
624
            store['__traceback'] = s.getvalue()
 
625
        cwd=os.getcwd()
 
626
        lcwd = os.listdir(cwd)
 
627
        exed = os.path.abspath(os.path.dirname(sys.argv[0]))
 
628
        store.update({  'gmt': time.asctime(time.gmtime(time.time())),
 
629
                        'platform': sys.platform,
 
630
                        'version': sys.version,
 
631
                        'hexversion': hex(sys.hexversion),
 
632
                        'executable': sys.executable,
 
633
                        'exec_prefix': sys.exec_prefix,
 
634
                        'prefix': sys.prefix,
 
635
                        'path': sys.path,
 
636
                        'argv': sys.argv,
 
637
                        'cwd': cwd,
 
638
                        'hostname': socket.gethostname(),
 
639
                        'lcwd': lcwd,
 
640
                        'byteorder': sys.byteorder,
 
641
                        'maxint': sys.maxint,
 
642
                        'maxint': getattr(sys,'maxunicode','????'),
 
643
                        'api_version': getattr(sys,'api_version','????'),
 
644
                        'version_info': getattr(sys,'version_info','????'),
 
645
                        'winver': getattr(sys,'winver','????'),
 
646
                        })
 
647
        for M,A in (
 
648
                (sys,('getwindowsversion','getfilesystemencoding')),
 
649
                (os,('uname', 'ctermid', 'getgid', 'getuid', 'getegid',
 
650
                    'geteuid', 'getlogin', 'getgroups', 'getpgrp', 'getpid', 'getppid',
 
651
                    )),
 
652
                ):
 
653
            for a in A:
 
654
                if hasattr(M,a):
 
655
                    try:
 
656
                        store[a] = getattr(M,a)()
 
657
                    except:
 
658
                        pass
 
659
        if exed!=cwd:
 
660
            try:
 
661
                store.update({'exed': exed, 'lexed': os.listdir(exed),})
 
662
            except:
 
663
                pass
 
664
        if getScript:
 
665
            fn = os.path.abspath(sys.argv[0])
 
666
            if os.path.isfile(fn):
 
667
                try:
 
668
                    store['__script'] = (fn,open(fn,'r').read())
 
669
                except:
 
670
                    pass
 
671
        module_versions = {}
 
672
        for n,m in sys.modules.items():
 
673
            if n=='reportlab' or n=='rlextra' or n[:10]=='reportlab.' or n[:8]=='rlextra.':
 
674
                v = getattr(m,'__version__',None)
 
675
                if v: module_versions[n] = v
 
676
        store['__module_versions'] = module_versions
 
677
        self.store['__payload'] = {}
 
678
        self._add(kw)
 
679
 
 
680
    def _add(self,D):
 
681
        payload = self.store['__payload']
 
682
        for k, v in D.items():
 
683
            payload[k] = v
 
684
 
 
685
    def add(self,**kw):
 
686
        self._add(kw)
 
687
 
 
688
    def dump(self):
 
689
        import pickle
 
690
        pickle.dump(self.store,open(self.fn,'wb'))
 
691
 
 
692
    def load(self):
 
693
        import pickle
 
694
        self.store = pickle.load(open(self.fn,'rb'))
 
695
 
 
696
    def _show_module_versions(self,k,v):
 
697
        self._writeln(k[2:])
 
698
        K = v.keys()
 
699
        K.sort()
 
700
        for k in K:
 
701
            vk = v[k]
 
702
            try:
 
703
                m = recursiveImport(k,sys.path[:],1)
 
704
                d = getattr(m,'__version__',None)==vk and 'SAME' or 'DIFFERENT'
 
705
            except:
 
706
                m = None
 
707
                d = '??????unknown??????'
 
708
            self._writeln('  %s = %s (%s)' % (k,vk,d))
 
709
 
 
710
    def _banner(self,k,what):
 
711
        self._writeln('###################%s %s##################' % (what,k[2:]))
 
712
 
 
713
    def _start(self,k):
 
714
        self._banner(k,'Start  ')
 
715
 
 
716
    def _finish(self,k):
 
717
        self._banner(k,'Finish ')
 
718
 
 
719
    def _show_lines(self,k,v):
 
720
        self._start(k)
 
721
        self._writeln(v)
 
722
        self._finish(k)
 
723
 
 
724
    def _show_file(self,k,v):
 
725
        k = '%s %s' % (k,os.path.basename(v[0]))
 
726
        self._show_lines(k,v[1])
 
727
 
 
728
    def _show_payload(self,k,v):
 
729
        if v:
 
730
            import pprint
 
731
            self._start(k)
 
732
            pprint.pprint(v,self.stdout)
 
733
            self._finish(k)
 
734
 
 
735
    specials = {'__module_versions': _show_module_versions,
 
736
                '__payload': _show_payload,
 
737
                '__traceback': _show_lines,
 
738
                '__script': _show_file,
 
739
                }
 
740
    def show(self):
 
741
        K = self.store.keys()
 
742
        K.sort()
 
743
        for k in K:
 
744
            if k not in self.specials.keys(): self._writeln('%-15s = %s' % (k,self.store[k]))
 
745
        for k in K:
 
746
            if k in self.specials.keys(): apply(self.specials[k],(self,k,self.store[k]))
 
747
 
 
748
    def payload(self,name):
 
749
        return self.store['__payload'][name]
 
750
 
 
751
    def __setitem__(self,name,value):
 
752
        self.store['__payload'][name] = value
 
753
 
 
754
    def __getitem__(self,name):
 
755
        return self.store['__payload'][name]
 
756
 
 
757
    def _writeln(self,msg):
 
758
        self.stdout.write(msg+'\n')
 
759
 
 
760
def _flatten(L,a):
 
761
    for x in L:
 
762
        if type(x) in SeqTypes: _flatten(x,a)
 
763
        else: a(x)
 
764
 
 
765
def flatten(L):
 
766
    '''recursively flatten the list or tuple L'''
 
767
    R = []
 
768
    _flatten(L,R.append)
 
769
    return R
 
770
 
 
771
def find_locals(func,depth=0):
 
772
    '''apply func to the locals at each stack frame till func returns a non false value'''
 
773
    while 1:
 
774
        _ = func(sys._getframe(depth).f_locals)
 
775
        if _: return _
 
776
        depth += 1