Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting.app / trunk / org.gvsig.scripting.app / org.gvsig.scripting.app.extension / src / main / resources / scripting / lib / console / inspect.py @ 359

History | View | Annotate | Download (30.6 KB)

1
"""Get useful information from live Python objects.
2

3
This module encapsulates the interface provided by the internal special
4
attributes (func_*, co_*, im_*, tb_*, etc.) in a friendlier fashion.
5
It also provides some help for examining source code and class layout.
6

7
Here are some of the useful functions provided by this module:
8

9
    ismodule(), isclass(), ismethod(), isfunction(), istraceback(),
10
        isframe(), iscode(), isbuiltin(), isroutine() - check object types
11
    getmembers() - get members of an object that satisfy a given condition
12

13
    getfile(), getsourcefile(), getsource() - find an object's source code
14
    getdoc(), getcomments() - get documentation on an object
15
    getmodule() - determine the module that an object came from
16
    getclasstree() - arrange classes so as to represent their hierarchy
17

18
    getargspec(), getargvalues() - get info about function arguments
19
    formatargspec(), formatargvalues() - format an argument spec
20
    getouterframes(), getinnerframes() - get info about frames
21
    currentframe() - get the current stack frame
22
    stack(), trace() - get info about frames on the stack or in a traceback
23
"""
24

    
25
# This module is in the public domain.  No warranties.
26

    
27
__author__ = 'Ka-Ping Yee <ping@lfw.org>'
28
__date__ = '1 Jan 2001'
29

    
30
import sys, os, types, string, re, dis, imp, tokenize
31

    
32
# ----------------------------------------------------------- type-checking
33
def ismodule(object):
34
    """Return true if the object is a module.
35

36
    Module objects provide these attributes:
37
        __doc__         documentation string
38
        __file__        filename (missing for built-in modules)"""
39
    return isinstance(object, types.ModuleType)
40

    
41
def isclass(object):
42
    """Return true if the object is a class.
43

44
    Class objects provide these attributes:
45
        __doc__         documentation string
46
        __module__      name of module in which this class was defined"""
47
    return isinstance(object, types.ClassType) or hasattr(object, '__bases__')
48

    
49
def ismethod(object):
50
    """Return true if the object is an instance method.
51

52
    Instance method objects provide these attributes:
53
        __doc__         documentation string
54
        __name__        name with which this method was defined
55
        im_class        class object in which this method belongs
56
        im_func         function object containing implementation of method
57
        im_self         instance to which this method is bound, or None"""
58
    return isinstance(object, types.MethodType)
59

    
60
def ismethoddescriptor(object):
61
    """Return true if the object is a method descriptor.
62

63
    But not if ismethod() or isclass() or isfunction() are true.
64

65
    This is new in Python 2.2, and, for example, is true of int.__add__.
66
    An object passing this test has a __get__ attribute but not a __set__
67
    attribute, but beyond that the set of attributes varies.  __name__ is
68
    usually sensible, and __doc__ often is.
69

70
    Methods implemented via descriptors that also pass one of the other
71
    tests return false from the ismethoddescriptor() test, simply because
72
    the other tests promise more -- you can, e.g., count on having the
73
    im_func attribute (etc) when an object passes ismethod()."""
74
    return (hasattr(object, "__get__")
75
            and not hasattr(object, "__set__") # else it's a data descriptor
76
            and not ismethod(object)           # mutual exclusion
77
            and not isfunction(object)
78
            and not isclass(object))
79

    
80
def isfunction(object):
81
    """Return true if the object is a user-defined function.
82

83
    Function objects provide these attributes:
84
        __doc__         documentation string
85
        __name__        name with which this function was defined
86
        func_code       code object containing compiled function bytecode
87
        func_defaults   tuple of any default values for arguments
88
        func_doc        (same as __doc__)
89
        func_globals    global namespace in which this function was defined
90
        func_name       (same as __name__)"""
91
    return isinstance(object, types.FunctionType)
92

    
93
def istraceback(object):
94
    """Return true if the object is a traceback.
95

96
    Traceback objects provide these attributes:
97
        tb_frame        frame object at this level
98
        tb_lasti        index of last attempted instruction in bytecode
99
        tb_lineno       current line number in Python source code
100
        tb_next         next inner traceback object (called by this level)"""
101
    return isinstance(object, types.TracebackType)
102

    
103
def isframe(object):
104
    """Return true if the object is a frame object.
105

106
    Frame objects provide these attributes:
107
        f_back          next outer frame object (this frame's caller)
108
        f_builtins      built-in namespace seen by this frame
109
        f_code          code object being executed in this frame
110
        f_exc_traceback traceback if raised in this frame, or None
111
        f_exc_type      exception type if raised in this frame, or None
112
        f_exc_value     exception value if raised in this frame, or None
113
        f_globals       global namespace seen by this frame
114
        f_lasti         index of last attempted instruction in bytecode
115
        f_lineno        current line number in Python source code
116
        f_locals        local namespace seen by this frame
117
        f_restricted    0 or 1 if frame is in restricted execution mode
118
        f_trace         tracing function for this frame, or None"""
119
    return isinstance(object, types.FrameType)
120

    
121
def iscode(object):
122
    """Return true if the object is a code object.
123

124
    Code objects provide these attributes:
125
        co_argcount     number of arguments (not including * or ** args)
126
        co_code         string of raw compiled bytecode
127
        co_consts       tuple of constants used in the bytecode
128
        co_filename     name of file in which this code object was created
129
        co_firstlineno  number of first line in Python source code
130
        co_flags        bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
131
        co_lnotab       encoded mapping of line numbers to bytecode indices
132
        co_name         name with which this code object was defined
133
        co_names        tuple of names of local variables
134
        co_nlocals      number of local variables
135
        co_stacksize    virtual machine stack space required
136
        co_varnames     tuple of names of arguments and local variables"""
137
    return isinstance(object, types.CodeType)
138

    
139
def isbuiltin(object):
140
    """Return true if the object is a built-in function or method.
141

142
    Built-in functions and methods provide these attributes:
143
        __doc__         documentation string
144
        __name__        original name of this function or method
145
        __self__        instance to which a method is bound, or None"""
146
    return isinstance(object, types.BuiltinFunctionType)
147

    
148
def isroutine(object):
149
    """Return true if the object is any kind of function or method."""
150
    return (isbuiltin(object)
151
            or isfunction(object)
152
            or ismethod(object)
153
            or ismethoddescriptor(object))
154

    
155
def getmembers(object, predicate=None):
156
    """Return all members of an object as (name, value) pairs sorted by name.
157
    Optionally, only return members that satisfy a given predicate."""
158
    results = []
159
    for key in dir(object):
160
        value = getattr(object, key)
161
        if not predicate or predicate(value):
162
            results.append((key, value))
163
    results.sort()
164
    return results
165

    
166
def classify_class_attrs(cls):
167
    """Return list of attribute-descriptor tuples.
168

169
    For each name in dir(cls), the return list contains a 4-tuple
170
    with these elements:
171

172
        0. The name (a string).
173

174
        1. The kind of attribute this is, one of these strings:
175
               'class method'    created via classmethod()
176
               'static method'   created via staticmethod()
177
               'property'        created via property()
178
               'method'          any other flavor of method
179
               'data'            not a method
180

181
        2. The class which defined this attribute (a class).
182

183
        3. The object as obtained directly from the defining class's
184
           __dict__, not via getattr.  This is especially important for
185
           data attributes:  C.data is just a data object, but
186
           C.__dict__['data'] may be a data descriptor with additional
187
           info, like a __doc__ string.
188
    """
189

    
190
    mro = getmro(cls)
191
    names = dir(cls)
192
    result = []
193
    for name in names:
194
        # Get the object associated with the name.
195
        # Getting an obj from the __dict__ sometimes reveals more than
196
        # using getattr.  Static and class methods are dramatic examples.
197
        if name in cls.__dict__:
198
            obj = cls.__dict__[name]
199
        else:
200
            obj = getattr(cls, name)
201

    
202
        # Figure out where it was defined.
203
        homecls = getattr(obj, "__objclass__", None)
204
        if homecls is None:
205
            # search the dicts.
206
            for base in mro:
207
                if name in base.__dict__:
208
                    homecls = base
209
                    break
210

    
211
        # Get the object again, in order to get it from the defining
212
        # __dict__ instead of via getattr (if possible).
213
        if homecls is not None and name in homecls.__dict__:
214
            obj = homecls.__dict__[name]
215

    
216
        # Also get the object via getattr.
217
        obj_via_getattr = getattr(cls, name)
218

    
219
        # Classify the object.
220
        if isinstance(obj, staticmethod):
221
            kind = "static method"
222
        elif isinstance(obj, classmethod):
223
            kind = "class method"
224
        elif isinstance(obj, property):
225
            kind = "property"
226
        elif (ismethod(obj_via_getattr) or
227
              ismethoddescriptor(obj_via_getattr)):
228
            kind = "method"
229
        else:
230
            kind = "data"
231

    
232
        result.append((name, kind, homecls, obj))
233

    
234
    return result
235

    
236
# ----------------------------------------------------------- class helpers
237
def _searchbases(cls, accum):
238
    # Simulate the "classic class" search order.
239
    if cls in accum:
240
        return
241
    accum.append(cls)
242
    for base in cls.__bases__:
243
        _searchbases(base, accum)
244

    
245
def getmro(cls):
246
    "Return tuple of base classes (including cls) in method resolution order."
247
    if hasattr(cls, "__mro__"):
248
        return cls.__mro__
249
    else:
250
        result = []
251
        _searchbases(cls, result)
252
        return tuple(result)
253

    
254
# -------------------------------------------------- source code extraction
255
def indentsize(line):
256
    """Return the indent size, in spaces, at the start of a line of text."""
257
    expline = string.expandtabs(line)
258
    return len(expline) - len(string.lstrip(expline))
259

    
260
def getdoc(object):
261
    """Get the documentation string for an object.
262

263
    All tabs are expanded to spaces.  To clean up docstrings that are
264
    indented to line up with blocks of code, any whitespace than can be
265
    uniformly removed from the second line onwards is removed."""
266

    
267
    try:
268
        doc = object.__doc__
269
    except AttributeError:
270
        return None
271
    # Jython doesn't like this syntax
272
    # if not isinstance(doc, (str, unicode)):
273
    # in Jython UnicodeType == StringType
274
    if not isinstance(doc, types.StringType):
275
        return None
276
    try:
277
        lines = string.split(string.expandtabs(doc), '\n')
278
    except UnicodeError:
279
        return None
280
    else:
281
        margin = None
282
        for line in lines[1:]:
283
            content = len(string.lstrip(line))
284
            if not content: continue
285
            indent = len(line) - content
286
            if margin is None: margin = indent
287
            else: margin = min(margin, indent)
288
        if margin is not None:
289
            for i in range(1, len(lines)): lines[i] = lines[i][margin:]
290
        return string.join(lines, '\n')
291

    
292
def getfile(object):
293
    """Work out which source or compiled file an object was defined in."""
294
    if ismodule(object):
295
        if hasattr(object, '__file__'):
296
            return object.__file__
297
        raise TypeError, 'arg is a built-in module'
298
    if isclass(object):
299
        object = sys.modules.get(object.__module__)
300
        if hasattr(object, '__file__'):
301
            return object.__file__
302
        raise TypeError, 'arg is a built-in class'
303
    if ismethod(object):
304
        object = object.im_func
305
    if isfunction(object):
306
        object = object.func_code
307
    if istraceback(object):
308
        object = object.tb_frame
309
    if isframe(object):
310
        object = object.f_code
311
    if iscode(object):
312
        return object.co_filename
313
    raise TypeError, 'arg is not a module, class, method, ' \
314
                     'function, traceback, frame, or code object'
315

    
316
def getmoduleinfo(path):
317
    """Get the module name, suffix, mode, and module type for a given file."""
318
    filename = os.path.basename(path)
319
    suffixes = map(lambda (suffix, mode, mtype):
320
                   (-len(suffix), suffix, mode, mtype), imp.get_suffixes())
321
    suffixes.sort() # try longest suffixes first, in case they overlap
322
    for neglen, suffix, mode, mtype in suffixes:
323
        if filename[neglen:] == suffix:
324
            return filename[:neglen], suffix, mode, mtype
325

    
326
def getmodulename(path):
327
    """Return the module name for a given file, or None."""
328
    info = getmoduleinfo(path)
329
    if info: return info[0]
330

    
331
def getsourcefile(object):
332
    """Return the Python source file an object was defined in, if it exists."""
333
    filename = getfile(object)
334
    if string.lower(filename[-4:]) in ['.pyc', '.pyo']:
335
        filename = filename[:-4] + '.py'
336
    for suffix, mode, kind in imp.get_suffixes():
337
        if 'b' in mode and string.lower(filename[-len(suffix):]) == suffix:
338
            # Looks like a binary file.  We want to only return a text file.
339
            return None
340
    if os.path.exists(filename):
341
        return filename
342

    
343
def getabsfile(object):
344
    """Return an absolute path to the source or compiled file for an object.
345

346
    The idea is for each object to have a unique origin, so this routine
347
    normalizes the result as much as possible."""
348
    return os.path.normcase(
349
        os.path.abspath(getsourcefile(object) or getfile(object)))
350

    
351
modulesbyfile = {}
352

    
353
def getmodule(object):
354
    """Return the module an object was defined in, or None if not found."""
355
    if ismodule(object):
356
        return object
357
    if isclass(object):
358
        return sys.modules.get(object.__module__)
359
    try:
360
        file = getabsfile(object)
361
    except TypeError:
362
        return None
363
    if modulesbyfile.has_key(file):
364
        return sys.modules[modulesbyfile[file]]
365
    for module in sys.modules.values():
366
        if hasattr(module, '__file__'):
367
            modulesbyfile[getabsfile(module)] = module.__name__
368
    if modulesbyfile.has_key(file):
369
        return sys.modules[modulesbyfile[file]]
370
    main = sys.modules['__main__']
371
    if hasattr(main, object.__name__):
372
        mainobject = getattr(main, object.__name__)
373
        if mainobject is object:
374
            return main
375
    builtin = sys.modules['__builtin__']
376
    if hasattr(builtin, object.__name__):
377
        builtinobject = getattr(builtin, object.__name__)
378
        if builtinobject is object:
379
            return builtin
380

    
381
def findsource(object):
382
    """Return the entire source file and starting line number for an object.
383

384
    The argument may be a module, class, method, function, traceback, frame,
385
    or code object.  The source code is returned as a list of all the lines
386
    in the file and the line number indexes a line in that list.  An IOError
387
    is raised if the source code cannot be retrieved."""
388
    try:
389
        file = open(getsourcefile(object))
390
    except (TypeError, IOError):
391
        raise IOError, 'could not get source code'
392
    lines = file.readlines()
393
    file.close()
394

    
395
    if ismodule(object):
396
        return lines, 0
397

    
398
    if isclass(object):
399
        name = object.__name__
400
        pat = re.compile(r'^\s*class\s*' + name + r'\b')
401
        for i in range(len(lines)):
402
            if pat.match(lines[i]): return lines, i
403
        else: raise IOError, 'could not find class definition'
404

    
405
    if ismethod(object):
406
        object = object.im_func
407
    if isfunction(object):
408
        object = object.func_code
409
    if istraceback(object):
410
        object = object.tb_frame
411
    if isframe(object):
412
        object = object.f_code
413
    if iscode(object):
414
        if not hasattr(object, 'co_firstlineno'):
415
            raise IOError, 'could not find function definition'
416
        lnum = object.co_firstlineno - 1
417
        pat = re.compile(r'^\s*def\s')
418
        while lnum > 0:
419
            if pat.match(lines[lnum]): break
420
            lnum = lnum - 1
421
        return lines, lnum
422
    raise IOError, 'could not find code object'
423

    
424
def getcomments(object):
425
    """Get lines of comments immediately preceding an object's source code."""
426
    try: lines, lnum = findsource(object)
427
    except IOError: return None
428

    
429
    if ismodule(object):
430
        # Look for a comment block at the top of the file.
431
        start = 0
432
        if lines and lines[0][:2] == '#!': start = 1
433
        while start < len(lines) and string.strip(lines[start]) in ['', '#']:
434
            start = start + 1
435
        if start < len(lines) and lines[start][:1] == '#':
436
            comments = []
437
            end = start
438
            while end < len(lines) and lines[end][:1] == '#':
439
                comments.append(string.expandtabs(lines[end]))
440
                end = end + 1
441
            return string.join(comments, '')
442

    
443
    # Look for a preceding block of comments at the same indentation.
444
    elif lnum > 0:
445
        indent = indentsize(lines[lnum])
446
        end = lnum - 1
447
        if end >= 0 and string.lstrip(lines[end])[:1] == '#' and \
448
            indentsize(lines[end]) == indent:
449
            comments = [string.lstrip(string.expandtabs(lines[end]))]
450
            if end > 0:
451
                end = end - 1
452
                comment = string.lstrip(string.expandtabs(lines[end]))
453
                while comment[:1] == '#' and indentsize(lines[end]) == indent:
454
                    comments[:0] = [comment]
455
                    end = end - 1
456
                    if end < 0: break
457
                    comment = string.lstrip(string.expandtabs(lines[end]))
458
            while comments and string.strip(comments[0]) == '#':
459
                comments[:1] = []
460
            while comments and string.strip(comments[-1]) == '#':
461
                comments[-1:] = []
462
            return string.join(comments, '')
463

    
464
class ListReader:
465
    """Provide a readline() method to return lines from a list of strings."""
466
    def __init__(self, lines):
467
        self.lines = lines
468
        self.index = 0
469

    
470
    def readline(self):
471
        i = self.index
472
        if i < len(self.lines):
473
            self.index = i + 1
474
            return self.lines[i]
475
        else: return ''
476

    
477
class EndOfBlock(Exception): pass
478

    
479
class BlockFinder:
480
    """Provide a tokeneater() method to detect the end of a code block."""
481
    def __init__(self):
482
        self.indent = 0
483
        self.started = 0
484
        self.last = 0
485

    
486
    def tokeneater(self, type, token, (srow, scol), (erow, ecol), line):
487
        if not self.started:
488
            if type == tokenize.NAME: self.started = 1
489
        elif type == tokenize.NEWLINE:
490
            self.last = srow
491
        elif type == tokenize.INDENT:
492
            self.indent = self.indent + 1
493
        elif type == tokenize.DEDENT:
494
            self.indent = self.indent - 1
495
            if self.indent == 0: raise EndOfBlock, self.last
496

    
497
def getblock(lines):
498
    """Extract the block of code at the top of the given list of lines."""
499
    try:
500
        tokenize.tokenize(ListReader(lines).readline, BlockFinder().tokeneater)
501
    except EndOfBlock, eob:
502
        return lines[:eob.args[0]]
503

    
504
def getsourcelines(object):
505
    """Return a list of source lines and starting line number for an object.
506

507
    The argument may be a module, class, method, function, traceback, frame,
508
    or code object.  The source code is returned as a list of the lines
509
    corresponding to the object and the line number indicates where in the
510
    original source file the first line of code was found.  An IOError is
511
    raised if the source code cannot be retrieved."""
512
    lines, lnum = findsource(object)
513

    
514
    if ismodule(object): return lines, 0
515
    else: return getblock(lines[lnum:]), lnum + 1
516

    
517
def getsource(object):
518
    """Return the text of the source code for an object.
519

520
    The argument may be a module, class, method, function, traceback, frame,
521
    or code object.  The source code is returned as a single string.  An
522
    IOError is raised if the source code cannot be retrieved."""
523
    lines, lnum = getsourcelines(object)
524
    return string.join(lines, '')
525

    
526
# --------------------------------------------------- class tree extraction
527
def walktree(classes, children, parent):
528
    """Recursive helper function for getclasstree()."""
529
    results = []
530
    classes.sort(lambda a, b: cmp(a.__name__, b.__name__))
531
    for c in classes:
532
        results.append((c, c.__bases__))
533
        if children.has_key(c):
534
            results.append(walktree(children[c], children, c))
535
    return results
536

    
537
def getclasstree(classes, unique=0):
538
    """Arrange the given list of classes into a hierarchy of nested lists.
539

540
    Where a nested list appears, it contains classes derived from the class
541
    whose entry immediately precedes the list.  Each entry is a 2-tuple
542
    containing a class and a tuple of its base classes.  If the 'unique'
543
    argument is true, exactly one entry appears in the returned structure
544
    for each class in the given list.  Otherwise, classes using multiple
545
    inheritance and their descendants will appear multiple times."""
546
    children = {}
547
    roots = []
548
    for c in classes:
549
        if c.__bases__:
550
            for parent in c.__bases__:
551
                if not children.has_key(parent):
552
                    children[parent] = []
553
                children[parent].append(c)
554
                if unique and parent in classes: break
555
        elif c not in roots:
556
            roots.append(c)
557
    for parent in children.keys():
558
        if parent not in classes:
559
            roots.append(parent)
560
    return walktree(roots, children, None)
561

    
562
# ------------------------------------------------ argument list extraction
563
# These constants are from Python's compile.h.
564
CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8
565

    
566
def getargs(co):
567
    """Get information about the arguments accepted by a code object.
568

569
    Three things are returned: (args, varargs, varkw), where 'args' is
570
    a list of argument names (possibly containing nested lists), and
571
    'varargs' and 'varkw' are the names of the * and ** arguments or None."""
572

    
573
    if not iscode(co): raise TypeError, 'arg is not a code object'
574

    
575
##     #jython doesn't have co_code
576
##     code = co.co_code
577
    nargs = co.co_argcount
578
    names = co.co_varnames
579
    args = list(names[:nargs])
580
    step = 0
581

    
582
## We don't have co_code, so skip this for now
583
##    
584
##     # The following acrobatics are for anonymous (tuple) arguments.
585
##     for i in range(nargs):
586
##         if args[i][:1] in ['', '.']:
587
##             stack, remain, count = [], [], []
588
##             while step < len(code):
589
##                 op = ord(code[step])
590
##                 step = step + 1
591
##                 if op >= dis.HAVE_ARGUMENT:
592
##                     opname = dis.opname[op]
593
##                     value = ord(code[step]) + ord(code[step+1])*256
594
##                     step = step + 2
595
##                     if opname in ['UNPACK_TUPLE', 'UNPACK_SEQUENCE']:
596
##                         remain.append(value)
597
##                         count.append(value)
598
##                     elif opname == 'STORE_FAST':
599
##                         stack.append(names[value])
600
##                         remain[-1] = remain[-1] - 1
601
##                         while remain[-1] == 0:
602
##                             remain.pop()
603
##                             size = count.pop()
604
##                             stack[-size:] = [stack[-size:]]
605
##                             if not remain: break
606
##                             remain[-1] = remain[-1] - 1
607
##                         if not remain: break
608
##             args[i] = stack[0]
609

    
610
    varargs = None
611
    if co.co_flags & CO_VARARGS:
612
        varargs = co.co_varnames[nargs]
613
        nargs = nargs + 1
614
    varkw = None
615
    if co.co_flags & CO_VARKEYWORDS:
616
        varkw = co.co_varnames[nargs]
617
    return args, varargs, varkw
618

    
619
def getargspec(func):
620
    """Get the names and default values of a function's arguments.
621

622
    A tuple of four things is returned: (args, varargs, varkw, defaults).
623
    'args' is a list of the argument names (it may contain nested lists).
624
    'varargs' and 'varkw' are the names of the * and ** arguments or None.
625
    'defaults' is an n-tuple of the default values of the last n arguments."""
626
    if not isfunction(func): raise TypeError, 'arg is not a Python function'
627
    args, varargs, varkw = getargs(func.func_code)
628
    return args, varargs, varkw, func.func_defaults
629

    
630
def getargvalues(frame):
631
    """Get information about arguments passed into a particular frame.
632

633
    A tuple of four things is returned: (args, varargs, varkw, locals).
634
    'args' is a list of the argument names (it may contain nested lists).
635
    'varargs' and 'varkw' are the names of the * and ** arguments or None.
636
    'locals' is the locals dictionary of the given frame."""
637
    args, varargs, varkw = getargs(frame.f_code)
638
    return args, varargs, varkw, frame.f_locals
639

    
640
def joinseq(seq):
641
    if len(seq) == 1:
642
        return '(' + seq[0] + ',)'
643
    else:
644
        return '(' + string.join(seq, ', ') + ')'
645

    
646
def strseq(object, convert, join=joinseq):
647
    """Recursively walk a sequence, stringifying each element."""
648
    if type(object) in [types.ListType, types.TupleType]:
649
        return join(map(lambda o, c=convert, j=join: strseq(o, c, j), object))
650
    else:
651
        return convert(object)
652

    
653
def formatargspec(args, varargs=None, varkw=None, defaults=None,
654
                  formatarg=str,
655
                  formatvarargs=lambda name: '*' + name,
656
                  formatvarkw=lambda name: '**' + name,
657
                  formatvalue=lambda value: '=' + repr(value),
658
                  join=joinseq):
659
    """Format an argument spec from the 4 values returned by getargspec.
660

661
    The first four arguments are (args, varargs, varkw, defaults).  The
662
    other four arguments are the corresponding optional formatting functions
663
    that are called to turn names and values into strings.  The ninth
664
    argument is an optional function to format the sequence of arguments."""
665
    specs = []
666
    if defaults:
667
        firstdefault = len(args) - len(defaults)
668
    for i in range(len(args)):
669
        spec = strseq(args[i], formatarg, join)
670
        if defaults and i >= firstdefault:
671
            spec = spec + formatvalue(defaults[i - firstdefault])
672
        specs.append(spec)
673
    if varargs:
674
        specs.append(formatvarargs(varargs))
675
    if varkw:
676
        specs.append(formatvarkw(varkw))
677
    return '(' + string.join(specs, ', ') + ')'
678

    
679
def formatargvalues(args, varargs, varkw, locals,
680
                    formatarg=str,
681
                    formatvarargs=lambda name: '*' + name,
682
                    formatvarkw=lambda name: '**' + name,
683
                    formatvalue=lambda value: '=' + repr(value),
684
                    join=joinseq):
685
    """Format an argument spec from the 4 values returned by getargvalues.
686

687
    The first four arguments are (args, varargs, varkw, locals).  The
688
    next four arguments are the corresponding optional formatting functions
689
    that are called to turn names and values into strings.  The ninth
690
    argument is an optional function to format the sequence of arguments."""
691
    def convert(name, locals=locals,
692
                formatarg=formatarg, formatvalue=formatvalue):
693
        return formatarg(name) + formatvalue(locals[name])
694
    specs = []
695
    for i in range(len(args)):
696
        specs.append(strseq(args[i], convert, join))
697
    if varargs:
698
        specs.append(formatvarargs(varargs) + formatvalue(locals[varargs]))
699
    if varkw:
700
        specs.append(formatvarkw(varkw) + formatvalue(locals[varkw]))
701
    return '(' + string.join(specs, ', ') + ')'
702

    
703
# -------------------------------------------------- stack frame extraction
704
def getframeinfo(frame, context=1):
705
    """Get information about a frame or traceback object.
706

707
    A tuple of five things is returned: the filename, the line number of
708
    the current line, the function name, a list of lines of context from
709
    the source code, and the index of the current line within that list.
710
    The optional second argument specifies the number of lines of context
711
    to return, which are centered around the current line."""
712
    if istraceback(frame):
713
        frame = frame.tb_frame
714
    if not isframe(frame):
715
        raise TypeError, 'arg is not a frame or traceback object'
716

    
717
    filename = getsourcefile(frame)
718
    lineno = getlineno(frame)
719
    if context > 0:
720
##         # Jython 2.1 can't handle //
721
##         start = lineno - 1 - context//2
722
        start = lineno - 1 - context/2
723
        try:
724
            lines, lnum = findsource(frame)
725
        except IOError:
726
            lines = index = None
727
        else:
728
            start = max(start, 1)
729
            start = min(start, len(lines) - context)
730
            lines = lines[start:start+context]
731
            index = lineno - 1 - start
732
    else:
733
        lines = index = None
734

    
735
    return (filename, lineno, frame.f_code.co_name, lines, index)
736

    
737
def getlineno(frame):
738
    """Get the line number from a frame object, allowing for optimization."""
739
    # Written by Marc-Andr? Lemburg; revised by Jim Hugunin and Fredrik Lundh.
740
    lineno = frame.f_lineno
741
    code = frame.f_code
742
    if hasattr(code, 'co_lnotab'):
743
        table = code.co_lnotab
744
        lineno = code.co_firstlineno
745
        addr = 0
746
        for i in range(0, len(table), 2):
747
            addr = addr + ord(table[i])
748
            if addr > frame.f_lasti: break
749
            lineno = lineno + ord(table[i+1])
750
    return lineno
751

    
752
def getouterframes(frame, context=1):
753
    """Get a list of records for a frame and all higher (calling) frames.
754

755
    Each record contains a frame object, filename, line number, function
756
    name, a list of lines of context, and index within the context."""
757
    framelist = []
758
    while frame:
759
        framelist.append((frame,) + getframeinfo(frame, context))
760
        frame = frame.f_back
761
    return framelist
762

    
763
def getinnerframes(tb, context=1):
764
    """Get a list of records for a traceback's frame and all lower frames.
765

766
    Each record contains a frame object, filename, line number, function
767
    name, a list of lines of context, and index within the context."""
768
    framelist = []
769
    while tb:
770
        framelist.append((tb.tb_frame,) + getframeinfo(tb, context))
771
        tb = tb.tb_next
772
    return framelist
773

    
774
def currentframe():
775
    """Return the frame object for the caller's stack frame."""
776
    try:
777
        raise 'catch me'
778
    except:
779
        return sys.exc_traceback.tb_frame.f_back
780

    
781
if hasattr(sys, '_getframe'): currentframe = sys._getframe
782

    
783
def stack(context=1):
784
    """Return a list of records for the stack above the caller's frame."""
785
    return getouterframes(currentframe().f_back, context)
786

    
787
def trace(context=1):
788
    """Return a list of records for the stack below the current exception."""
789
    return getinnerframes(sys.exc_traceback, context)