~launchpad-pqm/launchpad/devel

2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
1
# Copyright 2005 Canonical Ltd.  All rights reserved.
2
3
"""Handlers for warnings, to be installed when testing."""
4
5
__metaclass__ = type
6
7
import atexit
8
import warnings
9
import sys
10
import inspect
11
import StringIO
12
13
class WarningReport:
14
15
    def __init__(self, message, info):
16
        self.message = message
17
        self.info = info
18
19
    def __str__(self):
20
        info = str(self.info)
21
        if info:
22
            return info
23
        else:
24
            return self.message
25
26
class ImportantInfo:
27
28
    def __init__(self, expressiontext, viewclassname, templatefilename,
2419 by Canonical.com Patch Queue Manager
[trivial] improved warning output for warnings that occur in doctests. the now show the offending line of code.
29
        requesturl, viewclassfilename, viewclasslineno, viewclassfunc,
30
        doctestname, doctestline):
2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
31
        self.expressiontext = expressiontext
32
        self.viewclassname = viewclassname
33
        self.viewclassfilename = viewclassfilename
34
        self.viewclasslineno = viewclasslineno
35
        self.templatefilename = templatefilename
36
        self.requesturl = requesturl
37
        self.viewclassfunc = viewclassfunc
2419 by Canonical.com Patch Queue Manager
[trivial] improved warning output for warnings that occur in doctests. the now show the offending line of code.
38
        self.doctestname = doctestname
39
        self.doctestline = doctestline
2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
40
41
    def __str__(self):
42
        L = []
43
        if self.expressiontext:
44
            L.append('The expression: %s in %s' % (
45
                self.expressiontext, self.templatefilename))
46
        if self.viewclassname:
47
            L.append('The method %s.%s' % (
48
                self.viewclassname, self.viewclassfunc))
49
            #L.append('at line %s of file %s' % (
50
            #    self.viewclasslineno, self.viewclassfilename)
2419 by Canonical.com Patch Queue Manager
[trivial] improved warning output for warnings that occur in doctests. the now show the offending line of code.
51
        if self.doctestname:
52
            L.append("The doctest %s, at the line:" % self.doctestname)
53
            L.append("    >>> %s" % self.doctestline)
2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
54
        if self.requesturl:
55
            L.append('request url: %s' % self.requesturl)
56
        return '\n'.join(L)
57
58
# PageTemplateFile has .filename.
59
from zope.pagetemplate.pagetemplatefile import PageTemplateFile
60
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
61
62
# PythonExpr has .text, the text of the expression.
63
from zope.tales.pythonexpr import PythonExpr
64
65
# TrustedZopeContext has self.contexts, a dict with template, view, context,
66
# request, etc.
67
from zope.app.pagetemplate.engine import TrustedZopeContext
68
69
# TALInterpreter has self.sourceFile, a filename of a page template.
70
from zope.tal.talinterpreter import TALInterpreter
71
72
from zope.app.pagetemplate.simpleviewclass import simple
73
74
def find_important_info():
75
    stack = inspect.stack()
76
    try:
77
        important_classes = set([
78
            PythonExpr,
79
            TrustedZopeContext,
80
            TALInterpreter,
81
            ViewPageTemplateFile,
82
            simple
83
            ])
84
        important_objects = {}
85
        metadata = {}  # cls -> (filename, lineno, funcname)
86
87
        for frame, filename, lineno, func_name, context, lineidx in stack:
88
            try:
2419 by Canonical.com Patch Queue Manager
[trivial] improved warning output for warnings that occur in doctests. the now show the offending line of code.
89
                if (filename.startswith('<doctest ') and
90
                    "doctest" not in important_objects):
91
                    # Very fragile inspection of the state of the doctest
92
                    # runner.  So, enclosed in a try-except so it will at
93
                    # least fail gracefully if it fails.
94
                    try:
95
                        line = frame.f_back.f_locals['example'].source
96
                    except KeyboardInterrupt:
97
                        pass
98
                    except Exception:
99
                        line = "# cannot get line of code"
100
                    important_objects["doctest"] = (filename, line)
101
                    metadata["doctest"] = (filename, lineno, func_name)
2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
102
                if 'self' in frame.f_locals:
103
                    fself = frame.f_locals['self']
104
                    ftype = type(fself)
105
                    for cls in list(important_classes):
106
                        if isinstance(fself, cls):
107
                            important_objects[cls] = fself
108
                            metadata[cls] = (filename, lineno, func_name)
109
                            important_classes.remove(cls)
110
            finally:
111
                del frame
112
    finally:
113
        del stack
114
115
    expressiontext = ''
116
    if PythonExpr in important_objects:
117
        expressiontext = important_objects[PythonExpr].text
118
119
    viewclassname = ''
120
    viewclassfilename = ''
121
    viewclasslineno = ''
122
    viewclassfunc = ''
2419 by Canonical.com Patch Queue Manager
[trivial] improved warning output for warnings that occur in doctests. the now show the offending line of code.
123
    doctestname = ''
124
    doctestline = ''
2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
125
    if simple in important_objects:
126
        cls = important_objects[simple].__class__
127
        if cls is not simple:
128
            viewclassname = cls.__mro__[1].__name__
129
            viewclassfilename, viewclasslineno, viewclassfunc = (
130
                metadata[simple])
131
132
    templatefilename = ''
133
    if ViewPageTemplateFile in important_objects:
134
        templatefilename = important_objects[ViewPageTemplateFile].filename
135
        templatefilename = templatefilename.split('/')[-1]
136
137
    requesturl = ''
138
    if TrustedZopeContext in important_objects:
139
        ptcontexts = important_objects[TrustedZopeContext].contexts
140
        requesturl = ptcontexts['request'].getURL()
141
2419 by Canonical.com Patch Queue Manager
[trivial] improved warning output for warnings that occur in doctests. the now show the offending line of code.
142
    if "doctest" in important_objects:
143
        doctestname, doctestline = important_objects["doctest"]
2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
144
    return ImportantInfo(expressiontext, viewclassname, templatefilename,
2419 by Canonical.com Patch Queue Manager
[trivial] improved warning output for warnings that occur in doctests. the now show the offending line of code.
145
        requesturl, viewclassfilename, viewclasslineno, viewclassfunc,
146
        doctestname, doctestline)
2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
147
148
need_page_titles = []
149
no_order_by = []
150
other_warnings = []
151
152
old_show_warning = warnings.showwarning
153
def launchpad_showwarning(message, category, filename, lineno, file=None):
154
    if file is None:
155
        file = sys.stderr
156
    stream = StringIO.StringIO()
157
    old_show_warning(message, category, filename, lineno, stream)
158
    warning_message = stream.getvalue()
159
    important_info = find_important_info()
160
161
    if isinstance(message, UserWarning):
162
        args = message.args
163
        if args:
164
            arg = args[0]
165
            if arg.startswith('No page title in '):
166
                global need_page_titles
167
                need_page_titles.append(arg)
168
                return
169
            if arg == 'Getting a slice of an unordered set is unpredictable.':
170
                # find the page template and view class, if any
171
                # show these, plus the request.
172
                global no_order_by
173
                no_order_by.append(
174
                    WarningReport(warning_message, important_info)
175
                    )
176
                return
177
    other_warnings.append(WarningReport(warning_message, important_info))
178
179
def report_need_page_titles():
180
    global need_page_titles
181
    if need_page_titles:
182
        print
183
        print "The following pages need titles."
184
        for message in need_page_titles:
185
            print "   ", message
186
187
def report_no_order_by():
188
    global no_order_by
189
    if no_order_by:
190
        print
191
        print ("The following code has issues with"
192
               " ambiguous select results ordering.")
193
        for report in no_order_by:
194
            print
195
            print report
196
197
def report_other_warnings():
198
    global other_warnings
199
    if other_warnings:
200
        print
2482 by Canonical.com Patch Queue Manager
r=kiko + some [trivial] various menus landings.
201
        print "General warnings."
2103 by Canonical.com Patch Queue Manager
[trivial] improved warning output for tests, and fixed a bunch of ambiguous use of select results warnings. some xxx comments left in person code. one untested codepath in hct backend.
202
        for warninginfo in other_warnings:
203
            print
204
            print warninginfo.message,
205
            print warninginfo
206
207
def report_warnings():
208
    report_need_page_titles()
209
    report_no_order_by()
210
    report_other_warnings()
211
212
def install_warning_handler():
213
    warnings.showwarning = launchpad_showwarning
214
    atexit.register(report_warnings)