1#
2# Copyright (c) SAS Institute, Inc.
3#
4# Permission is hereby granted, free of charge, to any person obtaining a copy
5# of this software and associated documentation files (the "Software"), to deal
6# in the Software without restriction, including without limitation the rights
7# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8# copies of the Software, and to permit persons to whom the Software is
9# furnished to do so, subject to the following conditions:
10#
11# The above copyright notice and this permission notice shall be included in
12# all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20# THE SOFTWARE.
21#
22
23
24""" Tools for printing out extended information about frame variables """
25from __future__ import unicode_literals
26from __future__ import print_function
27from __future__ import division
28from __future__ import absolute_import
29
30import inspect
31import smtplib
32import sys
33import string
34import tempfile
35import traceback
36from six.moves import xmlrpc_client
37
38from six.moves.reprlib import Repr
39_repr = Repr()
40_repr.maxstring = 3000
41_saferepr = _repr.repr
42
43
44def printTraceBack(tb=None, output=sys.stderr, exc_type=None, exc_msg=None):
45    if isinstance(output, str):
46        output = open(output, 'w')
47
48    exc_info = sys.exc_info()
49    if tb is None:
50        tb = exc_info[2]
51
52    if exc_type is None:
53        exc_type = exc_info[0]
54
55    if exc_msg is None:
56        exc_msg = exc_info[1]
57
58    if exc_type is not None:
59        output.write('Exception: ')
60        exc_info = '\n'.join(traceback.format_exception_only(
61            exc_type, exc_msg))
62        output.write(exc_info)
63        output.write('\n\n')
64
65    lines = traceback.format_exception(exc_type, exc_msg, tb)
66    output.write(string.joinfields(lines, ""))
67
68    while tb:
69        _printFrame(tb.tb_frame, output=output)
70        tb = tb.tb_next
71
72
73def printFrame(frame=0, output=sys.stderr):
74    # if output is a path, assume it is a writable one
75    # otherwise, it must be an already opened file
76    if isinstance(output, str):
77        output = open(output, 'w')
78    # skip this frame because who cares about the printFrame func?
79    if isinstance(frame, int):
80        # stack was given in depth form
81        # (skip the current frame when counting depth)
82        frame = sys._getframe(frame + 1)
83    _printFrame(frame, output)
84
85
86def printStack(frame=0, output=sys.stderr):
87    if isinstance(output, str):
88        output = open(output, 'w')
89    if isinstance(frame, int):
90        # stack was given in depth form
91        # (skip the current frame when counting depth)
92        frame = sys._getframe(frame + 1)
93    while(frame):
94        output.write("*************************************\n")
95        _printFrame(frame, output)
96        frame = frame.f_back
97
98
99def mailStack(frame, recips, sender, subject, extracontent=None):
100    file = tempfile.TemporaryFile()
101    file.write('Subject: ' + subject + '\n\n')
102    if extracontent:
103        file.write(extracontent)
104    printStack(frame, file)
105    server = smtplib.SMTP('localhost')
106    file.seek(0)
107    server.sendmail(sender,
108                    recips,
109                    file.read())
110    server.close()
111    file.close()
112
113
114def _printFrame(f, output=sys.stderr):
115    c = f.f_code
116    argcount = c.co_argcount
117    varnames = c.co_varnames
118    args = varnames[:argcount]
119    locals = f.f_locals
120    globals = f.f_globals
121    output.write(">> %s:%s: %s.%s(%s)\n" % (
122        c.co_filename, f.f_lineno, globals['__name__'], c.co_name,
123        ', '.join(args)))
124
125    localkeys = [l for l in list(f.f_locals.keys())
126                 if not inspect.ismodule(locals[l])]
127    if argcount > 0:
128        output.write("  Params: \n")
129        for var in varnames[:argcount]:
130            if var in locals:
131                val = locals[var]
132                val = _getStringValue(val)
133                localkeys.remove(var)
134            else:
135                val = '<Unknown>'
136
137            output.write("    %s = %s\n" % (var, _saferepr(val)))
138    for hidden in ('__file__', '__name__', '__doc__'):
139        if hidden in localkeys:
140            localkeys.remove(hidden)
141    localkeys.sort()
142    if localkeys:
143        output.write("  Locals: \n")
144        for key in localkeys:
145            if key in locals:
146                val = locals[key]
147                val = _getStringValue(val)
148            else:
149                val = '<Unknown>'
150            output.write("    %s = %r\n" % (key, _saferepr(val)))
151
152
153def _getStringValue(val):
154    try:
155        if isinstance(val, xmlrpc_client.ServerProxy):
156            rval = "<Server Proxy>"
157        elif hasattr(val, 'asString'):
158            rval = val.asString()
159        elif inspect.isclass(val):
160            rval = '<Class %s.%s>' % (val.__module__, val.__name__)
161        elif not hasattr(val, '__str__'):
162            if hasattr(val, '__class__'):
163                rval = '<unprintable of class %s>' % val.__class__
164            else:
165                rval = '<unprintable>'
166        else:
167            rval = val
168        return rval
169    except Exception as e:
170        try:
171            return '<Exception occured while converting %s to string: %s' % (
172                repr(val), e)
173        except Exception as e:
174            return '<Exception occured while converting to repr: %s' % (e,)
175